summaryrefslogtreecommitdiff
path: root/mailnews/mime/src/mimemsg.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mailnews/mime/src/mimemsg.cpp')
-rw-r--r--mailnews/mime/src/mimemsg.cpp977
1 files changed, 977 insertions, 0 deletions
diff --git a/mailnews/mime/src/mimemsg.cpp b/mailnews/mime/src/mimemsg.cpp
new file mode 100644
index 0000000000..2d69570690
--- /dev/null
+++ b/mailnews/mime/src/mimemsg.cpp
@@ -0,0 +1,977 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsCOMPtr.h"
+#include "nsIMimeEmitter.h"
+#include "mimemsg.h"
+#include "mimemoz2.h"
+#include "prmem.h"
+#include "prio.h"
+#include "plstr.h"
+#include "msgCore.h"
+#include "prlog.h"
+#include "prprf.h"
+#include "nsMimeStringResources.h"
+#include "nsMimeTypes.h"
+#include "nsMsgMessageFlags.h"
+#include "nsStringGlue.h"
+#include "mimetext.h"
+#include "mimecryp.h"
+#include "mimetpfl.h"
+#include "nsINetUtil.h"
+#include "nsMsgUtils.h"
+#include "nsMsgI18N.h"
+
+#define MIME_SUPERCLASS mimeContainerClass
+MimeDefClass(MimeMessage, MimeMessageClass, mimeMessageClass,
+ &MIME_SUPERCLASS);
+
+static int MimeMessage_initialize (MimeObject *);
+static void MimeMessage_finalize (MimeObject *);
+static int MimeMessage_add_child (MimeObject *, MimeObject *);
+static int MimeMessage_parse_begin (MimeObject *);
+static int MimeMessage_parse_line (const char *, int32_t, MimeObject *);
+static int MimeMessage_parse_eof (MimeObject *, bool);
+static int MimeMessage_close_headers (MimeObject *obj);
+static int MimeMessage_write_headers_html (MimeObject *);
+static char *MimeMessage_partial_message_html(const char *data,
+ void *closure,
+ MimeHeaders *headers);
+
+#ifdef XP_UNIX
+extern void MimeHeaders_do_unix_display_hook_hack(MimeHeaders *);
+#endif /* XP_UNIX */
+
+#if defined(DEBUG) && defined(XP_UNIX)
+static int MimeMessage_debug_print (MimeObject *, PRFileDesc *, int32_t depth);
+#endif
+
+extern MimeObjectClass mimeMultipartClass;
+
+static int
+MimeMessageClassInitialize(MimeMessageClass *clazz)
+{
+ MimeObjectClass *oclass = (MimeObjectClass *) clazz;
+ MimeContainerClass *cclass = (MimeContainerClass *) clazz;
+
+ PR_ASSERT(!oclass->class_initialized);
+ oclass->initialize = MimeMessage_initialize;
+ oclass->finalize = MimeMessage_finalize;
+ oclass->parse_begin = MimeMessage_parse_begin;
+ oclass->parse_line = MimeMessage_parse_line;
+ oclass->parse_eof = MimeMessage_parse_eof;
+ cclass->add_child = MimeMessage_add_child;
+
+#if defined(DEBUG) && defined(XP_UNIX)
+ oclass->debug_print = MimeMessage_debug_print;
+#endif
+ return 0;
+}
+
+
+static int
+MimeMessage_initialize (MimeObject *object)
+{
+ MimeMessage *msg = (MimeMessage *)object;
+ msg->grabSubject = false;
+ msg->bodyLength = 0;
+ msg->sizeSoFar = 0;
+
+ return ((MimeObjectClass*)&MIME_SUPERCLASS)->initialize(object);
+}
+
+static void
+MimeMessage_finalize (MimeObject *object)
+{
+ MimeMessage *msg = (MimeMessage *)object;
+ if (msg->hdrs)
+ MimeHeaders_free(msg->hdrs);
+ msg->hdrs = 0;
+ ((MimeObjectClass*)&MIME_SUPERCLASS)->finalize(object);
+}
+
+static int
+MimeMessage_parse_begin (MimeObject *obj)
+{
+ MimeMessage *msg = (MimeMessage *)obj;
+
+ int status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_begin(obj);
+ if (status < 0) return status;
+
+ if (obj->parent)
+ {
+ msg->grabSubject = true;
+ }
+
+ /* Messages have separators before the headers, except for the outermost
+ message. */
+ return MimeObject_write_separator(obj);
+}
+
+
+static int
+MimeMessage_parse_line (const char *aLine, int32_t aLength, MimeObject *obj)
+{
+ const char * line = aLine;
+ int32_t length = aLength;
+
+ MimeMessage *msg = (MimeMessage *) obj;
+ int status = 0;
+
+ NS_ASSERTION(line && *line, "empty line in mime msg parse_line");
+ if (!line || !*line) return -1;
+
+ msg->sizeSoFar += length;
+
+ if (msg->grabSubject)
+ {
+ if ( (!PL_strncasecmp(line, "Subject: ", 9)) && (obj->parent) )
+ {
+ if ( (obj->headers) && (!obj->headers->munged_subject) )
+ {
+ obj->headers->munged_subject = (char *) PL_strndup(line + 9, length - 9);
+ char *tPtr = obj->headers->munged_subject;
+ while (*tPtr)
+ {
+ if ( (*tPtr == '\r') || (*tPtr == '\n') )
+ {
+ *tPtr = '\0';
+ break;
+ }
+ tPtr++;
+ }
+ }
+ }
+ }
+
+ /* If we already have a child object, then we're done parsing headers,
+ and all subsequent lines get passed to the inferior object without
+ further processing by us. (Our parent will stop feeding us lines
+ when this MimeMessage part is out of data.)
+ */
+ if (msg->container.nchildren)
+ {
+ MimeObject *kid = msg->container.children[0];
+ bool nl;
+ PR_ASSERT(kid);
+ if (!kid) return -1;
+
+ msg->bodyLength += length;
+
+ /* Don't allow MimeMessage objects to not end in a newline, since it
+ would be inappropriate for any following part to appear on the same
+ line as the last line of the message.
+
+ #### This assumes that the only time the `parse_line' method is
+ called with a line that doesn't end in a newline is when that line
+ is the last line.
+ */
+ nl = (length > 0 && (line[length-1] == '\r' || line[length-1] == '\n'));
+
+#ifdef MIME_DRAFTS
+ if (!mime_typep (kid, (MimeObjectClass*) &mimeMessageClass) &&
+ obj->options &&
+ obj->options->decompose_file_p &&
+ !obj->options->is_multipart_msg &&
+ obj->options->decompose_file_output_fn &&
+ !obj->options->decrypt_p)
+ {
+ // If we are processing a flowed plain text line, we need to parse the
+ // line in mimeInlineTextPlainFlowedClass.
+ if (mime_typep(kid, (MimeObjectClass *)&mimeInlineTextPlainFlowedClass))
+ {
+ // Remove any stuffed space.
+ if (length > 0 && ' ' == *line)
+ {
+ line++;
+ length--;
+ }
+ return kid->clazz->parse_line (line, length, kid);
+ }
+ else
+ {
+ status = obj->options->decompose_file_output_fn (line,
+ length,
+ obj->options->stream_closure);
+ if (status < 0) return status;
+ if (!nl) {
+ status = obj->options->decompose_file_output_fn (MSG_LINEBREAK,
+ MSG_LINEBREAK_LEN,
+ obj->options->stream_closure);
+ if (status < 0) return status;
+ }
+ return status;
+ }
+ }
+#endif /* MIME_DRAFTS */
+
+
+ if (nl)
+ return kid->clazz->parse_buffer (line, length, kid);
+ else
+ {
+ /* Hack a newline onto the end. */
+ char *s = (char *)PR_MALLOC(length + MSG_LINEBREAK_LEN + 1);
+ if (!s) return MIME_OUT_OF_MEMORY;
+ memcpy(s, line, length);
+ PL_strncpyz(s + length, MSG_LINEBREAK, MSG_LINEBREAK_LEN + 1);
+ status = kid->clazz->parse_buffer (s, length + MSG_LINEBREAK_LEN, kid);
+ PR_Free(s);
+ return status;
+ }
+ }
+
+ /* Otherwise we don't yet have a child object, which means we're not
+ done parsing our headers yet.
+ */
+ if (!msg->hdrs)
+ {
+ msg->hdrs = MimeHeaders_new();
+ if (!msg->hdrs) return MIME_OUT_OF_MEMORY;
+ }
+
+#ifdef MIME_DRAFTS
+ if ( obj->options &&
+ obj->options->decompose_file_p &&
+ ! obj->options->is_multipart_msg &&
+ obj->options->done_parsing_outer_headers &&
+ obj->options->decompose_file_output_fn )
+ {
+ status = obj->options->decompose_file_output_fn( line, length,
+ obj->options->stream_closure );
+ if (status < 0)
+ return status;
+ }
+#endif /* MIME_DRAFTS */
+
+ status = MimeHeaders_parse_line(line, length, msg->hdrs);
+ if (status < 0) return status;
+
+ /* If this line is blank, we're now done parsing headers, and should
+ examine our content-type to create our "body" part.
+ */
+ if (*line == '\r' || *line == '\n')
+ {
+ status = MimeMessage_close_headers(obj);
+ if (status < 0) return status;
+ }
+
+ return 0;
+}
+
+static int
+MimeMessage_close_headers (MimeObject *obj)
+{
+ MimeMessage *msg = (MimeMessage *) obj;
+ int status = 0;
+ char *ct = 0; /* Content-Type header */
+ MimeObject *body;
+
+ // Do a proper decoding of the munged subject.
+ if (obj->headers && msg->hdrs && msg->grabSubject && obj->headers->munged_subject) {
+ // nsMsgI18NConvertToUnicode wants nsAStrings...
+ nsDependentCString orig(obj->headers->munged_subject);
+ nsAutoString dest;
+ // First, get the Content-Type, then extract the charset="whatever" part of
+ // it.
+ nsCString charset;
+ nsCString contentType;
+ contentType.Adopt(MimeHeaders_get(msg->hdrs, HEADER_CONTENT_TYPE, false, false));
+ if (!contentType.IsEmpty())
+ charset.Adopt(MimeHeaders_get_parameter(contentType.get(), "charset", nullptr, nullptr));
+
+ // If we've got a charset, use nsMsgI18NConvertToUnicode to magically decode
+ // the munged subject.
+ if (!charset.IsEmpty()) {
+ nsresult rv = nsMsgI18NConvertToUnicode(charset.get(), orig, dest);
+ // If we managed to convert the string, replace munged_subject with the
+ // UTF8 version of it, otherwise, just forget about it (maybe there was an
+ // improperly encoded string in there).
+ PR_Free(obj->headers->munged_subject);
+ if (NS_SUCCEEDED(rv))
+ obj->headers->munged_subject = ToNewUTF8String(dest);
+ else
+ obj->headers->munged_subject = nullptr;
+ } else {
+ PR_Free(obj->headers->munged_subject);
+ obj->headers->munged_subject = nullptr;
+ }
+ }
+
+ if (msg->hdrs)
+ {
+ bool outer_p = !obj->headers; /* is this the outermost message? */
+
+
+#ifdef MIME_DRAFTS
+ if (outer_p &&
+ obj->options &&
+ (obj->options->decompose_file_p || obj->options->caller_need_root_headers) &&
+ obj->options->decompose_headers_info_fn)
+ {
+#ifdef ENABLE_SMIME
+ if (obj->options->decrypt_p && !mime_crypto_object_p(msg->hdrs, false, obj->options))
+ obj->options->decrypt_p = false;
+#endif /* ENABLE_SMIME */
+ if (!obj->options->caller_need_root_headers || (obj == obj->options->state->root))
+ status = obj->options->decompose_headers_info_fn (
+ obj->options->stream_closure,
+ msg->hdrs );
+ }
+#endif /* MIME_DRAFTS */
+
+
+ /* If this is the outermost message, we need to run the
+ `generate_header' callback. This happens here instead of
+ in `parse_begin', because it's only now that we've parsed
+ our headers. However, since this is the outermost message,
+ we have yet to write any HTML, so that's fine.
+ */
+ if (outer_p &&
+ obj->output_p &&
+ obj->options &&
+ obj->options->write_html_p &&
+ obj->options->generate_header_html_fn)
+ {
+ int lstatus = 0;
+ char *html = 0;
+
+ /* The generate_header_html_fn might return HTML, so it's important
+ that the output stream be set up with the proper type before we
+ make the MimeObject_write() call below. */
+ if (!obj->options->state->first_data_written_p)
+ {
+ lstatus = MimeObject_output_init (obj, TEXT_HTML);
+ if (lstatus < 0) return lstatus;
+ PR_ASSERT(obj->options->state->first_data_written_p);
+ }
+
+ html = obj->options->generate_header_html_fn(NULL,
+ obj->options->html_closure,
+ msg->hdrs);
+ if (html)
+ {
+ lstatus = MimeObject_write(obj, html, strlen(html), false);
+ PR_Free(html);
+ if (lstatus < 0) return lstatus;
+ }
+ }
+
+
+ /* Find the content-type of the body of this message.
+ */
+ {
+ bool ok = true;
+ char *mv = MimeHeaders_get (msg->hdrs, HEADER_MIME_VERSION,
+ true, false);
+
+#ifdef REQUIRE_MIME_VERSION_HEADER
+ /* If this is the outermost message, it must have a MIME-Version
+ header with the value 1.0 for us to believe what might be in
+ the Content-Type header. If the MIME-Version header is not
+ present, we must treat this message as untyped.
+ */
+ ok = (mv && !strcmp(mv, "1.0"));
+#else
+ /* #### actually, we didn't check this in Mozilla 2.0, and checking
+ it now could cause some compatibility nonsense, so for now, let's
+ just believe any Content-Type header we see.
+ */
+ ok = true;
+#endif
+
+ if (ok)
+ {
+ ct = MimeHeaders_get (msg->hdrs, HEADER_CONTENT_TYPE, true, false);
+
+ /* If there is no Content-Type header, but there is a MIME-Version
+ header, then assume that this *is* in fact a MIME message.
+ (I've seen messages with
+
+ MIME-Version: 1.0
+ Content-Transfer-Encoding: quoted-printable
+
+ and no Content-Type, and we should treat those as being of type
+ MimeInlineTextPlain rather than MimeUntypedText.)
+ */
+ if (mv && !ct)
+ ct = strdup(TEXT_PLAIN);
+ }
+
+ PR_FREEIF(mv); /* done with this now. */
+ }
+
+ /* If this message has a body which is encrypted and we're going to
+ decrypt it (whithout converting it to HTML, since decrypt_p and
+ write_html_p are never true at the same time)
+ */
+ if (obj->output_p &&
+ obj->options &&
+ obj->options->decrypt_p
+#ifdef ENABLE_SMIME
+ && !mime_crypto_object_p(msg->hdrs, false, obj->options)
+#endif /* ENABLE_SMIME */
+ )
+ {
+ /* The body of this message is not an encrypted object, so we need
+ to turn off the decrypt_p flag (to prevent us from s#$%ing the
+ body of the internal object up into one.) In this case,
+ our output will end up being identical to our input.
+ */
+ obj->options->decrypt_p = false;
+ }
+
+ /* Emit the HTML for this message's headers. Do this before
+ creating the object representing the body.
+ */
+ if (obj->output_p &&
+ obj->options &&
+ obj->options->write_html_p)
+ {
+ /* If citation headers are on, and this is not the outermost message,
+ turn them off. */
+ if (obj->options->headers == MimeHeadersCitation && !outer_p)
+ obj->options->headers = MimeHeadersSome;
+
+ /* Emit a normal header block. */
+ status = MimeMessage_write_headers_html(obj);
+ if (status < 0)
+ {
+ PR_FREEIF(ct);
+ return status;
+ }
+ }
+ else if (obj->output_p)
+ {
+ /* Dump the headers, raw. */
+ status = MimeObject_write(obj, "", 0, false); /* initialize */
+ if (status < 0)
+ {
+ PR_FREEIF(ct);
+ return status;
+ }
+ status = MimeHeaders_write_raw_headers(msg->hdrs, obj->options,
+ obj->options->decrypt_p);
+ if (status < 0)
+ {
+ PR_FREEIF(ct);
+ return status;
+ }
+ }
+
+#ifdef XP_UNIX
+ if (outer_p && obj->output_p)
+ /* Kludge from mimehdrs.c */
+ MimeHeaders_do_unix_display_hook_hack(msg->hdrs);
+#endif /* XP_UNIX */
+ }
+
+ /* Never put out a separator after a message header block. */
+ if (obj->options && obj->options->state)
+ obj->options->state->separator_suppressed_p = true;
+
+#ifdef MIME_DRAFTS
+ if ( !obj->headers && /* outer most message header */
+ obj->options &&
+ obj->options->decompose_file_p &&
+ ct )
+ obj->options->is_multipart_msg = PL_strcasestr(ct, "multipart/") != NULL;
+#endif /* MIME_DRAFTS */
+
+
+ body = mime_create(ct, msg->hdrs, obj->options);
+
+ PR_FREEIF(ct);
+ if (!body) return MIME_OUT_OF_MEMORY;
+ status = ((MimeContainerClass *) obj->clazz)->add_child (obj, body);
+ if (status < 0)
+ {
+ mime_free(body);
+ return status;
+ }
+
+ // Only do this if this is a Text Object!
+ if ( mime_typep(body, (MimeObjectClass *) &mimeInlineTextClass) )
+ {
+ ((MimeInlineText *) body)->needUpdateMsgWinCharset = true;
+ }
+
+ /* Now that we've added this new object to our list of children,
+ start its parser going. */
+ status = body->clazz->parse_begin(body);
+ if (status < 0) return status;
+
+ // Now notify the emitter if this is the outer most message, unless
+ // it is a part that is not the head of the message. If it's a part,
+ // we need to figure out the content type/charset of the part
+ //
+ bool outer_p = !obj->headers; /* is this the outermost message? */
+
+ if ( (outer_p || obj->options->notify_nested_bodies) &&
+ (!obj->options->part_to_load || obj->options->format_out == nsMimeOutput::nsMimeMessageBodyDisplay))
+ {
+ // call SetMailCharacterSetToMsgWindow() to set a menu charset
+ if (mime_typep(body, (MimeObjectClass *) &mimeInlineTextClass))
+ {
+ MimeInlineText *text = (MimeInlineText *) body;
+ if (text && text->charset && *text->charset)
+ SetMailCharacterSetToMsgWindow(body, text->charset);
+ }
+
+ char *msgID = MimeHeaders_get (msg->hdrs, HEADER_MESSAGE_ID,
+ false, false);
+
+ const char *outCharset = NULL;
+ if (!obj->options->force_user_charset) /* Only convert if the user prefs is false */
+ outCharset = "UTF-8";
+
+ mimeEmitterStartBody(obj->options, (obj->options->headers == MimeHeadersNone), msgID, outCharset);
+ PR_FREEIF(msgID);
+
+ // setting up truncated message html fotter function
+ char *xmoz = MimeHeaders_get(msg->hdrs, HEADER_X_MOZILLA_STATUS, false,
+ false);
+ if (xmoz)
+ {
+ uint32_t flags = 0;
+ char dummy = 0;
+ if (sscanf(xmoz, " %x %c", &flags, &dummy) == 1 &&
+ flags & nsMsgMessageFlags::Partial)
+ {
+ obj->options->html_closure = obj;
+ obj->options->generate_footer_html_fn =
+ MimeMessage_partial_message_html;
+ }
+ PR_FREEIF(xmoz);
+ }
+ }
+
+ return 0;
+}
+
+
+
+static int
+MimeMessage_parse_eof (MimeObject *obj, bool abort_p)
+{
+ int status;
+ bool outer_p;
+ MimeMessage *msg = (MimeMessage *)obj;
+ if (obj->closed_p) return 0;
+
+ /* Run parent method first, to flush out any buffered data. */
+ status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof(obj, abort_p);
+ if (status < 0) return status;
+
+ outer_p = !obj->headers; /* is this the outermost message? */
+
+ // Hack for messages with truncated headers (bug 244722)
+ // If there is no empty line in a message, the parser can't figure out where
+ // the headers end, causing parsing to hang. So we insert an extra newline
+ // to keep it happy. This is OK, since a message without any empty lines is
+ // broken anyway...
+ if(outer_p && msg->hdrs && ! msg->hdrs->done_p) {
+ MimeMessage_parse_line("\n", 1, obj);
+ }
+
+ // Once we get to the end of parsing the message, we will notify
+ // the emitter that we are done the the body.
+
+ // Mark the end of the mail body if we are actually emitting the
+ // body of the message (i.e. not Header ONLY)
+ if ((outer_p || obj->options->notify_nested_bodies) && obj->options &&
+ obj->options->write_html_p)
+ {
+ if (obj->options->generate_footer_html_fn)
+ {
+ mime_stream_data *msd =
+ (mime_stream_data *) obj->options->stream_closure;
+ if (msd)
+ {
+ char *html = obj->options->generate_footer_html_fn
+ (msd->orig_url_name, obj->options->html_closure, msg->hdrs);
+ if (html)
+ {
+ int lstatus = MimeObject_write(obj, html,
+ strlen(html),
+ false);
+ PR_Free(html);
+ if (lstatus < 0) return lstatus;
+ }
+ }
+ }
+ if ((!obj->options->part_to_load || obj->options->format_out == nsMimeOutput::nsMimeMessageBodyDisplay) &&
+ obj->options->headers != MimeHeadersOnly)
+ mimeEmitterEndBody(obj->options);
+ }
+
+#ifdef MIME_DRAFTS
+ if ( obj->options &&
+ obj->options->decompose_file_p &&
+ obj->options->done_parsing_outer_headers &&
+ ! obj->options->is_multipart_msg &&
+ ! mime_typep(obj, (MimeObjectClass*) &mimeEncryptedClass) &&
+ obj->options->decompose_file_close_fn ) {
+ status = obj->options->decompose_file_close_fn (
+ obj->options->stream_closure );
+
+ if ( status < 0 ) return status;
+ }
+#endif /* MIME_DRAFTS */
+
+
+ /* Put out a separator after every message/rfc822 object. */
+ if (!abort_p && !outer_p)
+ {
+ status = MimeObject_write_separator(obj);
+ if (status < 0) return status;
+ }
+
+ return 0;
+}
+
+
+static int
+MimeMessage_add_child (MimeObject *parent, MimeObject *child)
+{
+ MimeContainer *cont = (MimeContainer *) parent;
+ PR_ASSERT(parent && child);
+ if (!parent || !child) return -1;
+
+ /* message/rfc822 containers can only have one child. */
+ PR_ASSERT(cont->nchildren == 0);
+ if (cont->nchildren != 0) return -1;
+
+#ifdef MIME_DRAFTS
+ if ( parent->options &&
+ parent->options->decompose_file_p &&
+ ! parent->options->is_multipart_msg &&
+ ! mime_typep(child, (MimeObjectClass*) &mimeEncryptedClass) &&
+ parent->options->decompose_file_init_fn ) {
+ int status = 0;
+ status = parent->options->decompose_file_init_fn (
+ parent->options->stream_closure,
+ ((MimeMessage*)parent)->hdrs );
+ if ( status < 0 ) return status;
+ }
+#endif /* MIME_DRAFTS */
+
+ return ((MimeContainerClass*)&MIME_SUPERCLASS)->add_child (parent, child);
+}
+
+// This is necessary to determine which charset to use for a reply/forward
+char *
+DetermineMailCharset(MimeMessage *msg)
+{
+ char *retCharset = nullptr;
+
+ if ( (msg) && (msg->hdrs) )
+ {
+ char *ct = MimeHeaders_get (msg->hdrs, HEADER_CONTENT_TYPE,
+ false, false);
+ if (ct)
+ {
+ retCharset = MimeHeaders_get_parameter (ct, "charset", NULL, NULL);
+ PR_Free(ct);
+ }
+
+ if (!retCharset)
+ {
+ // If we didn't find "Content-Type: ...; charset=XX" then look
+ // for "X-Sun-Charset: XX" instead. (Maybe this should be done
+ // in MimeSunAttachmentClass, but it's harder there than here.)
+ retCharset = MimeHeaders_get (msg->hdrs, HEADER_X_SUN_CHARSET,
+ false, false);
+ }
+ }
+
+ if (!retCharset)
+ return strdup("ISO-8859-1");
+ else
+ return retCharset;
+}
+
+static int
+MimeMessage_write_headers_html (MimeObject *obj)
+{
+ MimeMessage *msg = (MimeMessage *) obj;
+ int status;
+
+ if (!obj->options || !obj->options->output_fn)
+ return 0;
+
+ PR_ASSERT(obj->output_p && obj->options->write_html_p);
+
+ // To support the no header option! Make sure we are not
+ // suppressing headers on included email messages...
+ if ( (obj->options->headers == MimeHeadersNone) &&
+ (obj == obj->options->state->root) )
+ {
+ // Ok, we are going to kick the Emitter for a StartHeader
+ // operation ONLY WHEN THE CHARSET OF THE ORIGINAL MESSAGE IS
+ // NOT US-ASCII ("ISO-8859-1")
+ //
+ // This is only to notify the emitter of the charset of the
+ // original message
+ char *mailCharset = DetermineMailCharset(msg);
+
+ if ( (mailCharset) && (PL_strcasecmp(mailCharset, "US-ASCII")) &&
+ (PL_strcasecmp(mailCharset, "ISO-8859-1")) )
+ mimeEmitterUpdateCharacterSet(obj->options, mailCharset);
+ PR_FREEIF(mailCharset);
+ return 0;
+ }
+
+ if (!obj->options->state->first_data_written_p)
+ {
+ status = MimeObject_output_init (obj, TEXT_HTML);
+ if (status < 0)
+ {
+ mimeEmitterEndHeader(obj->options, obj);
+ return status;
+ }
+ PR_ASSERT(obj->options->state->first_data_written_p);
+ }
+
+ // Start the header parsing by the emitter
+ char *msgID = MimeHeaders_get (msg->hdrs, HEADER_MESSAGE_ID,
+ false, false);
+ bool outer_p = !obj->headers; /* is this the outermost message? */
+ if (!outer_p && obj->options->format_out == nsMimeOutput::nsMimeMessageBodyDisplay &&
+ obj->options->part_to_load)
+ {
+ //Maybe we are displaying a embedded message as outer part!
+ char *id = mime_part_address(obj);
+ if (id)
+ {
+ outer_p = !strcmp(id, obj->options->part_to_load);
+ PR_Free(id);
+ }
+ }
+
+ // Ok, we should really find out the charset of this part. We always
+ // output UTF-8 for display, but the original charset is necessary for
+ // reply and forward operations.
+ //
+ char *mailCharset = DetermineMailCharset(msg);
+ mimeEmitterStartHeader(obj->options,
+ outer_p,
+ (obj->options->headers == MimeHeadersOnly),
+ msgID,
+ mailCharset);
+
+ // Change the default_charset by the charset of the original message
+ // ONLY WHEN THE CHARSET OF THE ORIGINAL MESSAGE IS NOT US-ASCII
+ // ("ISO-8859-1") and default_charset and mailCharset are different,
+ // or when there is no default_charset (this can happen with saved messages).
+ if ( (!obj->options->default_charset ||
+ ((mailCharset) && (PL_strcasecmp(mailCharset, "US-ASCII")) &&
+ (PL_strcasecmp(mailCharset, "ISO-8859-1")) &&
+ (PL_strcasecmp(obj->options->default_charset, mailCharset)))) &&
+ !obj->options->override_charset )
+ {
+ PR_FREEIF(obj->options->default_charset);
+ obj->options->default_charset = strdup(mailCharset);
+ }
+
+ PR_FREEIF(msgID);
+ PR_FREEIF(mailCharset);
+
+ status = MimeHeaders_write_all_headers (msg->hdrs, obj->options, false);
+ if (status < 0)
+ {
+ mimeEmitterEndHeader(obj->options, obj);
+ return status;
+ }
+
+ if (!msg->crypto_stamped_p)
+ {
+ /* If we're not writing a xlation stamp, and this is the outermost
+ message, then now is the time to run the post_header_html_fn.
+ (Otherwise, it will be run when the xlation-stamp is finally
+ closed off, in MimeXlateed_emit_buffered_child() or
+ MimeMultipartSigned_emit_child().)
+ */
+ if (obj->options &&
+ obj->options->state &&
+ obj->options->generate_post_header_html_fn &&
+ !obj->options->state->post_header_html_run_p)
+ {
+ char *html = 0;
+ PR_ASSERT(obj->options->state->first_data_written_p);
+ html = obj->options->generate_post_header_html_fn(NULL,
+ obj->options->html_closure,
+ msg->hdrs);
+ obj->options->state->post_header_html_run_p = true;
+ if (html)
+ {
+ status = MimeObject_write(obj, html, strlen(html), false);
+ PR_Free(html);
+ if (status < 0)
+ {
+ mimeEmitterEndHeader(obj->options, obj);
+ return status;
+ }
+ }
+ }
+ }
+
+ mimeEmitterEndHeader(obj->options, obj);
+
+ // rhp:
+ // For now, we are going to parse the entire message, even if we are
+ // only interested in headers...why? Well, because this is the only
+ // way to build the attachment list. Now we will have the attachment
+ // list in the output being created by the XML emitter. If we ever
+ // want to go back to where we were before, just uncomment the conditional
+ // and it will stop at header parsing.
+ //
+ // if (obj->options->headers == MimeHeadersOnly)
+ // return -1;
+ // else
+
+ return 0;
+}
+
+static char *
+MimeMessage_partial_message_html(const char *data, void *closure,
+ MimeHeaders *headers)
+{
+ MimeMessage *msg = (MimeMessage *)closure;
+ nsAutoCString orig_url(data);
+ char *uidl = MimeHeaders_get(headers, HEADER_X_UIDL, false, false);
+ char *msgId = MimeHeaders_get(headers, HEADER_MESSAGE_ID, false,
+ false);
+ char *msgIdPtr = PL_strchr(msgId, '<');
+
+ int32_t pos = orig_url.Find("mailbox-message");
+ if (pos != -1)
+ orig_url.Cut(pos + 7, 8);
+
+ pos = orig_url.FindChar('#');
+ if (pos != -1)
+ orig_url.Replace(pos, 1, "?number=", 8);
+
+ if (msgIdPtr)
+ msgIdPtr++;
+ else
+ msgIdPtr = msgId;
+ char *gtPtr = PL_strchr(msgIdPtr, '>');
+ if (gtPtr)
+ *gtPtr = 0;
+
+ bool msgBaseTruncated = (msg->bodyLength > MSG_LINEBREAK_LEN);
+
+ nsCString partialMsgHtml;
+ nsCString item;
+
+ partialMsgHtml.AppendLiteral("<div style=\"margin: 1em auto; border: 1px solid black; width: 80%\">");
+ partialMsgHtml.AppendLiteral("<div style=\"margin: 5px; padding: 10px; border: 1px solid gray; font-weight: bold; text-align: center;\">");
+
+ partialMsgHtml.AppendLiteral("<span style=\"font-size: 120%;\">");
+ if (msgBaseTruncated)
+ item.Adopt(MimeGetStringByName(u"MIME_MSG_PARTIAL_TRUNCATED"));
+ else
+ item.Adopt(MimeGetStringByName(u"MIME_MSG_PARTIAL_NOT_DOWNLOADED"));
+ partialMsgHtml += item;
+ partialMsgHtml.AppendLiteral("</span><hr>");
+
+ if (msgBaseTruncated)
+ item.Adopt(MimeGetStringByName(u"MIME_MSG_PARTIAL_TRUNCATED_EXPLANATION"));
+ else
+ item.Adopt(MimeGetStringByName(u"MIME_MSG_PARTIAL_NOT_DOWNLOADED_EXPLANATION"));
+ partialMsgHtml += item;
+ partialMsgHtml.AppendLiteral("<br><br>");
+
+ partialMsgHtml.AppendLiteral("<a href=\"");
+ partialMsgHtml.Append(orig_url);
+
+ if (msgIdPtr) {
+ partialMsgHtml.AppendLiteral("&messageid=");
+
+ MsgEscapeString(nsDependentCString(msgIdPtr), nsINetUtil::ESCAPE_URL_PATH,
+ item);
+
+ partialMsgHtml.Append(item);
+ }
+
+ if (uidl) {
+ partialMsgHtml.AppendLiteral("&uidl=");
+
+ MsgEscapeString(nsDependentCString(uidl), nsINetUtil::ESCAPE_XALPHAS,
+ item);
+
+ partialMsgHtml.Append(item);
+ }
+
+ partialMsgHtml.AppendLiteral("\">");
+ item.Adopt(MimeGetStringByName(u"MIME_MSG_PARTIAL_CLICK_FOR_REST"));
+ partialMsgHtml += item;
+ partialMsgHtml.AppendLiteral("</a>");
+
+ partialMsgHtml.AppendLiteral("</div></div>");
+
+ return ToNewCString(partialMsgHtml);
+}
+
+#if defined(DEBUG) && defined(XP_UNIX)
+static int
+MimeMessage_debug_print (MimeObject *obj, PRFileDesc *stream, int32_t depth)
+{
+ MimeMessage *msg = (MimeMessage *) obj;
+ char *addr = mime_part_address(obj);
+ int i;
+ for (i=0; i < depth; i++)
+ PR_Write(stream, " ", 2);
+/*
+ fprintf(stream, "<%s %s%s 0x%08X>\n",
+ obj->clazz->class_name,
+ addr ? addr : "???",
+ (msg->container.nchildren == 0 ? " (no body)" : ""),
+ (uint32_t) msg);
+*/
+ PR_FREEIF(addr);
+
+#if 0
+ if (msg->hdrs)
+ {
+ char *s;
+
+ depth++;
+
+# define DUMP(HEADER) \
+ for (i=0; i < depth; i++) \
+ PR_Write(stream, " ", 2); \
+ s = MimeHeaders_get (msg->hdrs, HEADER, false, true);
+/**
+ \
+ PR_Write(stream, HEADER ": %s\n", s ? s : ""); \
+**/
+
+ PR_FREEIF(s)
+
+ DUMP(HEADER_SUBJECT);
+ DUMP(HEADER_DATE);
+ DUMP(HEADER_FROM);
+ DUMP(HEADER_TO);
+ /* DUMP(HEADER_CC); */
+ DUMP(HEADER_NEWSGROUPS);
+ DUMP(HEADER_MESSAGE_ID);
+# undef DUMP
+
+ PR_Write(stream, "\n", 1);
+ }
+#endif
+
+ PR_ASSERT(msg->container.nchildren <= 1);
+ if (msg->container.nchildren == 1)
+ {
+ MimeObject *kid = msg->container.children[0];
+ int status = kid->clazz->debug_print (kid, stream, depth+1);
+ if (status < 0) return status;
+ }
+ return 0;
+}
+#endif