1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Authors: Jeffrey Stedfast <fejj@ximian.com>
5 * Copyright 2001 Ximian, Inc. (www.ximian.com)
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser 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.
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 Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
31 #include "camel-mime-filter-tohtml.h"
32 #include "camel-url-scanner.h"
33 #include "camel-utf8.h"
36 * TODO: convert common text/plain 'markup' to html. eg.:
38 * _word_ -> <u>_word_</u>
39 * *word* -> <b>*word*</b>
40 * /word/ -> <i>/word/</i>
45 #define FOOLISHLY_UNMUNGE_FROM 0
47 #define CONVERT_WEB_URLS CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS
48 #define CONVERT_ADDRSPEC CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES
54 { CONVERT_WEB_URLS, { "file://", "", camel_url_file_start, camel_url_file_end } },
55 { CONVERT_WEB_URLS, { "ftp://", "", camel_url_web_start, camel_url_web_end } },
56 { CONVERT_WEB_URLS, { "sftp://", "", camel_url_web_start, camel_url_web_end } },
57 { CONVERT_WEB_URLS, { "http://", "", camel_url_web_start, camel_url_web_end } },
58 { CONVERT_WEB_URLS, { "https://", "", camel_url_web_start, camel_url_web_end } },
59 { CONVERT_WEB_URLS, { "news://", "", camel_url_web_start, camel_url_web_end } },
60 { CONVERT_WEB_URLS, { "nntp://", "", camel_url_web_start, camel_url_web_end } },
61 { CONVERT_WEB_URLS, { "telnet://", "", camel_url_web_start, camel_url_web_end } },
62 { CONVERT_WEB_URLS, { "webcal://", "", camel_url_web_start, camel_url_web_end } },
63 { CONVERT_WEB_URLS, { "mailto:", "", camel_url_web_start, camel_url_web_end } },
64 { CONVERT_WEB_URLS, { "callto:", "", camel_url_web_start, camel_url_web_end } },
65 { CONVERT_WEB_URLS, { "h323:", "", camel_url_web_start, camel_url_web_end } },
66 { CONVERT_WEB_URLS, { "sip:", "", camel_url_web_start, camel_url_web_end } },
67 { CONVERT_WEB_URLS, { "www.", "http://", camel_url_web_start, camel_url_web_end } },
68 { CONVERT_WEB_URLS, { "ftp.", "ftp://", camel_url_web_start, camel_url_web_end } },
69 { CONVERT_ADDRSPEC, { "@", "mailto:", camel_url_addrspec_start, camel_url_addrspec_end } },
72 #define NUM_URL_PATTERNS (sizeof (patterns) / sizeof (patterns[0]))
74 static void camel_mime_filter_tohtml_class_init (CamelMimeFilterToHTMLClass *klass);
75 static void camel_mime_filter_tohtml_init (CamelMimeFilterToHTML *filter);
76 static void camel_mime_filter_tohtml_finalize (CamelObject *obj);
78 static CamelMimeFilterClass *camel_mime_filter_tohtml_parent;
82 camel_mime_filter_tohtml_get_type (void)
84 static CamelType type = CAMEL_INVALID_TYPE;
86 if (type == CAMEL_INVALID_TYPE) {
87 type = camel_type_register (camel_mime_filter_get_type (),
88 "CamelMimeFilterToHTML",
89 sizeof (CamelMimeFilterToHTML),
90 sizeof (CamelMimeFilterToHTMLClass),
91 (CamelObjectClassInitFunc) camel_mime_filter_tohtml_class_init,
93 (CamelObjectInitFunc) camel_mime_filter_tohtml_init,
94 (CamelObjectFinalizeFunc) camel_mime_filter_tohtml_finalize);
101 camel_mime_filter_tohtml_finalize (CamelObject *obj)
103 CamelMimeFilterToHTML *filter = (CamelMimeFilterToHTML *) obj;
105 camel_url_scanner_free (filter->scanner);
109 camel_mime_filter_tohtml_init (CamelMimeFilterToHTML *filter)
111 filter->scanner = camel_url_scanner_new ();
116 filter->pre_open = FALSE;
121 check_size (CamelMimeFilter *filter, char *outptr, char **outend, size_t len)
125 if (*outend - outptr >= len)
128 offset = outptr - filter->outbuf;
130 camel_mime_filter_set_size (filter, filter->outsize + len, TRUE);
132 *outend = filter->outbuf + filter->outsize;
134 return filter->outbuf + offset;
138 citation_depth (const char *in)
140 register const char *inptr = in;
146 #if FOOLISHLY_UNMUNGE_FROM
147 /* check that it isn't an escaped From line */
148 if (!strncmp (inptr, "From", 4))
152 while (*inptr != '\n') {
166 writeln (CamelMimeFilter *filter, const char *in, const char *inend, char *outptr, char **outend)
168 CamelMimeFilterToHTML *html = (CamelMimeFilterToHTML *) filter;
169 const char *inptr = in;
171 while (inptr < inend) {
174 outptr = check_size (filter, outptr, outend, 16);
176 u = camel_utf8_getc_limit (&inptr, (const unsigned char *) inend);
179 g_warning("Truncated utf8 buffer");
182 outptr = g_stpcpy (outptr, "<");
186 outptr = g_stpcpy (outptr, ">");
190 outptr = g_stpcpy (outptr, "&");
194 outptr = g_stpcpy (outptr, """);
198 if (html->flags & (CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES)) {
200 outptr = check_size (filter, outptr, outend, 7);
201 outptr = g_stpcpy (outptr, " ");
203 } while (html->column % 8);
206 /* otherwise, FALL THROUGH */
208 if (html->flags & CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES
209 && ((inptr == (in + 1) || (inptr < inend && (*inptr == ' ' || *inptr == '\t'))))) {
210 outptr = g_stpcpy (outptr, " ");
214 /* otherwise, FALL THROUGH */
216 if (u >= 20 && u <0x80)
219 if (html->flags & CAMEL_MIME_FILTER_TOHTML_ESCAPE_8BIT)
222 outptr += sprintf(outptr, "&#%u;", u);
233 html_convert (CamelMimeFilter *filter, char *in, size_t inlen, size_t prespace,
234 char **out, size_t *outlen, size_t *outprespace, gboolean flush)
236 CamelMimeFilterToHTML *html = (CamelMimeFilterToHTML *) filter;
237 register char *inptr, *outptr;
238 char *start, *outend;
243 if (html->pre_open) {
244 /* close the pre-tag */
245 outend = filter->outbuf + filter->outsize;
246 outptr = check_size (filter, filter->outbuf, &outend, 10);
247 outptr = g_stpcpy (outptr, "</pre>");
248 html->pre_open = FALSE;
250 *out = filter->outbuf;
251 *outlen = outptr - filter->outbuf;
252 *outprespace = filter->outpre;
262 camel_mime_filter_set_size (filter, inlen * 2 + 6, FALSE);
266 outptr = filter->outbuf;
267 outend = filter->outbuf + filter->outsize;
269 if (html->flags & CAMEL_MIME_FILTER_TOHTML_PRE && !html->pre_open) {
270 outptr = g_stpcpy (outptr, "<pre>");
271 html->pre_open = TRUE;
276 while (inptr < inend && *inptr != '\n')
279 if (inptr >= inend && !flush)
285 if (html->flags & CAMEL_MIME_FILTER_TOHTML_MARK_CITATION) {
286 if ((depth = citation_depth (start)) > 0) {
287 /* FIXME: we could easily support multiple colour depths here */
289 outptr = check_size (filter, outptr, &outend, 25);
290 outptr += sprintf(outptr, "<font color=\"#%06x\">", (html->colour & 0xffffff));
292 #if FOOLISHLY_UNMUNGE_FROM
293 else if (*start == '>') {
298 } else if (html->flags & CAMEL_MIME_FILTER_TOHTML_CITE) {
299 outptr = check_size (filter, outptr, &outend, 6);
300 outptr = g_stpcpy (outptr, "> ");
304 #define CONVERT_URLS (CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS | CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES)
305 if (html->flags & CONVERT_URLS) {
306 size_t matchlen, buflen, len;
312 if (camel_url_scanner_scan (html->scanner, start, len, &match)) {
313 /* write out anything before the first regex match */
314 outptr = writeln (filter, start, start + match.um_so,
317 start += match.um_so;
320 matchlen = match.um_eo - match.um_so;
322 buflen = 20 + strlen (match.prefix) + matchlen + matchlen;
323 outptr = check_size (filter, outptr, &outend, buflen);
325 /* write out the href tag */
326 outptr = g_stpcpy (outptr, "<a href=\"");
327 outptr = g_stpcpy (outptr, match.prefix);
328 memcpy (outptr, start, matchlen);
330 outptr = g_stpcpy (outptr, "\">");
332 /* now write the matched string */
333 memcpy (outptr, start, matchlen);
334 html->column += matchlen;
339 /* close the href tag */
340 outptr = g_stpcpy (outptr, "</a>");
342 /* nothing matched so write out the remainder of this line buffer */
343 outptr = writeln (filter, start, start + len, outptr, &outend);
348 outptr = writeln (filter, start, inptr, outptr, &outend);
351 if ((html->flags & CAMEL_MIME_FILTER_TOHTML_MARK_CITATION) && depth > 0) {
352 outptr = check_size (filter, outptr, &outend, 8);
353 outptr = g_stpcpy (outptr, "</font>");
357 if (html->flags & CAMEL_MIME_FILTER_TOHTML_CONVERT_NL) {
358 outptr = check_size (filter, outptr, &outend, 5);
359 outptr = g_stpcpy (outptr, "<br>");
366 } while (inptr < inend);
369 /* flush the rest of our input buffer */
371 outptr = writeln (filter, start, inend, outptr, &outend);
373 if (html->pre_open) {
374 /* close the pre-tag */
375 outptr = check_size (filter, outptr, &outend, 10);
376 outptr = g_stpcpy (outptr, "</pre>");
378 } else if (start < inend) {
380 camel_mime_filter_backup (filter, start, (unsigned) (inend - start));
383 *out = filter->outbuf;
384 *outlen = outptr - filter->outbuf;
385 *outprespace = filter->outpre;
389 filter_filter (CamelMimeFilter *filter, char *in, size_t len, size_t prespace,
390 char **out, size_t *outlen, size_t *outprespace)
392 html_convert (filter, in, len, prespace, out, outlen, outprespace, FALSE);
396 filter_complete (CamelMimeFilter *filter, char *in, size_t len, size_t prespace,
397 char **out, size_t *outlen, size_t *outprespace)
399 html_convert (filter, in, len, prespace, out, outlen, outprespace, TRUE);
403 filter_reset (CamelMimeFilter *filter)
405 CamelMimeFilterToHTML *html = (CamelMimeFilterToHTML *) filter;
408 html->pre_open = FALSE;
412 camel_mime_filter_tohtml_class_init (CamelMimeFilterToHTMLClass *klass)
414 CamelMimeFilterClass *filter_class = (CamelMimeFilterClass *) klass;
416 camel_mime_filter_tohtml_parent = CAMEL_MIME_FILTER_CLASS (camel_type_get_global_classfuncs (camel_mime_filter_get_type ()));
418 filter_class->reset = filter_reset;
419 filter_class->filter = filter_filter;
420 filter_class->complete = filter_complete;
425 * camel_mime_filter_tohtml_new:
426 * @flags: bitwise flags defining the behaviour
427 * @colour: colour to use when highlighting quoted text
429 * Create a new #CamelMimeFilterToHTML object to convert plain text
432 * Returns a new #CamelMimeFilterToHTML object
435 camel_mime_filter_tohtml_new (guint32 flags, guint32 colour)
437 CamelMimeFilterToHTML *new;
440 new = CAMEL_MIME_FILTER_TOHTML (camel_object_new (camel_mime_filter_tohtml_get_type ()));
443 new->colour = colour;
445 for (i = 0; i < NUM_URL_PATTERNS; i++) {
446 if (patterns[i].mask & flags)
447 camel_url_scanner_add (new->scanner, &patterns[i].pattern);
450 return CAMEL_MIME_FILTER (new);
455 * camel_text_to_html:
457 * @flags: bitwise flags defining the html conversion behaviour
458 * @colour: colour to use when syntax highlighting
460 * Convert @in from plain text into HTML.
462 * Returns a newly allocated string containing the HTMLified version
466 camel_text_to_html (const char *in, guint32 flags, guint32 colour)
468 CamelMimeFilter *filter;
469 size_t outlen, outpre;
472 g_return_val_if_fail (in != NULL, NULL);
474 filter = camel_mime_filter_tohtml_new (flags, colour);
476 camel_mime_filter_complete (filter, (char *) in, strlen (in), 0,
477 &outbuf, &outlen, &outpre);
479 outbuf = g_strndup (outbuf, outlen);
481 camel_object_unref (filter);