Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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"
// <for functions="HTML2Plaintext,HTMLSantinize">
#include "nsXPCOM.h"
#include "nsIParserUtils.h"
// </for>
#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<nsIMIMEService> mimeFinder(
do_GetService(NS_MIMESERVICE_CONTRACTID, &rv));
if (NS_SUCCEEDED(rv)) {
nsAutoCString fileExtension;
rv = mimeFinder->GetPrimaryExtension(contentType, EmptyCString(),
fileExtension);
if (NS_SUCCEEDED(rv) && !fileExtension.IsEmpty()) {
aAttach->m_realName.Append('.');
aAttach->m_realName.Append(fileExtension);
}
}
}
}
static int32_t attIndex = 0;
nsresult GenerateAttachmentData(MimeObject* object, const char* aMessageURL,
MimeDisplayOptions* options,
bool isAnAppleDoublePart, int32_t attSize,
nsMsgAttachmentData* aAttachData) {
nsCString imappart;
nsCString part;
bool isExternalAttachment = false;
/* be sure the object has not be marked as Not to be an attachment */
if (object->dontShowAsAttachment) return NS_OK;
part.Adopt(mime_part_address(object));
if (part.IsEmpty()) return NS_ERROR_OUT_OF_MEMORY;
if (options->missing_parts) imappart.Adopt(mime_imap_part_address(object));
char* urlSpec = nullptr;
if (!imappart.IsEmpty()) {
urlSpec = mime_set_url_imap_part(aMessageURL, imappart.get(), part.get());
} else {
char* no_part_url = nullptr;
if (options->part_to_load &&
options->format_out == nsMimeOutput::nsMimeMessageBodyDisplay)
no_part_url = mime_get_base_url(aMessageURL);
if (no_part_url) {
urlSpec = mime_set_url_part(no_part_url, part.get(), true);
PR_Free(no_part_url);
} else {
// if the mime object contains an external attachment URL, then use it,
// otherwise fall back to creating an attachment url based on the message
// URI and the part number.
urlSpec = mime_external_attachment_url(object);
isExternalAttachment = urlSpec ? true : false;
if (!urlSpec) urlSpec = mime_set_url_part(aMessageURL, part.get(), true);
}
}
if (!urlSpec) return NS_ERROR_OUT_OF_MEMORY;
if ((options->format_out == nsMimeOutput::nsMimeMessageBodyDisplay) &&
(PL_strncasecmp(aMessageURL, urlSpec, strlen(urlSpec)) == 0))
return NS_OK;
nsCString urlString(urlSpec);
nsMsgAttachmentData* tmp = &(aAttachData[attIndex++]);
tmp->m_realType = object->content_type;
tmp->m_realEncoding = object->encoding;
tmp->m_isExternalAttachment = isExternalAttachment;
tmp->m_isExternalLinkAttachment =
(isExternalAttachment &&
StringBeginsWith(urlString, "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<nsresult>(n);
// in case of an inline message (as body), we need an extra slot for the
// message itself that we will fill later...
if (isAnInlineMessage) n++;
*data = new nsMsgAttachmentData[n + 1];
if (!*data) return NS_ERROR_OUT_OF_MEMORY;
attIndex = 0;
// Now, build the list!
nsresult rv;
if (isAnInlineMessage) {
int32_t size = 0;
MimeGetSize(obj, &size);
rv = GenerateAttachmentData(obj, aMessageURL, obj->options, false, size,
*data);
if (NS_FAILED(rv)) {
delete[] *data; // release data in case of error return.
*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<nsIIOService> 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<nsIMailChannel> mailChannel = do_QueryInterface(msd->channel);
if (mailChannel) {
if (!PL_strcasecmp(aCharacterSet, "us-ascii")) {
mailChannel->SetMailCharacterSet("ISO-8859-1"_ns);
} else {
mailChannel->SetMailCharacterSet(nsDependentCString(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<nsIMIMEService> 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<nsICharsetConverterManager> 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(
"<DIV CLASS=\"moz-attached-image-container\"><IMG "
"ALT=\"[Image]\"></DIV>");
const char* prefix;
const char* url;
char* buf;
/* Wouldn't it be nice if attributes were case-sensitive? */
const char* scaledPrefix =
"<DIV CLASS=\"moz-attached-image-container\"><IMG "
"CLASS=\"moz-attached-image\" shrinktofit=\"yes\" SRC=\"";
const char* suffix = "\"></DIV>";
// Thunderbird doesn't have this pref.
#ifdef MOZ_SUITE
const char* unscaledPrefix =
"<DIV CLASS=\"moz-attached-image-container\"><IMG "
"CLASS=\"moz-attached-image\" SRC=\"";
nsCOMPtr<nsIPrefBranch> prefBranch;
nsCOMPtr<nsIPrefService> prefSvc(do_GetService(NS_PREFSERVICE_CONTRACTID));
bool resize = true;
if (prefSvc) prefSvc->GetBranch("", getter_AddRefs(prefBranch));
if (prefBranch)
prefBranch->GetBoolPref("mail.enable_automatic_image_resizing",
&resize); // ignore return value
prefix = resize ? scaledPrefix : unscaledPrefix;
#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<nsIMsgMessageUrl> 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::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::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;
}
extern "C" char* MimeGetStringByID(int32_t stringID) {
nsCOMPtr<nsIStringBundleService> stringBundleService =
mozilla::components::StringBundle::Service();
nsCOMPtr<nsIStringBundle> 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<nsIStringBundleService> stringBundleService =
do_GetService(NS_STRINGBUNDLE_CONTRACTID);
nsCOMPtr<nsIStringBundle> stringBundle;
stringBundleService->CreateBundle(MIME_URL, getter_AddRefs(stringBundle));
if (stringBundle) {
nsString v;
if (NS_SUCCEEDED(stringBundle->GetStringFromName(
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<nsICharsetConverterManager> 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<nsIPrefBranch> prefDefBranch;
nsCOMPtr<nsIPrefService> prefSvc(
do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
if (prefSvc)
rv = prefSvc->GetDefaultBranch("", getter_AddRefs(prefDefBranch));
if (!prefDefBranch) return rv;
// get original font size
int32_t originalSize;
rv = prefDefBranch->GetIntPref(prefStr.get(), &originalSize);
if (NS_FAILED(rv)) return rv;
// calculate percentage
*fontSizePercentage =
originalSize
? (int32_t)((float)*fontPixelSize / (float)originalSize * 100)
: 0;
}
return NS_OK;
}
/**
* This function synchronously converts an HTML document (as string)
* to plaintext (as string) using the Gecko converter.
*
* @param flags see nsIDocumentEncoder.h
*/
nsresult HTML2Plaintext(const nsString& inString, nsString& outString,
uint32_t flags, uint32_t wrapCol) {
nsCOMPtr<nsIParserUtils> utils = do_GetService(NS_PARSERUTILS_CONTRACTID);
return utils->ConvertToPlainText(inString, flags, wrapCol, outString);
}
/**
* This function synchronously sanitizes an HTML document (string->string)
* using the Gecko nsTreeSanitizer.
*/
nsresult HTMLSanitize(const nsString& inString, nsString& outString) {
// If you want to add alternative sanitization, you can insert a conditional
// call to another sanitizer and an early return here.
uint32_t flags = nsIParserUtils::SanitizerCidEmbedsOnly |
nsIParserUtils::SanitizerDropForms;
nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
bool dropPresentational = true;
bool dropMedia = false;
prefs->GetBoolPref(
"mailnews.display.html_sanitizer.drop_non_css_presentation",
&dropPresentational);
prefs->GetBoolPref("mailnews.display.html_sanitizer.drop_media", &dropMedia);
if (dropPresentational)
flags |= nsIParserUtils::SanitizerDropNonCSSPresentation;
if (dropMedia) flags |= nsIParserUtils::SanitizerDropMedia;
nsCOMPtr<nsIParserUtils> utils = do_GetService(NS_PARSERUTILS_CONTRACTID);
return utils->Sanitize(inString, flags, outString);
}