summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/mime/src/mimemsig.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mailnews/mime/src/mimemsig.cpp')
-rw-r--r--comm/mailnews/mime/src/mimemsig.cpp716
1 files changed, 716 insertions, 0 deletions
diff --git a/comm/mailnews/mime/src/mimemsig.cpp b/comm/mailnews/mime/src/mimemsig.cpp
new file mode 100644
index 0000000000..5f390abd94
--- /dev/null
+++ b/comm/mailnews/mime/src/mimemsig.cpp
@@ -0,0 +1,716 @@
+/* -*- Mode: C++; tab-width: 2; 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 "modmimee.h"
+#include "mimemsig.h"
+#include "nspr.h"
+
+#include "prmem.h"
+#include "plstr.h"
+#include "prerror.h"
+#include "nsMimeTypes.h"
+#include "msgCore.h"
+#include "nsMimeStringResources.h"
+#include "mimemoz2.h"
+#include "mimeeobj.h"
+#include "modmimee.h" // for MimeConverterOutputCallback
+#include "mozilla/Attributes.h"
+
+#define MIME_SUPERCLASS mimeMultipartClass
+MimeDefClass(MimeMultipartSigned, MimeMultipartSignedClass,
+ mimeMultipartSignedClass, &MIME_SUPERCLASS);
+
+static int MimeMultipartSigned_initialize(MimeObject*);
+static int MimeMultipartSigned_create_child(MimeObject*);
+static int MimeMultipartSigned_close_child(MimeObject*);
+static int MimeMultipartSigned_parse_line(const char*, int32_t, MimeObject*);
+static int MimeMultipartSigned_parse_child_line(MimeObject*, const char*,
+ int32_t, bool);
+static int MimeMultipartSigned_parse_eof(MimeObject*, bool);
+static void MimeMultipartSigned_finalize(MimeObject*);
+
+static int MimeMultipartSigned_emit_child(MimeObject* obj);
+
+extern "C" MimeSuppressedCryptoClass mimeSuppressedCryptoClass;
+
+static int MimeMultipartSignedClassInitialize(MimeMultipartSignedClass* clazz) {
+ MimeObjectClass* oclass = (MimeObjectClass*)clazz;
+ MimeMultipartClass* mclass = (MimeMultipartClass*)clazz;
+
+ oclass->initialize = MimeMultipartSigned_initialize;
+ oclass->parse_line = MimeMultipartSigned_parse_line;
+ oclass->parse_eof = MimeMultipartSigned_parse_eof;
+ oclass->finalize = MimeMultipartSigned_finalize;
+ mclass->create_child = MimeMultipartSigned_create_child;
+ mclass->parse_child_line = MimeMultipartSigned_parse_child_line;
+ mclass->close_child = MimeMultipartSigned_close_child;
+
+ PR_ASSERT(!oclass->class_initialized);
+ return 0;
+}
+
+static int MimeMultipartSigned_initialize(MimeObject* object) {
+ MimeMultipartSigned* sig = (MimeMultipartSigned*)object;
+
+ /* This is an abstract class; it shouldn't be directly instantiated. */
+ PR_ASSERT(object->clazz != (MimeObjectClass*)&mimeMultipartSignedClass);
+
+ sig->state = MimeMultipartSignedPreamble;
+
+ return ((MimeObjectClass*)&MIME_SUPERCLASS)->initialize(object);
+}
+
+static void MimeMultipartSigned_cleanup(MimeObject* obj, bool finalizing_p) {
+ MimeMultipart* mult = (MimeMultipart*)obj; /* #58075. Fix suggested by jwz */
+ MimeMultipartSigned* sig = (MimeMultipartSigned*)obj;
+ if (sig->part_buffer) {
+ MimePartBufferDestroy(sig->part_buffer);
+ sig->part_buffer = 0;
+ }
+ if (sig->body_hdrs) {
+ MimeHeaders_free(sig->body_hdrs);
+ sig->body_hdrs = 0;
+ }
+ if (sig->sig_hdrs) {
+ MimeHeaders_free(sig->sig_hdrs);
+ sig->sig_hdrs = 0;
+ }
+
+ mult->state = MimeMultipartEpilogue; /* #58075. Fix suggested by jwz */
+ sig->state = MimeMultipartSignedEpilogue;
+
+ if (finalizing_p && sig->crypto_closure) {
+ /* Don't free these until this object is really going away -- keep them
+ around for the lifetime of the MIME object, so that we can get at the
+ security info of sub-parts of the currently-displayed message. */
+ ((MimeMultipartSignedClass*)obj->clazz)->crypto_free(sig->crypto_closure);
+ sig->crypto_closure = 0;
+ }
+
+ if (sig->sig_decoder_data) {
+ MimeDecoderDestroy(sig->sig_decoder_data, true);
+ sig->sig_decoder_data = 0;
+ }
+}
+
+static int MimeMultipartSigned_parse_eof(MimeObject* obj, bool abort_p) {
+ MimeMultipartSigned* sig = (MimeMultipartSigned*)obj;
+ int status = 0;
+
+ if (obj->closed_p) return 0;
+
+ /* Close off the signature, if we've gotten that far.
+ */
+ if (sig->state == MimeMultipartSignedSignatureHeaders ||
+ sig->state == MimeMultipartSignedSignatureFirstLine ||
+ sig->state == MimeMultipartSignedSignatureLine ||
+ sig->state == MimeMultipartSignedEpilogue) {
+ status = (((MimeMultipartSignedClass*)obj->clazz)->crypto_signature_eof)(
+ sig->crypto_closure, abort_p);
+ if (status < 0) return status;
+ }
+
+ if (!abort_p) {
+ /* Now that we've read both the signed object and the signature (and
+ have presumably verified the signature) write out a blurb, and then
+ the signed object.
+ */
+ status = MimeMultipartSigned_emit_child(obj);
+ if (status < 0) {
+ obj->closed_p = true;
+ return status;
+ }
+ }
+
+ MimeMultipartSigned_cleanup(obj, false);
+ return ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof(obj, abort_p);
+}
+
+static void MimeMultipartSigned_finalize(MimeObject* obj) {
+ MimeMultipartSigned_cleanup(obj, true);
+ ((MimeObjectClass*)&MIME_SUPERCLASS)->finalize(obj);
+}
+
+static int MimeMultipartSigned_parse_line(const char* line, int32_t length,
+ MimeObject* obj) {
+ MimeMultipart* mult = (MimeMultipart*)obj;
+ MimeMultipartSigned* sig = (MimeMultipartSigned*)obj;
+ MimeMultipartParseState old_state = mult->state;
+ bool hash_line_p = true;
+ bool no_headers_p = false;
+ int status = 0;
+
+ /* First do the parsing for normal multipart/ objects by handing it off to
+ the superclass method. This includes calling the create_child and
+ close_child methods.
+ */
+ status =
+ (((MimeObjectClass*)(&MIME_SUPERCLASS))->parse_line(line, length, obj));
+ if (status < 0) return status;
+
+ /* The instance variable MimeMultipartClass->state tracks motion through
+ the various stages of multipart/ parsing. The instance variable
+ MimeMultipartSigned->state tracks the difference between the first
+ part (the body) and the second part (the signature.) This second,
+ more specific state variable is updated by noticing the transitions
+ of the first, more general state variable.
+ */
+ if (old_state != mult->state) /* there has been a state change */
+ {
+ switch (mult->state) {
+ case MimeMultipartPreamble:
+ PR_ASSERT(0); /* can't move *in* to preamble state. */
+ sig->state = MimeMultipartSignedPreamble;
+ break;
+
+ case MimeMultipartHeaders:
+ /* If we're moving in to the Headers state, then that means
+ that this line is the preceding boundary string (and we
+ should ignore it.)
+ */
+ hash_line_p = false;
+
+ if (sig->state == MimeMultipartSignedPreamble)
+ sig->state = MimeMultipartSignedBodyFirstHeader;
+ else if (sig->state == MimeMultipartSignedBodyFirstLine ||
+ sig->state == MimeMultipartSignedBodyLine)
+ sig->state = MimeMultipartSignedSignatureHeaders;
+ else if (sig->state == MimeMultipartSignedSignatureFirstLine ||
+ sig->state == MimeMultipartSignedSignatureLine)
+ sig->state = MimeMultipartSignedEpilogue;
+ break;
+
+ case MimeMultipartPartFirstLine:
+ if (sig->state == MimeMultipartSignedBodyFirstHeader) {
+ sig->state = MimeMultipartSignedBodyFirstLine;
+ no_headers_p = true;
+ } else if (sig->state == MimeMultipartSignedBodyHeaders)
+ sig->state = MimeMultipartSignedBodyFirstLine;
+ else if (sig->state == MimeMultipartSignedSignatureHeaders)
+ sig->state = MimeMultipartSignedSignatureFirstLine;
+ else
+ sig->state = MimeMultipartSignedEpilogue;
+ break;
+
+ case MimeMultipartPartLine:
+
+ PR_ASSERT(sig->state == MimeMultipartSignedBodyFirstLine ||
+ sig->state == MimeMultipartSignedBodyLine ||
+ sig->state == MimeMultipartSignedSignatureFirstLine ||
+ sig->state == MimeMultipartSignedSignatureLine);
+
+ if (sig->state == MimeMultipartSignedBodyFirstLine)
+ sig->state = MimeMultipartSignedBodyLine;
+ else if (sig->state == MimeMultipartSignedSignatureFirstLine)
+ sig->state = MimeMultipartSignedSignatureLine;
+ break;
+
+ case MimeMultipartEpilogue:
+ sig->state = MimeMultipartSignedEpilogue;
+ break;
+
+ default: /* bad state */
+ NS_ERROR("bad state in MultipartSigned parse line");
+ return -1;
+ break;
+ }
+ }
+
+ /* Perform multipart/signed-related actions on this line based on the state
+ of the parser.
+ */
+ switch (sig->state) {
+ case MimeMultipartSignedPreamble:
+ /* Do nothing. */
+ break;
+
+ case MimeMultipartSignedBodyFirstLine:
+ /* We have just moved out of the MimeMultipartSignedBodyHeaders
+ state, so cache away the headers that apply only to the body part.
+ */
+ NS_ASSERTION(mult->hdrs, "null multipart hdrs");
+ NS_ASSERTION(!sig->body_hdrs,
+ "signed part shouldn't have already have body_hdrs");
+ sig->body_hdrs = mult->hdrs;
+ mult->hdrs = 0;
+
+ /* fall through. */
+ [[fallthrough]];
+ case MimeMultipartSignedBodyFirstHeader:
+ case MimeMultipartSignedBodyHeaders:
+ case MimeMultipartSignedBodyLine:
+
+ if (!sig->crypto_closure) {
+ /* Set error change */
+ PR_SetError(0, 0);
+ /* Initialize the signature verification library. */
+ sig->crypto_closure =
+ (((MimeMultipartSignedClass*)obj->clazz)->crypto_init)(obj);
+ if (!sig->crypto_closure) {
+ status = PR_GetError();
+ NS_ASSERTION(status < 0, "got non-negative status");
+ if (status >= 0) status = -1;
+ return status;
+ }
+ }
+
+ if (hash_line_p) {
+ /* this is the first hashed line if this is the first header
+ (that is, if it's the first line in the header state after
+ a state change.)
+ */
+ bool first_line_p =
+ (no_headers_p || sig->state == MimeMultipartSignedBodyFirstHeader);
+
+ if (sig->state == MimeMultipartSignedBodyFirstHeader)
+ sig->state = MimeMultipartSignedBodyHeaders;
+
+ /* The newline issues here are tricky, since both the newlines
+ before and after the boundary string are to be considered part
+ of the boundary: this is so that a part can be specified such
+ that it does not end in a trailing newline.
+
+ To implement this, we send a newline *before* each line instead
+ of after, except for the first line, which is not preceded by a
+ newline.
+
+ For purposes of cryptographic hashing, we always hash line
+ breaks as CRLF -- the canonical, on-the-wire linebreaks, since
+ we have no idea of knowing what line breaks were used on the
+ originating system (SMTP rightly destroys that information.)
+ */
+
+ /* Remove the trailing newline... */
+ if (length > 0 && line[length - 1] == '\n') length--;
+ if (length > 0 && line[length - 1] == '\r') length--;
+
+ PR_ASSERT(sig->crypto_closure);
+
+ if (!first_line_p) {
+ /* Push out a preceding newline... */
+ char nl[] = CRLF;
+ status = (((MimeMultipartSignedClass*)obj->clazz)
+ ->crypto_data_hash(nl, 2, sig->crypto_closure));
+ if (status < 0) return status;
+ }
+
+ /* Now push out the line sans trailing newline. */
+ if (length > 0)
+ status = (((MimeMultipartSignedClass*)obj->clazz)
+ ->crypto_data_hash(line, length, sig->crypto_closure));
+ if (status < 0) return status;
+ }
+ break;
+
+ case MimeMultipartSignedSignatureHeaders:
+
+ if (sig->crypto_closure && old_state != mult->state) {
+ /* We have just moved out of the MimeMultipartSignedBodyLine
+ state, so tell the signature verification library that we've
+ reached the end of the signed data.
+ */
+ status = (((MimeMultipartSignedClass*)obj->clazz)->crypto_data_eof)(
+ sig->crypto_closure, false);
+ if (status < 0) return status;
+ }
+ break;
+
+ case MimeMultipartSignedSignatureFirstLine:
+ /* We have just moved out of the MimeMultipartSignedSignatureHeaders
+ state, so cache away the headers that apply only to the sig part.
+ */
+ PR_ASSERT(mult->hdrs);
+ PR_ASSERT(!sig->sig_hdrs);
+ sig->sig_hdrs = mult->hdrs;
+ mult->hdrs = 0;
+
+ /* If the signature block has an encoding, set up a decoder for it.
+ (Similar logic is in MimeLeafClass->parse_begin.)
+ */
+ {
+ MimeDecoderData* (*fn)(MimeConverterOutputCallback, void*) = 0;
+ nsCString encoding;
+ encoding.Adopt(MimeHeaders_get(
+ sig->sig_hdrs, HEADER_CONTENT_TRANSFER_ENCODING, true, false));
+ if (encoding.IsEmpty())
+ ;
+ else if (!PL_strcasecmp(encoding.get(), ENCODING_BASE64))
+ fn = &MimeB64DecoderInit;
+ else if (!PL_strcasecmp(encoding.get(), ENCODING_QUOTED_PRINTABLE)) {
+ sig->sig_decoder_data = MimeQPDecoderInit(
+ ((MimeConverterOutputCallback)(((MimeMultipartSignedClass*)
+ obj->clazz)
+ ->crypto_signature_hash)),
+ sig->crypto_closure);
+ if (!sig->sig_decoder_data) return MIME_OUT_OF_MEMORY;
+ } else if (!PL_strcasecmp(encoding.get(), ENCODING_UUENCODE) ||
+ !PL_strcasecmp(encoding.get(), ENCODING_UUENCODE2) ||
+ !PL_strcasecmp(encoding.get(), ENCODING_UUENCODE3) ||
+ !PL_strcasecmp(encoding.get(), ENCODING_UUENCODE4))
+ fn = &MimeUUDecoderInit;
+ else if (!PL_strcasecmp(encoding.get(), ENCODING_YENCODE))
+ fn = &MimeYDecoderInit;
+ if (fn) {
+ sig->sig_decoder_data =
+ fn(((MimeConverterOutputCallback)(((MimeMultipartSignedClass*)
+ obj->clazz)
+ ->crypto_signature_hash)),
+ sig->crypto_closure);
+ if (!sig->sig_decoder_data) return MIME_OUT_OF_MEMORY;
+ }
+ }
+
+ /* Show these headers to the crypto module. */
+ if (hash_line_p) {
+ status =
+ (((MimeMultipartSignedClass*)obj->clazz)->crypto_signature_init)(
+ sig->crypto_closure, obj, sig->sig_hdrs);
+ if (status < 0) return status;
+ }
+
+ /* fall through. */
+ [[fallthrough]];
+ case MimeMultipartSignedSignatureLine:
+ if (hash_line_p) {
+ /* Feed this line into the signature verification routines. */
+
+ if (sig->sig_decoder_data)
+ status =
+ MimeDecoderWrite(sig->sig_decoder_data, line, length, nullptr);
+ else
+ status =
+ (((MimeMultipartSignedClass*)obj->clazz)
+ ->crypto_signature_hash(line, length, sig->crypto_closure));
+ if (status < 0) return status;
+ }
+ break;
+
+ case MimeMultipartSignedEpilogue:
+ /* Nothing special to do here. */
+ break;
+
+ default: /* bad state */
+ PR_ASSERT(0);
+ return -1;
+ }
+
+ return status;
+}
+
+static int MimeMultipartSigned_create_child(MimeObject* parent) {
+ /* Don't actually create a child -- we call the superclass create_child
+ method later, after we've fully parsed everything. (And we only call
+ it once, for part #1, and never for part #2 (the signature.))
+ */
+ MimeMultipart* mult = (MimeMultipart*)parent;
+ mult->state = MimeMultipartPartFirstLine;
+ return 0;
+}
+
+static int MimeMultipartSigned_close_child(MimeObject* obj) {
+ /* The close_child method on MimeMultipartSigned doesn't actually do
+ anything to the children list, since the create_child method also
+ doesn't do anything.
+ */
+ MimeMultipart* mult = (MimeMultipart*)obj;
+ MimeContainer* cont = (MimeContainer*)obj;
+ MimeMultipartSigned* msig = (MimeMultipartSigned*)obj;
+
+ if (msig->part_buffer)
+ /* Closes the tmp file, if there is one: doesn't free the part_buffer. */
+ MimePartBufferClose(msig->part_buffer);
+
+ if (mult->hdrs) /* duplicated from MimeMultipart_close_child, ugh. */
+ {
+ MimeHeaders_free(mult->hdrs);
+ mult->hdrs = 0;
+ }
+
+ /* Should be no kids yet. */
+ PR_ASSERT(cont->nchildren == 0);
+ if (cont->nchildren != 0) return -1;
+
+ return 0;
+}
+
+static int MimeMultipartSigned_parse_child_line(MimeObject* obj,
+ const char* line,
+ int32_t length,
+ bool first_line_p) {
+ MimeMultipartSigned* sig = (MimeMultipartSigned*)obj;
+ MimeContainer* cont = (MimeContainer*)obj;
+ int status = 0;
+
+ /* Shouldn't have made any sub-parts yet. */
+ PR_ASSERT(cont->nchildren == 0);
+ if (cont->nchildren != 0) return -1;
+
+ switch (sig->state) {
+ case MimeMultipartSignedPreamble:
+ case MimeMultipartSignedBodyFirstHeader:
+ case MimeMultipartSignedBodyHeaders:
+ // How'd we get here? Oh well, fall through.
+ NS_ERROR("wrong state in parse child line");
+ [[fallthrough]];
+ case MimeMultipartSignedBodyFirstLine:
+ PR_ASSERT(first_line_p);
+ if (!sig->part_buffer) {
+ sig->part_buffer = MimePartBufferCreate();
+ if (!sig->part_buffer) return MIME_OUT_OF_MEMORY;
+ }
+ /* fall through */
+ [[fallthrough]];
+ case MimeMultipartSignedBodyLine: {
+ /* This is the first part; we are buffering it, and will emit it all
+ at the end (so that we know whether the signature matches before
+ showing anything to the user.)
+ */
+
+ /* The newline issues here are tricky, since both the newlines
+ before and after the boundary string are to be considered part
+ of the boundary: this is so that a part can be specified such
+ that it does not end in a trailing newline.
+
+ To implement this, we send a newline *before* each line instead
+ of after, except for the first line, which is not preceded by a
+ newline.
+ */
+
+ /* Remove the trailing newline... */
+ if (length > 0 && line[length - 1] == '\n') length--;
+ if (length > 0 && line[length - 1] == '\r') length--;
+
+ PR_ASSERT(sig->part_buffer);
+ PR_ASSERT(first_line_p ==
+ (sig->state == MimeMultipartSignedBodyFirstLine));
+
+ if (!first_line_p) {
+ /* Push out a preceding newline... */
+ char nl[] = MSG_LINEBREAK;
+ status = MimePartBufferWrite(sig->part_buffer, nl, MSG_LINEBREAK_LEN);
+ if (status < 0) return status;
+ }
+
+ /* Now push out the line sans trailing newline. */
+ if (length > 0)
+ status = MimePartBufferWrite(sig->part_buffer, line, length);
+ if (status < 0) return status;
+ } break;
+
+ case MimeMultipartSignedSignatureHeaders:
+ // How'd we get here? Oh well, fall through.
+ NS_ERROR("should have already parse sig hdrs");
+ [[fallthrough]];
+ case MimeMultipartSignedSignatureFirstLine:
+ case MimeMultipartSignedSignatureLine:
+ /* Nothing to do here -- hashing of the signature part is handled up
+ in MimeMultipartSigned_parse_line().
+ */
+ break;
+
+ case MimeMultipartSignedEpilogue:
+ /* Too many kids? MimeMultipartSigned_create_child() should have
+ prevented us from getting here. */
+ NS_ERROR("too many kids?");
+ return -1;
+ break;
+
+ default: /* bad state */
+ NS_ERROR("bad state in multipart signed parse line");
+ return -1;
+ break;
+ }
+
+ return status;
+}
+
+static int MimeMultipartSigned_emit_child(MimeObject* obj) {
+ MimeMultipartSigned* sig = (MimeMultipartSigned*)obj;
+ MimeMultipart* mult = (MimeMultipart*)obj;
+ MimeContainer* cont = (MimeContainer*)obj;
+ int status = 0;
+ MimeObject* body;
+
+ if (!sig->crypto_closure) {
+ // We might have decided to skip processing this part.
+ return 0;
+ }
+
+ NS_ASSERTION(sig->crypto_closure, "no crypto closure");
+
+ /* Emit some HTML saying whether the signature was cool.
+ But don't emit anything if in FO_QUOTE_MESSAGE mode.
+ */
+ if (obj->options && obj->options->headers != MimeHeadersCitation &&
+ obj->options->write_html_p && obj->options->output_fn &&
+ sig->crypto_closure) {
+ // Calling crypto_generate_html may trigger wanted side effects,
+ // but we're no longer using its results.
+ char* html = (((MimeMultipartSignedClass*)obj->clazz)
+ ->crypto_generate_html(sig->crypto_closure));
+ PR_FREEIF(html);
+
+ /* Now that we have written out the crypto stamp, the outermost header
+ block is well and truly closed. If this is in fact the outermost
+ message, then run the post_header_html_fn now.
+ */
+ if (obj->options && obj->options->state &&
+ obj->options->generate_post_header_html_fn &&
+ !obj->options->state->post_header_html_run_p) {
+ MimeHeaders* outer_headers = nullptr;
+ MimeObject* p;
+ for (p = obj; p->parent; p = p->parent) outer_headers = p->headers;
+ NS_ASSERTION(obj->options->state->first_data_written_p,
+ "should have already written some data");
+ html = obj->options->generate_post_header_html_fn(
+ NULL, obj->options->html_closure, outer_headers);
+ 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) return status;
+ }
+ }
+ }
+
+ /* Oh, this is fairly nasty. We're skipping over our "create child" method
+ and using the one our superclass defines. Perhaps instead we should add
+ a new method on this class, and initialize that method to be the
+ create_child method of the superclass. Whatever.
+ */
+
+ /* The superclass method expects to find the headers for the part that it's
+ to create in mult->hdrs, so ensure that they're there. */
+ NS_ASSERTION(!mult->hdrs, "shouldn't already have hdrs for multipart");
+ if (mult->hdrs) MimeHeaders_free(mult->hdrs);
+ mult->hdrs = sig->body_hdrs;
+ sig->body_hdrs = 0;
+
+ /* Run the superclass create_child method.
+ */
+ status = (((MimeMultipartClass*)(&MIME_SUPERCLASS))->create_child(obj));
+ if (status < 0) return status;
+
+ // Notify the charset of the first part.
+ if (obj->options && !(obj->options->override_charset)) {
+ MimeObject* firstChild = ((MimeContainer*)obj)->children[0];
+ char* disposition = MimeHeaders_get(
+ firstChild->headers, HEADER_CONTENT_DISPOSITION, true, false);
+ // check if need to show as inline
+ if (!disposition) {
+ const char* content_type = firstChild->content_type;
+ if (!PL_strcasecmp(content_type, TEXT_PLAIN) ||
+ !PL_strcasecmp(content_type, TEXT_HTML) ||
+ !PL_strcasecmp(content_type, TEXT_MDL) ||
+ !PL_strcasecmp(content_type, MULTIPART_ALTERNATIVE) ||
+ !PL_strcasecmp(content_type, MULTIPART_RELATED) ||
+ !PL_strcasecmp(content_type, MESSAGE_NEWS) ||
+ !PL_strcasecmp(content_type, MESSAGE_RFC822)) {
+ char* ct =
+ MimeHeaders_get(mult->hdrs, HEADER_CONTENT_TYPE, false, false);
+ if (ct) {
+ char* cset = MimeHeaders_get_parameter(ct, "charset", NULL, NULL);
+ if (cset) {
+ mimeEmitterUpdateCharacterSet(obj->options, cset);
+ SetMailCharacterSetToMsgWindow(obj, cset);
+ PR_Free(cset);
+ }
+ PR_Free(ct);
+ }
+ }
+ }
+ }
+
+ // The js emitter wants to know about the newly created child. Because
+ // MimeMultipartSigned dummies out its create_child operation, the logic
+ // in MimeMultipart_parse_line that would normally provide this notification
+ // does not get to fire.
+ if (obj->options && obj->options->notify_nested_bodies) {
+ MimeObject* kid = ((MimeContainer*)obj)->children[0];
+ // The emitter is expecting the content type with parameters; not the fully
+ // parsed thing, so get it from raw. (We do not do it in the charset
+ // notification block that just happened because it already has complex
+ // if-checks that do not jive with us.
+ char* ct = MimeHeaders_get(mult->hdrs, HEADER_CONTENT_TYPE, false, false);
+ mimeEmitterAddHeaderField(obj->options, HEADER_CONTENT_TYPE,
+ ct ? ct : "text/plain");
+ PR_Free(ct);
+
+ char* part_path = mime_part_address(kid);
+ if (part_path) {
+ mimeEmitterAddHeaderField(obj->options, "x-jsemitter-part-path",
+ part_path);
+ PR_Free(part_path);
+ }
+ }
+
+ /* Retrieve the child that it created.
+ */
+ NS_ASSERTION(cont->nchildren == 1, "should only have one child");
+ if (cont->nchildren != 1) return -1;
+ body = cont->children[0];
+ NS_ASSERTION(body, "missing body");
+ if (!body) return -1;
+
+ if (mime_typep(body, (MimeObjectClass*)&mimeSuppressedCryptoClass)) {
+ ((MimeMultipartSignedClass*)obj->clazz)
+ ->crypto_notify_suppressed_child(sig->crypto_closure);
+ }
+
+#ifdef MIME_DRAFTS
+ if (body->options->decompose_file_p) {
+ body->options->signed_p = true;
+ if (!mime_typep(body, (MimeObjectClass*)&mimeMultipartClass) &&
+ body->options->decompose_file_init_fn)
+ body->options->decompose_file_init_fn(body->options->stream_closure,
+ body->headers);
+ }
+#endif /* MIME_DRAFTS */
+
+ /* If there's no part_buffer, this is a zero-length signed message? */
+ if (sig->part_buffer) {
+#ifdef MIME_DRAFTS
+ if (body->options->decompose_file_p &&
+ !mime_typep(body, (MimeObjectClass*)&mimeMultipartClass) &&
+ body->options->decompose_file_output_fn)
+ status =
+ MimePartBufferRead(sig->part_buffer,
+ /* The (MimeConverterOutputCallback) cast is to
+ turn the `void' argument into `MimeObject'. */
+ ((MimeConverterOutputCallback)
+ body->options->decompose_file_output_fn),
+ body->options->stream_closure);
+ else
+#endif /* MIME_DRAFTS */
+
+ status = MimePartBufferRead(
+ sig->part_buffer,
+ /* The (MimeConverterOutputCallback) cast is to turn the
+ `void' argument into `MimeObject'. */
+ ((MimeConverterOutputCallback)body->clazz->parse_buffer), body);
+ if (status < 0) return status;
+ }
+
+ MimeMultipartSigned_cleanup(obj, false);
+
+ /* Done parsing. */
+ status = body->clazz->parse_eof(body, false);
+ if (status < 0) return status;
+ status = body->clazz->parse_end(body, false);
+ if (status < 0) return status;
+
+#ifdef MIME_DRAFTS
+ if (body->options->decompose_file_p &&
+ !mime_typep(body, (MimeObjectClass*)&mimeMultipartClass) &&
+ body->options->decompose_file_close_fn)
+ body->options->decompose_file_close_fn(body->options->stream_closure);
+#endif /* MIME_DRAFTS */
+
+ /* Put out a separator after every multipart/signed object. */
+ status = MimeObject_write_separator(obj);
+ if (status < 0) return status;
+
+ return 0;
+}