#include some headers we had forgotten to add previously, also added
[platform/upstream/evolution-data-server.git] / camel / camel-mime-filter-enriched.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
4  *
5  *  Copyright 2002 Ximian, Inc. (www.ximian.com)
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
20  *
21  */
22
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <stdio.h>
29 #include <string.h>
30
31 #include "string-utils.h"
32
33 #include "camel-mime-filter-enriched.h"
34
35 static struct {
36         char *enriched;
37         char *html;
38 } enriched_tags[] = {
39         { "bold",        "<b>"              },
40         { "/bold",       "</b>"             },
41         { "italic",      "<i>"              },
42         { "/italic",     "</i>"             },
43         { "fixed",       "<tt>"             },
44         { "/fixed",      "</tt>"            },
45         { "smaller",     "<font size=-1>"   },
46         { "/smaller",    "</font>"          },
47         { "bigger",      "<font size=+1>"   },
48         { "/bigger",     "</font>"          },
49         { "underline",   "<u>"              },
50         { "/underline",  "</u>"             },
51         { "center",      "<p align=center>" },
52         { "/center",     "</p>"             },
53         { "flushleft",   "<p align=left>"   },
54         { "/flushleft",  "</p>"             },
55         { "flushright",  "<p align=right>"  },
56         { "/flushright", "</p>"             },
57         { "excerpt",     "<blockquote>"     },
58         { "/excerpt",    "</blockquote>"    },
59         { "paragraph",   "<p>"              },
60         { "signature",   "<address>"        },
61         { "/signature",  "</address>"       },
62         { "comment",     "<!-- "            },
63         { "/comment",    " -->"             },
64         { "param",       "<!-- "            },
65         { "/param",      " -->"             },
66         { "np",          "<hr>"             }
67 };
68
69 #define NUM_ENRICHED_TAGS (sizeof (enriched_tags) / sizeof (enriched_tags[0]))
70
71 static GHashTable *enriched_hash = NULL;
72
73
74 static void camel_mime_filter_enriched_class_init (CamelMimeFilterEnrichedClass *klass);
75 static void camel_mime_filter_enriched_init       (CamelMimeFilterEnriched *filter);
76 static void camel_mime_filter_enriched_finalize   (CamelObject *obj);
77
78 static void filter_filter (CamelMimeFilter *filter, char *in, size_t len, size_t prespace,
79                            char **out, size_t *outlen, size_t *outprespace);
80 static void filter_complete (CamelMimeFilter *filter, char *in, size_t len, size_t prespace,
81                              char **out, size_t *outlen, size_t *outprespace);
82 static void filter_reset (CamelMimeFilter *filter);
83
84
85 static CamelMimeFilterClass *parent_class = NULL;
86
87
88 CamelType
89 camel_mime_filter_enriched_get_type (void)
90 {
91         static CamelType type = CAMEL_INVALID_TYPE;
92         
93         if (type == CAMEL_INVALID_TYPE) {
94                 type = camel_type_register (camel_mime_filter_get_type (),
95                                             "CamelMimeFilterEnriched",
96                                             sizeof (CamelMimeFilterEnriched),
97                                             sizeof (CamelMimeFilterEnrichedClass),
98                                             (CamelObjectClassInitFunc) camel_mime_filter_enriched_class_init,
99                                             NULL,
100                                             (CamelObjectInitFunc) camel_mime_filter_enriched_init,
101                                             (CamelObjectFinalizeFunc) camel_mime_filter_enriched_finalize);
102         }
103         
104         return type;
105 }
106
107 static void
108 camel_mime_filter_enriched_class_init (CamelMimeFilterEnrichedClass *klass)
109 {
110         CamelMimeFilterClass *filter_class = (CamelMimeFilterClass *) klass;
111         int i;
112         
113         parent_class = CAMEL_MIME_FILTER_CLASS (camel_mime_filter_get_type ());
114         
115         filter_class->reset = filter_reset;
116         filter_class->filter = filter_filter;
117         filter_class->complete = filter_complete;
118         
119         if (!enriched_hash) {
120                 enriched_hash = g_hash_table_new (g_strcase_hash, g_strcase_equal);
121                 for (i = 0; i < NUM_ENRICHED_TAGS; i++)
122                         g_hash_table_insert (enriched_hash, enriched_tags[i].enriched,
123                                              enriched_tags[i].html);
124         }
125 }
126
127 static void
128 camel_mime_filter_enriched_finalize (CamelObject *obj)
129 {
130         ;
131 }
132
133 static void
134 camel_mime_filter_enriched_init (CamelMimeFilterEnriched *filter)
135 {
136         filter->flags = 0;
137         filter->nofill = 0;
138 }
139
140 #define IS_RICHTEXT CAMEL_MIME_FILTER_ENRICHED_IS_RICHTEXT
141
142 static void
143 enriched_to_html (CamelMimeFilter *filter, char *in, size_t inlen, size_t prespace,
144                   char **out, size_t *outlen, size_t *outprespace, gboolean flush)
145 {
146         CamelMimeFilterEnriched *enriched = (CamelMimeFilterEnriched *) filter;
147         const char *tag, *inend, *outend;
148         register const char *inptr;
149         register char *outptr;
150         
151         camel_mime_filter_set_size (filter, inlen * 2 + 6, FALSE);
152         
153         inptr = in;
154         inend = in + inlen;
155         outptr = filter->outbuf;
156         outend = filter->outbuf + filter->outsize;
157         
158  loop:
159         do {
160                 while (inptr < inend && outptr < outend && !strchr (" <>&\n", *inptr))
161                         *outptr++ = *inptr++;
162                 
163                 if (outptr == outend)
164                         goto backup;
165                 
166                 if ((inptr + 1) >= inend)
167                         break;
168                 
169                 switch (*inptr++) {
170                 case ' ':
171                         while (inptr < inend && (outptr + 7) < outend && *inptr == ' ') {
172                                 memcpy (outptr, "&nbsp;", 6);
173                                 outptr += 6;
174                                 inptr++;
175                         }
176                         
177                         if (outptr < outend)
178                                 *outptr++ = ' ';
179                         
180                         break;
181                 case '\n':
182                         if (!(enriched->flags & IS_RICHTEXT) && enriched->nofill <= 0) {
183                                 /* text/enriched */
184                                 while (inptr < inend && (outptr + 4) < outend && *inptr == '\n') {
185                                         memcpy (outptr, "<br>", 4);
186                                         outptr += 4;
187                                         inptr++;
188                                 }
189                         } else {
190                                 /* text/richtext */
191                                 *outptr++ = ' ';
192                         }
193                         break;
194                 case '>':
195                         if ((outptr + 4) < outend) {
196                                 memcpy (outptr, "&gt;", 4);
197                                 outptr += 4;
198                         } else {
199                                 inptr--;
200                                 goto backup;
201                         }
202                         break;
203                 case '&':
204                         if ((outptr + 5) < outend) {
205                                 memcpy (outptr, "&amp;", 5);
206                                 outptr += 5;
207                         } else {
208                                 inptr--;
209                                 goto backup;
210                         }
211                         break;
212                 case '<':
213                         if (!(enriched->flags & IS_RICHTEXT)) {
214                                 /* text/enriched */
215                                 if ((outptr + 4) < outend && *inptr == '<') {
216                                         memcpy (outptr, "&lt;", 4);
217                                         outptr += 4;
218                                         inptr++;
219                                         break;
220                                 } else {
221                                         inptr--;
222                                         goto backup;
223                                 }
224                         } else {
225                                 /* text/richtext */
226                                 if ((inend - inptr) >= 3 && (outptr + 4) < outend) {
227                                         if (strncmp (inptr, "lt>", 3) == 0) {
228                                                 memcpy (outptr, "&lt;", 4);
229                                                 outptr += 4;
230                                                 inptr += 3;
231                                                 break;
232                                         } else if (strncmp (inptr, "nl>", 3) == 0) {
233                                                 memcpy (outptr, "<br>", 4);
234                                                 outptr += 4;
235                                                 inptr += 3;
236                                                 break;
237                                         }
238                                 } else {
239                                         inptr--;
240                                         goto backup;
241                                 }
242                         }
243                         
244                         tag = inptr;
245                         while (inptr < inend && *inptr != '>')
246                                 inptr++;
247                         
248                         if (inptr == inend) {
249                                 inptr = tag - 1;
250                                 goto need_input;
251                         }
252                         
253                         if (!strncmp (tag, "nofill>", 7)) {
254                                 if ((outptr + 5) < outend) {
255                                         memcpy (outptr, "<pre>", 5);
256                                         enriched->nofill++;
257                                         outptr += 5;
258                                 } else {
259                                         inptr = tag - 1;
260                                         goto backup;
261                                 }
262                         } else if (!strncmp (tag, "/nofill>", 8)) {
263                                 if ((outptr + 6) < outend) {
264                                         memcpy (outptr, "</pre>", 6);
265                                         enriched->nofill--;
266                                         outptr += 6;
267                                 } else {
268                                         inptr = tag - 1;
269                                         goto backup;
270                                 }
271                         } else {
272                                 const char *html_tag;
273                                 char *enriched_tag;
274                                 int len;
275                                 
276                                 enriched_tag = g_strndup (tag, (inptr - tag));
277                                 html_tag = g_hash_table_lookup (enriched_hash, enriched_tag);
278                                 g_free (enriched_tag);
279                                 if (html_tag) {
280                                         len = strlen (html_tag);
281                                         if ((outptr + len) < outend) {
282                                                 memcpy (outptr, html_tag, len);
283                                                 outptr += len;
284                                         } else {
285                                                 inptr = tag - 1;
286                                                 goto backup;
287                                         }
288                                 }
289                         }
290                         
291                         inptr++;
292                         break;
293                 default:
294                         break;
295                 }
296         } while (inptr < inend);
297         
298  need_input:
299         
300         /* the reason we ignore @flush here is because if there isn't
301            enough input to parse a tag, then there's nothing we can
302            do. */
303         
304         if (inptr < inend)
305                 camel_mime_filter_backup (filter, inptr, (unsigned) (inend - inptr));
306         
307         *out = filter->outbuf;
308         *outlen = outptr - filter->outbuf;
309         *outprespace = filter->outpre;
310         
311         return;
312         
313  backup:
314         
315         if (flush) {
316                 size_t offset, grow;
317                 
318                 grow = (inend - inptr) * 2 + 20;
319                 offset = outptr - filter->outbuf;
320                 camel_mime_filter_set_size (filter, filter->outsize + grow, TRUE);
321                 outend = filter->outbuf + filter->outsize;
322                 outptr = filter->outbuf + offset;
323                 
324                 goto loop;
325         } else {
326                 camel_mime_filter_backup (filter, inptr, (unsigned) (inend - inptr));
327         }
328         
329         *out = filter->outbuf;
330         *outlen = outptr - filter->outbuf;
331         *outprespace = filter->outpre;
332 }
333
334 static void
335 filter_filter (CamelMimeFilter *filter, char *in, size_t len, size_t prespace,
336                char **out, size_t *outlen, size_t *outprespace)
337 {
338         enriched_to_html (filter, in, len, prespace, out, outlen, outprespace, FALSE);
339 }
340
341 static void 
342 filter_complete (CamelMimeFilter *filter, char *in, size_t len, size_t prespace,
343                  char **out, size_t *outlen, size_t *outprespace)
344 {
345         enriched_to_html (filter, in, len, prespace, out, outlen, outprespace, TRUE);
346 }
347
348 static void
349 filter_reset (CamelMimeFilter *filter)
350 {
351         CamelMimeFilterEnriched *enriched = (CamelMimeFilterEnriched *) filter;
352         
353         enriched->nofill = 0;
354 }
355
356
357 /**
358  * camel_mime_filter_enriched_new:
359  * @flags:
360  *
361  * Creates a new CamelMimeFilterEnriched object.
362  *
363  * Returns a new CamelMimeFilter object.
364  **/
365 CamelMimeFilter *
366 camel_mime_filter_enriched_new (guint32 flags)
367 {
368         CamelMimeFilterEnriched *new;
369         int i;
370         
371         new = (CamelMimeFilterEnriched *) camel_object_new (CAMEL_TYPE_MIME_FILTER_ENRICHED);
372         new->flags = flags;
373         
374         return CAMEL_MIME_FILTER (new);
375 }