in mailnews/mime/src/mimedrft.cpp [1112:1662]
static void mime_parse_stream_complete(nsMIMESession* stream) {
NS_ASSERTION(stream->data_object, "null mime data");
if (!stream->data_object) {
return;
}
mime_draft_data* mdd = stream->data_object.AsMimeDraftData();
if (!mdd) {
return;
}
nsCOMPtr<nsIMsgCompFields> fields;
int htmlAction = 0;
int lineWidth = 0;
char* to_and_cc = 0;
char* re_subject = 0;
char* new_refs = 0;
char* from = 0;
char* repl = 0;
char* subj = 0;
char* id = 0;
char* refs = 0;
char* to = 0;
char* cc = 0;
char* bcc = 0;
char* fcc = 0;
char* org = 0;
char* grps = 0;
char* foll = 0;
char* priority = 0;
char* draftInfo = 0;
char* contentLanguage = 0;
char* identityKey = 0;
nsTArray<nsString> readOtherHeaders;
bool forward_inline = false;
bool bodyAsAttachment = false;
bool charsetOverride = false;
if (mdd->obj) {
int status;
status = mdd->obj->clazz->parse_eof(mdd->obj, false);
mdd->obj->clazz->parse_end(mdd->obj, status < 0 ? true : false);
// RICHIE
// We need to figure out how to pass the forwarded flag along with this
// operation.
// forward_inline = (mdd->format_out != FO_CMDLINE_ATTACHMENTS);
forward_inline = mdd->forwardInline;
NS_ASSERTION(mdd->options == mdd->obj->options,
"mime draft options not same as obj->options");
mime_free(mdd->obj);
mdd->obj = 0;
if (mdd->options) {
// save the override flag before it's unavailable
charsetOverride = mdd->options->override_charset;
// Override the charset only if requested. If the message doesn't have
// one and we're not overriding, we'll detect it later.
if (charsetOverride && mdd->options->default_charset) {
PR_FREEIF(mdd->mailcharset);
mdd->mailcharset = strdup(mdd->options->default_charset);
}
// mscott: aren't we leaking a bunch of strings here like the charset
// strings and such?
delete mdd->options;
mdd->options = 0;
}
if (mdd->stream) {
mdd->stream->complete(mdd->stream);
PR_Free(mdd->stream);
mdd->stream = 0;
}
}
//
// Now, process the attachments that we have gathered from the message
// on disk
//
nsMsgAttachmentData* newAttachData = mime_draft_process_attachments(mdd);
//
// time to bring up the compose windows with all the info gathered
//
if (mdd->headers) {
subj = MimeHeaders_get(mdd->headers, HEADER_SUBJECT, false, false);
if (forward_inline) {
if (subj) {
nsresult rv;
nsCOMPtr<nsIPrefBranch> prefBranch(
do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
if (NS_SUCCEEDED(rv)) {
nsAutoCString fwdPrefix;
prefBranch->GetCharPref("mail.forward_subject_prefix", fwdPrefix);
char* newSubj = PR_smprintf(
"%s: %s", !fwdPrefix.IsEmpty() ? fwdPrefix.get() : "Fwd", subj);
if (newSubj) {
PR_Free(subj);
subj = newSubj;
}
}
}
} else {
from = MimeHeaders_get(mdd->headers, HEADER_FROM, false, false);
repl = MimeHeaders_get(mdd->headers, HEADER_REPLY_TO, false, false);
to = MimeHeaders_get(mdd->headers, HEADER_TO, false, true);
cc = MimeHeaders_get(mdd->headers, HEADER_CC, false, true);
bcc = MimeHeaders_get(mdd->headers, HEADER_BCC, false, true);
/* These headers should not be RFC-1522-decoded. */
grps = MimeHeaders_get(mdd->headers, HEADER_NEWSGROUPS, false, true);
foll = MimeHeaders_get(mdd->headers, HEADER_FOLLOWUP_TO, false, true);
id = MimeHeaders_get(mdd->headers, HEADER_MESSAGE_ID, false, false);
refs = MimeHeaders_get(mdd->headers, HEADER_REFERENCES, false, true);
priority = MimeHeaders_get(mdd->headers, HEADER_X_PRIORITY, false, false);
// Other headers via pref.
nsCString otherHeaders;
nsTArray<nsCString> otherHeadersArray;
nsresult rv;
nsCOMPtr<nsIPrefBranch> pPrefBranch(
do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
pPrefBranch->GetCharPref("mail.compose.other.header", otherHeaders);
if (!otherHeaders.IsEmpty()) {
ToLowerCase(otherHeaders);
ParseString(otherHeaders, ',', otherHeadersArray);
for (auto otherHeader : otherHeadersArray) {
otherHeader.Trim(" ");
nsAutoCString result;
result.Assign(
MimeHeaders_get(mdd->headers, otherHeader.get(), false, false));
readOtherHeaders.AppendElement(NS_ConvertUTF8toUTF16(result));
}
}
}
CreateCompositionFields(from, repl, to, cc, bcc, fcc, grps, foll, org, subj,
refs, priority, readOtherHeaders, mdd->mailcharset,
getter_AddRefs(fields));
contentLanguage =
MimeHeaders_get(mdd->headers, HEADER_CONTENT_LANGUAGE, false, false);
if (contentLanguage) {
fields->SetContentLanguage(contentLanguage);
}
draftInfo = MimeHeaders_get(mdd->headers, HEADER_X_MOZILLA_DRAFT_INFO,
false, false);
// We always preserve an existing message ID, if present, apart from some
// exceptions.
bool keepID = fields != nullptr;
// Don't keep ID when forwarding inline.
if (forward_inline) keepID = false;
// nsMimeOutput::nsMimeMessageEditorTemplate is used for editing a message
// "as new", creating a message from a template or editing a template.
// Only in the latter case we want to preserve the ID.
if (mdd->format_out == nsMimeOutput::nsMimeMessageEditorTemplate &&
!PL_strstr(mdd->url_name, "&edittempl=true"))
keepID = false;
if (keepID) fields->SetMessageId(id);
if (draftInfo && fields && !forward_inline) {
char* parm = 0;
parm = MimeHeaders_get_parameter(draftInfo, "vcard", NULL, NULL);
fields->SetAttachVCard(parm && !strcmp(parm, "1"));
PR_FREEIF(parm);
parm = MimeHeaders_get_parameter(draftInfo, "receipt", NULL, NULL);
if (!parm || !strcmp(parm, "0"))
fields->SetReturnReceipt(false);
else {
int receiptType = 0;
fields->SetReturnReceipt(true);
sscanf(parm, "%d", &receiptType);
// slight change compared to 4.x; we used to use receipt= to tell
// whether the draft/template has request for either MDN or DNS or both
// return receipt; since the DNS is out of the picture we now use the
// header type - 1 to tell whether user has requested the return receipt
fields->SetReceiptHeaderType(((int32_t)receiptType) - 1);
}
PR_FREEIF(parm);
parm = MimeHeaders_get_parameter(draftInfo, "DSN", NULL, NULL);
fields->SetDSN(parm && !strcmp(parm, "1"));
PR_Free(parm);
parm = MimeHeaders_get_parameter(draftInfo, "html", NULL, NULL);
if (parm) sscanf(parm, "%d", &htmlAction);
PR_FREEIF(parm);
parm = MimeHeaders_get_parameter(draftInfo, "linewidth", NULL, NULL);
if (parm) sscanf(parm, "%d", &lineWidth);
PR_FREEIF(parm);
parm = MimeHeaders_get_parameter(draftInfo, "attachmentreminder", NULL,
NULL);
if (parm && !strcmp(parm, "1"))
fields->SetAttachmentReminder(true);
else
fields->SetAttachmentReminder(false);
PR_FREEIF(parm);
parm = MimeHeaders_get_parameter(draftInfo, "deliveryformat", NULL, NULL);
if (parm) {
int32_t deliveryFormat = nsIMsgCompSendFormat::Unset;
sscanf(parm, "%d", &deliveryFormat);
fields->SetDeliveryFormat(deliveryFormat);
}
PR_FREEIF(parm);
}
// identity to prefer when opening the message in the compose window?
identityKey = MimeHeaders_get(mdd->headers, HEADER_X_MOZILLA_IDENTITY_KEY,
false, false);
if (identityKey && *identityKey) {
nsresult rv = NS_OK;
nsCOMPtr<nsIMsgAccountManager> accountManager =
do_GetService("@mozilla.org/messenger/account-manager;1", &rv);
if (NS_SUCCEEDED(rv) && accountManager) {
nsCOMPtr<nsIMsgIdentity> overrulingIdentity;
rv = accountManager->GetIdentity(nsDependentCString(identityKey),
getter_AddRefs(overrulingIdentity));
if (NS_SUCCEEDED(rv) && overrulingIdentity) {
mdd->identity = overrulingIdentity;
fields->SetCreatorIdentityKey(identityKey);
}
}
}
if (mdd->messageBody) {
MSG_ComposeFormat composeFormat = nsIMsgCompFormat::Default;
if (!mdd->messageBody->m_type.IsEmpty()) {
if (mdd->messageBody->m_type.LowerCaseFindASCII("text/html") !=
kNotFound)
composeFormat = nsIMsgCompFormat::HTML;
else if (mdd->messageBody->m_type.LowerCaseFindASCII("text/plain") !=
kNotFound ||
mdd->messageBody->m_type.LowerCaseEqualsLiteral("text"))
composeFormat = nsIMsgCompFormat::PlainText;
else
// We cannot use this kind of data for the message body! Therefore,
// move it as attachment
bodyAsAttachment = true;
} else
composeFormat = nsIMsgCompFormat::PlainText;
char* body = nullptr;
if (!bodyAsAttachment && mdd->messageBody->m_tmpFile) {
int64_t fileSize;
nsCOMPtr<nsIFile> tempFileCopy;
mdd->messageBody->m_tmpFile->Clone(getter_AddRefs(tempFileCopy));
mdd->messageBody->m_tmpFile = tempFileCopy;
tempFileCopy = nullptr;
mdd->messageBody->m_tmpFile->GetFileSize(&fileSize);
uint32_t bodyLen = 0;
// The stream interface can only read up to 4GB (32bit uint).
// It is highly unlikely to encounter a body lager than that limit,
// so we just skip it instead of reading it in chunks.
if (fileSize < UINT32_MAX) {
bodyLen = fileSize;
body = (char*)PR_MALLOC(bodyLen + 1);
}
if (body) {
memset(body, 0, bodyLen + 1);
uint32_t bytesRead;
nsCOMPtr<nsIInputStream> inputStream;
nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream),
mdd->messageBody->m_tmpFile);
if (NS_FAILED(rv)) {
delete[] newAttachData;
PR_Free(body);
return;
}
inputStream->Read(body, bodyLen, &bytesRead);
inputStream->Close();
// Convert the body to UTF-8
char* mimeCharset = nullptr;
// Get a charset from the header if no override is set.
if (!charsetOverride)
mimeCharset = MimeHeaders_get_parameter(
mdd->messageBody->m_type.get(), "charset", nullptr, nullptr);
// If no charset is specified in the header then use the default.
nsAutoCString bodyCharset;
if (mimeCharset) {
bodyCharset.Adopt(mimeCharset);
} else if (mdd->mailcharset) {
bodyCharset.Assign(mdd->mailcharset);
}
if (bodyCharset.IsEmpty()) {
nsAutoCString detectedCharset;
// We need to detect it.
rv = MIME_detect_charset(body, bodyLen, detectedCharset);
if (NS_SUCCEEDED(rv) && !detectedCharset.IsEmpty()) {
bodyCharset = detectedCharset;
}
}
if (!bodyCharset.IsEmpty()) {
nsAutoString tmpUnicodeBody;
rv = nsMsgI18NConvertToUnicode(
bodyCharset, nsDependentCString(body), tmpUnicodeBody);
if (NS_FAILED(rv)) // Tough luck, ASCII/ISO-8859-1 then...
CopyASCIItoUTF16(nsDependentCString(body), tmpUnicodeBody);
char* newBody = ToNewUTF8String(tmpUnicodeBody);
if (newBody) {
PR_Free(body);
body = newBody;
}
}
}
}
bool convertToPlainText = false;
if (forward_inline) {
if (mdd->identity) {
bool identityComposeHTML;
mdd->identity->GetComposeHtml(&identityComposeHTML);
if ((identityComposeHTML && !mdd->overrideComposeFormat) ||
(!identityComposeHTML && mdd->overrideComposeFormat)) {
// In the end, we're going to compose in HTML mode...
if (body && composeFormat == nsIMsgCompFormat::PlainText) {
// ... but the message body is currently plain text.
convert_plaintext_body_to_html(&body);
}
// Body is now HTML, set the format too (so headers are inserted in
// correct format).
composeFormat = nsIMsgCompFormat::HTML;
} else if ((identityComposeHTML && mdd->overrideComposeFormat) ||
!identityComposeHTML) {
// In the end, we're going to compose in plain text mode...
if (composeFormat == nsIMsgCompFormat::HTML) {
// ... but the message body is currently HTML.
// We'll do the conversion later on when headers have been
// inserted, body has been set and converted to unicode.
convertToPlainText = true;
}
}
}
mime_insert_forwarded_message_headers(&body, mdd->headers,
composeFormat, mdd->mailcharset);
}
MSG_ComposeType msgComposeType = 0; // Keep compilers happy.
if (mdd->format_out == nsMimeOutput::nsMimeMessageEditorTemplate) {
if (PL_strstr(mdd->url_name, "?redirect=true") ||
PL_strstr(mdd->url_name, "&redirect=true"))
msgComposeType = nsIMsgCompType::Redirect;
else if (PL_strstr(mdd->url_name, "?editasnew=true") ||
PL_strstr(mdd->url_name, "&editasnew=true"))
msgComposeType = nsIMsgCompType::EditAsNew;
else if (PL_strstr(mdd->url_name, "?edittempl=true") ||
PL_strstr(mdd->url_name, "&edittempl=true"))
msgComposeType = nsIMsgCompType::EditTemplate;
else
msgComposeType = nsIMsgCompType::Template;
}
if (body && msgComposeType == nsIMsgCompType::EditAsNew) {
// When editing as new, we respect the identities preferred format
// which can be overridden.
if (mdd->identity) {
bool identityComposeHTML;
mdd->identity->GetComposeHtml(&identityComposeHTML);
if (composeFormat == nsIMsgCompFormat::HTML &&
identityComposeHTML == mdd->overrideComposeFormat) {
// We we have HTML:
// If they want HTML and they want to override it (true == true)
// or they don't want HTML and they don't want to override it
// (false == false), then convert. Conversion happens below.
convertToPlainText = true;
composeFormat = nsIMsgCompFormat::PlainText;
} else if (composeFormat == nsIMsgCompFormat::PlainText &&
identityComposeHTML != mdd->overrideComposeFormat) {
// We have plain text:
// If they want HTML and they don't want to override it (true !=
// false) or they don't want HTML and they want to override it
// (false != true), then convert.
convert_plaintext_body_to_html(&body);
composeFormat = nsIMsgCompFormat::HTML;
}
}
} else if (body && mdd->overrideComposeFormat &&
(msgComposeType == nsIMsgCompType::Template ||
msgComposeType == nsIMsgCompType::EditTemplate ||
!mdd->forwardInline)) // Draft processing.
{
// When using a template and overriding, the user gets the
// "other" format.
if (composeFormat == nsIMsgCompFormat::PlainText) {
convert_plaintext_body_to_html(&body);
composeFormat = nsIMsgCompFormat::HTML;
} else {
// Conversion happens below.
convertToPlainText = true;
composeFormat = nsIMsgCompFormat::PlainText;
}
}
// convert from UTF-8 to UTF-16
if (body) {
fields->SetBody(NS_ConvertUTF8toUTF16(body));
PR_Free(body);
}
//
// At this point, we need to create a message compose window or editor
// window via XP-COM with the information that we have retrieved from
// the message store.
//
if (mdd->format_out == nsMimeOutput::nsMimeMessageEditorTemplate) {
// Set the draft ID when editing a template so the original is
// overwritten when saving the template again.
// Note that always setting the draft ID here would cause drafts to be
// overwritten when edited "as new", which is undesired.
if (msgComposeType == nsIMsgCompType::EditTemplate) {
fields->SetDraftId(nsDependentCString(mdd->url_name));
fields->SetTemplateId(nsDependentCString(
mdd->url_name)); // Remember original template ID.
}
if (convertToPlainText) fields->ConvertBodyToPlainText();
CreateTheComposeWindow(fields, newAttachData, msgComposeType,
composeFormat, mdd->identity,
mdd->originalMsgURI, mdd->origMsgHdr);
} else {
if (mdd->forwardInline) {
if (convertToPlainText) fields->ConvertBodyToPlainText();
if (mdd->overrideComposeFormat)
composeFormat = nsIMsgCompFormat::OppositeOfDefault;
if (mdd->forwardInlineFilter) {
fields->SetTo(mdd->forwardToAddress);
ForwardMsgInline(fields, newAttachData, composeFormat,
mdd->identity, mdd->originalMsgURI,
mdd->origMsgHdr);
} else
CreateTheComposeWindow(fields, newAttachData,
nsIMsgCompType::ForwardInline, composeFormat,
mdd->identity, mdd->originalMsgURI,
mdd->origMsgHdr);
} else {
if (convertToPlainText) fields->ConvertBodyToPlainText();
fields->SetDraftId(nsDependentCString(mdd->url_name));
CreateTheComposeWindow(fields, newAttachData, nsIMsgCompType::Draft,
composeFormat, mdd->identity,
mdd->originalMsgURI, mdd->origMsgHdr);
}
}
} else {
//
// At this point, we need to create a message compose window via
// XP-COM with the information that we have retrieved from the message
// store.
//
if (mdd->format_out == nsMimeOutput::nsMimeMessageEditorTemplate) {
#ifdef NS_DEBUG
printf(
"RICHIE: Time to create the EDITOR with this template - NO "
"body!!!!\n");
#endif
CreateTheComposeWindow(fields, newAttachData, nsIMsgCompType::Template,
nsIMsgCompFormat::Default, mdd->identity,
EmptyCString(), mdd->origMsgHdr);
} else {
#ifdef NS_DEBUG
printf("Time to create the composition window WITHOUT a body!!!!\n");
#endif
if (mdd->forwardInline) {
MSG_ComposeFormat composeFormat =
(mdd->overrideComposeFormat) ? nsIMsgCompFormat::OppositeOfDefault
: nsIMsgCompFormat::Default;
CreateTheComposeWindow(fields, newAttachData,
nsIMsgCompType::ForwardInline, composeFormat,
mdd->identity, mdd->originalMsgURI,
mdd->origMsgHdr);
} else {
fields->SetDraftId(nsDependentCString(mdd->url_name));
CreateTheComposeWindow(fields, newAttachData, nsIMsgCompType::Draft,
nsIMsgCompFormat::Default, mdd->identity,
EmptyCString(), mdd->origMsgHdr);
}
}
}
} else {
CreateCompositionFields(from, repl, to, cc, bcc, fcc, grps, foll, org, subj,
refs, priority, readOtherHeaders, mdd->mailcharset,
getter_AddRefs(fields));
if (fields)
CreateTheComposeWindow(fields, newAttachData, nsIMsgCompType::New,
nsIMsgCompFormat::Default, mdd->identity,
EmptyCString(), mdd->origMsgHdr);
}
if (mdd->headers) MimeHeaders_free(mdd->headers);
//
// Free the original attachment structure...
// Make sure we only cleanup the local copy of the memory and not kill
// files we need on disk
//
if (bodyAsAttachment)
mdd->messageBody->m_tmpFile = nullptr;
else if (mdd->messageBody && mdd->messageBody->m_tmpFile)
mdd->messageBody->m_tmpFile->Remove(false);
delete mdd->messageBody;
for (uint32_t i = 0; i < mdd->attachments.Length(); i++)
mdd->attachments[i]->m_tmpFile = nullptr;
PR_FREEIF(mdd->mailcharset);
mdd->identity = nullptr;
PR_Free(mdd->url_name);
mdd->origMsgHdr = nullptr;
PR_Free(mdd);
PR_FREEIF(to_and_cc);
PR_FREEIF(re_subject);
PR_FREEIF(new_refs);
PR_FREEIF(from);
PR_FREEIF(repl);
PR_FREEIF(subj);
PR_FREEIF(id);
PR_FREEIF(refs);
PR_FREEIF(to);
PR_FREEIF(cc);
PR_FREEIF(grps);
PR_FREEIF(foll);
PR_FREEIF(priority);
PR_FREEIF(draftInfo);
PR_Free(identityKey);
delete[] newAttachData;
}