/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "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 "nsIStringBundle.h" #include "nsString.h" #include "nsMimeStringResources.h" #include "nsStreamConverter.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 "nsMsgUtils.h" #include "nsIChannel.h" #include "nsIMailChannel.h" #include "mimeebod.h" #include "mimeeobj.h" // #include "nsXPCOM.h" #include "nsLayoutCID.h" #include "nsIParserUtils.h" // #include "mozilla/Components.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) {} //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Attachment handling routines //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // MimeObject* mime_get_main_object(MimeObject* obj); // Appends a "filename" parameter with the attachment name to the object url. void AppendFilenameParameterToAttachmentDataUrl( const nsMsgAttachmentData* attachmentData, nsCString& url) { url.AppendLiteral("&filename="); nsAutoCString aResult; if (NS_SUCCEEDED(MsgEscapeString(attachmentData->m_realName, nsINetUtil::ESCAPE_XALPHAS, aResult))) { url.Append(aResult); } else { url.Append(attachmentData->m_realName); } if (attachmentData->m_realType.EqualsLiteral("message/rfc822") && !StringEndsWith(url, ".eml"_ns, nsCaseInsensitiveCStringComparator)) { url.AppendLiteral(".eml"); } } 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 believe 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, "text"_ns, 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); nsCString urlString(tmpURL); if (!tmp->m_realName.IsEmpty()) { AppendFilenameParameterToAttachmentDataUrl(tmp, urlString); } rv = nsMimeNewURI(getter_AddRefs(tmp->m_url), urlString.get(), 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, "multipart"_ns, 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 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, "http"_ns, 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) { AppendFilenameParameterToAttachmentDataUrl(tmp, urlString); } else if (tmp->m_isExternalAttachment) { // Allows the JS mime emitter to figure out the part information. urlString.AppendLiteral("?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.AppendLiteral(".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(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. *data = nullptr; return rv; } } rv = BuildAttachmentList((MimeObject*)cobj, *data, aMessageURL); if (NS_FAILED(rv)) { delete[] * data; // release data in case of error return. *data = nullptr; } return rv; } extern "C" void NotifyEmittersOfAttachmentList(MimeDisplayOptions* opt, nsMsgAttachmentData* data) { 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.EqualsLiteral("attachment") && tmp->m_displayableInline && (tmp->m_realName.IsEmpty() || (!tmp->m_hasFilename && (opt->html_as_p != 4 || opt->metadata_only)))) { ++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); ++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 pService = mozilla::components::IO::Service(); 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) { nsCOMPtr mailChannel = do_QueryInterface(msd->channel); if (mailChannel) { if (!PL_strcasecmp(aCharacterSet, "us-ascii")) { mailChannel->SetMailCharacterSet("ISO-8859-1"_ns); } else { mailChannel->SetMailCharacterSet(nsCString(aCharacterSet)); } } } } return rv; } 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 mimeFinder( do_GetService(NS_MIMESERVICE_CONTRACTID, &rv)); if (mimeFinder) { nsAutoCString type; mimeFinder->GetTypeFromExtension(nsDependentCString(ext), type); retType = ToNewCString(type); } } return retType; } int ConvertToUTF8(const char* stringToUse, int32_t inLength, const char* input_charset, nsACString& outString) { nsresult rv = NS_OK; // Look up Thunderbird's special aliases from charsetalias.properties. nsCOMPtr ccm = do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, -1); nsCString newCharset; rv = ccm->GetCharsetAlias(input_charset, newCharset); NS_ENSURE_SUCCESS(rv, -1); if (newCharset.Equals("UTF-7", nsCaseInsensitiveCStringComparator)) { nsAutoString utf16; rv = CopyUTF7toUTF16(nsDependentCSubstring(stringToUse, inLength), utf16); if (NS_FAILED(rv)) return -1; CopyUTF16toUTF8(utf16, outString); return 0; } auto encoding = mozilla::Encoding::ForLabel(newCharset); NS_ENSURE_TRUE(encoding, -1); // Impossible since GetCharsetAlias() already checked. rv = encoding->DecodeWithoutBOMHandling( nsDependentCSubstring(stringToUse, inLength), outString); return NS_SUCCEEDED(rv) ? 0 : -1; } static int mime_convert_charset(const char* input_line, int32_t input_length, const char* input_charset, nsACString& convertedString, void* stream_closure) { return ConvertToUTF8(input_line, input_length, input_charset, convertedString); } 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; 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); } delete[] 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) free(msd->url_name); if (msd->orig_url_name) 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) free(msd->url_name); if (msd->orig_url_name) 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; PR_ASSERT(mid); if (!mid) return 0; /* Internal-external-reconnect only works when going to the screen. */ if (!mid->istream) return strdup( "
"); const char* prefix; const char* url; char* buf; /* Wouldn't it be nice if attributes were case-sensitive? */ const char* scaledPrefix = "
"; // Thunderbird doesn't have this pref. #ifdef MOZ_SUITE const char* unscaledPrefix = "
prefBranch; nsCOMPtr 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; #else prefix = scaledPrefix; #endif 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 connection 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; show_attachment_inline_text = 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; // 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 msgUrl = do_QueryInterface(uri); if (msgUrl) { nsAutoCString orgSpec; msgUrl->GetOriginalSpec(orgSpec); msd->orig_url_name = ToNewCString(orgSpec); } } } 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.inline_attachments.text", &(msd->options->show_attachment_inline_text)); 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; } 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 || format_out == nsMimeOutput::nsMimeMessageQuoting || format_out == nsMimeOutput::nsMimeMessageBodyQuoting) 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 stringBundleService = mozilla::components::StringBundle::Service(); nsCOMPtr stringBundle; stringBundleService->CreateBundle(MIME_URL, getter_AddRefs(stringBundle)); if (stringBundle) { nsString v; if (NS_SUCCEEDED(stringBundle->GetStringFromID(stringID, v))) return ToNewUTF8String(v); } return strdup("???"); } extern "C" char* MimeGetStringByName(const char16_t* stringName) { nsCOMPtr stringBundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID); nsCOMPtr stringBundle; stringBundleService->CreateBundle(MIME_URL, getter_AddRefs(stringBundle)); if (stringBundle) { nsString v; if (NS_SUCCEEDED(stringBundle->GetStringFromName( NS_ConvertUTF16toUTF8(stringName).get(), 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* cSet = MimeHeaders_get_parameter(ct, "charset", nullptr, nullptr); if (cSet) { // The content-type does specify a charset. 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. if (msd->format_out == nsMimeOutput::nsMimeMessageSaveAs) { // The previous version of this code would have entered an infinite // loop. But it never showed up, so it's not clear that we ever get // here... See bug #1597891. PR_FREEIF(obj->options->default_charset); obj->options->default_charset = cSet; cSet = nullptr; // Ownership was transferred. obj->options->override_charset = true; MOZ_DIAGNOSTIC_ASSERT( false, "Ahh. So this code _is_ run after all! (see bug 1597891)"); } 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.AssignLiteral("us-ascii"); else charset.Assign(text->charset); nsCOMPtr charSetConverterManager2; 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(), fontLang); if (NS_FAILED(rv)) return rv; // get a font size from pref prefStr.Assign(!styleFixed ? "font.size.variable." : "font.size.monospace."); prefStr.Append(fontLang); rv = prefBranch->GetIntPref(prefStr.get(), fontPixelSize); if (NS_FAILED(rv)) return rv; nsCOMPtr prefDefBranch; nsCOMPtr 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 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 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 utils = do_GetService(NS_PARSERUTILS_CONTRACTID); return utils->Sanitize(inString, flags, outString); }