summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/mime/src/mimemult.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mailnews/mime/src/mimemult.cpp')
-rw-r--r--comm/mailnews/mime/src/mimemult.cpp676
1 files changed, 676 insertions, 0 deletions
diff --git a/comm/mailnews/mime/src/mimemult.cpp b/comm/mailnews/mime/src/mimemult.cpp
new file mode 100644
index 0000000000..29caf5f5b0
--- /dev/null
+++ b/comm/mailnews/mime/src/mimemult.cpp
@@ -0,0 +1,676 @@
+/* -*- 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 "msgCore.h"
+#include "mimemult.h"
+#include "mimemoz2.h"
+#include "mimeeobj.h"
+
+#include "prlog.h"
+#include "prmem.h"
+#include "plstr.h"
+#include "prio.h"
+#include "nsMimeStringResources.h"
+#include "nsMimeTypes.h"
+#include <ctype.h>
+
+#ifdef XP_MACOSX
+extern MimeObjectClass mimeMultipartAppleDoubleClass;
+#endif
+
+#define MIME_SUPERCLASS mimeContainerClass
+MimeDefClass(MimeMultipart, MimeMultipartClass, mimeMultipartClass,
+ &MIME_SUPERCLASS);
+
+static int MimeMultipart_initialize(MimeObject*);
+static void MimeMultipart_finalize(MimeObject*);
+static int MimeMultipart_parse_line(const char* line, int32_t length,
+ MimeObject*);
+static int MimeMultipart_parse_eof(MimeObject* object, bool abort_p);
+
+static MimeMultipartBoundaryType MimeMultipart_check_boundary(MimeObject*,
+ const char*,
+ int32_t);
+static int MimeMultipart_create_child(MimeObject*);
+static bool MimeMultipart_output_child_p(MimeObject*, MimeObject*);
+static int MimeMultipart_parse_child_line(MimeObject*, const char*, int32_t,
+ bool);
+static int MimeMultipart_close_child(MimeObject*);
+
+extern "C" MimeObjectClass mimeMultipartAlternativeClass;
+extern "C" MimeObjectClass mimeMultipartRelatedClass;
+extern "C" MimeObjectClass mimeMultipartSignedClass;
+extern "C" MimeObjectClass mimeInlineTextVCardClass;
+extern "C" MimeExternalObjectClass mimeExternalObjectClass;
+extern "C" MimeSuppressedCryptoClass mimeSuppressedCryptoClass;
+
+#if defined(DEBUG) && defined(XP_UNIX)
+static int MimeMultipart_debug_print(MimeObject*, PRFileDesc*, int32_t);
+#endif
+
+static int MimeMultipartClassInitialize(MimeMultipartClass* clazz) {
+ MimeObjectClass* oclass = (MimeObjectClass*)clazz;
+ MimeMultipartClass* mclass = (MimeMultipartClass*)clazz;
+
+ PR_ASSERT(!oclass->class_initialized);
+ oclass->initialize = MimeMultipart_initialize;
+ oclass->finalize = MimeMultipart_finalize;
+ oclass->parse_line = MimeMultipart_parse_line;
+ oclass->parse_eof = MimeMultipart_parse_eof;
+
+ mclass->check_boundary = MimeMultipart_check_boundary;
+ mclass->create_child = MimeMultipart_create_child;
+ mclass->output_child_p = MimeMultipart_output_child_p;
+ mclass->parse_child_line = MimeMultipart_parse_child_line;
+ mclass->close_child = MimeMultipart_close_child;
+
+#if defined(DEBUG) && defined(XP_UNIX)
+ oclass->debug_print = MimeMultipart_debug_print;
+#endif
+
+ return 0;
+}
+
+static int MimeMultipart_initialize(MimeObject* object) {
+ MimeMultipart* mult = (MimeMultipart*)object;
+ char* ct;
+
+ /* This is an abstract class; it shouldn't be directly instantiated. */
+ PR_ASSERT(object->clazz != (MimeObjectClass*)&mimeMultipartClass);
+
+ ct = MimeHeaders_get(object->headers, HEADER_CONTENT_TYPE, false, false);
+ mult->boundary =
+ (ct ? MimeHeaders_get_parameter(ct, HEADER_PARM_BOUNDARY, NULL, NULL)
+ : 0);
+ PR_FREEIF(ct);
+ mult->state = MimeMultipartPreamble;
+ return ((MimeObjectClass*)&MIME_SUPERCLASS)->initialize(object);
+}
+
+static void MimeMultipart_finalize(MimeObject* object) {
+ MimeMultipart* mult = (MimeMultipart*)object;
+
+ object->clazz->parse_eof(object, false);
+
+ PR_FREEIF(mult->boundary);
+ if (mult->hdrs) MimeHeaders_free(mult->hdrs);
+ mult->hdrs = 0;
+ ((MimeObjectClass*)&MIME_SUPERCLASS)->finalize(object);
+}
+
+int MimeWriteAString(MimeObject* obj, const nsACString& string) {
+ const nsCString& flatString = PromiseFlatCString(string);
+ return MimeObject_write(obj, flatString.get(), flatString.Length(), true);
+}
+
+static int MimeMultipart_parse_line(const char* line, int32_t length,
+ MimeObject* obj) {
+ MimeMultipart* mult = (MimeMultipart*)obj;
+ MimeContainer* container = (MimeContainer*)obj;
+ int status = 0;
+ MimeMultipartBoundaryType boundary;
+
+ NS_ASSERTION(line && *line, "empty line in multipart parse_line");
+ if (!line || !*line) return -1;
+
+ NS_ASSERTION(!obj->closed_p, "obj shouldn't already be closed");
+ if (obj->closed_p) return -1;
+
+ /* If we're supposed to write this object, but aren't supposed to convert
+ it to HTML, simply pass it through unaltered. */
+ if (obj->output_p && obj->options && !obj->options->write_html_p &&
+ obj->options->output_fn &&
+ obj->options->format_out != nsMimeOutput::nsMimeMessageAttach)
+ return MimeObject_write(obj, line, length, true);
+
+ if (mult->state == MimeMultipartEpilogue) /* already done */
+ boundary = MimeMultipartBoundaryTypeNone;
+ else
+ boundary =
+ ((MimeMultipartClass*)obj->clazz)->check_boundary(obj, line, length);
+
+ if (boundary == MimeMultipartBoundaryTypeTerminator ||
+ boundary == MimeMultipartBoundaryTypeSeparator) {
+ /* Match! Close the currently-open part, move on to the next
+ state, and discard this line.
+ */
+ bool endOfPart = (mult->state != MimeMultipartPreamble);
+ if (endOfPart) status = ((MimeMultipartClass*)obj->clazz)->close_child(obj);
+ if (status < 0) return status;
+
+ if (boundary == MimeMultipartBoundaryTypeTerminator)
+ mult->state = MimeMultipartEpilogue;
+ else {
+ mult->state = MimeMultipartHeaders;
+
+ /* Reset the header parser for this upcoming part. */
+ NS_ASSERTION(!mult->hdrs, "mult->hdrs should be null here");
+ if (mult->hdrs) MimeHeaders_free(mult->hdrs);
+ mult->hdrs = MimeHeaders_new();
+ if (!mult->hdrs) return MIME_OUT_OF_MEMORY;
+ if (obj->options && obj->options->state &&
+ obj->options->state->partsToStrip.Length() > 0) {
+ nsAutoCString newPart(mime_part_address(obj));
+ newPart.Append('.');
+ newPart.AppendInt(container->nchildren + 1);
+ obj->options->state->strippingPart = false;
+ // check if this is a sub-part of a part we're stripping.
+ for (uint32_t partIndex = 0;
+ partIndex < obj->options->state->partsToStrip.Length();
+ partIndex++) {
+ nsCString& curPartToStrip =
+ obj->options->state->partsToStrip[partIndex];
+ if (newPart.Find(curPartToStrip) == 0 &&
+ (newPart.Length() == curPartToStrip.Length() ||
+ newPart.CharAt(curPartToStrip.Length()) == '.')) {
+ obj->options->state->strippingPart = true;
+ if (partIndex < obj->options->state->detachToFiles.Length())
+ obj->options->state->detachedFilePath =
+ obj->options->state->detachToFiles[partIndex];
+ break;
+ }
+ }
+ }
+ }
+
+ // if stripping out attachments, write the boundary line. Otherwise, return
+ // to ignore it.
+ if (obj->options &&
+ obj->options->format_out == nsMimeOutput::nsMimeMessageAttach) {
+ // Because MimeMultipart_parse_child_line strips out the
+ // the CRLF of the last line before the end of a part, we need to add that
+ // back in here.
+ if (endOfPart) MimeWriteAString(obj, nsLiteralCString(MSG_LINEBREAK));
+
+ status = MimeObject_write(obj, line, length, true);
+ }
+ return 0;
+ }
+
+ /* Otherwise, this isn't a boundary string. So do whatever it is we
+ should do with this line (parse it as a header, feed it to the
+ child part, ignore it, etc.) */
+
+ switch (mult->state) {
+ case MimeMultipartPreamble:
+ case MimeMultipartEpilogue:
+ /* Ignore this line. */
+ break;
+
+ case MimeMultipartHeaders:
+ /* Parse this line as a header for the sub-part. */
+ {
+ status = MimeHeaders_parse_line(line, length, mult->hdrs);
+ bool stripping = false;
+
+ if (status < 0) return status;
+
+ // If this line is blank, we're now done parsing headers, and should
+ // now examine the content-type to create this "body" part.
+ //
+ if (*line == '\r' || *line == '\n') {
+ if (obj->options && obj->options->state &&
+ obj->options->state->strippingPart) {
+ stripping = true;
+ bool detachingPart =
+ obj->options->state->detachedFilePath.Length() > 0;
+
+ nsAutoCString fileName;
+ fileName.Adopt(MimeHeaders_get_name(mult->hdrs, obj->options));
+ // clang-format off
+ if (detachingPart) {
+ char *contentType =
+ MimeHeaders_get(mult->hdrs, "Content-Type", false, false);
+ if (contentType) {
+ MimeWriteAString(obj, "Content-Type: "_ns);
+ MimeWriteAString(obj, nsDependentCString(contentType));
+ PR_Free(contentType);
+ }
+ MimeWriteAString(obj, nsLiteralCString(MSG_LINEBREAK));
+ MimeWriteAString(obj, "Content-Disposition: attachment; filename=\""_ns);
+ MimeWriteAString(obj, fileName);
+ MimeWriteAString(obj, "\""_ns MSG_LINEBREAK);
+ MimeWriteAString(obj, "X-Mozilla-External-Attachment-URL: "_ns);
+ MimeWriteAString(obj, obj->options->state->detachedFilePath);
+ MimeWriteAString(obj, nsLiteralCString(MSG_LINEBREAK));
+ MimeWriteAString(obj, "X-Mozilla-Altered: AttachmentDetached; date=\""_ns);
+ } else {
+ nsAutoCString header("Content-Type: text/x-moz-deleted; name=\"Deleted: ");
+ header.Append(fileName);
+ MimeWriteAString(obj, header);
+ MimeWriteAString(obj, "\""_ns MSG_LINEBREAK
+ "Content-Transfer-Encoding: 8bit"_ns MSG_LINEBREAK);
+ MimeWriteAString(obj, "Content-Disposition: inline; filename=\"Deleted: "_ns);
+ MimeWriteAString(obj, fileName);
+ MimeWriteAString(obj, "\""_ns MSG_LINEBREAK
+ "X-Mozilla-Altered: AttachmentDeleted; date=\""_ns);
+ }
+ nsCString result;
+ char timeBuffer[128];
+ PRExplodedTime now;
+ PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &now);
+ PR_FormatTimeUSEnglish(timeBuffer, sizeof(timeBuffer),
+ "%a %b %d %H:%M:%S %Y", &now);
+ MimeWriteAString(obj, nsDependentCString(timeBuffer));
+ MimeWriteAString(obj, "\""_ns MSG_LINEBREAK);
+ MimeWriteAString(obj, MSG_LINEBREAK
+ "You deleted an attachment from this message. The original "_ns
+ "MIME headers for the attachment were:"_ns MSG_LINEBREAK);
+ MimeHeaders_write_raw_headers(mult->hdrs, obj->options, false);
+ // clang-format on
+ }
+ int32_t old_nchildren = container->nchildren;
+ status = ((MimeMultipartClass*)obj->clazz)->create_child(obj);
+ if (status < 0) return status;
+ NS_ASSERTION(mult->state != MimeMultipartHeaders,
+ "mult->state shouldn't be MimeMultipartHeaders");
+
+ if (!stripping && container->nchildren > old_nchildren &&
+ obj->options &&
+ !mime_typep(obj,
+ (MimeObjectClass*)&mimeMultipartAlternativeClass)) {
+ // Notify emitter about content type and part path.
+ MimeObject* kid = container->children[container->nchildren - 1];
+ MimeMultipart_notify_emitter(kid);
+ }
+ }
+ break;
+ }
+
+ case MimeMultipartPartFirstLine:
+ /* Hand this line off to the sub-part. */
+ status = (((MimeMultipartClass*)obj->clazz)
+ ->parse_child_line(obj, line, length, true));
+ if (status < 0) return status;
+ mult->state = MimeMultipartPartLine;
+ break;
+
+ case MimeMultipartPartLine:
+ /* Hand this line off to the sub-part. */
+ status = (((MimeMultipartClass*)obj->clazz)
+ ->parse_child_line(obj, line, length, false));
+ if (status < 0) return status;
+ break;
+
+ default:
+ NS_ERROR("unexpected state in parse line");
+ return -1;
+ }
+
+ if (obj->options &&
+ obj->options->format_out == nsMimeOutput::nsMimeMessageAttach &&
+ (!(obj->options->state && obj->options->state->strippingPart) &&
+ mult->state != MimeMultipartPartLine))
+ return MimeObject_write(obj, line, length, false);
+ return 0;
+}
+
+void MimeMultipart_notify_emitter(MimeObject* obj) {
+ char* ct = nullptr;
+
+ NS_ASSERTION(obj->options,
+ "MimeMultipart_notify_emitter called with null options");
+ if (!obj->options) return;
+
+ ct = MimeHeaders_get(obj->headers, HEADER_CONTENT_TYPE, false, false);
+ if (obj->options->notify_nested_bodies) {
+ mimeEmitterAddHeaderField(obj->options, HEADER_CONTENT_TYPE,
+ ct ? ct : TEXT_PLAIN);
+ char* part_path = mime_part_address(obj);
+ if (part_path) {
+ mimeEmitterAddHeaderField(obj->options, "x-jsemitter-part-path",
+ part_path);
+ PR_Free(part_path);
+ }
+ }
+
+ // Examine the headers and see if there is a special charset
+ // (i.e. non US-ASCII) for this message. If so, we need to
+ // tell the emitter that this is the case for use in any
+ // possible reply or forward operation.
+ if (ct &&
+ (obj->options->notify_nested_bodies || MimeObjectIsMessageBody(obj))) {
+ char* cset = MimeHeaders_get_parameter(ct, "charset", NULL, NULL);
+ if (cset) {
+ mimeEmitterUpdateCharacterSet(obj->options, cset);
+ if (!obj->options->override_charset)
+ // Also set this charset to msgWindow
+ SetMailCharacterSetToMsgWindow(obj, cset);
+ PR_Free(cset);
+ }
+ }
+
+ PR_FREEIF(ct);
+}
+
+static MimeMultipartBoundaryType MimeMultipart_check_boundary(MimeObject* obj,
+ const char* line,
+ int32_t length) {
+ MimeMultipart* mult = (MimeMultipart*)obj;
+ int32_t blen;
+ bool term_p;
+
+ if (!mult->boundary || line[0] != '-' || line[1] != '-')
+ return MimeMultipartBoundaryTypeNone;
+
+ /* This is a candidate line to be a boundary. Check it out... */
+ blen = strlen(mult->boundary);
+ term_p = false;
+
+ /* strip trailing whitespace (including the newline.) */
+ while (length > 2 && IS_SPACE(line[length - 1])) length--;
+
+ /* Could this be a terminating boundary? */
+ if (length == blen + 4 && line[length - 1] == '-' &&
+ line[length - 2] == '-') {
+ term_p = true;
+ }
+
+ // looks like we have a separator but first, we need to check it's not for one
+ // of the part's children.
+ MimeContainer* cont = (MimeContainer*)obj;
+ if (cont->nchildren > 0) {
+ MimeObject* kid = cont->children[cont->nchildren - 1];
+ if (kid)
+ if (mime_typep(kid, (MimeObjectClass*)&mimeMultipartClass)) {
+ // Don't ask the kid to check the boundary if it has already detected a
+ // Teminator
+ MimeMultipart* mult = (MimeMultipart*)kid;
+ if (mult->state != MimeMultipartEpilogue)
+ if (MimeMultipart_check_boundary(kid, line, length) !=
+ MimeMultipartBoundaryTypeNone)
+ return MimeMultipartBoundaryTypeNone;
+ }
+ }
+
+ if (term_p) length -= 2;
+
+ if (blen == length - 2 && !strncmp(line + 2, mult->boundary, length - 2))
+ return (term_p ? MimeMultipartBoundaryTypeTerminator
+ : MimeMultipartBoundaryTypeSeparator);
+ else
+ return MimeMultipartBoundaryTypeNone;
+}
+
+static int MimeMultipart_create_child(MimeObject* obj) {
+ MimeMultipart* mult = (MimeMultipart*)obj;
+ int status;
+ char* ct = (mult->hdrs ? MimeHeaders_get(mult->hdrs, HEADER_CONTENT_TYPE,
+ true, false)
+ : 0);
+ const char* dct = (((MimeMultipartClass*)obj->clazz)->default_part_type);
+ MimeObject* body = NULL;
+
+ mult->state = MimeMultipartPartFirstLine;
+ if (obj->options) obj->options->is_child = true;
+
+ /* Don't pass in NULL as the content-type (this means that the
+ auto-uudecode-hack won't ever be done for subparts of a
+ multipart, but only for untyped children of message/rfc822.
+ */
+ body = mime_create(((ct && *ct) ? ct : (dct ? dct : TEXT_PLAIN)), mult->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;
+ }
+
+#ifdef MIME_DRAFTS
+ if (obj->options && obj->options->decompose_file_p &&
+ obj->options->is_multipart_msg && obj->options->decompose_file_init_fn) {
+ if (!mime_typep(obj, (MimeObjectClass*)&mimeMultipartRelatedClass) &&
+ !mime_typep(obj, (MimeObjectClass*)&mimeMultipartAlternativeClass) &&
+ !mime_typep(obj, (MimeObjectClass*)&mimeMultipartSignedClass) &&
+ /* bug 21869 -- due to the fact that we are not generating the
+ correct mime class object for content-typ multipart/signed part
+ the above check failed. to solve the problem in general and not
+ to cause early termination when parsing message for opening as
+ draft we can simply make sure that the child is not a multipart
+ mime object. this way we could have a proper decomposing message
+ part functions set correctly */
+ !mime_typep(body, (MimeObjectClass*)&mimeMultipartClass) &&
+ !((mime_typep(body, (MimeObjectClass*)&mimeExternalObjectClass) ||
+ mime_typep(body, (MimeObjectClass*)&mimeSuppressedCryptoClass)) &&
+ (!strcmp(body->content_type, "text/vcard") ||
+ !strcmp(body->content_type, "text/x-vcard")))) {
+ status = obj->options->decompose_file_init_fn(
+ obj->options->stream_closure, mult->hdrs);
+ if (status < 0) return status;
+ }
+ }
+#endif /* MIME_DRAFTS */
+
+ /* Now that we've added this new object to our list of children,
+ start its parser going (if we want to display it.)
+ */
+ body->output_p =
+ (((MimeMultipartClass*)obj->clazz)->output_child_p(obj, body));
+ if (body->output_p) {
+ status = body->clazz->parse_begin(body);
+
+#ifdef XP_MACOSX
+ /* if we are saving an apple double attachment, we need to set correctly the
+ * content type of the channel */
+ if (mime_typep(obj, (MimeObjectClass*)&mimeMultipartAppleDoubleClass)) {
+ mime_stream_data* msd = (mime_stream_data*)body->options->stream_closure;
+ if (!body->options->write_html_p && body->content_type &&
+ !PL_strcasecmp(body->content_type, APPLICATION_APPLEFILE)) {
+ if (msd && msd->channel)
+ msd->channel->SetContentType(nsLiteralCString(APPLICATION_APPLEFILE));
+ }
+ }
+#endif
+
+ if (status < 0) return status;
+ }
+
+ return 0;
+}
+
+static bool MimeMultipart_output_child_p(MimeObject* obj, MimeObject* child) {
+ /* We don't output a child if we're stripping it. */
+ if (obj->options && obj->options->state && obj->options->state->strippingPart)
+ return false;
+ /* if we are saving an apple double attachment, ignore the appledouble wrapper
+ * part */
+ return (obj->options && obj->options->write_html_p) ||
+ PL_strcasecmp(child->content_type, MULTIPART_APPLEDOUBLE);
+}
+
+static int MimeMultipart_close_child(MimeObject* object) {
+ MimeMultipart* mult = (MimeMultipart*)object;
+ MimeContainer* cont = (MimeContainer*)object;
+
+ if (!mult->hdrs) return 0;
+
+ MimeHeaders_free(mult->hdrs);
+ mult->hdrs = 0;
+
+ NS_ASSERTION(cont->nchildren > 0, "badly formed mime message");
+ if (cont->nchildren > 0) {
+ MimeObject* kid = cont->children[cont->nchildren - 1];
+ // If we have a child and it has not already been closed, process it.
+ // The kid would be already be closed if we encounter a multipart section
+ // that did not have a fully delineated header block. No header block means
+ // no creation of a new child, but the termination case still happens and
+ // we still end up here. Obviously, we don't want to close the child a
+ // second time and the best thing we can do is nothing.
+ if (kid && !kid->closed_p) {
+ int status;
+ status = kid->clazz->parse_eof(kid, false);
+ if (status < 0) return status;
+ status = kid->clazz->parse_end(kid, false);
+ if (status < 0) return status;
+
+#ifdef MIME_DRAFTS
+ if (object->options && object->options->decompose_file_p &&
+ object->options->is_multipart_msg &&
+ object->options->decompose_file_close_fn) {
+ // clang-format off
+ if (!mime_typep(object, (MimeObjectClass *)&mimeMultipartRelatedClass) &&
+ !mime_typep(object, (MimeObjectClass *)&mimeMultipartAlternativeClass) &&
+ !mime_typep(object, (MimeObjectClass *)&mimeMultipartSignedClass) &&
+ /* bug 21869 -- due to the fact that we are not generating the
+ correct mime class object for content-typ multipart/signed part
+ the above check failed. to solve the problem in general and not
+ to cause early termination when parsing message for opening as
+ draft we can simply make sure that the child is not a multipart
+ mime object. this way we could have a proper decomposing message
+ part functions set correctly */
+ !mime_typep(kid, (MimeObjectClass *)&mimeMultipartClass) &&
+ !((mime_typep(kid, (MimeObjectClass *)&mimeExternalObjectClass) ||
+ mime_typep(kid, (MimeObjectClass *)&mimeSuppressedCryptoClass)) &&
+ (!strcmp(kid->content_type, "text/vcard") ||
+ !strcmp(kid->content_type, "text/x-vcard")))) {
+ status = object->options->decompose_file_close_fn(
+ object->options->stream_closure);
+ if (status < 0) return status;
+ }
+ // clang-format on
+ }
+#endif /* MIME_DRAFTS */
+ }
+ }
+ return 0;
+}
+
+static int MimeMultipart_parse_child_line(MimeObject* obj, const char* line,
+ int32_t length, bool first_line_p) {
+ MimeContainer* cont = (MimeContainer*)obj;
+ int status;
+ MimeObject* kid;
+
+ PR_ASSERT(cont->nchildren > 0);
+ if (cont->nchildren <= 0) return -1;
+
+ kid = cont->children[cont->nchildren - 1];
+ PR_ASSERT(kid);
+ if (!kid) return -1;
+
+#ifdef MIME_DRAFTS
+ if (obj->options && obj->options->decompose_file_p &&
+ obj->options->is_multipart_msg &&
+ obj->options->decompose_file_output_fn) {
+ if (!mime_typep(obj, (MimeObjectClass*)&mimeMultipartAlternativeClass) &&
+ !mime_typep(obj, (MimeObjectClass*)&mimeMultipartRelatedClass) &&
+ !mime_typep(obj, (MimeObjectClass*)&mimeMultipartSignedClass) &&
+ /* bug 21869 -- due to the fact that we are not generating the
+ correct mime class object for content-typ multipart/signed part
+ the above check failed. to solve the problem in general and not
+ to cause early termination when parsing message for opening as
+ draft we can simply make sure that the child is not a multipart
+ mime object. this way we could have a proper decomposing message
+ part functions set correctly */
+ !mime_typep(kid, (MimeObjectClass*)&mimeMultipartClass) &&
+ !((mime_typep(kid, (MimeObjectClass*)&mimeExternalObjectClass) ||
+ mime_typep(kid, (MimeObjectClass*)&mimeSuppressedCryptoClass)) &&
+ (!strcmp(kid->content_type, "text/vcard") ||
+ !strcmp(kid->content_type, "text/x-vcard"))))
+ return obj->options->decompose_file_output_fn(
+ line, length, obj->options->stream_closure);
+ }
+#endif /* MIME_DRAFTS */
+
+ /* 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--;
+
+ if (!first_line_p) {
+ /* Push out a preceding newline... */
+ char nl[] = MSG_LINEBREAK;
+ status = kid->clazz->parse_buffer(nl, MSG_LINEBREAK_LEN, kid);
+ if (status < 0) return status;
+ }
+
+ /* Now push out the line sans trailing newline. */
+ return kid->clazz->parse_buffer(line, length, kid);
+}
+
+static int MimeMultipart_parse_eof(MimeObject* obj, bool abort_p) {
+ MimeMultipart* mult = (MimeMultipart*)obj;
+ MimeContainer* cont = (MimeContainer*)obj;
+
+ if (obj->closed_p) return 0;
+
+ /* Push out the last trailing line if there's one in the buffer. If
+ this happens, this object does not end in a trailing newline (and
+ the parse_line method will be called with a string with no trailing
+ newline, which isn't the usual case.)
+ */
+ if (!abort_p && obj->ibuffer_fp > 0) {
+ /* There is leftover data without a terminating newline. */
+ int status = obj->clazz->parse_line(obj->ibuffer, obj->ibuffer_fp, obj);
+ obj->ibuffer_fp = 0;
+ if (status < 0) {
+ obj->closed_p = true;
+ return status;
+ }
+ }
+
+ /* Now call parse_eof for our active child, if there is one.
+ */
+ if (cont->nchildren > 0 && (mult->state == MimeMultipartPartLine ||
+ mult->state == MimeMultipartPartFirstLine)) {
+ MimeObject* kid = cont->children[cont->nchildren - 1];
+ NS_ASSERTION(kid, "not expecting null kid");
+ if (kid) {
+ int status = kid->clazz->parse_eof(kid, abort_p);
+ if (status < 0) return status;
+ }
+ }
+
+ return ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof(obj, abort_p);
+}
+
+#if defined(DEBUG) && defined(XP_UNIX)
+static int MimeMultipart_debug_print(MimeObject* obj, PRFileDesc* stream,
+ int32_t depth) {
+ /* MimeMultipart *mult = (MimeMultipart *) obj; */
+ MimeContainer* cont = (MimeContainer*)obj;
+ char* addr = mime_part_address(obj);
+ int i;
+ for (i = 0; i < depth; i++) PR_Write(stream, " ", 2);
+ /**
+ fprintf(stream, "<%s %s (%d kid%s) boundary=%s 0x%08X>\n",
+ obj->clazz->class_name,
+ addr ? addr : "???",
+ cont->nchildren, (cont->nchildren == 1 ? "" : "s"),
+ (mult->boundary ? mult->boundary : "(none)"),
+ (uint32_t) mult);
+ **/
+ PR_FREEIF(addr);
+
+ /*
+ if (cont->nchildren > 0)
+ fprintf(stream, "\n");
+ */
+
+ for (i = 0; i < cont->nchildren; i++) {
+ MimeObject* kid = cont->children[i];
+ int status = kid->clazz->debug_print(kid, stream, depth + 1);
+ if (status < 0) return status;
+ }
+
+ /*
+ if (cont->nchildren > 0)
+ fprintf(stream, "\n");
+ */
+
+ return 0;
+}
+#endif