/* -*- 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" // #include "nsXPCOM.h" #include "nsLayoutCID.h" #include "nsIComponentManager.h" #include "nsIParserUtils.h" // #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; inchildren; 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 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) { char * disp = MimeHeaders_get (child->headers, HEADER_CONTENT_DISPOSITION, true, false); if (MimeHeaders_get_name(child->headers, nullptr) && (!disp || PL_strcasecmp(disp, "attachment"))) // it has a filename and isn't being displayed inline skip = false; } 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. 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 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 uri; channel->GetURI(getter_AddRefs(uri)); if (uri) { nsCOMPtr msgurl (do_QueryInterface(uri)); if (msgurl) { nsCOMPtr msgWindow; msgurl->GetMsgWindow(getter_AddRefs(msgWindow)); if (msgWindow) rv = msgWindow->SetMailCharacterSet(!PL_strcasecmp(aCharacterSet, "us-ascii") ? static_cast(NS_LITERAL_CSTRING("ISO-8859-1")) : static_cast(nsDependentCString(aCharacterSet))); } } } } } return rv; } static void ResetMsgHeaderSinkProps(nsIURI *uri) { nsCOMPtr msgurl(do_QueryInterface(uri)); if (!msgurl) return; nsCOMPtr msgWindow; msgurl->GetMsgWindow(getter_AddRefs(msgWindow)); if (!msgWindow) return; nsCOMPtr 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 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 aUri; if (NS_SUCCEEDED(msd->channel->GetURI(getter_AddRefs(aUri)))) { nsCOMPtr 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 = "

"; 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("

\"[Image]\"

"); nsCOMPtr 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; 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 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 stringBundleService = mozilla::services::GetStringBundleService(); nsCOMPtr 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 stringBundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID); nsCOMPtr 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 charSetConverterManager2; nsCOMPtr 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 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); }