aboutsummaryrefslogtreecommitdiffstats
path: root/camel
diff options
context:
space:
mode:
Diffstat (limited to 'camel')
-rw-r--r--camel/ChangeLog5
-rw-r--r--camel/Makefile.am2
-rw-r--r--camel/camel-mime-filter-tohtml.c520
-rw-r--r--camel/camel-mime-filter-tohtml.h75
4 files changed, 602 insertions, 0 deletions
diff --git a/camel/ChangeLog b/camel/ChangeLog
index 7d8610c4bd..2d0cb690ee 100644
--- a/camel/ChangeLog
+++ b/camel/ChangeLog
@@ -1,5 +1,10 @@
2001-11-16 Jeffrey Stedfast <fejj@ximian.com>
+ * camel-mime-filter-tohtml.c (camel_mime_filter_tohtml_new): New
+ mime filter to convert plain text to html.
+
+2001-11-16 Jeffrey Stedfast <fejj@ximian.com>
+
Since some mail clients like Outlook are broken, we need to set a
name parameter on pgp signed and encrypted parts.
diff --git a/camel/Makefile.am b/camel/Makefile.am
index 38c0cb12c9..dc6ae18406 100644
--- a/camel/Makefile.am
+++ b/camel/Makefile.am
@@ -52,6 +52,7 @@ libcamel_la_SOURCES = \
camel-mime-filter-crlf.c \
camel-mime-filter-from.c \
camel-mime-filter-html.c \
+ camel-mime-filter-tohtml.c \
camel-mime-filter-index.c \
camel-mime-filter-linewrap.c \
camel-mime-filter-save.c \
@@ -137,6 +138,7 @@ libcamelinclude_HEADERS = \
camel-mime-filter-crlf.h \
camel-mime-filter-from.h \
camel-mime-filter-html.h \
+ camel-mime-filter-tohtml.h \
camel-mime-filter-index.h \
camel-mime-filter-linewrap.h \
camel-mime-filter-save.h \
diff --git a/camel/camel-mime-filter-tohtml.c b/camel/camel-mime-filter-tohtml.c
new file mode 100644
index 0000000000..22dc3ff737
--- /dev/null
+++ b/camel/camel-mime-filter-tohtml.c
@@ -0,0 +1,520 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright 2001 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "camel-mime-filter-tohtml.h"
+
+#define d(x)
+
+static void camel_mime_filter_tohtml_class_init (CamelMimeFilterToHTMLClass *klass);
+static void camel_mime_filter_tohtml_init (CamelObject *o);
+static void camel_mime_filter_tohtml_finalize (CamelObject *o);
+
+static CamelMimeFilterClass *camel_mime_filter_tohtml_parent;
+
+
+CamelType
+camel_mime_filter_tohtml_get_type (void)
+{
+ static CamelType type = CAMEL_INVALID_TYPE;
+
+ if (type == CAMEL_INVALID_TYPE) {
+ type = camel_type_register (camel_mime_filter_get_type (),
+ "CamelMimeFilterToHTML",
+ sizeof (CamelMimeFilterToHTML),
+ sizeof (CamelMimeFilterToHTMLClass),
+ (CamelObjectClassInitFunc) camel_mime_filter_tohtml_class_init,
+ NULL,
+ (CamelObjectInitFunc) camel_mime_filter_tohtml_init,
+ (CamelObjectFinalizeFunc) camel_mime_filter_tohtml_finalize);
+ }
+
+ return type;
+}
+
+static void
+camel_mime_filter_tohtml_finalize (CamelObject *o)
+{
+ ;
+}
+
+static void
+camel_mime_filter_tohtml_init (CamelObject *o)
+{
+ ;
+}
+
+
+static char *
+check_size (CamelMimeFilter *filter, char *outptr, char **outend, size_t len)
+{
+ size_t offset;
+
+ if (*outend - outptr >= len)
+ return outptr;
+
+ offset = outptr - filter->outbuf;
+
+ camel_mime_filter_set_size (filter, filter->outsize + len, TRUE);
+
+ *outend = filter->outbuf + filter->outsize;
+
+ return filter->outbuf + offset;
+}
+
+/* 1 = non-email-address chars: "()<>@,;:\\\"/[]`'|\n\t " */
+/* 2 = non-url chars: "()<>,;\\\"[]`'|\n\t " */
+/* 3 = trailing url garbage: ",.!?;:>)]}\\`'-_|\n\t " */
+static unsigned short special_chars[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 7, 4, 3, 0, 0, 0, 0, 7, 3, 7, 0, 0, 7, 4, 4, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 7, 3, 0, 7, 4,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 7, 3, 0, 4,
+ 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 4, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+
+#define IS_NON_ADDR (1 << 0)
+#define IS_NON_URL (1 << 1)
+#define IS_GARBAGE (1 << 2)
+
+#define NON_EMAIL_CHARS "()<>@,;:\\\"/[]`'|\n\t "
+#define NON_URL_CHARS "()<>,;\\\"[]`'|\n\t "
+#define TRAILING_URL_GARBAGE ",.!?;:>)}\\`'-_|\n\t "
+
+#define is_addr_char(c) (isprint (c) && !(special_chars[(unsigned char) c] & IS_NON_ADDR))
+#define is_url_char(c) (isprint (c) && !(special_chars[(unsigned char) c] & IS_NON_URL))
+#define is_trailing_garbage(c) (!isprint (c) || (special_chars[(unsigned char) c] & IS_GARBAGE))
+
+#if 0
+static void
+table_init (void)
+{
+ char *c;
+
+ memset (special_chars, 0, sizeof (special_chars));
+ for (c = NON_EMAIL_CHARS; *c; c++)
+ special_chars[(int) *c] |= IS_NON_ADDR;
+ for (c = NON_URL_CHARS; *c; c++)
+ special_chars[(int) *c] |= IS_NON_URL;
+ for (c = TRAILING_URL_GARBAGE; *c; c++)
+ special_chars[(int) *c] |= IS_GARBAGE;
+}
+#endif
+
+static char *
+url_extract (char **in, int inlen, gboolean check, gboolean *backup)
+{
+ unsigned char *inptr, *inend, *p;
+ char *url;
+
+ inptr = (unsigned char *) *in;
+ inend = inptr + inlen;
+
+ while (inptr < inend && is_url_char (*inptr))
+ inptr++;
+
+ if ((char *) inptr == *in)
+ return NULL;
+
+ /* back up if we probably went too far. */
+ while (inptr > (unsigned char *) *in && is_trailing_garbage (*(inptr - 1)))
+ inptr--;
+
+ if (check) {
+ /* make sure we weren't fooled. */
+ p = memchr (*in, ':', (char *) inptr - *in);
+ if (!p)
+ return NULL;
+ }
+
+ if (inptr == inend && backup) {
+ *backup = TRUE;
+ return NULL;
+ }
+
+ url = g_strndup (*in, (char *) inptr - *in);
+ *in = inptr;
+
+ return url;
+}
+
+static char *
+email_address_extract (char **in, char *inend, char *start, char **outptr, gboolean *backup)
+{
+ char *addr, *pre, *end, *dot;
+
+ /* *in points to the '@'. Look backward for a valid local-part */
+ for (pre = *in; pre - 1 >= start && is_addr_char (*(pre - 1)); pre--);
+
+ if (pre == *in)
+ return NULL;
+
+ /* Now look forward for a valid domain part */
+ for (end = *in + 1, dot = NULL; end < inend && is_addr_char (*end); end++) {
+ if (*end == '.' && !dot)
+ dot = end;
+ }
+
+ if (end >= inend && backup) {
+ *backup = TRUE;
+ *outptr -= (*in - pre);
+ *in = pre;
+ return NULL;
+ }
+
+ if (!dot)
+ return NULL;
+
+ /* Remove trailing garbage */
+ while (end > *in && is_trailing_garbage (*(end - 1)))
+ end--;
+ if (dot > end)
+ return NULL;
+
+ addr = g_strndup (pre, end - pre);
+ *outptr -= (*in - pre);
+ *in = end;
+
+ return addr;
+}
+
+static gboolean
+is_citation (char *inptr, char *inend, gboolean saw_citation, gboolean *backup)
+{
+ if (*inptr != '>')
+ return FALSE;
+
+ if (inend - inptr >= 6) {
+ /* make sure this isn't just mbox From-magling... */
+ if (strncmp (inptr, ">From ", 6) != 0)
+ return TRUE;
+ } else if (backup) {
+ /* we don't have enough data to tell, so return */
+ *backup = TRUE;
+ return saw_citation;
+ }
+
+ /* if the previous line was a citation, then say this one is too */
+ if (saw_citation)
+ return TRUE;
+
+ /* otherwise it was just an isolated ">From " line */
+ return FALSE;
+}
+
+static gboolean
+is_protocol (char *inptr, char *inend, gboolean *backup)
+{
+ if (inend - inptr >= 8) {
+ if (!strncasecmp (inptr, "http://", 7) ||
+ !strncasecmp (inptr, "https://", 8) ||
+ !strncasecmp (inptr, "ftp://", 6) ||
+ !strncasecmp (inptr, "nntp://", 7) ||
+ !strncasecmp (inptr, "mailto:", 7) ||
+ !strncasecmp (inptr, "news:", 5))
+ return TRUE;
+ } else if (backup) {
+ *backup = TRUE;
+ return FALSE;
+ }
+
+ return FALSE;
+}
+
+static void
+html_convert (CamelMimeFilter *filter, char *in, size_t inlen, size_t prespace,
+ char **out, size_t *outlen, size_t *outprespace, gboolean flush)
+{
+ CamelMimeFilterToHTML *html = (CamelMimeFilterToHTML *) filter;
+ char *inptr, *inend, *outptr, *outend, *start;
+ gboolean backup = FALSE;
+
+ camel_mime_filter_set_size (filter, inlen * 2 + 6, FALSE);
+
+ inptr = start = in;
+ inend = in + inlen;
+ outptr = filter->outbuf;
+ outend = filter->outbuf + filter->outsize;
+
+ if (html->flags & CAMEL_MIME_FILTER_TOHTML_PRE && !html->pre_open) {
+ outptr += sprintf (outptr, "%s", "<pre>");
+ html->pre_open = TRUE;
+ }
+
+ while (inptr < inend) {
+ unsigned char u;
+
+ if (html->flags & CAMEL_MIME_FILTER_TOHTML_MARK_CITATION && html->column == 0) {
+ html->saw_citation = is_citation (inptr, inend, html->saw_citation,
+ flush ? &backup : NULL);
+ if (backup)
+ break;
+
+ if (html->saw_citation) {
+ if (!html->coloured) {
+ char font[25];
+
+ g_snprintf (font, 25, "<font color=\"#%06x\">", html->colour);
+
+ outptr = check_size (filter, outptr, &outend, 25);
+ outptr += sprintf (outptr, "%s", font);
+ html->coloured = TRUE;
+ }
+ } else if (html->coloured) {
+ outptr = check_size (filter, outptr, &outend, 10);
+ outptr += sprintf (outptr, "%s", "</font>");
+ html->coloured = FALSE;
+ }
+
+ /* display mbox-mangled ">From " as "From " */
+ if (*inptr == '>' && !html->saw_citation)
+ inptr++;
+ } else if (html->flags & CAMEL_MIME_FILTER_TOHTML_CITE && html->column == 0) {
+ outptr = check_size (filter, outptr, &outend, 6);
+ outptr += sprintf (outptr, "%s", "&gt; ");
+ }
+
+ if (html->flags & CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS && isalpha ((int) *inptr)) {
+ char *refurl = NULL, *dispurl = NULL;
+
+ if (is_protocol (inptr, inend, flush ? &backup : NULL)) {
+ dispurl = url_extract (&inptr, inend - inptr, TRUE,
+ flush ? &backup : NULL);
+ if (backup)
+ break;
+
+ if (dispurl)
+ refurl = g_strdup (dispurl);
+ } else {
+ if (backup)
+ break;
+
+ if (!strncasecmp (inptr, "www.", 4) && ((unsigned char) inptr[4]) < 0x80
+ && isalnum ((int) inptr[4])) {
+ dispurl = url_extract (&inptr, inend - inptr, FALSE,
+ flush ? &backup : NULL);
+ if (backup)
+ break;
+
+ if (dispurl)
+ refurl = g_strdup_printf ("http://%s", dispurl);
+ }
+ }
+
+ if (dispurl) {
+ outptr = check_size (filter, outptr, &outend,
+ strlen (refurl) +
+ strlen (dispurl) + 15);
+ outptr += sprintf (outptr, "<a href=\"%s\">%s</a>",
+ refurl, dispurl);
+ html->column += strlen (dispurl);
+ g_free (refurl);
+ g_free (dispurl);
+ }
+
+ if (inptr >= inend)
+ break;
+ }
+
+ if (*inptr == '@' && (html->flags & CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES)) {
+ char *addr, *outaddr;
+
+ addr = email_address_extract (&inptr, inend, start, &outptr,
+ flush ? &backup : NULL);
+ if (backup)
+ break;
+
+ if (addr) {
+ outaddr = g_strdup_printf ("<a href=\"mailto:%s\">%s</a>",
+ addr, addr);
+ outptr = check_size (filter, outptr, &outend, strlen (outaddr));
+ outptr += sprintf (outptr, "%s", outaddr);
+ html->column += strlen (addr);
+ g_free (addr);
+ g_free (outaddr);
+ }
+ }
+
+ outptr = check_size (filter, outptr, &outend, 32);
+
+ switch ((u = (unsigned char) *inptr++)) {
+ case '<':
+ outptr += sprintf (outptr, "%s", "&lt;");
+ html->column++;
+ break;
+
+ case '>':
+ outptr += sprintf (outptr, "%s", "&gt;");
+ html->column++;
+ break;
+
+ case '&':
+ outptr += sprintf (outptr, "%s", "&amp;");
+ html->column++;
+ break;
+
+ case '"':
+ outptr += sprintf (outptr, "%s", "&quot;");
+ html->column++;
+ break;
+
+ case '\n':
+ if (html->flags & CAMEL_MIME_FILTER_TOHTML_CONVERT_NL)
+ outptr += sprintf (outptr, "%s", "<br>");
+
+ *outptr++ = '\n';
+ start = inptr;
+ html->column = 0;
+ break;
+
+ case '\t':
+ if (html->flags & (CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES)) {
+ do {
+ outptr = check_size (filter, outptr, &outend, 7);
+ outptr += sprintf (outptr, "%s", "&nbsp;");
+ html->column++;
+ } while (html->column % 8);
+ break;
+ }
+ /* otherwise, FALL THROUGH */
+
+ case ' ':
+ if (html->flags & CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES) {
+ if (inptr == in || (inptr < inend && (*(inptr + 1) == ' ' ||
+ *(inptr + 1) == '\t' ||
+ *(inptr - 1) == '\n'))) {
+ outptr += sprintf (outptr, "%s", "&nbsp;");
+ html->column++;
+ break;
+ }
+ }
+ /* otherwise, FALL THROUGH */
+
+ default:
+ if ((u >= 0x20 && u < 0x80) ||
+ (u == '\r' || u == '\t')) {
+ /* Default case, just copy. */
+ *outptr++ = (char) u;
+ } else {
+ if (html->flags & CAMEL_MIME_FILTER_TOHTML_ESCAPE_8BIT)
+ *outptr++ = '?';
+ else
+ outptr += g_snprintf (outptr, 9, "&#%d;", (int) u);
+ }
+ html->column++;
+ break;
+ }
+ }
+
+ if (inptr < inend)
+ camel_mime_filter_backup (filter, inptr, inend - inptr);
+
+ if (flush && html->pre_open) {
+ outptr = check_size (filter, outptr, &outend, 10);
+ outptr += sprintf (outptr, "%s", "</pre>");
+ html->pre_open = FALSE;
+ }
+
+ *out = filter->outbuf;
+ *outlen = outptr - filter->outbuf;
+ *outprespace = filter->outpre;
+}
+
+static void
+filter_filter (CamelMimeFilter *filter, char *in, size_t len, size_t prespace,
+ char **out, size_t *outlen, size_t *outprespace)
+{
+ html_convert (filter, in, len, prespace, out, outlen, outprespace, FALSE);
+}
+
+static void
+filter_complete (CamelMimeFilter *filter, char *in, size_t len, size_t prespace,
+ char **out, size_t *outlen, size_t *outprespace)
+{
+ html_convert (filter, in, len, prespace, out, outlen, outprespace, TRUE);
+}
+
+static void
+filter_reset (CamelMimeFilter *filter)
+{
+ CamelMimeFilterToHTML *html = (CamelMimeFilterToHTML *) filter;
+
+ html->column = 0;
+ html->pre_open = FALSE;
+ html->saw_citation = FALSE;
+ html->coloured = FALSE;
+}
+
+static void
+camel_mime_filter_tohtml_class_init (CamelMimeFilterToHTMLClass *klass)
+{
+ CamelMimeFilterClass *filter_class = (CamelMimeFilterClass *) klass;
+
+ camel_mime_filter_tohtml_parent = CAMEL_MIME_FILTER_CLASS (camel_type_get_global_classfuncs (camel_mime_filter_get_type ()));
+
+ filter_class->reset = filter_reset;
+ filter_class->filter = filter_filter;
+ filter_class->complete = filter_complete;
+}
+
+
+/**
+ * camel_mime_filter_tohtml_new:
+ * @flags:
+ * @colour:
+ *
+ * Creates a new CamelMimeFilterToHTML object.
+ *
+ * Returns a new CamelMimeFilter object.
+ **/
+CamelMimeFilter *
+camel_mime_filter_tohtml_new (guint32 flags, guint32 colour)
+{
+ CamelMimeFilterToHTML *new;
+
+ new = CAMEL_MIME_FILTER_TOHTML (camel_object_new (camel_mime_filter_tohtml_get_type ()));
+
+ new->flags = flags;
+ new->colour = colour;
+
+ return CAMEL_MIME_FILTER (new);
+}
diff --git a/camel/camel-mime-filter-tohtml.h b/camel/camel-mime-filter-tohtml.h
new file mode 100644
index 0000000000..46c4051b87
--- /dev/null
+++ b/camel/camel-mime-filter-tohtml.h
@@ -0,0 +1,75 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright 2001 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifndef __CAMEL_MIME_FILTER_TOHTML_H__
+#define __CAMEL_MIME_FILTER_TOHTML_H__
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+#include <camel/camel-mime-filter.h>
+
+#define CAMEL_MIME_FILTER_TOHTML(obj) CAMEL_CHECK_CAST (obj, camel_mime_filter_tohtml_get_type (), CamelMimeFilterToHTML)
+#define CAMEL_MIME_FILTER_TOHTML_CLASS(klass) CAMEL_CHECK_CLASS_CAST (klass, camel_mime_filter_tohtml_get_type (), CamelMimeFilterToHTMLClass)
+#define CAMEL_IS_MIME_FILTER_TOHTML(obj) CAMEL_CHECK_TYPE (obj, camel_mime_filter_tohtml_get_type ())
+
+#define CAMEL_MIME_FILTER_TOHTML_PRE (1 << 0)
+#define CAMEL_MIME_FILTER_TOHTML_CONVERT_NL (1 << 1)
+#define CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES (1 << 2)
+#define CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS (1 << 3)
+#define CAMEL_MIME_FILTER_TOHTML_MARK_CITATION (1 << 4)
+#define CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES (1 << 5)
+#define CAMEL_MIME_FILTER_TOHTML_ESCAPE_8BIT (1 << 6)
+#define CAMEL_MIME_FILTER_TOHTML_CITE (1 << 7)
+
+typedef struct _CamelMimeFilterToHTMLClass CamelMimeFilterToHTMLClass;
+typedef struct _CamelMimeFilterToHTML CamelMimeFilterToHTML;
+
+struct _CamelMimeFilterToHTML {
+ CamelMimeFilter parent;
+
+ guint32 flags;
+ guint32 colour;
+
+ guint32 column : 29;
+ guint32 pre_open : 1;
+ guint32 saw_citation : 1;
+ guint32 coloured : 1;
+};
+
+struct _CamelMimeFilterToHTMLClass {
+ CamelMimeFilterClass parent_class;
+};
+
+
+CamelType camel_mime_filter_tohtml_get_type (void);
+
+CamelMimeFilter *camel_mime_filter_tohtml_new (guint32 flags, guint32 colour);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __CAMEL_MIME_FILTER_TOHTML_H__ */