summaryrefslogtreecommitdiffstats
path: root/mailnews/mime/src/mimei.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mailnews/mime/src/mimei.cpp')
-rw-r--r--mailnews/mime/src/mimei.cpp1920
1 files changed, 1920 insertions, 0 deletions
diff --git a/mailnews/mime/src/mimei.cpp b/mailnews/mime/src/mimei.cpp
new file mode 100644
index 000000000..c0a134a62
--- /dev/null
+++ b/mailnews/mime/src/mimei.cpp
@@ -0,0 +1,1920 @@
+/* -*- 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/.
+ * This Original Code has been modified by IBM Corporation. Modifications made by IBM
+ * described herein are Copyright (c) International Business Machines Corporation, 2000.
+ * Modifications to Mozilla code or documentation identified per MPL Section 3.3
+ *
+ * Date Modified by Description of modification
+ * 04/20/2000 IBM Corp. OS/2 VisualAge build.
+ */
+
+#include "nsCOMPtr.h"
+#include "mimeobj.h" /* MimeObject (abstract) */
+#include "mimecont.h" /* |--- MimeContainer (abstract) */
+#include "mimemult.h" /* | |--- MimeMultipart (abstract) */
+#include "mimemmix.h" /* | | |--- MimeMultipartMixed */
+#include "mimemdig.h" /* | | |--- MimeMultipartDigest */
+#include "mimempar.h" /* | | |--- MimeMultipartParallel */
+#include "mimemalt.h" /* | | |--- MimeMultipartAlternative */
+#include "mimemrel.h" /* | | |--- MimeMultipartRelated */
+#include "mimemapl.h" /* | | |--- MimeMultipartAppleDouble */
+#include "mimesun.h" /* | | |--- MimeSunAttachment */
+#include "mimemsig.h" /* | | |--- MimeMultipartSigned (abstract)*/
+#ifdef ENABLE_SMIME
+#include "mimemcms.h" /* | | |---MimeMultipartSignedCMS */
+#endif
+#include "mimecryp.h" /* | |--- MimeEncrypted (abstract) */
+#ifdef ENABLE_SMIME
+#include "mimecms.h" /* | | |--- MimeEncryptedPKCS7 */
+#endif
+#include "mimemsg.h" /* | |--- MimeMessage */
+#include "mimeunty.h" /* | |--- MimeUntypedText */
+#include "mimeleaf.h" /* |--- MimeLeaf (abstract) */
+#include "mimetext.h" /* | |--- MimeInlineText (abstract) */
+#include "mimetpla.h" /* | | |--- MimeInlineTextPlain */
+#include "mimethpl.h" /* | | | |--- M.I.TextHTMLAsPlaintext */
+#include "mimetpfl.h" /* | | |--- MimeInlineTextPlainFlowed */
+#include "mimethtm.h" /* | | |--- MimeInlineTextHTML */
+#include "mimethsa.h" /* | | | |--- M.I.TextHTMLSanitized */
+#include "mimeTextHTMLParsed.h" /*| | |--- M.I.TextHTMLParsed */
+#include "mimetric.h" /* | | |--- MimeInlineTextRichtext */
+#include "mimetenr.h" /* | | | |--- MimeInlineTextEnriched */
+/* SUPPORTED VIA PLUGIN | | |--- MimeInlineTextVCard */
+#include "mimeiimg.h" /* | |--- MimeInlineImage */
+#include "mimeeobj.h" /* | |--- MimeExternalObject */
+#include "mimeebod.h" /* |--- MimeExternalBody */
+ /* If you add classes here,also add them to mimei.h */
+#include "prlog.h"
+#include "prmem.h"
+#include "prenv.h"
+#include "plstr.h"
+#include "prlink.h"
+#include "prprf.h"
+#include "mimecth.h"
+#include "mimebuf.h"
+#include "nsIServiceManager.h"
+#include "mimemoz2.h"
+#include "nsIMimeContentTypeHandler.h"
+#include "nsIComponentManager.h"
+#include "nsCategoryManagerUtils.h"
+#include "nsXPCOMCID.h"
+#include "nsISimpleMimeConverter.h"
+#include "nsSimpleMimeConverterStub.h"
+#include "nsTArray.h"
+#include "nsMimeStringResources.h"
+#include "nsMimeTypes.h"
+#include "nsMsgUtils.h"
+#include "nsIPrefBranch.h"
+#include "mozilla/Preferences.h"
+#include "imgLoader.h"
+
+#include "nsIMsgMailNewsUrl.h"
+#include "nsIMsgHdr.h"
+
+using namespace mozilla;
+
+// forward declaration
+void getMsgHdrForCurrentURL(MimeDisplayOptions *opts, nsIMsgDBHdr ** aMsgHdr);
+
+#define IMAP_EXTERNAL_CONTENT_HEADER "X-Mozilla-IMAP-Part"
+#define EXTERNAL_ATTACHMENT_URL_HEADER "X-Mozilla-External-Attachment-URL"
+
+/* ==========================================================================
+ Allocation and destruction
+ ==========================================================================
+ */
+static int mime_classinit(MimeObjectClass *clazz);
+
+/*
+ * These are the necessary defines/variables for doing
+ * content type handlers in external plugins.
+ */
+typedef struct {
+ char content_type[128];
+ bool force_inline_display;
+} cthandler_struct;
+
+nsTArray<cthandler_struct*> *ctHandlerList = NULL;
+
+/*
+ * This will return TRUE if the content_type is found in the
+ * list, FALSE if it is not found.
+ */
+bool
+find_content_type_attribs(const char *content_type,
+ bool *force_inline_display)
+{
+ *force_inline_display = false;
+ if (!ctHandlerList)
+ return false;
+
+ for (size_t i = 0; i < ctHandlerList->Length(); i++)
+ {
+ cthandler_struct *ptr = ctHandlerList->ElementAt(i);
+ if (PL_strcasecmp(content_type, ptr->content_type) == 0)
+ {
+ *force_inline_display = ptr->force_inline_display;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void
+add_content_type_attribs(const char *content_type,
+ contentTypeHandlerInitStruct *ctHandlerInfo)
+{
+ cthandler_struct *ptr = NULL;
+ bool force_inline_display;
+
+ if (find_content_type_attribs(content_type, &force_inline_display))
+ return;
+
+ if ( (!content_type) || (!ctHandlerInfo) )
+ return;
+
+ if (!ctHandlerList)
+ ctHandlerList = new nsTArray<cthandler_struct*>();
+
+ if (!ctHandlerList)
+ return;
+
+ ptr = (cthandler_struct *) PR_MALLOC(sizeof(cthandler_struct));
+ if (!ptr)
+ return;
+
+ PL_strncpy(ptr->content_type, content_type, sizeof(ptr->content_type));
+ ptr->force_inline_display = ctHandlerInfo->force_inline_display;
+ ctHandlerList->AppendElement(ptr);
+}
+
+/*
+ * This routine will find all content type handler for a specifc content
+ * type (if it exists)
+ */
+bool
+force_inline_display(const char *content_type)
+{
+ bool force_inline_disp;
+
+ find_content_type_attribs(content_type, &force_inline_disp);
+ return (force_inline_disp);
+}
+
+/*
+ * This routine will find all content type handler for a specifc content
+ * type (if it exists) and is defined to the nsRegistry
+ */
+MimeObjectClass *
+mime_locate_external_content_handler(const char *content_type,
+ contentTypeHandlerInitStruct *ctHandlerInfo)
+{
+ if (!content_type || !*(content_type)) // null or empty content type
+ return nullptr;
+
+ MimeObjectClass *newObj = NULL;
+ nsresult rv;
+
+ nsAutoCString lookupID("@mozilla.org/mimecth;1?type=");
+ nsAutoCString contentType;
+ ToLowerCase(nsDependentCString(content_type), contentType);
+ lookupID += contentType;
+
+ nsCOMPtr<nsIMimeContentTypeHandler> ctHandler = do_CreateInstance(lookupID.get(), &rv);
+ if (NS_FAILED(rv) || !ctHandler) {
+ nsCOMPtr<nsICategoryManager> catman =
+ do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
+ if (NS_FAILED(rv))
+ return nullptr;
+
+ nsCString value;
+ rv = catman->GetCategoryEntry(NS_SIMPLEMIMECONVERTERS_CATEGORY,
+ contentType.get(), getter_Copies(value));
+ if (NS_FAILED(rv) || value.IsEmpty())
+ return nullptr;
+ rv = MIME_NewSimpleMimeConverterStub(contentType.get(),
+ getter_AddRefs(ctHandler));
+ if (NS_FAILED(rv) || !ctHandler)
+ return nullptr;
+ }
+
+ rv = ctHandler->CreateContentTypeHandlerClass(contentType.get(), ctHandlerInfo, &newObj);
+ if (NS_FAILED(rv))
+ return nullptr;
+
+ add_content_type_attribs(contentType.get(), ctHandlerInfo);
+ return newObj;
+}
+
+/* This is necessary to expose the MimeObject method outside of this DLL */
+int
+MIME_MimeObject_write(MimeObject *obj, const char *output, int32_t length, bool user_visible_p)
+{
+ return MimeObject_write(obj, output, length, user_visible_p);
+}
+
+MimeObject *
+mime_new (MimeObjectClass *clazz, MimeHeaders *hdrs,
+ const char *override_content_type)
+{
+ int size = clazz->instance_size;
+ MimeObject *object;
+ int status;
+
+ /* Some assertions to verify that this isn't random junk memory... */
+ NS_ASSERTION(clazz->class_name && strlen(clazz->class_name) > 0, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
+ NS_ASSERTION(size > 0 && size < 1000, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
+
+ if (!clazz->class_initialized)
+ {
+ status = mime_classinit(clazz);
+ if (status < 0) return 0;
+ }
+
+ NS_ASSERTION(clazz->initialize && clazz->finalize, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
+
+ if (hdrs)
+ {
+ hdrs = MimeHeaders_copy (hdrs);
+ if (!hdrs) return 0;
+ }
+
+ object = (MimeObject *) PR_MALLOC(size);
+ if (!object) return 0;
+
+ memset(object, 0, size);
+ object->clazz = clazz;
+ object->headers = hdrs;
+ object->dontShowAsAttachment = false;
+
+ if (override_content_type && *override_content_type)
+ object->content_type = strdup(override_content_type);
+
+ status = clazz->initialize(object);
+ if (status < 0)
+ {
+ clazz->finalize(object);
+ PR_Free(object);
+ return 0;
+ }
+
+ return object;
+}
+
+void
+mime_free (MimeObject *object)
+{
+# ifdef DEBUG__
+ int i, size = object->clazz->instance_size;
+ uint32_t *array = (uint32_t*) object;
+# endif /* DEBUG */
+
+ object->clazz->finalize(object);
+
+# ifdef DEBUG__
+ for (i = 0; i < (size / sizeof(*array)); i++)
+ array[i] = (uint32_t) 0xDEADBEEF;
+# endif /* DEBUG */
+
+ PR_Free(object);
+}
+
+
+bool mime_is_allowed_class(const MimeObjectClass *clazz,
+ int32_t types_of_classes_to_disallow)
+{
+ if (types_of_classes_to_disallow == 0)
+ return true;
+ bool avoid_html = (types_of_classes_to_disallow >= 1);
+ bool avoid_images = (types_of_classes_to_disallow >= 2);
+ bool avoid_strange_content = (types_of_classes_to_disallow >= 3);
+ bool allow_only_vanilla_classes = (types_of_classes_to_disallow == 100);
+
+ if (allow_only_vanilla_classes)
+ /* A "safe" class is one that is unlikely to have security bugs or to
+ allow security exploits or one that is essential for the usefulness
+ of the application, even for paranoid users.
+ What's included here is more personal judgement than following
+ strict rules, though, unfortunately.
+ The function returns true only for known good classes, i.e. is a
+ "whitelist" in this case.
+ This idea comes from Georgi Guninski.
+ */
+ return
+ (
+ clazz == (MimeObjectClass *)&mimeInlineTextPlainClass ||
+ clazz == (MimeObjectClass *)&mimeInlineTextPlainFlowedClass ||
+ clazz == (MimeObjectClass *)&mimeInlineTextHTMLSanitizedClass ||
+ clazz == (MimeObjectClass *)&mimeInlineTextHTMLAsPlaintextClass ||
+ /* The latter 2 classes bear some risk, because they use the Gecko
+ HTML parser, but the user has the option to make an explicit
+ choice in this case, via html_as. */
+ clazz == (MimeObjectClass *)&mimeMultipartMixedClass ||
+ clazz == (MimeObjectClass *)&mimeMultipartAlternativeClass ||
+ clazz == (MimeObjectClass *)&mimeMultipartDigestClass ||
+ clazz == (MimeObjectClass *)&mimeMultipartAppleDoubleClass ||
+ clazz == (MimeObjectClass *)&mimeMessageClass ||
+ clazz == (MimeObjectClass *)&mimeExternalObjectClass ||
+ /* mimeUntypedTextClass? -- does uuencode */
+#ifdef ENABLE_SMIME
+ clazz == (MimeObjectClass *)&mimeMultipartSignedCMSClass ||
+ clazz == (MimeObjectClass *)&mimeEncryptedCMSClass ||
+#endif
+ clazz == 0
+ );
+
+ /* Contrairy to above, the below code is a "blacklist", i.e. it
+ *excludes* some "bad" classes. */
+ return
+ !(
+ (avoid_html
+ && (
+ clazz == (MimeObjectClass *)&mimeInlineTextHTMLParsedClass
+ /* Should not happen - we protect against that in
+ mime_find_class(). Still for safety... */
+ )) ||
+ (avoid_images
+ && (
+ clazz == (MimeObjectClass *)&mimeInlineImageClass
+ )) ||
+ (avoid_strange_content
+ && (
+ clazz == (MimeObjectClass *)&mimeInlineTextEnrichedClass ||
+ clazz == (MimeObjectClass *)&mimeInlineTextRichtextClass ||
+ clazz == (MimeObjectClass *)&mimeSunAttachmentClass ||
+ clazz == (MimeObjectClass *)&mimeExternalBodyClass
+ ))
+ );
+}
+
+void getMsgHdrForCurrentURL(MimeDisplayOptions *opts, nsIMsgDBHdr ** aMsgHdr)
+{
+ *aMsgHdr = nullptr;
+
+ if (!opts)
+ return;
+
+ mime_stream_data *msd = (mime_stream_data *) (opts->stream_closure);
+ if (!msd)
+ return;
+
+ nsCOMPtr<nsIChannel> channel = msd->channel; // note the lack of ref counting...
+ if (channel)
+ {
+ nsCOMPtr<nsIURI> uri;
+ nsCOMPtr<nsIMsgMessageUrl> msgURI;
+ channel->GetURI(getter_AddRefs(uri));
+ if (uri)
+ {
+ msgURI = do_QueryInterface(uri);
+ if (msgURI)
+ {
+ msgURI->GetMessageHeader(aMsgHdr);
+ if (*aMsgHdr)
+ return;
+ nsCString rdfURI;
+ msgURI->GetUri(getter_Copies(rdfURI));
+ if (!rdfURI.IsEmpty())
+ {
+ nsCOMPtr<nsIMsgDBHdr> msgHdr;
+ GetMsgDBHdrFromURI(rdfURI.get(), getter_AddRefs(msgHdr));
+ NS_IF_ADDREF(*aMsgHdr = msgHdr);
+ }
+ }
+ }
+ }
+
+ return;
+}
+
+MimeObjectClass *
+mime_find_class (const char *content_type, MimeHeaders *hdrs,
+ MimeDisplayOptions *opts, bool exact_match_p)
+{
+ MimeObjectClass *clazz = 0;
+ MimeObjectClass *tempClass = 0;
+ contentTypeHandlerInitStruct ctHandlerInfo;
+
+ // Read some prefs
+ nsIPrefBranch *prefBranch = GetPrefBranch(opts);
+ int32_t html_as = 0; // def. see below
+ int32_t types_of_classes_to_disallow = 0; /* Let only a few libmime classes
+ process incoming data. This protects from bugs (e.g. buffer overflows)
+ and from security loopholes (e.g. allowing unchecked HTML in some
+ obscure classes, although the user has html_as > 0).
+ This option is mainly for the UI of html_as.
+ 0 = allow all available classes
+ 1 = Use hardcoded blacklist to avoid rendering (incoming) HTML
+ 2 = ... and images
+ 3 = ... and some other uncommon content types
+ 4 = show all body parts
+ 100 = Use hardcoded whitelist to avoid even more bugs(buffer overflows).
+ This mode will limit the features available (e.g. uncommon
+ attachment types and inline images) and is for paranoid users.
+ */
+ if (opts && opts->format_out != nsMimeOutput::nsMimeMessageFilterSniffer &&
+ opts->format_out != nsMimeOutput::nsMimeMessageDecrypt
+ && opts->format_out != nsMimeOutput::nsMimeMessageAttach)
+ if (prefBranch)
+ {
+ prefBranch->GetIntPref("mailnews.display.html_as", &html_as);
+ prefBranch->GetIntPref("mailnews.display.disallow_mime_handlers",
+ &types_of_classes_to_disallow);
+ if (types_of_classes_to_disallow > 0 && html_as == 0)
+ // We have non-sensical prefs. Do some fixup.
+ html_as = 1;
+ }
+
+ // First, check to see if the message has been marked as JUNK. If it has,
+ // then force the message to be rendered as simple, unless this has been
+ // called by a filtering routine.
+ bool sanitizeJunkMail = false;
+
+ // it is faster to read the pref first then figure out the msg hdr for the current url only if we have to
+ // XXX instead of reading this pref every time, part of mime should be an observer listening to this pref change
+ // and updating internal state accordingly. But none of the other prefs in this file seem to be doing that...=(
+ if (prefBranch)
+ prefBranch->GetBoolPref("mail.spam.display.sanitize", &sanitizeJunkMail);
+
+ if (sanitizeJunkMail &&
+ !(opts && opts->format_out == nsMimeOutput::nsMimeMessageFilterSniffer))
+ {
+ nsCOMPtr<nsIMsgDBHdr> msgHdr;
+ getMsgHdrForCurrentURL(opts, getter_AddRefs(msgHdr));
+ if (msgHdr)
+ {
+ nsCString junkScoreStr;
+ (void) msgHdr->GetStringProperty("junkscore", getter_Copies(junkScoreStr));
+ if (html_as == 0 && junkScoreStr.get() && atoi(junkScoreStr.get()) > 50)
+ html_as = 3; // 3 == Simple HTML
+ } // if msgHdr
+ } // if we are supposed to sanitize junk mail
+
+ /*
+ * What we do first is check for an external content handler plugin.
+ * This will actually extend the mime handling by calling a routine
+ * which will allow us to load an external content type handler
+ * for specific content types. If one is not found, we will drop back
+ * to the default handler.
+ */
+ if ((tempClass = mime_locate_external_content_handler(content_type, &ctHandlerInfo)) != NULL)
+ {
+#ifdef MOZ_THUNDERBIRD
+ // This is a case where we only want to add this property if we are a thunderbird build AND
+ // we have found an external mime content handler for text/calendar
+ // This will enable iMIP support in Lightning
+ if ( hdrs && (!PL_strncasecmp(content_type, "text/calendar", 13)))
+ {
+ char *full_content_type = MimeHeaders_get(hdrs, HEADER_CONTENT_TYPE, false, false);
+ if (full_content_type)
+ {
+ char *imip_method = MimeHeaders_get_parameter(full_content_type, "method", NULL, NULL);
+ nsCOMPtr<nsIMsgDBHdr> msgHdr;
+ getMsgHdrForCurrentURL(opts, getter_AddRefs(msgHdr));
+ if (msgHdr)
+ msgHdr->SetStringProperty("imip_method", (imip_method) ? imip_method : "nomethod");
+ // PR_Free checks for null
+ PR_Free(imip_method);
+ PR_Free(full_content_type);
+ }
+ }
+#endif
+
+ if (types_of_classes_to_disallow > 0
+ && (!PL_strncasecmp(content_type, "text/x-vcard", 12))
+ )
+ /* Use a little hack to prevent some dangerous plugins, which ship
+ with Mozilla, to run.
+ For the truely user-installed plugins, we rely on the judgement
+ of the user. */
+ {
+ if (!exact_match_p)
+ clazz = (MimeObjectClass *)&mimeExternalObjectClass; // As attachment
+ }
+ else
+ clazz = (MimeObjectClass *)tempClass;
+ }
+ else
+ {
+ if (!content_type || !*content_type ||
+ !PL_strcasecmp(content_type, "text")) /* with no / in the type */
+ clazz = (MimeObjectClass *)&mimeUntypedTextClass;
+
+ /* Subtypes of text...
+ */
+ else if (!PL_strncasecmp(content_type, "text/", 5))
+ {
+ if (!PL_strcasecmp(content_type+5, "html"))
+ {
+ if (opts &&
+ (opts->format_out == nsMimeOutput::nsMimeMessageSaveAs ||
+ opts->format_out == nsMimeOutput::nsMimeMessageFilterSniffer ||
+ opts->format_out == nsMimeOutput::nsMimeMessageDecrypt ||
+ opts->format_out == nsMimeOutput::nsMimeMessageAttach))
+ // SaveAs in new modes doesn't work yet.
+ {
+ // Don't use the parsed HTML class if we're ...
+ // - saving the HTML of a message
+ // - getting message content for filtering
+ // - snarfing attachments (nsMimeMessageDecrypt used in SnarfMsgAttachment)
+ // - processing attachments (like deleting attachments).
+ clazz = (MimeObjectClass *)&mimeInlineTextHTMLClass;
+ types_of_classes_to_disallow = 0;
+ }
+ else if (html_as == 0 || html_as == 4) // Render sender's HTML
+ clazz = (MimeObjectClass *)&mimeInlineTextHTMLParsedClass;
+ else if (html_as == 1) // convert HTML to plaintext
+ // Do a HTML->TXT->HTML conversion, see mimethpl.h.
+ clazz = (MimeObjectClass *)&mimeInlineTextHTMLAsPlaintextClass;
+ else if (html_as == 2) // display HTML source
+ /* This is for the freaks. Treat HTML as plaintext,
+ which will cause the HTML source to be displayed.
+ Not very user-friendly, but some seem to want this. */
+ clazz = (MimeObjectClass *)&mimeInlineTextPlainClass;
+ else if (html_as == 3) // Sanitize
+ // Strip all but allowed HTML
+ clazz = (MimeObjectClass *)&mimeInlineTextHTMLSanitizedClass;
+ else // Goofy pref
+ /* User has an unknown pref value. Maybe he used a newer Mozilla
+ with a new alternative to avoid HTML. Defaulting to option 1,
+ which is less dangerous than defaulting to the raw HTML. */
+ clazz = (MimeObjectClass *)&mimeInlineTextHTMLAsPlaintextClass;
+ }
+ else if (!PL_strcasecmp(content_type+5, "enriched"))
+ clazz = (MimeObjectClass *)&mimeInlineTextEnrichedClass;
+ else if (!PL_strcasecmp(content_type+5, "richtext"))
+ clazz = (MimeObjectClass *)&mimeInlineTextRichtextClass;
+ else if (!PL_strcasecmp(content_type+5, "rtf"))
+ clazz = (MimeObjectClass *)&mimeExternalObjectClass;
+ else if (!PL_strcasecmp(content_type+5, "plain"))
+ {
+ // Preliminary use the normal plain text
+ clazz = (MimeObjectClass *)&mimeInlineTextPlainClass;
+
+ if (opts && opts->format_out != nsMimeOutput::nsMimeMessageFilterSniffer
+ && opts->format_out != nsMimeOutput::nsMimeMessageAttach
+ && opts->format_out != nsMimeOutput::nsMimeMessageRaw)
+ {
+ bool disable_format_flowed = false;
+ if (prefBranch)
+ prefBranch->GetBoolPref("mailnews.display.disable_format_flowed_support",
+ &disable_format_flowed);
+
+ if(!disable_format_flowed)
+ {
+ // Check for format=flowed, damn, it is already stripped away from
+ // the contenttype!
+ // Look in headers instead even though it's expensive and clumsy
+ // First find Content-Type:
+ char *content_type_row =
+ (hdrs
+ ? MimeHeaders_get(hdrs, HEADER_CONTENT_TYPE,
+ false, false)
+ : 0);
+ // Then the format parameter if there is one.
+ // I would rather use a PARAM_FORMAT but I can't find the right
+ // place to put the define. The others seems to be in net.h
+ // but is that really really the right place? There is also
+ // a nsMimeTypes.h but that one isn't included. Bug?
+ char *content_type_format =
+ (content_type_row
+ ? MimeHeaders_get_parameter(content_type_row, "format", NULL,NULL)
+ : 0);
+
+ if (content_type_format && !PL_strcasecmp(content_type_format,
+ "flowed"))
+ clazz = (MimeObjectClass *)&mimeInlineTextPlainFlowedClass;
+ PR_FREEIF(content_type_format);
+ PR_FREEIF(content_type_row);
+ }
+ }
+ }
+ else if (!exact_match_p)
+ clazz = (MimeObjectClass *)&mimeInlineTextPlainClass;
+ }
+
+ /* Subtypes of multipart...
+ */
+ else if (!PL_strncasecmp(content_type, "multipart/", 10))
+ {
+ // When html_as is 4, we want all MIME parts of the message to
+ // show up in the displayed message body, if they are MIME types
+ // that we know how to display, and also in the attachment pane
+ // if it's appropriate to put them there. Both
+ // multipart/alternative and multipart/related play games with
+ // hiding various MIME parts, and we don't want that to happen,
+ // so we prevent that by parsing those MIME types as
+ // multipart/mixed, which won't mess with anything.
+ //
+ // When our output format is nsMimeOutput::nsMimeMessageAttach,
+ // i.e., we are reformatting the message to remove attachments,
+ // we are in a similar boat. The code for deleting
+ // attachments properly in that mode is in mimemult.cpp
+ // functions which are inherited by mimeMultipartMixedClass but
+ // not by mimeMultipartAlternativeClass or
+ // mimeMultipartRelatedClass. Therefore, to ensure that
+ // everything is handled properly, in this context too we parse
+ // those MIME types as multipart/mixed.
+ bool basic_formatting = (html_as == 4) ||
+ (opts && opts->format_out == nsMimeOutput::nsMimeMessageAttach);
+ if (!PL_strcasecmp(content_type+10, "alternative"))
+ clazz = basic_formatting ? (MimeObjectClass *)&mimeMultipartMixedClass :
+ (MimeObjectClass *)&mimeMultipartAlternativeClass;
+ else if (!PL_strcasecmp(content_type+10, "related"))
+ clazz = basic_formatting ? (MimeObjectClass *)&mimeMultipartMixedClass :
+ (MimeObjectClass *)&mimeMultipartRelatedClass;
+ else if (!PL_strcasecmp(content_type+10, "digest"))
+ clazz = (MimeObjectClass *)&mimeMultipartDigestClass;
+ else if (!PL_strcasecmp(content_type+10, "appledouble") ||
+ !PL_strcasecmp(content_type+10, "header-set"))
+ clazz = (MimeObjectClass *)&mimeMultipartAppleDoubleClass;
+ else if (!PL_strcasecmp(content_type+10, "parallel"))
+ clazz = (MimeObjectClass *)&mimeMultipartParallelClass;
+ else if (!PL_strcasecmp(content_type+10, "mixed"))
+ clazz = (MimeObjectClass *)&mimeMultipartMixedClass;
+#ifdef ENABLE_SMIME
+ else if (!PL_strcasecmp(content_type+10, "signed"))
+ {
+ /* Check that the "protocol" and "micalg" parameters are ones we
+ know about. */
+ char *ct = (hdrs
+ ? MimeHeaders_get(hdrs, HEADER_CONTENT_TYPE,
+ false, false)
+ : 0);
+ char *proto = (ct
+ ? MimeHeaders_get_parameter(ct, PARAM_PROTOCOL, NULL, NULL)
+ : 0);
+ char *micalg = (ct
+ ? MimeHeaders_get_parameter(ct, PARAM_MICALG, NULL, NULL)
+ : 0);
+
+ if (proto
+ && (
+ (/* is a signature */
+ !PL_strcasecmp(proto, APPLICATION_XPKCS7_SIGNATURE)
+ ||
+ !PL_strcasecmp(proto, APPLICATION_PKCS7_SIGNATURE))
+ && micalg
+ && (!PL_strcasecmp(micalg, PARAM_MICALG_MD5) ||
+ !PL_strcasecmp(micalg, PARAM_MICALG_MD5_2) ||
+ !PL_strcasecmp(micalg, PARAM_MICALG_SHA1) ||
+ !PL_strcasecmp(micalg, PARAM_MICALG_SHA1_2) ||
+ !PL_strcasecmp(micalg, PARAM_MICALG_SHA1_3) ||
+ !PL_strcasecmp(micalg, PARAM_MICALG_SHA1_4) ||
+ !PL_strcasecmp(micalg, PARAM_MICALG_SHA1_5) ||
+ !PL_strcasecmp(micalg, PARAM_MICALG_SHA256) ||
+ !PL_strcasecmp(micalg, PARAM_MICALG_SHA256_2) ||
+ !PL_strcasecmp(micalg, PARAM_MICALG_SHA256_3) ||
+ !PL_strcasecmp(micalg, PARAM_MICALG_SHA384) ||
+ !PL_strcasecmp(micalg, PARAM_MICALG_SHA384_2) ||
+ !PL_strcasecmp(micalg, PARAM_MICALG_SHA384_3) ||
+ !PL_strcasecmp(micalg, PARAM_MICALG_SHA512) ||
+ !PL_strcasecmp(micalg, PARAM_MICALG_SHA512_2) ||
+ !PL_strcasecmp(micalg, PARAM_MICALG_SHA512_3) ||
+ !PL_strcasecmp(micalg, PARAM_MICALG_MD2))))
+ clazz = (MimeObjectClass *)&mimeMultipartSignedCMSClass;
+ else
+ clazz = 0;
+ PR_FREEIF(proto);
+ PR_FREEIF(micalg);
+ PR_FREEIF(ct);
+ }
+#endif
+
+ if (!clazz && !exact_match_p)
+ /* Treat all unknown multipart subtypes as "multipart/mixed" */
+ clazz = (MimeObjectClass *)&mimeMultipartMixedClass;
+
+ /* If we are sniffing a message, let's treat alternative parts as mixed */
+ if (opts && opts->format_out == nsMimeOutput::nsMimeMessageFilterSniffer)
+ if (clazz == (MimeObjectClass *)&mimeMultipartAlternativeClass)
+ clazz = (MimeObjectClass *)&mimeMultipartMixedClass;
+ }
+
+ /* Subtypes of message...
+ */
+ else if (!PL_strncasecmp(content_type, "message/", 8))
+ {
+ if (!PL_strcasecmp(content_type+8, "rfc822") ||
+ !PL_strcasecmp(content_type+8, "news"))
+ clazz = (MimeObjectClass *)&mimeMessageClass;
+ else if (!PL_strcasecmp(content_type+8, "external-body"))
+ clazz = (MimeObjectClass *)&mimeExternalBodyClass;
+ else if (!PL_strcasecmp(content_type+8, "partial"))
+ /* I guess these are most useful as externals, for now... */
+ clazz = (MimeObjectClass *)&mimeExternalObjectClass;
+ else if (!exact_match_p)
+ /* Treat all unknown message subtypes as "text/plain" */
+ clazz = (MimeObjectClass *)&mimeInlineTextPlainClass;
+ }
+
+ /* The magic image types which we are able to display internally...
+ */
+ else if (!PL_strncasecmp(content_type, "image/", 6)) {
+ if (imgLoader::SupportImageWithMimeType(content_type, AcceptedMimeTypes::IMAGES_AND_DOCUMENTS))
+ clazz = (MimeObjectClass *)&mimeInlineImageClass;
+ else
+ clazz = (MimeObjectClass *)&mimeExternalObjectClass;
+ }
+
+#ifdef ENABLE_SMIME
+ else if (!PL_strcasecmp(content_type, APPLICATION_XPKCS7_MIME)
+ || !PL_strcasecmp(content_type, APPLICATION_PKCS7_MIME)) {
+
+ if (Preferences::GetBool("mailnews.p7m_subparts_external", false) &&
+ opts->is_child) {
+ // We do not allow encrypted parts except as top level.
+ // Allowing them would leak the plain text in case the part is
+ // cleverly hidden and the decrypted content gets included in
+ // replies and forwards.
+ clazz = (MimeObjectClass *)&mimeExternalObjectClass;
+ return clazz;
+ }
+
+ char *ct = (hdrs ? MimeHeaders_get(hdrs, HEADER_CONTENT_TYPE,
+ false, false)
+ : nullptr);
+ char *st = (ct ? MimeHeaders_get_parameter(ct, "smime-type", NULL, NULL)
+ : nullptr);
+
+ /* by default, assume that it is an encrypted message */
+ clazz = (MimeObjectClass *)&mimeEncryptedCMSClass;
+
+ /* if the smime-type parameter says that it's a certs-only or
+ compressed file, then show it as an attachment, however
+ (MimeEncryptedCMS doesn't handle these correctly) */
+ if (st &&
+ (!PL_strcasecmp(st, "certs-only") ||
+ !PL_strcasecmp(st, "compressed-data")))
+ clazz = (MimeObjectClass *)&mimeExternalObjectClass;
+ else {
+ /* look at the file extension... less reliable, but still covered
+ by the S/MIME specification (RFC 3851, section 3.2.1) */
+ char *name = (hdrs ? MimeHeaders_get_name(hdrs, opts) : nullptr);
+ if (name) {
+ char *suf = PL_strrchr(name, '.');
+ bool p7mExternal = false;
+
+ if (prefBranch)
+ prefBranch->GetBoolPref("mailnews.p7m_external", &p7mExternal);
+ if (suf &&
+ ((!PL_strcasecmp(suf, ".p7m") && p7mExternal) ||
+ !PL_strcasecmp(suf, ".p7c") ||
+ !PL_strcasecmp(suf, ".p7z")))
+ clazz = (MimeObjectClass *)&mimeExternalObjectClass;
+ }
+ PR_Free(name);
+ }
+ PR_Free(st);
+ PR_Free(ct);
+ }
+#endif
+ /* A few types which occur in the real world and which we would otherwise
+ treat as non-text types (which would be bad) without this special-case...
+ */
+ else if (!PL_strcasecmp(content_type, APPLICATION_PGP) ||
+ !PL_strcasecmp(content_type, APPLICATION_PGP2))
+ clazz = (MimeObjectClass *)&mimeInlineTextPlainClass;
+
+ else if (!PL_strcasecmp(content_type, SUN_ATTACHMENT))
+ clazz = (MimeObjectClass *)&mimeSunAttachmentClass;
+
+ /* Everything else gets represented as a clickable link.
+ */
+ else if (!exact_match_p)
+ clazz = (MimeObjectClass *)&mimeExternalObjectClass;
+
+ if (!mime_is_allowed_class(clazz, types_of_classes_to_disallow))
+ {
+ /* Do that check here (not after the if block), because we want to allow
+ user-installed plugins. */
+ if(!exact_match_p)
+ clazz = (MimeObjectClass *)&mimeExternalObjectClass;
+ else
+ clazz = 0;
+ }
+ }
+
+#ifdef ENABLE_SMIME
+ // see bug #189988
+ if (opts && opts->format_out == nsMimeOutput::nsMimeMessageDecrypt &&
+ (clazz != (MimeObjectClass *)&mimeEncryptedCMSClass)) {
+ clazz = (MimeObjectClass *)&mimeExternalObjectClass;
+ }
+#endif
+
+ if (!exact_match_p)
+ NS_ASSERTION(clazz, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
+ if (!clazz) return 0;
+
+ NS_ASSERTION(clazz, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
+
+ if (clazz && !clazz->class_initialized)
+ {
+ int status = mime_classinit(clazz);
+ if (status < 0) return 0;
+ }
+
+ return clazz;
+}
+
+
+MimeObject *
+mime_create (const char *content_type, MimeHeaders *hdrs,
+ MimeDisplayOptions *opts, bool forceInline /* = false */)
+{
+ /* If there is no Content-Disposition header, or if the Content-Disposition
+ is ``inline'', then we display the part inline (and let mime_find_class()
+ decide how.)
+
+ If there is any other Content-Disposition (either ``attachment'' or some
+ disposition that we don't recognise) then we always display the part as
+ an external link, by using MimeExternalObject to display it.
+
+ But Content-Disposition is ignored for all containers except `message'.
+ (including multipart/mixed, and multipart/digest.) It's not clear if
+ this is to spec, but from a usability standpoint, I think it's necessary.
+ */
+
+ MimeObjectClass *clazz = 0;
+ char *content_disposition = 0;
+ MimeObject *obj = 0;
+ char *override_content_type = 0;
+
+ /* We've had issues where the incoming content_type is invalid, of a format:
+ content_type="=?windows-1252?q?application/pdf" (bug 659355)
+ We decided to fix that by simply trimming the stuff before the ?
+ */
+ if (content_type)
+ {
+ const char *lastQuestion = strrchr(content_type, '?');
+ if (lastQuestion)
+ content_type = lastQuestion + 1; // the substring after the last '?'
+ }
+
+ /* There are some clients send out all attachments with a content-type
+ of application/octet-stream. So, if we have an octet-stream attachment,
+ try to guess what type it really is based on the file extension. I HATE
+ that we have to do this...
+ */
+ if (hdrs && opts && opts->file_type_fn &&
+
+ /* ### mwelch - don't override AppleSingle */
+ (content_type ? PL_strcasecmp(content_type, APPLICATION_APPLEFILE) : true) &&
+ /* ## davidm Apple double shouldn't use this #$%& either. */
+ (content_type ? PL_strcasecmp(content_type, MULTIPART_APPLEDOUBLE) : true) &&
+ (!content_type ||
+ !PL_strcasecmp(content_type, APPLICATION_OCTET_STREAM) ||
+ !PL_strcasecmp(content_type, UNKNOWN_CONTENT_TYPE)))
+ {
+ char *name = MimeHeaders_get_name(hdrs, opts);
+ if (name)
+ {
+ override_content_type = opts->file_type_fn (name, opts->stream_closure);
+ // appledouble isn't a valid override content type, and makes
+ // attachments invisible.
+ if (!PL_strcasecmp(override_content_type, MULTIPART_APPLEDOUBLE))
+ override_content_type = nullptr;
+ PR_FREEIF(name);
+
+ // Workaroung for saving '.eml" file encoded with base64.
+ // Do not override with message/rfc822 whenever Transfer-Encoding is
+ // base64 since base64 encoding of message/rfc822 is invalid.
+ // Our MimeMessageClass has no capability to decode it.
+ if (!PL_strcasecmp(override_content_type, MESSAGE_RFC822)) {
+ nsCString encoding;
+ encoding.Adopt(MimeHeaders_get(hdrs,
+ HEADER_CONTENT_TRANSFER_ENCODING,
+ true, false));
+ if (encoding.LowerCaseEqualsLiteral(ENCODING_BASE64))
+ override_content_type = nullptr;
+ }
+
+ // If we get here and it is not the unknown content type from the
+ // file name, let's do some better checking not to inline something bad
+ if (override_content_type &&
+ *override_content_type &&
+ (PL_strcasecmp(override_content_type, UNKNOWN_CONTENT_TYPE)))
+ content_type = override_content_type;
+ }
+ }
+
+ clazz = mime_find_class(content_type, hdrs, opts, false);
+
+ NS_ASSERTION(clazz, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
+ if (!clazz) goto FAIL;
+
+ if (opts && opts->part_to_load)
+ /* Always ignore Content-Disposition when we're loading some specific
+ sub-part (which may be within some container that we wouldn't otherwise
+ descend into, if the container itself had a Content-Disposition of
+ `attachment'. */
+ content_disposition = 0;
+
+ else if (mime_subclass_p(clazz,(MimeObjectClass *)&mimeContainerClass) &&
+ !mime_subclass_p(clazz,(MimeObjectClass *)&mimeMessageClass))
+ /* Ignore Content-Disposition on all containers except `message'.
+ That is, Content-Disposition is ignored for multipart/mixed objects,
+ but is obeyed for message/rfc822 objects. */
+ content_disposition = 0;
+
+ else
+ {
+ /* Check to see if the plugin should override the content disposition
+ to make it appear inline. One example is a vcard which has a content
+ disposition of an "attachment;" */
+ if (force_inline_display(content_type))
+ NS_MsgSACopy(&content_disposition, "inline");
+ else
+ content_disposition = (hdrs
+ ? MimeHeaders_get(hdrs, HEADER_CONTENT_DISPOSITION, true, false)
+ : 0);
+ }
+
+ if (!content_disposition || !PL_strcasecmp(content_disposition, "inline"))
+ ; /* Use the class we've got. */
+ else
+ {
+ // override messages that have content disposition set to "attachment"
+ // even though we probably should show them inline.
+ if ( (clazz != (MimeObjectClass *)&mimeInlineTextClass) &&
+ (clazz != (MimeObjectClass *)&mimeInlineTextPlainClass) &&
+ (clazz != (MimeObjectClass *)&mimeInlineTextPlainFlowedClass) &&
+ (clazz != (MimeObjectClass *)&mimeInlineTextHTMLClass) &&
+ (clazz != (MimeObjectClass *)&mimeInlineTextHTMLParsedClass) &&
+ (clazz != (MimeObjectClass *)&mimeInlineTextHTMLSanitizedClass) &&
+ (clazz != (MimeObjectClass *)&mimeInlineTextHTMLAsPlaintextClass) &&
+ (clazz != (MimeObjectClass *)&mimeInlineTextRichtextClass) &&
+ (clazz != (MimeObjectClass *)&mimeInlineTextEnrichedClass) &&
+ (clazz != (MimeObjectClass *)&mimeMessageClass) &&
+ (clazz != (MimeObjectClass *)&mimeInlineImageClass) )
+ // not a special inline type, so show as attachment
+ clazz = (MimeObjectClass *)&mimeExternalObjectClass;
+ }
+
+ /* If the option `Show Attachments Inline' is off, now would be the time to change our mind... */
+ /* Also, if we're doing a reply (i.e. quoting the body), then treat that according to preference. */
+ if (opts && ((!opts->show_attachment_inline_p && !forceInline) ||
+ (!opts->quote_attachment_inline_p &&
+ (opts->format_out == nsMimeOutput::nsMimeMessageQuoting ||
+ opts->format_out == nsMimeOutput::nsMimeMessageBodyQuoting))))
+ {
+ if (mime_subclass_p(clazz, (MimeObjectClass *)&mimeInlineTextClass))
+ {
+ /* It's a text type. Write it only if it's the *first* part
+ that we're writing, and then only if it has no "filename"
+ specified (the assumption here being, if it has a filename,
+ it wasn't simply typed into the text field -- it was actually
+ an attached document.) */
+ if (opts->state && opts->state->first_part_written_p)
+ clazz = (MimeObjectClass *)&mimeExternalObjectClass;
+ else
+ {
+ /* If there's a name, then write this as an attachment. */
+ char *name = (hdrs ? MimeHeaders_get_name(hdrs, opts) : nullptr);
+ if (name)
+ {
+ clazz = (MimeObjectClass *)&mimeExternalObjectClass;
+ PR_Free(name);
+ }
+ }
+ }
+ else
+ if (mime_subclass_p(clazz,(MimeObjectClass *)&mimeContainerClass) &&
+ !mime_subclass_p(clazz,(MimeObjectClass *)&mimeMessageClass))
+ /* Multipart subtypes are ok, except for messages; descend into
+ multiparts, and defer judgement.
+
+ Encrypted blobs are just like other containers (make the crypto
+ layer invisible, and treat them as simple containers. So there's
+ no easy way to save encrypted data directly to disk; it will tend
+ to always be wrapped inside a message/rfc822. That's ok.) */
+ ;
+ else if (opts && opts->part_to_load &&
+ mime_subclass_p(clazz,(MimeObjectClass *)&mimeMessageClass))
+ /* Descend into messages only if we're looking for a specific sub-part. */
+ ;
+ else
+ {
+ /* Anything else, and display it as a link (and cause subsequent
+ text parts to also be displayed as links.) */
+ clazz = (MimeObjectClass *)&mimeExternalObjectClass;
+ }
+ }
+
+ PR_FREEIF(content_disposition);
+ obj = mime_new (clazz, hdrs, content_type);
+
+ FAIL:
+
+ /* If we decided to ignore the content-type in the headers of this object
+ (see above) then make sure that our new content-type is stored in the
+ object itself. (Or free it, if we're in an out-of-memory situation.)
+ */
+ if (override_content_type)
+ {
+ if (obj)
+ {
+ PR_FREEIF(obj->content_type);
+ obj->content_type = override_content_type;
+ }
+ else
+ {
+ PR_Free(override_content_type);
+ }
+ }
+
+ return obj;
+}
+
+
+
+static int mime_classinit_1(MimeObjectClass *clazz, MimeObjectClass *target);
+
+static int
+mime_classinit(MimeObjectClass *clazz)
+{
+ int status;
+ if (clazz->class_initialized)
+ return 0;
+
+ NS_ASSERTION(clazz->class_initialize, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
+ if (!clazz->class_initialize)
+ return -1;
+
+ /* First initialize the superclass.
+ */
+ if (clazz->superclass && !clazz->superclass->class_initialized)
+ {
+ status = mime_classinit(clazz->superclass);
+ if (status < 0) return status;
+ }
+
+ /* Now run each of the superclass-init procedures in turn,
+ parentmost-first. */
+ status = mime_classinit_1(clazz, clazz);
+ if (status < 0) return status;
+
+ /* Now we're done. */
+ clazz->class_initialized = true;
+ return 0;
+}
+
+static int
+mime_classinit_1(MimeObjectClass *clazz, MimeObjectClass *target)
+{
+ int status;
+ if (clazz->superclass)
+ {
+ status = mime_classinit_1(clazz->superclass, target);
+ if (status < 0) return status;
+ }
+ return clazz->class_initialize(target);
+}
+
+
+bool
+mime_subclass_p(MimeObjectClass *child, MimeObjectClass *parent)
+{
+ if (child == parent)
+ return true;
+ else if (!child->superclass)
+ return false;
+ else
+ return mime_subclass_p(child->superclass, parent);
+}
+
+bool
+mime_typep(MimeObject *obj, MimeObjectClass *clazz)
+{
+ return mime_subclass_p(obj->clazz, clazz);
+}
+
+
+
+/* URL munging
+ */
+
+
+/* Returns a string describing the location of the part (like "2.5.3").
+ This is not a full URL, just a part-number.
+ */
+char *
+mime_part_address(MimeObject *obj)
+{
+ if (!obj->parent)
+ return strdup("0");
+ else
+ {
+ /* Find this object in its parent. */
+ int32_t i, j = -1;
+ char buf [20];
+ char *higher = 0;
+ MimeContainer *cont = (MimeContainer *) obj->parent;
+ NS_ASSERTION(mime_typep(obj->parent,
+ (MimeObjectClass *)&mimeContainerClass), "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
+ for (i = 0; i < cont->nchildren; i++)
+ if (cont->children[i] == obj)
+ {
+ j = i+1;
+ break;
+ }
+ if (j == -1)
+ {
+ NS_ERROR("No children under MeimContainer");
+ return 0;
+ }
+
+ PR_snprintf(buf, sizeof(buf), "%ld", j);
+ if (obj->parent->parent)
+ {
+ higher = mime_part_address(obj->parent);
+ if (!higher) return 0; /* MIME_OUT_OF_MEMORY */
+ }
+
+ if (!higher)
+ return strdup(buf);
+ else
+ {
+ uint32_t slen = strlen(higher) + strlen(buf) + 3;
+ char *s = (char *)PR_MALLOC(slen);
+ if (!s)
+ {
+ PR_Free(higher);
+ return 0; /* MIME_OUT_OF_MEMORY */
+ }
+ PL_strncpyz(s, higher, slen);
+ PL_strcatn(s, slen, ".");
+ PL_strcatn(s, slen, buf);
+ PR_Free(higher);
+ return s;
+ }
+ }
+}
+
+
+/* Returns a string describing the location of the *IMAP* part (like "2.5.3").
+ This is not a full URL, just a part-number.
+ This part is explicitly passed in the X-Mozilla-IMAP-Part header.
+ Return value must be freed by the caller.
+ */
+char *
+mime_imap_part_address(MimeObject *obj)
+{
+ if (!obj || !obj->headers)
+ return 0;
+ else
+ return MimeHeaders_get(obj->headers, IMAP_EXTERNAL_CONTENT_HEADER, false, false);
+}
+
+/* Returns a full URL if the current mime object has a EXTERNAL_ATTACHMENT_URL_HEADER
+ header.
+ Return value must be freed by the caller.
+*/
+char *
+mime_external_attachment_url(MimeObject *obj)
+{
+ if (!obj || !obj->headers)
+ return 0;
+ else
+ return MimeHeaders_get(obj->headers, EXTERNAL_ATTACHMENT_URL_HEADER, false, false);
+}
+
+#ifdef ENABLE_SMIME
+/* Asks whether the given object is one of the cryptographically signed
+ or encrypted objects that we know about. (MimeMessageClass uses this
+ to decide if the headers need to be presented differently.)
+ */
+bool
+mime_crypto_object_p(MimeHeaders *hdrs, bool clearsigned_counts, MimeDisplayOptions *opts)
+{
+ char *ct;
+ MimeObjectClass *clazz;
+
+ if (!hdrs) return false;
+
+ ct = MimeHeaders_get (hdrs, HEADER_CONTENT_TYPE, true, false);
+ if (!ct) return false;
+
+ /* Rough cut -- look at the string before doing a more complex comparison. */
+ if (PL_strcasecmp(ct, MULTIPART_SIGNED) &&
+ PL_strncasecmp(ct, "application/", 12))
+ {
+ PR_Free(ct);
+ return false;
+ }
+
+ /* It's a candidate for being a crypto object. Let's find out for sure... */
+ clazz = mime_find_class(ct, hdrs, opts, true);
+ PR_Free(ct);
+
+ if (clazz == ((MimeObjectClass *)&mimeEncryptedCMSClass))
+ return true;
+ else if (clearsigned_counts &&
+ clazz == ((MimeObjectClass *)&mimeMultipartSignedCMSClass))
+ return true;
+ else
+ return false;
+}
+
+/* Whether the given object has written out the HTML version of its headers
+ in such a way that it will have a "crypto stamp" next to the headers. If
+ this is true, then the child must write out its HTML slightly differently
+ to take this into account...
+ */
+bool
+mime_crypto_stamped_p(MimeObject *obj)
+{
+ if (!obj) return false;
+ if (mime_typep (obj, (MimeObjectClass *) &mimeMessageClass))
+ return ((MimeMessage *) obj)->crypto_stamped_p;
+ else
+ return false;
+}
+
+#endif // ENABLE_SMIME
+
+/* Puts a part-number into a URL. If append_p is true, then the part number
+ is appended to any existing part-number already in that URL; otherwise,
+ it replaces it.
+ */
+char *
+mime_set_url_part(const char *url, const char *part, bool append_p)
+{
+ const char *part_begin = 0;
+ const char *part_end = 0;
+ bool got_q = false;
+ const char *s;
+ char *result;
+
+ if (!url || !part) return 0;
+
+ nsAutoCString urlString(url);
+ int32_t typeIndex = urlString.Find("?type=application/x-message-display");
+ if (typeIndex != -1)
+ {
+ urlString.Cut(typeIndex, sizeof("?type=application/x-message-display") - 1);
+ if (urlString.CharAt(typeIndex) == '&')
+ urlString.Replace(typeIndex, 1, '?');
+ url = urlString.get();
+ }
+
+ for (s = url; *s; s++)
+ {
+ if (*s == '?')
+ {
+ got_q = true;
+ if (!PL_strncasecmp(s, "?part=", 6))
+ part_begin = (s += 6);
+ }
+ else if (got_q && *s == '&' && !PL_strncasecmp(s, "&part=", 6))
+ part_begin = (s += 6);
+
+ if (part_begin)
+ {
+ for (; (*s && *s != '?' && *s != '&'); s++)
+ ;
+ part_end = s;
+ break;
+ }
+ }
+
+ uint32_t resultlen = strlen(url) + strlen(part) + 10;
+ result = (char *) PR_MALLOC(resultlen);
+ if (!result) return 0;
+
+ if (part_begin)
+ {
+ if (append_p)
+ {
+ memcpy(result, url, part_end - url);
+ result [part_end - url] = '.';
+ result [part_end - url + 1] = 0;
+ }
+ else
+ {
+ memcpy(result, url, part_begin - url);
+ result [part_begin - url] = 0;
+ }
+ }
+ else
+ {
+ PL_strncpyz(result, url, resultlen);
+ if (got_q)
+ PL_strcatn(result, resultlen, "&part=");
+ else
+ PL_strcatn(result, resultlen, "?part=");
+ }
+
+ PL_strcatn(result, resultlen, part);
+
+ if (part_end && *part_end)
+ PL_strcatn(result, resultlen, part_end);
+
+ /* Semi-broken kludge to omit a trailing "?part=0". */
+ {
+ int L = strlen(result);
+ if (L > 6 &&
+ (result[L-7] == '?' || result[L-7] == '&') &&
+ !strcmp("part=0", result + L - 6))
+ result[L-7] = 0;
+ }
+
+ return result;
+}
+
+
+
+/* Puts an *IMAP* part-number into a URL.
+ Strips off any previous *IMAP* part numbers, since they are absolute, not relative.
+ */
+char *
+mime_set_url_imap_part(const char *url, const char *imappart, const char *libmimepart)
+{
+ char *result = 0;
+ char *whereCurrent = PL_strstr(url, "/;section=");
+ if (whereCurrent)
+ {
+ *whereCurrent = 0;
+ }
+
+ uint32_t resultLen = strlen(url) + strlen(imappart) + strlen(libmimepart) + 17;
+ result = (char *) PR_MALLOC(resultLen);
+ if (!result) return 0;
+
+ PL_strncpyz(result, url, resultLen);
+ PL_strcatn(result, resultLen, "/;section=");
+ PL_strcatn(result, resultLen, imappart);
+ PL_strcatn(result, resultLen, "?part=");
+ PL_strcatn(result, resultLen, libmimepart);
+
+ if (whereCurrent)
+ *whereCurrent = '/';
+
+ return result;
+}
+
+
+/* Given a part ID, looks through the MimeObject tree for a sub-part whose ID
+ number matches, and returns the MimeObject (else NULL.)
+ (part is not a URL -- it's of the form "1.3.5".)
+ */
+MimeObject *
+mime_address_to_part(const char *part, MimeObject *obj)
+{
+ /* Note: this is an N^2 operation, but the number of parts in a message
+ shouldn't ever be large enough that this really matters... */
+
+ bool match;
+
+ if (!part || !*part)
+ {
+ match = !obj->parent;
+ }
+ else
+ {
+ char *part2 = mime_part_address(obj);
+ if (!part2) return 0; /* MIME_OUT_OF_MEMORY */
+ match = !strcmp(part, part2);
+ PR_Free(part2);
+ }
+
+ if (match)
+ {
+ /* These are the droids we're looking for. */
+ return obj;
+ }
+ else if (!mime_typep(obj, (MimeObjectClass *) &mimeContainerClass))
+ {
+ /* Not a container, pull up, pull up! */
+ return 0;
+ }
+ else
+ {
+ int32_t i;
+ MimeContainer *cont = (MimeContainer *) obj;
+ for (i = 0; i < cont->nchildren; i++)
+ {
+ MimeObject *o2 = mime_address_to_part(part, cont->children[i]);
+ if (o2) return o2;
+ }
+ return 0;
+ }
+}
+
+/* Given a part ID, looks through the MimeObject tree for a sub-part whose ID
+ number matches; if one is found, returns the Content-Name of that part.
+ Else returns NULL. (part is not a URL -- it's of the form "1.3.5".)
+ */
+char *
+mime_find_content_type_of_part(const char *part, MimeObject *obj)
+{
+ char *result = 0;
+
+ obj = mime_address_to_part(part, obj);
+ if (!obj) return 0;
+
+ result = (obj->headers ? MimeHeaders_get(obj->headers, HEADER_CONTENT_TYPE, true, false) : 0);
+
+ return result;
+}
+
+/* Given a part ID, looks through the MimeObject tree for a sub-part whose ID
+ number matches; if one is found, returns the Content-Name of that part.
+ Else returns NULL. (part is not a URL -- it's of the form "1.3.5".)
+ */
+char *
+mime_find_suggested_name_of_part(const char *part, MimeObject *obj)
+{
+ char *result = 0;
+
+ obj = mime_address_to_part(part, obj);
+ if (!obj) return 0;
+
+ result = (obj->headers ? MimeHeaders_get_name(obj->headers, obj->options) : 0);
+
+ /* If this part doesn't have a name, but this part is one fork of an
+ AppleDouble, and the AppleDouble itself has a name, then use that. */
+ if (!result &&
+ obj->parent &&
+ obj->parent->headers &&
+ mime_typep(obj->parent,
+ (MimeObjectClass *) &mimeMultipartAppleDoubleClass))
+ result = MimeHeaders_get_name(obj->parent->headers, obj->options);
+
+ /* Else, if this part is itself an AppleDouble, and one of its children
+ has a name, then use that (check data fork first, then resource.) */
+ if (!result &&
+ mime_typep(obj, (MimeObjectClass *) &mimeMultipartAppleDoubleClass))
+ {
+ MimeContainer *cont = (MimeContainer *) obj;
+ if (cont->nchildren > 1 &&
+ cont->children[1] &&
+ cont->children[1]->headers)
+ result = MimeHeaders_get_name(cont->children[1]->headers, obj->options);
+
+ if (!result &&
+ cont->nchildren > 0 &&
+ cont->children[0] &&
+ cont->children[0]->headers)
+ result = MimeHeaders_get_name(cont->children[0]->headers, obj->options);
+ }
+
+ /* Ok, now we have the suggested name, if any.
+ Now we remove any extensions that correspond to the
+ Content-Transfer-Encoding. For example, if we see the headers
+
+ Content-Type: text/plain
+ Content-Disposition: inline; filename=foo.text.uue
+ Content-Transfer-Encoding: x-uuencode
+
+ then we would look up (in mime.types) the file extensions which are
+ associated with the x-uuencode encoding, find that "uue" is one of
+ them, and remove that from the end of the file name, thus returning
+ "foo.text" as the name. This is because, by the time this file ends
+ up on disk, its content-transfer-encoding will have been removed;
+ therefore, we should suggest a file name that indicates that.
+ */
+ if (result && obj->encoding && *obj->encoding)
+ {
+ int32_t L = strlen(result);
+ const char **exts = 0;
+
+ /*
+ I'd like to ask the mime.types file, "what extensions correspond
+ to obj->encoding (which happens to be "x-uuencode") but doing that
+ in a non-sphagetti way would require brain surgery. So, since
+ currently uuencode is the only content-transfer-encoding which we
+ understand which traditionally has an extension, we just special-
+ case it here! Icepicks in my forehead!
+
+ Note that it's special-cased in a similar way in libmsg/compose.c.
+ */
+ if (!PL_strcasecmp(obj->encoding, ENCODING_UUENCODE))
+ {
+ static const char *uue_exts[] = { "uu", "uue", 0 };
+ exts = uue_exts;
+ }
+
+ while (exts && *exts)
+ {
+ const char *ext = *exts;
+ int32_t L2 = strlen(ext);
+ if (L > L2 + 1 && /* long enough */
+ result[L - L2 - 1] == '.' && /* '.' in right place*/
+ !PL_strcasecmp(ext, result + (L - L2))) /* ext matches */
+ {
+ result[L - L2 - 1] = 0; /* truncate at '.' and stop. */
+ break;
+ }
+ exts++;
+ }
+ }
+
+ return result;
+}
+
+/* Parse the various "?" options off the URL and into the options struct.
+ */
+int
+mime_parse_url_options(const char *url, MimeDisplayOptions *options)
+{
+ const char *q;
+
+ if (!url || !*url) return 0;
+ if (!options) return 0;
+
+ MimeHeadersState default_headers = options->headers;
+
+ q = PL_strrchr (url, '?');
+ if (! q) return 0;
+ q++;
+ while (*q)
+ {
+ const char *end, *value, *name_end;
+ for (end = q; *end && *end != '&'; end++)
+ ;
+ for (value = q; *value != '=' && value < end; value++)
+ ;
+ name_end = value;
+ if (value < end) value++;
+ if (name_end <= q)
+ ;
+ else if (!PL_strncasecmp ("headers", q, name_end - q))
+ {
+ if (end > value && !PL_strncasecmp ("only", value, end-value))
+ options->headers = MimeHeadersOnly;
+ else if (end > value && !PL_strncasecmp ("none", value, end-value))
+ options->headers = MimeHeadersNone;
+ else if (end > value && !PL_strncasecmp ("all", value, end - value))
+ options->headers = MimeHeadersAll;
+ else if (end > value && !PL_strncasecmp ("some", value, end - value))
+ options->headers = MimeHeadersSome;
+ else if (end > value && !PL_strncasecmp ("micro", value, end - value))
+ options->headers = MimeHeadersMicro;
+ else if (end > value && !PL_strncasecmp ("cite", value, end - value))
+ options->headers = MimeHeadersCitation;
+ else if (end > value && !PL_strncasecmp ("citation", value, end-value))
+ options->headers = MimeHeadersCitation;
+ else
+ options->headers = default_headers;
+ }
+ else if (!PL_strncasecmp ("part", q, name_end - q) &&
+ options->format_out != nsMimeOutput::nsMimeMessageBodyQuoting)
+ {
+ PR_FREEIF (options->part_to_load);
+ if (end > value)
+ {
+ options->part_to_load = (char *) PR_MALLOC(end - value + 1);
+ if (!options->part_to_load)
+ return MIME_OUT_OF_MEMORY;
+ memcpy(options->part_to_load, value, end-value);
+ options->part_to_load[end-value] = 0;
+ }
+ }
+ else if (!PL_strncasecmp ("rot13", q, name_end - q))
+ {
+ options->rot13_p = end <= value || !PL_strncasecmp ("true", value, end - value);
+ }
+ else if (!PL_strncasecmp ("emitter", q, name_end - q))
+ {
+ if ((end > value) && !PL_strncasecmp ("js", value, end - value))
+ {
+ // the js emitter needs to hear about nested message bodies
+ // in order to build a proper representation.
+ options->notify_nested_bodies = true;
+ // show_attachment_inline_p has the side-effect of letting the
+ // emitter see all parts of a multipart/alternative, which it
+ // really appreciates.
+ options->show_attachment_inline_p = true;
+ // however, show_attachment_inline_p also results in a few
+ // subclasses writing junk into the body for display purposes.
+ // put a stop to these shenanigans by enabling write_pure_bodies.
+ // current offenders are:
+ // - MimeInlineImage
+ options->write_pure_bodies = true;
+ // we don't actually care about the data in the attachments, just the
+ // metadata (i.e. size)
+ options->metadata_only = true;
+ }
+ }
+
+ q = end;
+ if (*q)
+ q++;
+ }
+
+
+/* Compatibility with the "?part=" syntax used in the old (Mozilla 2.0)
+ MIME parser.
+
+ Basically, the problem is that the old part-numbering code was totally
+ busted: here's a comparison of the old and new numberings with a pair
+ of hypothetical messages (one with a single part, and one with nested
+ containers.)
+ NEW: OLD: OR:
+ message/rfc822
+ image/jpeg 1 0 0
+
+ message/rfc822
+ multipart/mixed 1 0 0
+ text/plain 1.1 1 1
+ image/jpeg 1.2 2 2
+ message/rfc822 1.3 - 3
+ text/plain 1.3.1 3 -
+ message/rfc822 1.4 - 4
+ multipart/mixed 1.4.1 4 -
+ text/plain 1.4.1.1 4.1 -
+ image/jpeg 1.4.1.2 4.2 -
+ text/plain 1.5 5 5
+
+ The "NEW" column is how the current code counts. The "OLD" column is
+ what "?part=" references would do in 3.0b4 and earlier; you'll see that
+ you couldn't directly refer to the child message/rfc822 objects at all!
+ But that's when it got really weird, because if you turned on
+ "Attachments As Links" (or used a URL like "?inline=false&part=...")
+ then you got a totally different numbering system (seen in the "OR"
+ column.) Gag!
+
+ So, the problem is, ClariNet had been using these part numbers in their
+ HTML news feeds, as a sleazy way of transmitting both complex HTML layouts
+ and images using NNTP as transport, without invoking HTTP.
+
+ The following clause is to provide some small amount of backward
+ compatibility. By looking at that table, one can see that in the new
+ model, "part=0" has no meaning, and neither does "part=2" or "part=3"
+ and so on.
+
+ "part=1" is ambiguous between the old and new way, as is any part
+ specification that has a "." in it.
+
+ So, the compatibility hack we do here is: if the part is "0", then map
+ that to "1". And if the part is >= "2", then prepend "1." to it (so that
+ we map "2" to "1.2", and "3" to "1.3".)
+
+ This leaves the URLs compatible in the cases of:
+
+ = single part messages
+ = references to elements of a top-level multipart except the first
+
+ and leaves them incompatible for:
+
+ = the first part of a top-level multipart
+ = all elements deeper than the outermost part
+
+ Life s#$%s when you don't properly think out things that end up turning
+ into de-facto standards...
+ */
+
+ if (options->part_to_load &&
+ !PL_strchr(options->part_to_load, '.')) /* doesn't contain a dot */
+ {
+ if (!strcmp(options->part_to_load, "0")) /* 0 */
+ {
+ PR_Free(options->part_to_load);
+ options->part_to_load = strdup("1");
+ if (!options->part_to_load)
+ return MIME_OUT_OF_MEMORY;
+ }
+ else if (strcmp(options->part_to_load, "1")) /* not 1 */
+ {
+ const char *prefix = "1.";
+ uint32_t slen = strlen(options->part_to_load) + strlen(prefix) + 1;
+ char *s = (char *) PR_MALLOC(slen);
+ if (!s) return MIME_OUT_OF_MEMORY;
+ PL_strncpyz(s, prefix, slen);
+ PL_strcatn(s, slen, options->part_to_load);
+ PR_Free(options->part_to_load);
+ options->part_to_load = s;
+ }
+ }
+
+ return 0;
+}
+
+
+/* Some output-generation utility functions...
+ */
+
+int
+MimeOptions_write(MimeHeaders *hdrs, MimeDisplayOptions *opt, const char *data,
+ int32_t length, bool user_visible_p)
+{
+ int status = 0;
+ void* closure = 0;
+ if (!opt || !opt->output_fn || !opt->state)
+ return 0;
+
+ closure = opt->output_closure;
+ if (!closure) closure = opt->stream_closure;
+
+// PR_ASSERT(opt->state->first_data_written_p);
+
+ if (opt->state->separator_queued_p && user_visible_p)
+ {
+ opt->state->separator_queued_p = false;
+ if (opt->state->separator_suppressed_p)
+ opt->state->separator_suppressed_p = false;
+ else {
+ const char *sep = "<BR><FIELDSET CLASS=\"mimeAttachmentHeader\">";
+ int lstatus = opt->output_fn(sep, strlen(sep), closure);
+ opt->state->separator_suppressed_p = false;
+ if (lstatus < 0) return lstatus;
+
+ nsCString name;
+ name.Adopt(MimeHeaders_get_name(hdrs, opt));
+ MimeHeaders_convert_header_value(opt, name, false);
+
+ if (!name.IsEmpty()) {
+ sep = "<LEGEND CLASS=\"mimeAttachmentHeaderName\">";
+ lstatus = opt->output_fn(sep, strlen(sep), closure);
+ opt->state->separator_suppressed_p = false;
+ if (lstatus < 0) return lstatus;
+
+ nsCString escapedName;
+ escapedName.Adopt(MsgEscapeHTML(name.get()));
+
+ lstatus = opt->output_fn(escapedName.get(),
+ escapedName.Length(), closure);
+ opt->state->separator_suppressed_p = false;
+ if (lstatus < 0) return lstatus;
+
+ sep = "</LEGEND>";
+ lstatus = opt->output_fn(sep, strlen(sep), closure);
+ opt->state->separator_suppressed_p = false;
+ if (lstatus < 0) return lstatus;
+ }
+
+ sep = "</FIELDSET><BR/>";
+ lstatus = opt->output_fn(sep, strlen(sep), closure);
+ opt->state->separator_suppressed_p = false;
+ if (lstatus < 0) return lstatus;
+ }
+ }
+ if (user_visible_p)
+ opt->state->separator_suppressed_p = false;
+
+ if (length > 0)
+ {
+ status = opt->output_fn(data, length, closure);
+ if (status < 0) return status;
+ }
+
+ return 0;
+}
+
+int
+MimeObject_write(MimeObject *obj, const char *output, int32_t length,
+ bool user_visible_p)
+{
+ if (!obj->output_p) return 0;
+
+ // if we're stripping attachments, check if any parent is not being ouput
+ if (obj->options->format_out == nsMimeOutput::nsMimeMessageAttach)
+ {
+ // if true, mime generates a separator in html - we don't want that.
+ user_visible_p = false;
+
+ for (MimeObject *parent = obj->parent; parent; parent = parent->parent)
+ {
+ if (!parent->output_p)
+ return 0;
+ }
+ }
+ if (!obj->options->state->first_data_written_p)
+ {
+ int status = MimeObject_output_init(obj, 0);
+ if (status < 0) return status;
+ NS_ASSERTION(obj->options->state->first_data_written_p, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
+ }
+
+ return MimeOptions_write(obj->headers, obj->options, output, length, user_visible_p);
+}
+
+int
+MimeObject_write_separator(MimeObject *obj)
+{
+ if (obj->options && obj->options->state &&
+ // we never want separators if we are asking for pure bodies
+ !obj->options->write_pure_bodies)
+ obj->options->state->separator_queued_p = true;
+ return 0;
+}
+
+int
+MimeObject_output_init(MimeObject *obj, const char *content_type)
+{
+ if (obj &&
+ obj->options &&
+ obj->options->state &&
+ !obj->options->state->first_data_written_p)
+ {
+ int status;
+ const char *charset = 0;
+ char *name = 0, *x_mac_type = 0, *x_mac_creator = 0;
+
+ if (!obj->options->output_init_fn)
+ {
+ obj->options->state->first_data_written_p = true;
+ return 0;
+ }
+
+ if (obj->headers)
+ {
+ char *ct;
+ name = MimeHeaders_get_name(obj->headers, obj->options);
+
+ ct = MimeHeaders_get(obj->headers, HEADER_CONTENT_TYPE,
+ false, false);
+ if (ct)
+ {
+ x_mac_type = MimeHeaders_get_parameter(ct, PARAM_X_MAC_TYPE, NULL, NULL);
+ x_mac_creator= MimeHeaders_get_parameter(ct, PARAM_X_MAC_CREATOR, NULL, NULL);
+ /* if don't have a x_mac_type and x_mac_creator, we need to try to get it from its parent */
+ if (!x_mac_type && !x_mac_creator && obj->parent && obj->parent->headers)
+ {
+ char * ctp = MimeHeaders_get(obj->parent->headers, HEADER_CONTENT_TYPE, false, false);
+ if (ctp)
+ {
+ x_mac_type = MimeHeaders_get_parameter(ctp, PARAM_X_MAC_TYPE, NULL, NULL);
+ x_mac_creator= MimeHeaders_get_parameter(ctp, PARAM_X_MAC_CREATOR, NULL, NULL);
+ PR_Free(ctp);
+ }
+ }
+
+ if (!(obj->options->override_charset)) {
+ char *charset = MimeHeaders_get_parameter(ct, "charset", nullptr, nullptr);
+ if (charset)
+ {
+ PR_FREEIF(obj->options->default_charset);
+ obj->options->default_charset = charset;
+ }
+ }
+ PR_Free(ct);
+ }
+ }
+
+ if (mime_typep(obj, (MimeObjectClass *) &mimeInlineTextClass))
+ charset = ((MimeInlineText *)obj)->charset;
+
+ if (!content_type)
+ content_type = obj->content_type;
+ if (!content_type)
+ content_type = TEXT_PLAIN;
+
+ //
+ // Set the charset on the channel we are dealing with so people know
+ // what the charset is set to. Do this for quoting/Printing ONLY!
+ //
+ extern void ResetChannelCharset(MimeObject *obj);
+ if ( (obj->options) &&
+ ( (obj->options->format_out == nsMimeOutput::nsMimeMessageQuoting) ||
+ (obj->options->format_out == nsMimeOutput::nsMimeMessageBodyQuoting) ||
+ (obj->options->format_out == nsMimeOutput::nsMimeMessageSaveAs) ||
+ (obj->options->format_out == nsMimeOutput::nsMimeMessagePrintOutput) ) )
+ ResetChannelCharset(obj);
+
+ status = obj->options->output_init_fn (content_type, charset, name,
+ x_mac_type, x_mac_creator,
+ obj->options->stream_closure);
+ PR_FREEIF(name);
+ PR_FREEIF(x_mac_type);
+ PR_FREEIF(x_mac_creator);
+ obj->options->state->first_data_written_p = true;
+ return status;
+ }
+ return 0;
+}
+
+char *
+mime_get_base_url(const char *url)
+{
+ if (!url)
+ return nullptr;
+
+ const char *s = strrchr(url, '?');
+ if (s && !strncmp(s, "?type=application/x-message-display", sizeof("?type=application/x-message-display") - 1))
+ {
+ const char *nextTerm = strchr(s, '&');
+ s = (nextTerm) ? nextTerm : s + strlen(s) - 1;
+ }
+ // we need to keep the ?number part of the url, or we won't know
+ // which local message the part belongs to.
+ if (s && *s && *(s+1) && !strncmp(s + 1, "number=", sizeof("number=") - 1))
+ {
+ const char *nextTerm = strchr(++s, '&');
+ s = (nextTerm) ? nextTerm : s + strlen(s) - 1;
+ }
+ char *result = (char *) PR_MALLOC(strlen(url) + 1);
+ NS_ASSERTION(result, "out of memory");
+ if (!result)
+ return nullptr;
+
+ memcpy(result, url, s - url);
+ result[s - url] = 0;
+ return result;
+}