summaryrefslogtreecommitdiffstats
path: root/mailnews/mime/src/mimemoz2.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mailnews/mime/src/mimemoz2.cpp')
-rw-r--r--mailnews/mime/src/mimemoz2.cpp2212
1 files changed, 2212 insertions, 0 deletions
diff --git a/mailnews/mime/src/mimemoz2.cpp b/mailnews/mime/src/mimemoz2.cpp
new file mode 100644
index 000000000..c47c5c8a5
--- /dev/null
+++ b/mailnews/mime/src/mimemoz2.cpp
@@ -0,0 +1,2212 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "prlog.h"
+#include "nsCOMPtr.h"
+#include "modlmime.h"
+#include "mimeobj.h"
+#include "mimemsg.h"
+#include "mimetric.h" /* for MIME_RichtextConverter */
+#include "mimethtm.h"
+#include "mimemsig.h"
+#include "mimemrel.h"
+#include "mimemalt.h"
+#include "mimebuf.h"
+#include "mimemapl.h"
+#include "prprf.h"
+#include "mimei.h" /* for moved MimeDisplayData struct */
+#include "mimebuf.h"
+#include "prmem.h"
+#include "plstr.h"
+#include "prmem.h"
+#include "mimemoz2.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "nsIServiceManager.h"
+#include "nsIStringBundle.h"
+#include "nsStringGlue.h"
+#include "nsMimeStringResources.h"
+#include "nsStreamConverter.h"
+#include "nsIMsgSend.h"
+#include "nsIMsgMailNewsUrl.h"
+#include "mozITXTToHTMLConv.h"
+#include "nsCExternalHandlerService.h"
+#include "nsIMIMEService.h"
+#include "nsIImapUrl.h"
+#include "nsMsgI18N.h"
+#include "nsICharsetConverterManager.h"
+#include "nsMimeTypes.h"
+#include "nsIIOService.h"
+#include "nsIURI.h"
+#include "nsNetCID.h"
+#include "nsIMsgWindow.h"
+#include "nsIMimeMiscStatus.h"
+#include "nsMsgUtils.h"
+#include "nsIChannel.h"
+#include "nsITransport.h"
+#include "mimeebod.h"
+#include "mimeeobj.h"
+// <for functions="HTML2Plaintext,HTMLSantinize">
+#include "nsXPCOM.h"
+#include "nsLayoutCID.h"
+#include "nsIComponentManager.h"
+#include "nsIParserUtils.h"
+// </for>
+#include "mozilla/Services.h"
+#include "mozilla/Unused.h"
+
+void ValidateRealName(nsMsgAttachmentData *aAttach, MimeHeaders *aHdrs);
+
+static MimeHeadersState MIME_HeaderType;
+static bool MIME_WrapLongLines;
+static bool MIME_VariableWidthPlaintext;
+
+mime_stream_data::mime_stream_data() : url_name(nullptr), orig_url_name(nullptr),
+ pluginObj2(nullptr), istream(nullptr), obj(nullptr), options(nullptr),
+ headers(nullptr), output_emitter(nullptr), firstCheck(false)
+{
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Attachment handling routines
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+MimeObject *mime_get_main_object(MimeObject* obj);
+
+nsresult MimeGetSize(MimeObject *child, int32_t *size) {
+ bool isLeaf = mime_subclass_p(child->clazz, (MimeObjectClass *) &mimeLeafClass);
+ bool isContainer = mime_subclass_p(child->clazz, (MimeObjectClass *) &mimeContainerClass);
+ bool isMsg = mime_subclass_p(child->clazz, (MimeObjectClass *) &mimeMessageClass);
+
+ if (isLeaf) {
+ *size += ((MimeLeaf *)child)->sizeSoFar;
+ } else if (isMsg) {
+ *size += ((MimeMessage *)child)->sizeSoFar;
+ } else if (isContainer) {
+ int i;
+ MimeContainer *cont = (MimeContainer *)child;
+ for (i = 0; i < cont->nchildren; ++i) {
+ MimeGetSize(cont->children[i], size);
+ }
+ }
+ return NS_OK;
+}
+
+nsresult
+ProcessBodyAsAttachment(MimeObject *obj, nsMsgAttachmentData **data)
+{
+ nsMsgAttachmentData *tmp;
+ char *disp = nullptr;
+ char *charset = nullptr;
+
+ // Ok, this is the special case when somebody sends an "attachment" as the
+ // body of an RFC822 message...I really don't think this is the way this
+ // should be done. I belive this should really be a multipart/mixed message
+ // with an empty body part, but what can ya do...our friends to the North seem
+ // to do this.
+ MimeObject *child = obj;
+
+ *data = new nsMsgAttachmentData[2];
+ if (!*data)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ tmp = *data;
+ tmp->m_realType = child->content_type;
+ tmp->m_realEncoding = child->encoding;
+ disp = MimeHeaders_get(child->headers, HEADER_CONTENT_DISPOSITION, false, false);
+ tmp->m_realName.Adopt(MimeHeaders_get_parameter(disp, "name", &charset, NULL));
+ if (!tmp->m_realName.IsEmpty())
+ {
+ char *fname = NULL;
+ fname = mime_decode_filename(tmp->m_realName.get(), charset, obj->options);
+ free(charset);
+ if (fname)
+ tmp->m_realName.Adopt(fname);
+ }
+ else
+ {
+ tmp->m_realName.Adopt(MimeHeaders_get_name(child->headers, obj->options));
+
+ if (tmp->m_realName.IsEmpty() &&
+ tmp->m_realType.LowerCaseEqualsLiteral(MESSAGE_RFC822))
+ {
+ // We haven't actually parsed the message "attachment", so just give it a
+ // generic name.
+ tmp->m_realName = "AttachedMessage.eml";
+ }
+ }
+
+ tmp->m_hasFilename = !tmp->m_realName.IsEmpty();
+
+ if (tmp->m_realName.IsEmpty() &&
+ StringBeginsWith(tmp->m_realType, NS_LITERAL_CSTRING("text"),
+ nsCaseInsensitiveCStringComparator()))
+ ValidateRealName(tmp, child->headers);
+
+ tmp->m_displayableInline = obj->clazz->displayable_inline_p(obj->clazz,
+ obj->headers);
+
+ char *tmpURL = nullptr;
+ char *id = nullptr;
+ char *id_imap = nullptr;
+
+ id = mime_part_address (obj);
+ if (obj->options->missing_parts)
+ id_imap = mime_imap_part_address (obj);
+
+ tmp->m_isDownloaded = !id_imap;
+
+ if (! id)
+ {
+ delete [] *data;
+ *data = nullptr;
+ PR_FREEIF(id_imap);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (obj->options && obj->options->url)
+ {
+ const char *url = obj->options->url;
+ nsresult rv;
+ if (id_imap && id)
+ {
+ // if this is an IMAP part.
+ tmpURL = mime_set_url_imap_part(url, id_imap, id);
+ rv = nsMimeNewURI(getter_AddRefs(tmp->m_url), tmpURL, nullptr);
+ }
+ else
+ {
+ // This is just a normal MIME part as usual.
+ tmpURL = mime_set_url_part(url, id, true);
+ rv = nsMimeNewURI(getter_AddRefs(tmp->m_url), tmpURL, nullptr);
+ }
+
+ if (!tmp->m_url || NS_FAILED(rv))
+ {
+ delete [] *data;
+ *data = nullptr;
+ PR_FREEIF(id);
+ PR_FREEIF(id_imap);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+ PR_FREEIF(id);
+ PR_FREEIF(id_imap);
+ PR_FREEIF(tmpURL);
+ tmp->m_description.Adopt(MimeHeaders_get(child->headers, HEADER_CONTENT_DESCRIPTION, false, false));
+
+ tmp->m_size = 0;
+ MimeGetSize(child, &tmp->m_size);
+
+ return NS_OK;
+}
+
+int32_t
+CountTotalMimeAttachments(MimeContainer *aObj)
+{
+ int32_t i;
+ int32_t rc = 0;
+
+ if ( (!aObj) || (!aObj->children) || (aObj->nchildren <= 0) )
+ return 0;
+
+ if (!mime_typep(((MimeObject *) aObj), (MimeObjectClass*) &mimeContainerClass))
+ return 0;
+
+ for (i=0; i<aObj->nchildren; i++)
+ rc += CountTotalMimeAttachments((MimeContainer *)aObj->children[i]) + 1;
+
+ return rc;
+}
+
+void
+ValidateRealName(nsMsgAttachmentData *aAttach, MimeHeaders *aHdrs)
+{
+ // Sanity.
+ if (!aAttach)
+ return;
+
+ // Do we need to validate?
+ if (!aAttach->m_realName.IsEmpty())
+ return;
+
+ // Internal MIME structures need not be named!
+ if (aAttach->m_realType.IsEmpty() ||
+ StringBeginsWith(aAttach->m_realType, NS_LITERAL_CSTRING("multipart"),
+ nsCaseInsensitiveCStringComparator()))
+ return;
+
+ //
+ // Now validate any other name we have for the attachment!
+ //
+ if (aAttach->m_realName.IsEmpty())
+ {
+ aAttach->m_realName = "attachment";
+ nsresult rv = NS_OK;
+ nsAutoCString contentType (aAttach->m_realType);
+ int32_t pos = contentType.FindChar(';');
+ if (pos > 0)
+ contentType.SetLength(pos);
+
+ nsCOMPtr<nsIMIMEService> mimeFinder (do_GetService(NS_MIMESERVICE_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv))
+ {
+ nsAutoCString fileExtension;
+ rv = mimeFinder->GetPrimaryExtension(contentType, EmptyCString(), fileExtension);
+
+ if (NS_SUCCEEDED(rv) && !fileExtension.IsEmpty())
+ {
+ aAttach->m_realName.Append('.');
+ aAttach->m_realName.Append(fileExtension);
+ }
+ }
+ }
+}
+
+static int32_t attIndex = 0;
+
+nsresult
+GenerateAttachmentData(MimeObject *object, const char *aMessageURL, MimeDisplayOptions *options,
+ bool isAnAppleDoublePart, int32_t attSize, nsMsgAttachmentData *aAttachData)
+{
+ nsCString imappart;
+ nsCString part;
+ bool isExternalAttachment = false;
+
+ /* be sure the object has not be marked as Not to be an attachment */
+ if (object->dontShowAsAttachment)
+ return NS_OK;
+
+ part.Adopt(mime_part_address(object));
+ if (part.IsEmpty())
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ if (options->missing_parts)
+ imappart.Adopt(mime_imap_part_address(object));
+
+ char *urlSpec = nullptr;
+ if (!imappart.IsEmpty())
+ {
+ urlSpec = mime_set_url_imap_part(aMessageURL, imappart.get(), part.get());
+ }
+ else
+ {
+ char *no_part_url = nullptr;
+ if (options->part_to_load && options->format_out == nsMimeOutput::nsMimeMessageBodyDisplay)
+ no_part_url = mime_get_base_url(aMessageURL);
+ if (no_part_url) {
+ urlSpec = mime_set_url_part(no_part_url, part.get(), true);
+ PR_Free(no_part_url);
+ }
+ else
+ {
+ // if the mime object contains an external attachment URL, then use it, otherwise
+ // fall back to creating an attachment url based on the message URI and the
+ // part number.
+ urlSpec = mime_external_attachment_url(object);
+ isExternalAttachment = urlSpec ? true : false;
+ if (!urlSpec)
+ urlSpec = mime_set_url_part(aMessageURL, part.get(), true);
+ }
+ }
+
+ if (!urlSpec)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ if ((options->format_out == nsMimeOutput::nsMimeMessageBodyDisplay) && (PL_strncasecmp(aMessageURL, urlSpec, strlen(urlSpec)) == 0))
+ return NS_OK;
+
+ nsCString urlString(urlSpec);
+
+ nsMsgAttachmentData *tmp = &(aAttachData[attIndex++]);
+
+ tmp->m_realType = object->content_type;
+ tmp->m_realEncoding = object->encoding;
+ tmp->m_isExternalAttachment = isExternalAttachment;
+ tmp->m_isExternalLinkAttachment =
+ (isExternalAttachment &&
+ StringBeginsWith(urlString, NS_LITERAL_CSTRING("http"),
+ nsCaseInsensitiveCStringComparator()));
+ tmp->m_size = attSize;
+ tmp->m_sizeExternalStr = "-1";
+ tmp->m_disposition.Adopt(MimeHeaders_get(object->headers, HEADER_CONTENT_DISPOSITION, true, false));
+ tmp->m_displayableInline = object->clazz->displayable_inline_p(object->clazz, object->headers);
+
+ char *part_addr = mime_imap_part_address(object);
+ tmp->m_isDownloaded = !part_addr;
+ PR_FREEIF(part_addr);
+
+ int32_t i;
+ char *charset = nullptr;
+ char *disp = MimeHeaders_get(object->headers, HEADER_CONTENT_DISPOSITION, false, false);
+ if (disp)
+ {
+ tmp->m_realName.Adopt(MimeHeaders_get_parameter(disp, "filename", &charset, nullptr));
+ if (isAnAppleDoublePart)
+ for (i = 0; i < 2 && tmp->m_realName.IsEmpty(); i ++)
+ {
+ PR_FREEIF(disp);
+ free(charset);
+ disp = MimeHeaders_get(((MimeContainer *)object)->children[i]->headers, HEADER_CONTENT_DISPOSITION, false, false);
+ tmp->m_realName.Adopt(MimeHeaders_get_parameter(disp, "filename", &charset, nullptr));
+ }
+
+ if (!tmp->m_realName.IsEmpty())
+ {
+ // check encoded type
+ //
+ // The parameter of Content-Disposition must use RFC 2231.
+ // But old Netscape 4.x and Outlook Express etc. use RFC2047.
+ // So we should parse both types.
+
+ char *fname = nullptr;
+ fname = mime_decode_filename(tmp->m_realName.get(), charset, options);
+ free(charset);
+
+ if (fname)
+ tmp->m_realName.Adopt(fname);
+ }
+
+ PR_FREEIF(disp);
+ }
+
+ disp = MimeHeaders_get(object->headers, HEADER_CONTENT_TYPE, false, false);
+ if (disp)
+ {
+ tmp->m_xMacType.Adopt(MimeHeaders_get_parameter(disp, PARAM_X_MAC_TYPE, nullptr, nullptr));
+ tmp->m_xMacCreator.Adopt(MimeHeaders_get_parameter(disp, PARAM_X_MAC_CREATOR, nullptr, nullptr));
+
+ if (tmp->m_realName.IsEmpty())
+ {
+ tmp->m_realName.Adopt(MimeHeaders_get_parameter(disp, "name", &charset, nullptr));
+ if (isAnAppleDoublePart)
+ // the data fork is the 2nd part, and we should ALWAYS look there first for the file name
+ for (i = 1; i >= 0 && tmp->m_realName.IsEmpty(); i --)
+ {
+ PR_FREEIF(disp);
+ free(charset);
+ disp = MimeHeaders_get(((MimeContainer *)object)->children[i]->headers, HEADER_CONTENT_TYPE, false, false);
+ tmp->m_realName.Adopt(MimeHeaders_get_parameter(disp, "name", &charset, nullptr));
+ tmp->m_realType.Adopt(
+ MimeHeaders_get(((MimeContainer *)object)->children[i]->headers,
+ HEADER_CONTENT_TYPE, true, false));
+ }
+
+ if (!tmp->m_realName.IsEmpty())
+ {
+ // check encoded type
+ //
+ // The parameter of Content-Disposition must use RFC 2231.
+ // But old Netscape 4.x and Outlook Express etc. use RFC2047.
+ // So we should parse both types.
+
+ char *fname = nullptr;
+ fname = mime_decode_filename(tmp->m_realName.get(), charset, options);
+ free(charset);
+
+ if (fname)
+ tmp->m_realName.Adopt(fname);
+ }
+ }
+
+ if (tmp->m_isExternalLinkAttachment)
+ {
+ // If an external link attachment part's Content-Type contains a
+ // |size| parm, store it in m_sizeExternalStr. Let the msgHeaderSink
+ // addAttachmentField() figure out if it's sane, and don't bother
+ // strtol'ing it to an int only to emit it as a string.
+ char* sizeStr = MimeHeaders_get_parameter(disp, "size", nullptr, nullptr);
+ if (sizeStr)
+ tmp->m_sizeExternalStr = sizeStr;
+ }
+
+ PR_FREEIF(disp);
+ }
+
+ tmp->m_description.Adopt(MimeHeaders_get(object->headers, HEADER_CONTENT_DESCRIPTION,
+ false, false));
+
+ // Now, do the right thing with the name!
+ if (tmp->m_realName.IsEmpty() && !(tmp->m_realType.LowerCaseEqualsLiteral(MESSAGE_RFC822)))
+ {
+ // Keep in mind that the name was provided by us and this is probably not a
+ // real attachment.
+ tmp->m_hasFilename = false;
+ /* If this attachment doesn't have a name, just give it one... */
+ tmp->m_realName.Adopt(MimeGetStringByID(MIME_MSG_DEFAULT_ATTACHMENT_NAME));
+ if (!tmp->m_realName.IsEmpty())
+ {
+ char *newName = PR_smprintf(tmp->m_realName.get(), part.get());
+ if (newName)
+ tmp->m_realName.Adopt(newName);
+ }
+ else
+ tmp->m_realName.Adopt(mime_part_address(object));
+ } else {
+ tmp->m_hasFilename = true;
+ }
+
+ if (!tmp->m_realName.IsEmpty() && !tmp->m_isExternalAttachment)
+ {
+ urlString.Append("&filename=");
+ nsAutoCString aResult;
+ if (NS_SUCCEEDED(MsgEscapeString(tmp->m_realName,
+ nsINetUtil::ESCAPE_XALPHAS, aResult)))
+ urlString.Append(aResult);
+ else
+ urlString.Append(tmp->m_realName);
+ if (tmp->m_realType.EqualsLiteral("message/rfc822") &&
+ !StringEndsWith(urlString, NS_LITERAL_CSTRING(".eml"), nsCaseInsensitiveCStringComparator()))
+ urlString.Append(".eml");
+ } else if (tmp->m_isExternalAttachment) {
+ // Allows the JS mime emitter to figure out the part information.
+ urlString.Append("?part=");
+ urlString.Append(part);
+ } else if (tmp->m_realType.LowerCaseEqualsLiteral(MESSAGE_RFC822)) {
+ // Special case...if this is a enclosed RFC822 message, give it a nice
+ // name.
+ if (object->headers->munged_subject)
+ {
+ nsCString subject;
+ subject.Assign(object->headers->munged_subject);
+ MimeHeaders_convert_header_value(options, subject, false);
+ tmp->m_realName.Assign(subject);
+ tmp->m_realName.Append(".eml");
+ }
+ else
+ tmp->m_realName = "ForwardedMessage.eml";
+ }
+
+ nsresult rv = nsMimeNewURI(getter_AddRefs(tmp->m_url), urlString.get(), nullptr);
+
+ PR_FREEIF(urlSpec);
+
+ if (NS_FAILED(rv) || !tmp->m_url)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ ValidateRealName(tmp, object->headers);
+
+ return NS_OK;
+}
+
+nsresult
+BuildAttachmentList(MimeObject *anObject, nsMsgAttachmentData *aAttachData, const char *aMessageURL)
+{
+ nsresult rv;
+ int32_t i;
+ MimeContainer *cobj = (MimeContainer *) anObject;
+ bool found_output = false;
+
+ if ( (!anObject) || (!cobj->children) || (!cobj->nchildren) ||
+ (mime_typep(anObject, (MimeObjectClass *)&mimeExternalBodyClass)))
+ return NS_OK;
+
+ for (i = 0; i < cobj->nchildren ; i++)
+ {
+ MimeObject *child = cobj->children[i];
+ char *ct = child->content_type;
+
+ // We're going to ignore the output_p attribute because we want to output
+ // any part with a name to work around bug 674473
+
+ // Skip the first child that's being output if it's in fact a message body.
+ // Start by assuming that it is, until proven otherwise in the code below.
+ bool skip = true;
+ if (found_output)
+ // not first child being output
+ skip = false;
+ else if (! ct)
+ // no content type so can't be message body
+ skip = false;
+ else if (PL_strcasecmp (ct, TEXT_PLAIN) &&
+ PL_strcasecmp (ct, TEXT_HTML) &&
+ PL_strcasecmp (ct, TEXT_MDL))
+ // not a type we recognize as a message body
+ skip = false;
+ // we're displaying all body parts
+ if (child->options->html_as_p == 4)
+ skip = false;
+ if (skip && child->headers)
+ {
+ // If it has a filename, we don't skip it regardless of the
+ // content disposition which can be "inline" or "attachment".
+ // Inline parts are not shown when attachments aren't displayed
+ // inline, so the only chance to see the part is as attachment.
+ char * name = MimeHeaders_get_name(child->headers, nullptr);
+ if (name)
+ skip = false;
+ PR_FREEIF(name);
+ }
+
+ found_output = true;
+ if (skip)
+ continue;
+
+ // We should generate an attachment for leaf object only but...
+ bool isALeafObject = mime_subclass_p(child->clazz, (MimeObjectClass *) &mimeLeafClass);
+
+ // ...we will generate an attachment for inline message too.
+ bool isAnInlineMessage = mime_typep(child, (MimeObjectClass *) &mimeMessageClass);
+
+ // AppleDouble part need special care: we need to fetch the part as well its two
+ // children for the needed info as they could be anywhere, eventually, they won't contain
+ // a name or file name. In any case we need to build only one attachment data
+ bool isAnAppleDoublePart = mime_typep(child, (MimeObjectClass *) &mimeMultipartAppleDoubleClass) &&
+ ((MimeContainer *)child)->nchildren == 2;
+
+ // The function below does not necessarily set the size to something (I
+ // don't think it will work for external objects, for instance, since they
+ // are neither containers nor leafs).
+ int32_t attSize = 0;
+ MimeGetSize(child, &attSize);
+
+ if (isALeafObject || isAnInlineMessage || isAnAppleDoublePart)
+ {
+ rv = GenerateAttachmentData(child, aMessageURL, anObject->options, isAnAppleDoublePart, attSize, aAttachData);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Now build the attachment list for the children of our object...
+ if (!isALeafObject && !isAnAppleDoublePart)
+ {
+ rv = BuildAttachmentList((MimeObject *)child, aAttachData, aMessageURL);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+
+ return NS_OK;
+
+}
+
+extern "C" nsresult
+MimeGetAttachmentList(MimeObject *tobj, const char *aMessageURL, nsMsgAttachmentData **data)
+{
+ MimeObject *obj;
+ MimeContainer *cobj;
+ int32_t n;
+ bool isAnInlineMessage;
+
+ if (!data)
+ return NS_ERROR_INVALID_ARG;
+ *data = nullptr;
+
+ obj = mime_get_main_object(tobj);
+ if (!obj)
+ return NS_OK;
+
+ if (!mime_subclass_p(obj->clazz, (MimeObjectClass*) &mimeContainerClass))
+ return ProcessBodyAsAttachment(obj, data);
+
+ isAnInlineMessage = mime_typep(obj, (MimeObjectClass *) &mimeMessageClass);
+
+ cobj = (MimeContainer*) obj;
+ n = CountTotalMimeAttachments(cobj);
+ if (n <= 0)
+ // XXX n is a regular number here, not meaningful as an nsresult
+ return static_cast<nsresult>(n);
+
+ // in case of an inline message (as body), we need an extra slot for the
+ // message itself that we will fill later...
+ if (isAnInlineMessage)
+ n ++;
+
+ *data = new nsMsgAttachmentData[n + 1];
+ if (!*data)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ attIndex = 0;
+
+ // Now, build the list!
+
+ nsresult rv;
+
+ if (isAnInlineMessage)
+ {
+ int32_t size = 0;
+ MimeGetSize(obj, &size);
+ rv = GenerateAttachmentData(obj, aMessageURL, obj->options, false, size,
+ *data);
+ if (NS_FAILED(rv))
+ {
+ delete [] *data; // release data in case of error return.
+ return rv;
+ }
+
+ }
+ rv = BuildAttachmentList((MimeObject *) cobj, *data, aMessageURL);
+ if (NS_FAILED(rv))
+ {
+ delete [] *data; // release data in case of error return.
+ }
+ return rv;
+}
+
+extern "C" void
+MimeFreeAttachmentList(nsMsgAttachmentData *data)
+{
+ delete [] data;
+}
+
+extern "C" void
+NotifyEmittersOfAttachmentList(MimeDisplayOptions *opt,
+ nsMsgAttachmentData *data)
+{
+ int32_t i = 0;
+ nsMsgAttachmentData *tmp = data;
+
+ if (!tmp)
+ return;
+
+ while (tmp->m_url)
+ {
+ // The code below implements the following logic:
+ // - Always display the attachment if the Content-Disposition is
+ // "attachment" or if it can't be displayed inline.
+ // - If there's no name at all, just skip it (we don't know what to do with
+ // it then).
+ // - If the attachment has a "provided name" (i.e. not something like "Part
+ // 1.2"), display it.
+ // - If we're asking for all body parts and NOT asking for metadata only,
+ // display it.
+ // - Otherwise, skip it.
+ if (!tmp->m_disposition.Equals("attachment") && tmp->m_displayableInline &&
+ (tmp->m_realName.IsEmpty() || (!tmp->m_hasFilename &&
+ (opt->html_as_p != 4 || opt->metadata_only))))
+ {
+ ++i;
+ ++tmp;
+ continue;
+ }
+
+ nsAutoCString spec;
+ if (tmp->m_url) {
+ if (tmp->m_isExternalLinkAttachment)
+ mozilla::Unused << tmp->m_url->GetAsciiSpec(spec);
+ else
+ mozilla::Unused << tmp->m_url->GetSpec(spec);
+ }
+
+ nsAutoCString sizeStr;
+ if (tmp->m_isExternalLinkAttachment)
+ sizeStr.Append(tmp->m_sizeExternalStr);
+ else
+ sizeStr.AppendInt(tmp->m_size);
+
+ nsAutoCString downloadedStr;
+ downloadedStr.AppendInt(tmp->m_isDownloaded);
+
+ mimeEmitterStartAttachment(opt, tmp->m_realName.get(), tmp->m_realType.get(),
+ spec.get(), tmp->m_isExternalAttachment);
+ mimeEmitterAddAttachmentField(opt, HEADER_X_MOZILLA_PART_URL, spec.get());
+ mimeEmitterAddAttachmentField(opt, HEADER_X_MOZILLA_PART_SIZE, sizeStr.get());
+ mimeEmitterAddAttachmentField(opt, HEADER_X_MOZILLA_PART_DOWNLOADED, downloadedStr.get());
+
+ if ( (opt->format_out == nsMimeOutput::nsMimeMessageQuoting) ||
+ (opt->format_out == nsMimeOutput::nsMimeMessageBodyQuoting) ||
+ (opt->format_out == nsMimeOutput::nsMimeMessageSaveAs) ||
+ (opt->format_out == nsMimeOutput::nsMimeMessagePrintOutput))
+ {
+ mimeEmitterAddAttachmentField(opt, HEADER_CONTENT_DESCRIPTION, tmp->m_description.get());
+ mimeEmitterAddAttachmentField(opt, HEADER_CONTENT_TYPE, tmp->m_realType.get());
+ mimeEmitterAddAttachmentField(opt, HEADER_CONTENT_ENCODING, tmp->m_realEncoding.get());
+ }
+
+ mimeEmitterEndAttachment(opt);
+ ++i;
+ ++tmp;
+ }
+ mimeEmitterEndAllAttachments(opt);
+}
+
+// Utility to create a nsIURI object...
+extern "C" nsresult
+nsMimeNewURI(nsIURI** aInstancePtrResult, const char *aSpec, nsIURI *aBase)
+{
+ if (nullptr == aInstancePtrResult)
+ return NS_ERROR_NULL_POINTER;
+
+ nsCOMPtr<nsIIOService> pService =
+ mozilla::services::GetIOService();
+ NS_ENSURE_TRUE(pService, NS_ERROR_FACTORY_NOT_REGISTERED);
+
+ return pService->NewURI(nsDependentCString(aSpec), nullptr, aBase, aInstancePtrResult);
+}
+
+extern "C" nsresult
+SetMailCharacterSetToMsgWindow(MimeObject *obj, const char *aCharacterSet)
+{
+ nsresult rv = NS_OK;
+
+ if (obj && obj->options)
+ {
+ mime_stream_data *msd = (mime_stream_data *) (obj->options->stream_closure);
+ if (msd)
+ {
+ nsIChannel *channel = msd->channel;
+ if (channel)
+ {
+ nsCOMPtr<nsIURI> uri;
+ channel->GetURI(getter_AddRefs(uri));
+ if (uri)
+ {
+ nsCOMPtr<nsIMsgMailNewsUrl> msgurl (do_QueryInterface(uri));
+ if (msgurl)
+ {
+ nsCOMPtr<nsIMsgWindow> msgWindow;
+ msgurl->GetMsgWindow(getter_AddRefs(msgWindow));
+ if (msgWindow)
+ rv = msgWindow->SetMailCharacterSet(!PL_strcasecmp(aCharacterSet, "us-ascii") ?
+ static_cast<const nsCString&>(NS_LITERAL_CSTRING("ISO-8859-1")) :
+ static_cast<const nsCString&>(nsDependentCString(aCharacterSet)));
+ }
+ }
+ }
+ }
+ }
+
+ return rv;
+}
+
+static void ResetMsgHeaderSinkProps(nsIURI *uri)
+{
+ nsCOMPtr<nsIMsgMailNewsUrl> msgurl(do_QueryInterface(uri));
+ if (!msgurl)
+ return;
+
+ nsCOMPtr<nsIMsgWindow> msgWindow;
+ msgurl->GetMsgWindow(getter_AddRefs(msgWindow));
+ if (!msgWindow)
+ return;
+
+ nsCOMPtr<nsIMsgHeaderSink> msgHeaderSink;
+ msgWindow->GetMsgHeaderSink(getter_AddRefs(msgHeaderSink));
+ if (!msgHeaderSink)
+ return;
+
+ msgHeaderSink->ResetProperties();
+}
+
+static char *
+mime_file_type (const char *filename, void *stream_closure)
+{
+ char *retType = nullptr;
+ char *ext = nullptr;
+ nsresult rv;
+
+ ext = PL_strrchr(filename, '.');
+ if (ext)
+ {
+ ext++;
+ nsCOMPtr<nsIMIMEService> mimeFinder (do_GetService(NS_MIMESERVICE_CONTRACTID, &rv));
+ if (mimeFinder) {
+ nsAutoCString type;
+ mimeFinder->GetTypeFromExtension(nsDependentCString(ext), type);
+ retType = ToNewCString(type);
+ }
+ }
+
+ return retType;
+}
+
+int ConvertUsingEncoderAndDecoder(const char *stringToUse, int32_t inLength,
+ nsIUnicodeEncoder *encoder, nsIUnicodeDecoder *decoder,
+ char **pConvertedString, int32_t *outLength)
+{
+ // buffer size 144 =
+ // 72 (default line len for compose)
+ // times 2 (converted byte len might be larger)
+ const int klocalbufsize = 144;
+ // do the conversion
+ char16_t *unichars;
+ int32_t unicharLength;
+ int32_t srcLen = inLength;
+ int32_t dstLength = 0;
+ char *dstPtr;
+ nsresult rv;
+
+ // use this local buffer if possible
+ char16_t localbuf[klocalbufsize+1];
+ if (inLength > klocalbufsize) {
+ rv = decoder->GetMaxLength(stringToUse, srcLen, &unicharLength);
+ // allocate temporary buffer to hold unicode string
+ unichars = new char16_t[unicharLength];
+ }
+ else {
+ unichars = localbuf;
+ unicharLength = klocalbufsize+1;
+ }
+ if (unichars == nullptr) {
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ }
+ else {
+ // convert to unicode, replacing failed chars with 0xFFFD as in
+ // the methode used in nsXMLHttpRequest::ConvertBodyToText and nsScanner::Append
+ //
+ // We will need several pass to convert the whole string if it has invalid characters
+ // 'totalChars' is where the sum of the number of converted characters will be done
+ // 'dataLen' is the number of character left to convert
+ // 'outLen' is the number of characters still available in the output buffer as input of decoder->Convert
+ // and the number of characters written in it as output.
+ int32_t totalChars = 0,
+ inBufferIndex = 0,
+ outBufferIndex = 0;
+ int32_t dataLen = srcLen,
+ outLen = unicharLength;
+
+ do {
+ int32_t inBufferLength = dataLen;
+ rv = decoder->Convert(&stringToUse[inBufferIndex],
+ &inBufferLength,
+ &unichars[outBufferIndex],
+ &outLen);
+ totalChars += outLen;
+ // Done if conversion successful
+ if (NS_SUCCEEDED(rv))
+ break;
+
+ // We consume one byte, replace it with U+FFFD
+ // and try the conversion again.
+ outBufferIndex += outLen;
+ unichars[outBufferIndex++] = char16_t(0xFFFD);
+ // totalChars is updated here
+ outLen = unicharLength - (++totalChars);
+
+ inBufferIndex += inBufferLength + 1;
+ dataLen -= inBufferLength + 1;
+
+ decoder->Reset();
+
+ // If there is not at least one byte available after the one we
+ // consumed, we're done
+ } while ( dataLen > 0 );
+
+ rv = encoder->GetMaxLength(unichars, totalChars, &dstLength);
+ // allocale an output buffer
+ dstPtr = (char *) PR_Malloc(dstLength + 1);
+ if (dstPtr == nullptr) {
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ }
+ else {
+ int32_t buffLength = dstLength;
+ // convert from unicode
+ rv = encoder->SetOutputErrorBehavior(nsIUnicodeEncoder::kOnError_Replace, nullptr, '?');
+ if (NS_SUCCEEDED(rv)) {
+ rv = encoder->Convert(unichars, &totalChars, dstPtr, &dstLength);
+ if (NS_SUCCEEDED(rv)) {
+ int32_t finLen = buffLength - dstLength;
+ rv = encoder->Finish((char *)(dstPtr+dstLength), &finLen);
+ if (NS_SUCCEEDED(rv)) {
+ dstLength += finLen;
+ }
+ dstPtr[dstLength] = '\0';
+ *pConvertedString = dstPtr; // set the result string
+ *outLength = dstLength;
+ }
+ }
+ }
+ if (inLength > klocalbufsize)
+ delete [] unichars;
+ }
+
+ return NS_SUCCEEDED(rv) ? 0 : -1;
+}
+
+
+static int
+mime_convert_charset (const char *input_line, int32_t input_length,
+ const char *input_charset, const char *output_charset,
+ char **output_ret, int32_t *output_size_ret,
+ void *stream_closure, nsIUnicodeDecoder *decoder, nsIUnicodeEncoder *encoder)
+{
+ int32_t res = -1;
+ char *convertedString = NULL;
+ int32_t convertedStringLen = 0;
+ if (encoder && decoder)
+ {
+ res = ConvertUsingEncoderAndDecoder(input_line, input_length, encoder, decoder, &convertedString, &convertedStringLen);
+ }
+ if (res != 0)
+ {
+ *output_ret = 0;
+ *output_size_ret = 0;
+ }
+ else
+ {
+ *output_ret = (char *) convertedString;
+ *output_size_ret = convertedStringLen;
+ }
+
+ return 0;
+}
+
+static int
+mime_output_fn(const char *buf, int32_t size, void *stream_closure)
+{
+ uint32_t written = 0;
+ mime_stream_data *msd = (mime_stream_data *) stream_closure;
+ if ( (!msd->pluginObj2) && (!msd->output_emitter) )
+ return -1;
+
+ // Fire pending start request
+ ((nsStreamConverter*)msd->pluginObj2)->FirePendingStartRequest();
+
+
+ // Now, write to the WriteBody method if this is a message body and not
+ // a part retrevial
+ if (!msd->options->part_to_load || msd->options->format_out == nsMimeOutput::nsMimeMessageBodyDisplay)
+ {
+ if (msd->output_emitter)
+ {
+ msd->output_emitter->WriteBody(Substring(buf, buf+size),
+ &written);
+ }
+ }
+ else
+ {
+ if (msd->output_emitter)
+ {
+ msd->output_emitter->Write(Substring(buf, buf+size), &written);
+ }
+ }
+ return written;
+}
+
+extern "C" int
+mime_display_stream_write (nsMIMESession *stream,
+ const char* buf,
+ int32_t size)
+{
+ mime_stream_data *msd = (mime_stream_data *) ((nsMIMESession *)stream)->data_object;
+
+ MimeObject *obj = (msd ? msd->obj : 0);
+ if (!obj) return -1;
+
+ //
+ // Ok, now check to see if this is a display operation for a MIME Parts on Demand
+ // enabled call.
+ //
+ if (msd->firstCheck)
+ {
+ if (msd->channel)
+ {
+ nsCOMPtr<nsIURI> aUri;
+ if (NS_SUCCEEDED(msd->channel->GetURI(getter_AddRefs(aUri))))
+ {
+ nsCOMPtr<nsIImapUrl> imapURL = do_QueryInterface(aUri);
+ if (imapURL)
+ {
+ nsImapContentModifiedType cModified;
+ if (NS_SUCCEEDED(imapURL->GetContentModified(&cModified)))
+ {
+ if ( cModified != nsImapContentModifiedTypes::IMAP_CONTENT_NOT_MODIFIED )
+ msd->options->missing_parts = true;
+ }
+ }
+ }
+ }
+
+ msd->firstCheck = false;
+ }
+
+ return obj->clazz->parse_buffer((char *) buf, size, obj);
+}
+
+extern "C" void
+mime_display_stream_complete (nsMIMESession *stream)
+{
+ mime_stream_data *msd = (mime_stream_data *) ((nsMIMESession *)stream)->data_object;
+ MimeObject *obj = (msd ? msd->obj : 0);
+ if (obj)
+ {
+ int status;
+ bool abortNow = false;
+
+ if ((obj->options) && (obj->options->headers == MimeHeadersOnly))
+ abortNow = true;
+
+ status = obj->clazz->parse_eof(obj, abortNow);
+ obj->clazz->parse_end(obj, (status < 0 ? true : false));
+
+ //
+ // Ok, now we are going to process the attachment data by getting all
+ // of the attachment info and then driving the emitter with this data.
+ //
+ if (!msd->options->part_to_load || msd->options->format_out == nsMimeOutput::nsMimeMessageBodyDisplay)
+ {
+ nsMsgAttachmentData *attachments;
+ nsresult rv = MimeGetAttachmentList(obj, msd->url_name, &attachments);
+ if (NS_SUCCEEDED(rv))
+ {
+ NotifyEmittersOfAttachmentList(msd->options, attachments);
+ MimeFreeAttachmentList(attachments);
+ }
+ }
+
+ // Release the conversion object - this has to be done after
+ // we finish processing data.
+ if ( obj->options)
+ {
+ NS_IF_RELEASE(obj->options->conv);
+ }
+
+ // Destroy the object now.
+ PR_ASSERT(msd->options == obj->options);
+ mime_free(obj);
+ obj = NULL;
+ if (msd->options)
+ {
+ delete msd->options;
+ msd->options = 0;
+ }
+ }
+
+ if (msd->headers)
+ MimeHeaders_free (msd->headers);
+
+ if (msd->url_name)
+ NS_Free(msd->url_name);
+
+ if (msd->orig_url_name)
+ NS_Free(msd->orig_url_name);
+
+ delete msd;
+}
+
+extern "C" void
+mime_display_stream_abort (nsMIMESession *stream, int status)
+{
+ mime_stream_data *msd = (mime_stream_data *) ((nsMIMESession *)stream)->data_object;
+
+ MimeObject *obj = (msd ? msd->obj : 0);
+ if (obj)
+ {
+ if (!obj->closed_p)
+ obj->clazz->parse_eof(obj, true);
+ if (!obj->parsed_p)
+ obj->clazz->parse_end(obj, true);
+
+ // Destroy code....
+ PR_ASSERT(msd->options == obj->options);
+ mime_free(obj);
+ if (msd->options)
+ {
+ delete msd->options;
+ msd->options = 0;
+ }
+ }
+
+ if (msd->headers)
+ MimeHeaders_free (msd->headers);
+
+ if (msd->url_name)
+ NS_Free(msd->url_name);
+
+ if (msd->orig_url_name)
+ NS_Free(msd->orig_url_name);
+
+ delete msd;
+}
+
+static int
+mime_output_init_fn (const char *type,
+ const char *charset,
+ const char *name,
+ const char *x_mac_type,
+ const char *x_mac_creator,
+ void *stream_closure)
+{
+ mime_stream_data *msd = (mime_stream_data *) stream_closure;
+
+ // Now, all of this stream creation is done outside of libmime, so this
+ // is just a check of the pluginObj member and returning accordingly.
+ if (!msd->pluginObj2)
+ return -1;
+ else
+ return 0;
+}
+
+static void *mime_image_begin(const char *image_url, const char *content_type,
+ void *stream_closure);
+static void mime_image_end(void *image_closure, int status);
+static char *mime_image_make_image_html(void *image_data);
+static int mime_image_write_buffer(const char *buf, int32_t size, void *image_closure);
+
+/* Interface between libmime and inline display of images: the abomination
+ that is known as "internal-external-reconnect".
+ */
+class mime_image_stream_data {
+public:
+ mime_image_stream_data();
+
+ mime_stream_data *msd;
+ char *url;
+ nsMIMESession *istream;
+};
+
+mime_image_stream_data::mime_image_stream_data()
+{
+ url = nullptr;
+ istream = nullptr;
+ msd = nullptr;
+}
+
+static void *
+mime_image_begin(const char *image_url, const char *content_type,
+ void *stream_closure)
+{
+ mime_stream_data *msd = (mime_stream_data *) stream_closure;
+ class mime_image_stream_data *mid;
+
+ mid = new mime_image_stream_data;
+ if (!mid) return nullptr;
+
+
+ mid->msd = msd;
+
+ mid->url = (char *) strdup(image_url);
+ if (!mid->url)
+ {
+ PR_Free(mid);
+ return nullptr;
+ }
+
+ mid->istream = (nsMIMESession *) msd->pluginObj2;
+ return mid;
+}
+
+static void
+mime_image_end(void *image_closure, int status)
+{
+ mime_image_stream_data *mid =
+ (mime_image_stream_data *) image_closure;
+
+ PR_ASSERT(mid);
+ if (!mid)
+ return;
+
+ PR_FREEIF(mid->url);
+ delete mid;
+}
+
+
+static char *
+mime_image_make_image_html(void *image_closure)
+{
+ mime_image_stream_data *mid =
+ (mime_image_stream_data *) image_closure;
+
+ const char *prefix;
+ /* Wouldn't it be nice if attributes were case-sensitive? */
+ const char *scaledPrefix = "<P><CENTER><IMG CLASS=\"moz-attached-image\" shrinktofit=\"yes\" SRC=\"";
+ const char *unscaledPrefix = "<P><CENTER><IMG CLASS=\"moz-attached-image\" SRC=\"";
+ const char *suffix = "\"></CENTER><P>";
+ const char *url;
+ char *buf;
+
+ PR_ASSERT(mid);
+ if (!mid) return 0;
+
+ /* Internal-external-reconnect only works when going to the screen. */
+ if (!mid->istream)
+ return strdup("<P><CENTER><IMG SRC=\"resource://gre-resources/loading-image.png\" ALT=\"[Image]\"></CENTER><P>");
+
+ nsCOMPtr<nsIPrefBranch> prefBranch;
+ nsCOMPtr<nsIPrefService> prefSvc(do_GetService(NS_PREFSERVICE_CONTRACTID));
+ bool resize = true;
+
+ if (prefSvc)
+ prefSvc->GetBranch("", getter_AddRefs(prefBranch));
+ if (prefBranch)
+ prefBranch->GetBoolPref("mail.enable_automatic_image_resizing", &resize); // ignore return value
+ prefix = resize ? scaledPrefix : unscaledPrefix;
+
+ if ( (!mid->url) || (!(*mid->url)) )
+ url = "";
+ else
+ url = mid->url;
+
+ uint32_t buflen = strlen(prefix) + strlen(suffix) + strlen(url) + 20;
+ buf = (char *) PR_MALLOC (buflen);
+ if (!buf)
+ return 0;
+ *buf = 0;
+
+ PL_strcatn (buf, buflen, prefix);
+ PL_strcatn (buf, buflen, url);
+ PL_strcatn (buf, buflen, suffix);
+ return buf;
+}
+
+static int
+mime_image_write_buffer(const char *buf, int32_t size, void *image_closure)
+{
+ mime_image_stream_data *mid =
+ (mime_image_stream_data *) image_closure;
+ mime_stream_data *msd = mid->msd;
+
+ if ( ( (!msd->output_emitter) ) &&
+ ( (!msd->pluginObj2) ) )
+ return -1;
+
+ return size;
+}
+
+MimeObject*
+mime_get_main_object(MimeObject* obj)
+{
+ MimeContainer *cobj;
+ if (!(mime_subclass_p(obj->clazz, (MimeObjectClass*) &mimeMessageClass)))
+ {
+ return obj;
+ }
+ cobj = (MimeContainer*) obj;
+ if (cobj->nchildren != 1) return obj;
+ obj = cobj->children[0];
+ while (obj)
+ {
+ if ( (!mime_subclass_p(obj->clazz,
+ (MimeObjectClass*) &mimeMultipartSignedClass)) &&
+ (PL_strcasecmp(obj->content_type, MULTIPART_SIGNED) != 0)
+ )
+ {
+ return obj;
+ }
+ else
+ {
+ if (mime_subclass_p(obj->clazz, (MimeObjectClass*)&mimeContainerClass))
+ {
+ // We don't care about a signed/smime object; Go inside to the
+ // thing that we signed or smime'ed
+ //
+ cobj = (MimeContainer*) obj;
+ if (cobj->nchildren > 0)
+ obj = cobj->children[0];
+ else
+ obj = nullptr;
+ }
+ else
+ {
+ // we received a message with a child object that looks like a signed
+ // object, but it is not a subclass of mimeContainer, so let's
+ // return the given child object.
+ return obj;
+ }
+ }
+ }
+ return nullptr;
+}
+
+static
+bool MimeObjectIsMessageBodyNoClimb(MimeObject *parent,
+ MimeObject *looking_for,
+ bool *stop)
+{
+ MimeContainer *container = (MimeContainer *)parent;
+ int32_t i;
+ char *disp;
+
+ NS_ASSERTION(stop, "NULL stop to MimeObjectIsMessageBodyNoClimb");
+
+ for (i = 0; i < container->nchildren; i++) {
+ MimeObject *child = container->children[i];
+ bool is_body = true;
+
+ // The body can't be something we're not displaying.
+ if (! child->output_p)
+ is_body = false;
+ else if ((disp = MimeHeaders_get (child->headers, HEADER_CONTENT_DISPOSITION,
+ true, false))) {
+ PR_Free(disp);
+ is_body = false;
+ }
+ else if (PL_strcasecmp (child->content_type, TEXT_PLAIN) &&
+ PL_strcasecmp (child->content_type, TEXT_HTML) &&
+ PL_strcasecmp (child->content_type, TEXT_MDL) &&
+ PL_strcasecmp (child->content_type, MESSAGE_NEWS) &&
+ PL_strcasecmp (child->content_type, MESSAGE_RFC822))
+ is_body = false;
+
+ if (is_body || child == looking_for) {
+ *stop = true;
+ return child == looking_for;
+ }
+
+ // The body could be down inside a multipart child, so search recursively.
+ if (mime_subclass_p(child->clazz, (MimeObjectClass*) &mimeContainerClass)) {
+ is_body = MimeObjectIsMessageBodyNoClimb(child, looking_for, stop);
+ if (is_body || *stop)
+ return is_body;
+ }
+ }
+ return false;
+}
+
+/* Should this be static in mimemult.cpp? */
+bool MimeObjectIsMessageBody(MimeObject *looking_for)
+{
+ bool stop = false;
+ MimeObject *root = looking_for;
+ while (root->parent)
+ root = root->parent;
+ return MimeObjectIsMessageBodyNoClimb(root, looking_for, &stop);
+}
+
+//
+// New Stream Converter Interface
+//
+
+// Get the connnection to prefs service manager
+nsIPrefBranch *
+GetPrefBranch(MimeDisplayOptions *opt)
+{
+ if (!opt)
+ return nullptr;
+
+ return opt->m_prefBranch;
+}
+
+// Get the text converter...
+mozITXTToHTMLConv *
+GetTextConverter(MimeDisplayOptions *opt)
+{
+ if (!opt)
+ return nullptr;
+
+ return opt->conv;
+}
+
+MimeDisplayOptions::MimeDisplayOptions()
+{
+ conv = nullptr; // For text conversion...
+ format_out = 0; // The format out type
+ url = nullptr;
+
+ memset(&headers,0, sizeof(headers));
+ fancy_headers_p = false;
+
+ output_vcard_buttons_p = false;
+
+ variable_width_plaintext_p = false;
+ wrap_long_lines_p = false;
+ rot13_p = false;
+ part_to_load = nullptr;
+
+ no_output_p = false;
+ write_html_p = false;
+
+ decrypt_p = false;
+
+ whattodo = 0 ;
+ default_charset = nullptr;
+ override_charset = false;
+ force_user_charset = false;
+ stream_closure = nullptr;
+
+ /* For setting up the display stream, so that the MIME parser can inform
+ the caller of the type of the data it will be getting. */
+ output_init_fn = nullptr;
+ output_fn = nullptr;
+
+ output_closure = nullptr;
+
+ charset_conversion_fn = nullptr;
+ rfc1522_conversion_p = false;
+
+ file_type_fn = nullptr;
+
+ passwd_prompt_fn = nullptr;
+
+ html_closure = nullptr;
+
+ generate_header_html_fn = nullptr;
+ generate_post_header_html_fn = nullptr;
+ generate_footer_html_fn = nullptr;
+ generate_reference_url_fn = nullptr;
+ generate_mailto_url_fn = nullptr;
+ generate_news_url_fn = nullptr;
+
+ image_begin = nullptr;
+ image_end = nullptr;
+ image_write_buffer = nullptr;
+ make_image_html = nullptr;
+ state = nullptr;
+
+#ifdef MIME_DRAFTS
+ decompose_file_p = false;
+ done_parsing_outer_headers = false;
+ is_multipart_msg = false;
+ decompose_init_count = 0;
+
+ signed_p = false;
+ caller_need_root_headers = false;
+ decompose_headers_info_fn = nullptr;
+ decompose_file_init_fn = nullptr;
+ decompose_file_output_fn = nullptr;
+ decompose_file_close_fn = nullptr;
+#endif /* MIME_DRAFTS */
+
+ attachment_icon_layer_id = 0;
+
+ missing_parts = false;
+ show_attachment_inline_p = false;
+ quote_attachment_inline_p = false;
+ notify_nested_bodies = false;
+ write_pure_bodies = false;
+ metadata_only = false;
+}
+
+MimeDisplayOptions::~MimeDisplayOptions()
+{
+ PR_FREEIF(part_to_load);
+ PR_FREEIF(default_charset);
+}
+////////////////////////////////////////////////////////////////
+// Bridge routines for new stream converter XP-COM interface
+////////////////////////////////////////////////////////////////
+extern "C" void *
+mime_bridge_create_display_stream(
+ nsIMimeEmitter *newEmitter,
+ nsStreamConverter *newPluginObj2,
+ nsIURI *uri,
+ nsMimeOutputType format_out,
+ uint32_t whattodo,
+ nsIChannel *aChannel)
+{
+ int status = 0;
+ MimeObject *obj;
+ mime_stream_data *msd;
+ nsMIMESession *stream = 0;
+
+ if (!uri)
+ return nullptr;
+
+ msd = new mime_stream_data;
+ if (!msd)
+ return NULL;
+
+ // Assign the new mime emitter - will handle output operations
+ msd->output_emitter = newEmitter;
+ msd->firstCheck = true;
+
+ // Store the URL string for this decode operation
+ nsAutoCString urlString;
+ nsresult rv;
+
+ // Keep a hold of the channel...
+ msd->channel = aChannel;
+ rv = uri->GetSpec(urlString);
+ if (NS_SUCCEEDED(rv))
+ {
+ if (!urlString.IsEmpty())
+ {
+ msd->url_name = ToNewCString(urlString);
+ if (!(msd->url_name))
+ {
+ delete msd;
+ return NULL;
+ }
+ nsCOMPtr<nsIMsgMessageUrl> msgUrl = do_QueryInterface(uri);
+ if (msgUrl)
+ msgUrl->GetOriginalSpec(&msd->orig_url_name);
+ }
+ }
+
+ msd->format_out = format_out; // output format
+ msd->pluginObj2 = newPluginObj2; // the plugin object pointer
+
+ msd->options = new MimeDisplayOptions;
+ if (!msd->options)
+ {
+ delete msd;
+ return 0;
+ }
+// memset(msd->options, 0, sizeof(*msd->options));
+ msd->options->format_out = format_out; // output format
+
+ msd->options->m_prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv))
+ {
+ delete msd;
+ return nullptr;
+ }
+
+ // Need the text converter...
+ rv = CallCreateInstance(MOZ_TXTTOHTMLCONV_CONTRACTID, &(msd->options->conv));
+ if (NS_FAILED(rv))
+ {
+ msd->options->m_prefBranch = nullptr;
+ delete msd;
+ return nullptr;
+ }
+
+ //
+ // Set the defaults, based on the context, and the output-type.
+ //
+ MIME_HeaderType = MimeHeadersAll;
+ msd->options->write_html_p = true;
+ switch (format_out)
+ {
+ case nsMimeOutput::nsMimeMessageSplitDisplay: // the wrapper HTML output to produce the split header/body display
+ case nsMimeOutput::nsMimeMessageHeaderDisplay: // the split header/body display
+ case nsMimeOutput::nsMimeMessageBodyDisplay: // the split header/body display
+ msd->options->fancy_headers_p = true;
+ msd->options->output_vcard_buttons_p = true;
+ break;
+
+ case nsMimeOutput::nsMimeMessageSaveAs: // Save As operations
+ case nsMimeOutput::nsMimeMessageQuoting: // all HTML quoted/printed output
+ case nsMimeOutput::nsMimeMessagePrintOutput:
+ msd->options->fancy_headers_p = true;
+ break;
+
+ case nsMimeOutput::nsMimeMessageBodyQuoting: // only HTML body quoted output
+ MIME_HeaderType = MimeHeadersNone;
+ break;
+
+ case nsMimeOutput::nsMimeMessageAttach: // handling attachment storage
+ msd->options->write_html_p = false;
+ break;
+ case nsMimeOutput::nsMimeMessageRaw: // the raw RFC822 data (view source) and attachments
+ case nsMimeOutput::nsMimeMessageDraftOrTemplate: // Loading drafts & templates
+ case nsMimeOutput::nsMimeMessageEditorTemplate: // Loading templates into editor
+ case nsMimeOutput::nsMimeMessageFilterSniffer: // generating an output that can be scan by a message filter
+ break;
+
+ case nsMimeOutput::nsMimeMessageDecrypt:
+ msd->options->decrypt_p = true;
+ msd->options->write_html_p = false;
+ break;
+ }
+
+ ////////////////////////////////////////////////////////////
+ // Now, get the libmime prefs...
+ ////////////////////////////////////////////////////////////
+
+ MIME_WrapLongLines = true;
+ MIME_VariableWidthPlaintext = true;
+ msd->options->force_user_charset = false;
+
+ if (msd->options->m_prefBranch)
+ {
+ msd->options->m_prefBranch->GetBoolPref("mail.wrap_long_lines", &MIME_WrapLongLines);
+ msd->options->m_prefBranch->GetBoolPref("mail.fixed_width_messages", &MIME_VariableWidthPlaintext);
+ //
+ // Charset overrides takes place here
+ //
+ // We have a bool pref (mail.force_user_charset) to deal with attachments.
+ // 1) If true - libmime does NO conversion and just passes it through to raptor
+ // 2) If false, then we try to use the charset of the part and if not available,
+ // the charset of the root message
+ //
+ msd->options->m_prefBranch->GetBoolPref("mail.force_user_charset", &(msd->options->force_user_charset));
+ msd->options->m_prefBranch->GetBoolPref("mail.inline_attachments", &(msd->options->show_attachment_inline_p));
+ msd->options->m_prefBranch->GetBoolPref("mail.reply_quote_inline", &(msd->options->quote_attachment_inline_p));
+ msd->options->m_prefBranch->GetIntPref("mailnews.display.html_as", &(msd->options->html_as_p));
+ }
+ /* This pref is written down in with the
+ opposite sense of what we like to use... */
+ MIME_VariableWidthPlaintext = !MIME_VariableWidthPlaintext;
+
+ msd->options->wrap_long_lines_p = MIME_WrapLongLines;
+ msd->options->headers = MIME_HeaderType;
+
+ // We need to have the URL to be able to support the various
+ // arguments
+ status = mime_parse_url_options(msd->url_name, msd->options);
+ if (status < 0)
+ {
+ PR_FREEIF(msd->options->part_to_load);
+ PR_Free(msd->options);
+ delete msd;
+ return 0;
+ }
+
+ if (msd->options->headers == MimeHeadersMicro &&
+ (msd->url_name == NULL || (strncmp(msd->url_name, "news:", 5) != 0 &&
+ strncmp(msd->url_name, "snews:", 6) != 0)) )
+ msd->options->headers = MimeHeadersMicroPlus;
+
+ msd->options->url = msd->url_name;
+ msd->options->output_init_fn = mime_output_init_fn;
+
+ msd->options->output_fn = mime_output_fn;
+
+ msd->options->whattodo = whattodo;
+ msd->options->charset_conversion_fn = mime_convert_charset;
+ msd->options->rfc1522_conversion_p = true;
+ msd->options->file_type_fn = mime_file_type;
+ msd->options->stream_closure = msd;
+ msd->options->passwd_prompt_fn = 0;
+
+ msd->options->image_begin = mime_image_begin;
+ msd->options->image_end = mime_image_end;
+ msd->options->make_image_html = mime_image_make_image_html;
+ msd->options->image_write_buffer = mime_image_write_buffer;
+
+ msd->options->variable_width_plaintext_p = MIME_VariableWidthPlaintext;
+
+ // If this is a part, then we should emit the HTML to render the data
+ // (i.e. embedded images)
+ if (msd->options->part_to_load && msd->options->format_out != nsMimeOutput::nsMimeMessageBodyDisplay)
+ msd->options->write_html_p = false;
+
+ obj = mime_new ((MimeObjectClass *)&mimeMessageClass, (MimeHeaders *) NULL, MESSAGE_RFC822);
+ if (!obj)
+ {
+ delete msd->options;
+ delete msd;
+ return 0;
+ }
+
+ obj->options = msd->options;
+ msd->obj = obj;
+
+ /* Both of these better not be true at the same time. */
+ PR_ASSERT(! (obj->options->decrypt_p && obj->options->write_html_p));
+
+ stream = PR_NEW(nsMIMESession);
+ if (!stream)
+ {
+ delete msd->options;
+ delete msd;
+ PR_Free(obj);
+ return 0;
+ }
+
+ ResetMsgHeaderSinkProps(uri);
+
+ memset (stream, 0, sizeof (*stream));
+ stream->name = "MIME Conversion Stream";
+ stream->complete = mime_display_stream_complete;
+ stream->abort = mime_display_stream_abort;
+ stream->put_block = mime_display_stream_write;
+ stream->data_object = msd;
+
+ status = obj->clazz->initialize(obj);
+ if (status >= 0)
+ status = obj->clazz->parse_begin(obj);
+ if (status < 0)
+ {
+ PR_Free(stream);
+ delete msd->options;
+ delete msd;
+ PR_Free(obj);
+ return 0;
+ }
+
+ return stream;
+}
+
+//
+// Emitter Wrapper Routines!
+//
+nsIMimeEmitter *
+GetMimeEmitter(MimeDisplayOptions *opt)
+{
+ mime_stream_data *msd = (mime_stream_data *)opt->stream_closure;
+ if (!msd)
+ return NULL;
+
+ nsIMimeEmitter *ptr = (nsIMimeEmitter *)(msd->output_emitter);
+ return ptr;
+}
+
+mime_stream_data *
+GetMSD(MimeDisplayOptions *opt)
+{
+ if (!opt)
+ return nullptr;
+ mime_stream_data *msd = (mime_stream_data *)opt->stream_closure;
+ return msd;
+}
+
+bool
+NoEmitterProcessing(nsMimeOutputType format_out)
+{
+ if ( (format_out == nsMimeOutput::nsMimeMessageDraftOrTemplate) ||
+ (format_out == nsMimeOutput::nsMimeMessageEditorTemplate))
+ return true;
+ else
+ return false;
+}
+
+extern "C" nsresult
+mimeEmitterAddAttachmentField(MimeDisplayOptions *opt, const char *field, const char *value)
+{
+ // Check for draft processing...
+ if (NoEmitterProcessing(opt->format_out))
+ return NS_OK;
+
+ mime_stream_data *msd = GetMSD(opt);
+ if (!msd)
+ return NS_ERROR_FAILURE;
+
+ if (msd->output_emitter)
+ {
+ nsIMimeEmitter *emitter = (nsIMimeEmitter *)msd->output_emitter;
+ return emitter->AddAttachmentField(field, value);
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+extern "C" nsresult
+mimeEmitterAddHeaderField(MimeDisplayOptions *opt, const char *field, const char *value)
+{
+ // Check for draft processing...
+ if (NoEmitterProcessing(opt->format_out))
+ return NS_OK;
+
+ mime_stream_data *msd = GetMSD(opt);
+ if (!msd)
+ return NS_ERROR_FAILURE;
+
+ if (msd->output_emitter)
+ {
+ nsIMimeEmitter *emitter = (nsIMimeEmitter *)msd->output_emitter;
+ return emitter->AddHeaderField(field, value);
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+extern "C" nsresult
+mimeEmitterAddAllHeaders(MimeDisplayOptions *opt, const char *allheaders, const int32_t allheadersize)
+{
+ // Check for draft processing...
+ if (NoEmitterProcessing(opt->format_out))
+ return NS_OK;
+
+ mime_stream_data *msd = GetMSD(opt);
+ if (!msd)
+ return NS_ERROR_FAILURE;
+
+ if (msd->output_emitter)
+ {
+ nsIMimeEmitter *emitter = (nsIMimeEmitter *)msd->output_emitter;
+ return emitter->AddAllHeaders(Substring(allheaders,
+ allheaders + allheadersize));
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+extern "C" nsresult
+mimeEmitterStartAttachment(MimeDisplayOptions *opt, const char *name, const char *contentType, const char *url,
+ bool aIsExternalAttachment)
+{
+ // Check for draft processing...
+ if (NoEmitterProcessing(opt->format_out))
+ return NS_OK;
+
+ mime_stream_data *msd = GetMSD(opt);
+ if (!msd)
+ return NS_ERROR_FAILURE;
+
+ if (msd->output_emitter)
+ {
+ nsIMimeEmitter *emitter = (nsIMimeEmitter *)msd->output_emitter;
+ return emitter->StartAttachment(nsDependentCString(name), contentType, url,
+ aIsExternalAttachment);
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+extern "C" nsresult
+mimeEmitterEndAttachment(MimeDisplayOptions *opt)
+{
+ // Check for draft processing...
+ if (NoEmitterProcessing(opt->format_out))
+ return NS_OK;
+
+ mime_stream_data *msd = GetMSD(opt);
+ if (!msd)
+ return NS_ERROR_FAILURE;
+
+ if (msd->output_emitter)
+ {
+ nsIMimeEmitter *emitter = (nsIMimeEmitter *)msd->output_emitter;
+ if (emitter)
+ return emitter->EndAttachment();
+ else
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+extern "C" nsresult
+mimeEmitterEndAllAttachments(MimeDisplayOptions *opt)
+{
+ // Check for draft processing...
+ if (NoEmitterProcessing(opt->format_out))
+ return NS_OK;
+
+ mime_stream_data *msd = GetMSD(opt);
+ if (!msd)
+ return NS_ERROR_FAILURE;
+
+ if (msd->output_emitter)
+ {
+ nsIMimeEmitter *emitter = (nsIMimeEmitter *)msd->output_emitter;
+ if (emitter)
+ return emitter->EndAllAttachments();
+ else
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+extern "C" nsresult
+mimeEmitterStartBody(MimeDisplayOptions *opt, bool bodyOnly, const char *msgID, const char *outCharset)
+{
+ // Check for draft processing...
+ if (NoEmitterProcessing(opt->format_out))
+ return NS_OK;
+
+ mime_stream_data *msd = GetMSD(opt);
+ if (!msd)
+ return NS_ERROR_FAILURE;
+
+ if (msd->output_emitter)
+ {
+ nsIMimeEmitter *emitter = (nsIMimeEmitter *)msd->output_emitter;
+ return emitter->StartBody(bodyOnly, msgID, outCharset);
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+extern "C" nsresult
+mimeEmitterEndBody(MimeDisplayOptions *opt)
+{
+ // Check for draft processing...
+ if (NoEmitterProcessing(opt->format_out))
+ return NS_OK;
+
+ mime_stream_data *msd = GetMSD(opt);
+ if (!msd)
+ return NS_ERROR_FAILURE;
+
+ if (msd->output_emitter)
+ {
+ nsIMimeEmitter *emitter = (nsIMimeEmitter *)msd->output_emitter;
+ return emitter->EndBody();
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+extern "C" nsresult
+mimeEmitterEndHeader(MimeDisplayOptions *opt, MimeObject *obj)
+{
+ // Check for draft processing...
+ if (NoEmitterProcessing(opt->format_out))
+ return NS_OK;
+
+ mime_stream_data *msd = GetMSD(opt);
+ if (!msd)
+ return NS_ERROR_FAILURE;
+
+ if (msd->output_emitter)
+ {
+ nsIMimeEmitter *emitter = (nsIMimeEmitter *)msd->output_emitter;
+
+ nsCString name;
+ if (msd->format_out == nsMimeOutput::nsMimeMessageSplitDisplay ||
+ msd->format_out == nsMimeOutput::nsMimeMessageHeaderDisplay ||
+ msd->format_out == nsMimeOutput::nsMimeMessageBodyDisplay ||
+ msd->format_out == nsMimeOutput::nsMimeMessageSaveAs ||
+ msd->format_out == nsMimeOutput::nsMimeMessagePrintOutput) {
+ if (obj->headers) {
+ nsMsgAttachmentData attachment;
+ attIndex = 0;
+ nsresult rv = GenerateAttachmentData(obj, msd->url_name, opt, false,
+ 0, &attachment);
+
+ if (NS_SUCCEEDED(rv))
+ name.Assign(attachment.m_realName);
+ }
+ }
+
+ MimeHeaders_convert_header_value(opt, name, false);
+ return emitter->EndHeader(name);
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+extern "C" nsresult
+mimeEmitterUpdateCharacterSet(MimeDisplayOptions *opt, const char *aCharset)
+{
+ // Check for draft processing...
+ if (NoEmitterProcessing(opt->format_out))
+ return NS_OK;
+
+ mime_stream_data *msd = GetMSD(opt);
+ if (!msd)
+ return NS_ERROR_FAILURE;
+
+ if (msd->output_emitter)
+ {
+ nsIMimeEmitter *emitter = (nsIMimeEmitter *)msd->output_emitter;
+ return emitter->UpdateCharacterSet(aCharset);
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+extern "C" nsresult
+mimeEmitterStartHeader(MimeDisplayOptions *opt, bool rootMailHeader, bool headerOnly, const char *msgID,
+ const char *outCharset)
+{
+ // Check for draft processing...
+ if (NoEmitterProcessing(opt->format_out))
+ return NS_OK;
+
+ mime_stream_data *msd = GetMSD(opt);
+ if (!msd)
+ return NS_ERROR_FAILURE;
+
+ if (msd->output_emitter)
+ {
+ nsIMimeEmitter *emitter = (nsIMimeEmitter *)msd->output_emitter;
+ return emitter->StartHeader(rootMailHeader, headerOnly, msgID, outCharset);
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+
+extern "C" nsresult
+mimeSetNewURL(nsMIMESession *stream, char *url)
+{
+ if ( (!stream) || (!url) || (!*url) )
+ return NS_ERROR_FAILURE;
+
+ mime_stream_data *msd = (mime_stream_data *)stream->data_object;
+ if (!msd)
+ return NS_ERROR_FAILURE;
+
+ char *tmpPtr = strdup(url);
+ if (!tmpPtr)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ PR_FREEIF(msd->url_name);
+ msd->url_name = tmpPtr;
+ return NS_OK;
+}
+
+#define MIME_URL "chrome://messenger/locale/mime.properties"
+
+extern "C"
+char *
+MimeGetStringByID(int32_t stringID)
+{
+ nsCOMPtr<nsIStringBundleService> stringBundleService =
+ mozilla::services::GetStringBundleService();
+
+ nsCOMPtr<nsIStringBundle> stringBundle;
+ stringBundleService->CreateBundle(MIME_URL, getter_AddRefs(stringBundle));
+ if (stringBundle)
+ {
+ nsString v;
+ if (NS_SUCCEEDED(stringBundle->GetStringFromID(stringID, getter_Copies(v))))
+ return ToNewUTF8String(v);
+ }
+
+ return strdup("???");
+}
+
+extern "C"
+char *
+MimeGetStringByName(const char16_t *stringName)
+{
+ nsCOMPtr<nsIStringBundleService> stringBundleService =
+ do_GetService(NS_STRINGBUNDLE_CONTRACTID);
+
+ nsCOMPtr<nsIStringBundle> stringBundle;
+ stringBundleService->CreateBundle(MIME_URL, getter_AddRefs(stringBundle));
+ if (stringBundle)
+ {
+ nsString v;
+ if (NS_SUCCEEDED(stringBundle->GetStringFromName(stringName, getter_Copies(v))))
+ return ToNewUTF8String(v);
+ }
+
+ return strdup("???");
+}
+
+void
+ResetChannelCharset(MimeObject *obj)
+{
+ if (obj->options && obj->options->stream_closure &&
+ obj->options->default_charset && obj->headers )
+ {
+ mime_stream_data *msd = (mime_stream_data *) (obj->options->stream_closure);
+ char *ct = MimeHeaders_get (obj->headers, HEADER_CONTENT_TYPE, false, false);
+ if ( (ct) && (msd) && (msd->channel) )
+ {
+ char *ptr = strstr(ct, "charset=");
+ if (ptr)
+ {
+ // First, setup the channel!
+ msd->channel->SetContentType(nsDependentCString(ct));
+
+ // Second, if this is a Save As operation, then we need to convert
+ // to override the output charset!
+ mime_stream_data *msd = GetMSD(obj->options);
+ if ( (msd) && (msd->format_out == nsMimeOutput::nsMimeMessageSaveAs) )
+ {
+ // Extract the charset alone
+ char *cSet = nullptr;
+ if (*(ptr+8) == '"')
+ cSet = strdup(ptr+9);
+ else
+ cSet = strdup(ptr+8);
+ if (cSet)
+ {
+ char *ptr2 = cSet;
+ while ( (*cSet) && (*cSet != ' ') && (*cSet != ';') &&
+ (*cSet != '\r') && (*cSet != '\n') && (*cSet != '"') )
+ ptr2++;
+
+ if (*cSet) {
+ PR_FREEIF(obj->options->default_charset);
+ obj->options->default_charset = strdup(cSet);
+ obj->options->override_charset = true;
+ }
+
+ PR_FREEIF(cSet);
+ }
+ }
+ }
+ PR_FREEIF(ct);
+ }
+ }
+}
+
+ ////////////////////////////////////////////////////////////
+ // Function to get up mail/news fontlang
+ ////////////////////////////////////////////////////////////
+
+
+nsresult GetMailNewsFont(MimeObject *obj, bool styleFixed, int32_t *fontPixelSize,
+ int32_t *fontSizePercentage, nsCString& fontLang)
+{
+ nsresult rv = NS_OK;
+
+ nsIPrefBranch *prefBranch = GetPrefBranch(obj->options);
+ if (prefBranch) {
+ MimeInlineText *text = (MimeInlineText *) obj;
+ nsAutoCString charset;
+
+ // get a charset
+ if (!text->initializeCharset)
+ ((MimeInlineTextClass*)&mimeInlineTextClass)->initialize_charset(obj);
+
+ if (!text->charset || !(*text->charset))
+ charset.Assign("us-ascii");
+ else
+ charset.Assign(text->charset);
+
+ nsCOMPtr<nsICharsetConverterManager> charSetConverterManager2;
+ nsCOMPtr<nsIAtom> langGroupAtom;
+ nsAutoCString prefStr;
+
+ ToLowerCase(charset);
+
+ charSetConverterManager2 = do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
+ if ( NS_FAILED(rv))
+ return rv;
+
+ // get a language, e.g. x-western, ja
+ rv = charSetConverterManager2->GetCharsetLangGroup(charset.get(), getter_AddRefs(langGroupAtom));
+ if (NS_FAILED(rv))
+ return rv;
+ rv = langGroupAtom->ToUTF8String(fontLang);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // get a font size from pref
+ prefStr.Assign(!styleFixed ? "font.size.variable." : "font.size.fixed.");
+ prefStr.Append(fontLang);
+ rv = prefBranch->GetIntPref(prefStr.get(), fontPixelSize);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsIPrefBranch> prefDefBranch;
+ nsCOMPtr<nsIPrefService> prefSvc(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ if(prefSvc)
+ rv = prefSvc->GetDefaultBranch("", getter_AddRefs(prefDefBranch));
+
+ if(!prefDefBranch)
+ return rv;
+
+ // get original font size
+ int32_t originalSize;
+ rv = prefDefBranch->GetIntPref(prefStr.get(), &originalSize);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // calculate percentage
+ *fontSizePercentage = originalSize ?
+ (int32_t)((float)*fontPixelSize / (float)originalSize * 100) : 0;
+
+ }
+
+ return NS_OK;
+}
+
+
+
+/**
+ * This function synchronously converts an HTML document (as string)
+ * to plaintext (as string) using the Gecko converter.
+ *
+ * @param flags see nsIDocumentEncoder.h
+ */
+nsresult
+HTML2Plaintext(const nsString& inString, nsString& outString,
+ uint32_t flags, uint32_t wrapCol)
+{
+ nsCOMPtr<nsIParserUtils> utils =
+ do_GetService(NS_PARSERUTILS_CONTRACTID);
+ return utils->ConvertToPlainText(inString, flags, wrapCol, outString);
+}
+
+
+/**
+ * This function synchronously sanitizes an HTML document (string->string)
+ * using the Gecko nsTreeSanitizer.
+ */
+nsresult
+HTMLSanitize(const nsString& inString, nsString& outString)
+{
+ // If you want to add alternative sanitization, you can insert a conditional
+ // call to another sanitizer and an early return here.
+
+ uint32_t flags = nsIParserUtils::SanitizerCidEmbedsOnly |
+ nsIParserUtils::SanitizerDropForms;
+
+ nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
+
+ bool dropPresentational = true;
+ bool dropMedia = false;
+ prefs->GetBoolPref(
+ "mailnews.display.html_sanitizer.drop_non_css_presentation",
+ &dropPresentational);
+ prefs->GetBoolPref(
+ "mailnews.display.html_sanitizer.drop_media",
+ &dropMedia);
+ if (dropPresentational)
+ flags |= nsIParserUtils::SanitizerDropNonCSSPresentation;
+ if (dropMedia)
+ flags |= nsIParserUtils::SanitizerDropMedia;
+
+ nsCOMPtr<nsIParserUtils> utils = do_GetService(NS_PARSERUTILS_CONTRACTID);
+ return utils->Sanitize(inString, flags, outString);
+}