diff options
Diffstat (limited to 'mailnews/mime/src/mimedrft.cpp')
-rw-r--r-- | mailnews/mime/src/mimedrft.cpp | 2084 |
1 files changed, 2084 insertions, 0 deletions
diff --git a/mailnews/mime/src/mimedrft.cpp b/mailnews/mime/src/mimedrft.cpp new file mode 100644 index 000000000..90ca027d8 --- /dev/null +++ b/mailnews/mime/src/mimedrft.cpp @@ -0,0 +1,2084 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * 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/. + * This Original Code has been modified by IBM Corporation. Modifications made by IBM + * described herein are Copyright (c) International Business Machines Corporation, 2000. + * Modifications to Mozilla code or documentation identified per MPL Section 3.3 + * + * Date Modified by Description of modification + * 04/20/2000 IBM Corp. OS/2 VisualAge build. + */ +#include "nsCOMPtr.h" +#include "modmimee.h" +#include "mimeobj.h" +#include "modlmime.h" +#include "mimei.h" +#include "mimebuf.h" +#include "mimemoz2.h" +#include "mimemsg.h" +#include "nsMimeTypes.h" +#include <ctype.h> + +#include "prmem.h" +#include "plstr.h" +#include "prprf.h" +#include "prio.h" +#include "nsIPrefService.h" +#include "nsIPrefBranch.h" +#include "msgCore.h" +#include "nsIMsgSend.h" +#include "nsMimeStringResources.h" +#include "nsIIOService.h" +#include "nsNetUtil.h" +#include "comi18n.h" +#include "nsIMsgAttachment.h" +#include "nsIMsgCompFields.h" +#include "nsMsgCompCID.h" +#include "nsIMsgComposeService.h" +#include "nsMsgAttachmentData.h" +#include "nsMsgI18N.h" +#include "nsNativeCharsetUtils.h" +#include "nsDirectoryServiceDefs.h" +#include "nsIMsgMessageService.h" +#include "nsMsgUtils.h" +#include "nsCExternalHandlerService.h" +#include "nsIMIMEService.h" +#include "nsIMsgAccountManager.h" +#include "nsMsgBaseCID.h" +#include "nsIMimeConverter.h" // for MimeConverterOutputCallback +#include "mozilla/mailnews/MimeHeaderParser.h" + +using namespace mozilla::mailnews; + +// +// Header strings... +// +#define HEADER_NNTP_POSTING_HOST "NNTP-Posting-Host" +#define MIME_HEADER_TABLE "<TABLE CELLPADDING=0 CELLSPACING=0 BORDER=0 class=\"moz-email-headers-table\">" +#define HEADER_START_JUNK "<TR><TH VALIGN=BASELINE ALIGN=RIGHT NOWRAP>" +#define HEADER_MIDDLE_JUNK ": </TH><TD>" +#define HEADER_END_JUNK "</TD></TR>" + +// +// Forward declarations... +// +extern "C" char *MIME_StripContinuations(char *original); +int mime_decompose_file_init_fn(void *stream_closure, MimeHeaders *headers); +int mime_decompose_file_output_fn(const char *buf, int32_t size, void *stream_closure); +int mime_decompose_file_close_fn(void *stream_closure); +extern int MimeHeaders_build_heads_list(MimeHeaders *hdrs); + +// CID's +static NS_DEFINE_CID(kCMsgComposeServiceCID, NS_MSGCOMPOSESERVICE_CID); + +mime_draft_data::mime_draft_data() : url_name(nullptr), format_out(0), + stream(nullptr), obj(nullptr), options(nullptr), headers(nullptr), + messageBody(nullptr), curAttachment(nullptr), + decoder_data(nullptr), mailcharset(nullptr), forwardInline(false), + forwardInlineFilter(false), overrideComposeFormat(false), + originalMsgURI(nullptr) +{ +} +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// +// THIS SHOULD ALL MOVE TO ANOTHER FILE AFTER LANDING! +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// + +// safe filename for all OSes +#define SAFE_TMP_FILENAME "nsmime.tmp" + +// +// Create a file for the a unique temp file +// on the local machine. Caller must free memory +// +nsresult +nsMsgCreateTempFile(const char *tFileName, nsIFile **tFile) +{ + if (!tFileName || !*tFileName) + tFileName = SAFE_TMP_FILENAME; + + nsresult rv = GetSpecialDirectoryWithFileName(NS_OS_TEMP_DIR, + tFileName, + tFile); + + NS_ENSURE_SUCCESS(rv, rv); + + rv = (*tFile)->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 00600); + if (NS_FAILED(rv)) + NS_RELEASE(*tFile); + + return rv; +} + + +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// +// END OF - THIS SHOULD ALL MOVE TO ANOTHER FILE AFTER LANDING! +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// + +typedef enum { + nsMsg_RETURN_RECEIPT_BOOL_HEADER_MASK = 0, + nsMsg_ENCRYPTED_BOOL_HEADER_MASK, + nsMsg_SIGNED_BOOL_HEADER_MASK, + nsMsg_UUENCODE_BINARY_BOOL_HEADER_MASK, + nsMsg_ATTACH_VCARD_BOOL_HEADER_MASK, + nsMsg_LAST_BOOL_HEADER_MASK // last boolean header mask; must be the last one + // DON'T remove. +} nsMsgBoolHeaderSet; + +#ifdef NS_DEBUG +extern "C" void +mime_dump_attachments(nsMsgAttachmentData *attachData) +{ + int32_t i = 0; + class nsMsgAttachmentData *tmp = attachData; + + while (tmp && tmp->m_url) + { + printf("Real Name : %s\n", tmp->m_realName.get()); + + if (tmp->m_url) + { + ; + printf("URL : %s\n", tmp->m_url->GetSpecOrDefault().get()); + } + + printf("Desired Type : %s\n", tmp->m_desiredType.get()); + printf("Real Type : %s\n", tmp->m_realType.get()); + printf("Real Encoding : %s\n", tmp->m_realEncoding.get()); + printf("Description : %s\n", tmp->m_description.get()); + printf("Mac Type : %s\n", tmp->m_xMacType.get()); + printf("Mac Creator : %s\n", tmp->m_xMacCreator.get()); + printf("Size in bytes : %d\n", tmp->m_size); + i++; + tmp++; + } +} +#endif + +nsresult CreateComposeParams(nsCOMPtr<nsIMsgComposeParams> &pMsgComposeParams, + nsIMsgCompFields * compFields, + nsMsgAttachmentData *attachmentList, + MSG_ComposeType composeType, + MSG_ComposeFormat composeFormat, + nsIMsgIdentity * identity, + const char *originalMsgURI, + nsIMsgDBHdr *origMsgHdr) +{ +#ifdef NS_DEBUG + mime_dump_attachments(attachmentList); +#endif + + nsresult rv; + nsMsgAttachmentData *curAttachment = attachmentList; + if (curAttachment) + { + nsAutoCString spec; + + while (curAttachment && curAttachment->m_url) + { + rv = curAttachment->m_url->GetSpec(spec); + if (NS_SUCCEEDED(rv)) + { + nsCOMPtr<nsIMsgAttachment> attachment = do_CreateInstance(NS_MSGATTACHMENT_CONTRACTID, &rv); + if (NS_SUCCEEDED(rv) && attachment) + { + nsAutoString nameStr; + rv = ConvertToUnicode("UTF-8", curAttachment->m_realName.get(), nameStr); + if (NS_FAILED(rv)) + CopyASCIItoUTF16(curAttachment->m_realName, nameStr); + attachment->SetName(nameStr); + attachment->SetUrl(spec); + attachment->SetTemporary(true); + attachment->SetContentType(curAttachment->m_realType.get()); + attachment->SetMacType(curAttachment->m_xMacType.get()); + attachment->SetMacCreator(curAttachment->m_xMacCreator.get()); + attachment->SetSize(curAttachment->m_size); + if (!curAttachment->m_cloudPartInfo.IsEmpty()) + { + nsCString provider; + nsCString cloudUrl; + attachment->SetSendViaCloud(true); + provider.Adopt( + MimeHeaders_get_parameter(curAttachment->m_cloudPartInfo.get(), + "provider", nullptr, nullptr)); + cloudUrl.Adopt( + MimeHeaders_get_parameter(curAttachment->m_cloudPartInfo.get(), + "url", nullptr, nullptr)); + attachment->SetCloudProviderKey(provider); + attachment->SetContentLocation(cloudUrl); + } + compFields->AddAttachment(attachment); + } + } + curAttachment++; + } + } + + MSG_ComposeFormat format = composeFormat; // Format to actually use. + if (identity && composeType == nsIMsgCompType::ForwardInline) + { + bool composeHtml = false; + identity->GetComposeHtml(&composeHtml); + if (composeHtml) + format = (composeFormat == nsIMsgCompFormat::OppositeOfDefault) ? + nsIMsgCompFormat::PlainText : nsIMsgCompFormat::HTML; + else + format = (composeFormat == nsIMsgCompFormat::OppositeOfDefault) ? + nsIMsgCompFormat::HTML : nsIMsgCompFormat::PlainText; + } + + pMsgComposeParams = do_CreateInstance(NS_MSGCOMPOSEPARAMS_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + pMsgComposeParams->SetType(composeType); + pMsgComposeParams->SetFormat(format); + pMsgComposeParams->SetIdentity(identity); + pMsgComposeParams->SetComposeFields(compFields); + if (originalMsgURI) + pMsgComposeParams->SetOriginalMsgURI(originalMsgURI); + if (origMsgHdr) + pMsgComposeParams->SetOrigMsgHdr(origMsgHdr); + return NS_OK; +} + +nsresult +CreateTheComposeWindow(nsIMsgCompFields * compFields, + nsMsgAttachmentData *attachmentList, + MSG_ComposeType composeType, + MSG_ComposeFormat composeFormat, + nsIMsgIdentity * identity, + const char * originalMsgURI, + nsIMsgDBHdr * origMsgHdr + ) +{ + nsCOMPtr<nsIMsgComposeParams> pMsgComposeParams; + nsresult rv = CreateComposeParams(pMsgComposeParams, compFields, + attachmentList, + composeType, + composeFormat, + identity, + originalMsgURI, + origMsgHdr); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIMsgComposeService> msgComposeService = + do_GetService(kCMsgComposeServiceCID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + return msgComposeService->OpenComposeWindowWithParams(nullptr /* default chrome */, pMsgComposeParams); +} + +nsresult +ForwardMsgInline(nsIMsgCompFields *compFields, + nsMsgAttachmentData *attachmentList, + MSG_ComposeFormat composeFormat, + nsIMsgIdentity *identity, + const char *originalMsgURI, + nsIMsgDBHdr *origMsgHdr) +{ + nsCOMPtr<nsIMsgComposeParams> pMsgComposeParams; + nsresult rv = CreateComposeParams(pMsgComposeParams, compFields, + attachmentList, + nsIMsgCompType::ForwardInline, + composeFormat, + identity, + originalMsgURI, + origMsgHdr); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIMsgComposeService> msgComposeService = + do_GetService(kCMsgComposeServiceCID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + // create the nsIMsgCompose object to send the object + nsCOMPtr<nsIMsgCompose> pMsgCompose(do_CreateInstance(NS_MSGCOMPOSE_CONTRACTID, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + /** initialize nsIMsgCompose, Send the message, wait for send completion response **/ + rv = pMsgCompose->Initialize(pMsgComposeParams, nullptr, nullptr); + NS_ENSURE_SUCCESS(rv,rv); + + rv = pMsgCompose->SendMsg(nsIMsgSend::nsMsgDeliverNow, identity, nullptr, nullptr, nullptr); + if (NS_SUCCEEDED(rv)) + { + nsCOMPtr<nsIMsgFolder> origFolder; + origMsgHdr->GetFolder(getter_AddRefs(origFolder)); + if (origFolder) + origFolder->AddMessageDispositionState( + origMsgHdr, nsIMsgFolder::nsMsgDispositionState_Forwarded); + } + return rv; +} + +nsresult +CreateCompositionFields(const char *from, + const char *reply_to, + const char *to, + const char *cc, + const char *bcc, + const char *fcc, + const char *newsgroups, + const char *followup_to, + const char *organization, + const char *subject, + const char *references, + const char *priority, + const char *newspost_url, + char *charset, + nsIMsgCompFields **_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + + nsresult rv; + *_retval = nullptr; + + nsCOMPtr<nsIMsgCompFields> cFields = do_CreateInstance(NS_MSGCOMPFIELDS_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(cFields, NS_ERROR_OUT_OF_MEMORY); + + // Now set all of the passed in stuff... + cFields->SetCharacterSet(!PL_strcasecmp("us-ascii", charset) ? "ISO-8859-1" : charset); + + nsAutoCString val; + nsAutoString outString; + + if (from) { + ConvertRawBytesToUTF16(from, charset, outString); + cFields->SetFrom(outString); + } + + if (subject) { + MIME_DecodeMimeHeader(subject, charset, false, true, val); + cFields->SetSubject(NS_ConvertUTF8toUTF16(!val.IsEmpty() ? val.get() : subject)); + } + + if (reply_to) { + ConvertRawBytesToUTF16(reply_to, charset, outString); + cFields->SetReplyTo(outString); + } + + if (to) { + ConvertRawBytesToUTF16(to, charset, outString); + cFields->SetTo(outString); + } + + if (cc) { + ConvertRawBytesToUTF16(cc, charset, outString); + cFields->SetCc(outString); + } + + if (bcc) { + ConvertRawBytesToUTF16(bcc, charset, outString); + cFields->SetBcc(outString); + } + + if (fcc) { + MIME_DecodeMimeHeader(fcc, charset, false, true, val); + cFields->SetFcc(NS_ConvertUTF8toUTF16(!val.IsEmpty() ? val.get() : fcc)); + } + + if (newsgroups) { + // fixme: the newsgroups header had better be decoded using the server-side + // character encoding,but this |charset| might be different from it. + MIME_DecodeMimeHeader(newsgroups, charset, false, true, val); + cFields->SetNewsgroups(NS_ConvertUTF8toUTF16(!val.IsEmpty() ? val.get() : newsgroups)); + } + + if (followup_to) { + MIME_DecodeMimeHeader(followup_to, charset, false, true, val); + cFields->SetFollowupTo(NS_ConvertUTF8toUTF16(!val.IsEmpty() ? val.get() : followup_to)); + } + + if (organization) { + MIME_DecodeMimeHeader(organization, charset, false, true, val); + cFields->SetOrganization(NS_ConvertUTF8toUTF16(!val.IsEmpty() ? val.get() : organization)); + } + + if (references) { + MIME_DecodeMimeHeader(references, charset, false, true, val); + cFields->SetReferences(!val.IsEmpty() ? val.get() : references); + } + + if (priority) { + MIME_DecodeMimeHeader(priority, charset, false, true, val); + nsMsgPriorityValue priorityValue; + NS_MsgGetPriorityFromString(!val.IsEmpty() ? val.get() : priority, priorityValue); + nsAutoCString priorityName; + NS_MsgGetUntranslatedPriorityName(priorityValue, priorityName); + cFields->SetPriority(priorityName.get()); + } + + if (newspost_url) { + MIME_DecodeMimeHeader(newspost_url, charset, false, true, val); + cFields->SetNewspostUrl(!val.IsEmpty() ? val.get() : newspost_url); + } + + *_retval = cFields; + NS_IF_ADDREF(*_retval); + + return rv; +} + +static int +dummy_file_write(char *buf, int32_t size, void *fileHandle) +{ + if (!fileHandle) + return -1; + + nsIOutputStream *tStream = (nsIOutputStream *)fileHandle; + uint32_t bytesWritten; + tStream->Write(buf, size, &bytesWritten); + return (int)bytesWritten; +} + +static int +mime_parse_stream_write(nsMIMESession *stream, const char *buf, int32_t size) +{ + mime_draft_data *mdd = (mime_draft_data *)stream->data_object; + NS_ASSERTION(mdd, "null mime draft data!"); + + if (!mdd || !mdd->obj) + return -1; + + return mdd->obj->clazz->parse_buffer((char *)buf, size, mdd->obj); +} + +static void +mime_free_attachments(nsTArray<nsMsgAttachedFile *> &attachments) +{ + if (attachments.Length() <= 0) + return; + + for (uint32_t i = 0; i < attachments.Length(); i++) + { + if (attachments[i]->m_tmpFile) + { + attachments[i]->m_tmpFile->Remove(false); + attachments[i]->m_tmpFile = nullptr; + } + delete attachments[i]; + } +} + +static nsMsgAttachmentData * +mime_draft_process_attachments(mime_draft_data *mdd) +{ + if (!mdd) + return nullptr; + + nsMsgAttachmentData *attachData = NULL, *tmp = NULL; + nsMsgAttachedFile *tmpFile = NULL; + + //It's possible we must treat the message body as attachment! + bool bodyAsAttachment = false; + if (mdd->messageBody && + !mdd->messageBody->m_type.IsEmpty() && + mdd->messageBody->m_type.Find("text/html", CaseInsensitiveCompare) == -1 && + mdd->messageBody->m_type.Find("text/plain", CaseInsensitiveCompare) == -1 && + !mdd->messageBody->m_type.LowerCaseEqualsLiteral("text")) + bodyAsAttachment = true; + + if (!mdd->attachments.Length() && !bodyAsAttachment) + return nullptr; + + int32_t totalCount = mdd->attachments.Length(); + if (bodyAsAttachment) + totalCount++; + attachData = new nsMsgAttachmentData[totalCount + 1]; + if (!attachData) + return nullptr; + + tmp = attachData; + + for (int i = 0, attachmentsIndex = 0; i < totalCount; i++, tmp++) + { + if (bodyAsAttachment && i == 0) + tmpFile = mdd->messageBody; + else + tmpFile = mdd->attachments[attachmentsIndex++]; + + if (tmpFile->m_type.LowerCaseEqualsLiteral("text/x-vcard")) + tmp->m_realName = tmpFile->m_description; + + if (tmpFile->m_origUrl) + { + nsAutoCString tmpSpec; + if (NS_FAILED(tmpFile->m_origUrl->GetSpec(tmpSpec))) + goto FAIL; + + if (NS_FAILED(nsMimeNewURI(getter_AddRefs(tmp->m_url), tmpSpec.get(), nullptr))) + goto FAIL; + + if (tmp->m_realName.IsEmpty()) + { + if (!tmpFile->m_realName.IsEmpty()) + tmp->m_realName = tmpFile->m_realName; + else { + if (tmpFile->m_type.Find(MESSAGE_RFC822, CaseInsensitiveCompare) != -1) + // we have the odd case of processing an e-mail that had an unnamed + // eml message attached + tmp->m_realName = "ForwardedMessage.eml"; + + else + tmp->m_realName = tmpSpec.get(); + } + } + } + + tmp->m_desiredType = tmpFile->m_type; + tmp->m_realType = tmpFile->m_type; + tmp->m_realEncoding = tmpFile->m_encoding; + tmp->m_description = tmpFile->m_description; + tmp->m_cloudPartInfo = tmpFile->m_cloudPartInfo; + tmp->m_xMacType = tmpFile->m_xMacType; + tmp->m_xMacCreator = tmpFile->m_xMacCreator; + tmp->m_size = tmpFile->m_size; + } + return attachData; + +FAIL: + delete [] attachData; + return nullptr; +} + +static void +mime_intl_insert_message_header_1(char **body, + const char *hdr_value, + const char *hdr_str, + const char *html_hdr_str, + const char *mailcharset, + bool htmlEdit) +{ + if (!body || !hdr_value || !hdr_str) + return; + + if (htmlEdit) + { + NS_MsgSACat(body, HEADER_START_JUNK); + } + else + { + NS_MsgSACat(body, MSG_LINEBREAK); + } + if (!html_hdr_str) + html_hdr_str = hdr_str; + NS_MsgSACat(body, html_hdr_str); + if (htmlEdit) + { + NS_MsgSACat(body, HEADER_MIDDLE_JUNK); + } + else + NS_MsgSACat(body, ": "); + + // MIME decode header + nsAutoCString utf8Value; + MIME_DecodeMimeHeader(hdr_value, mailcharset, false, true, utf8Value); + if (!utf8Value.IsEmpty()) { + char *escaped = nullptr; + if (htmlEdit) + escaped = MsgEscapeHTML(utf8Value.get()); + NS_MsgSACat(body, escaped ? escaped : utf8Value.get()); + NS_Free(escaped); + } else { + NS_MsgSACat(body, hdr_value); // raw MIME encoded string + } + + if (htmlEdit) + NS_MsgSACat(body, HEADER_END_JUNK); +} + +char * +MimeGetNamedString(int32_t id) +{ + static char retString[256]; + + retString[0] = '\0'; + char *tString = MimeGetStringByID(id); + if (tString) + { + PL_strncpy(retString, tString, sizeof(retString)); + PR_Free(tString); + } + return retString; +} + +void +MimeGetForwardHeaderDelimiter(nsACString &retString) +{ + nsCString defaultValue; + defaultValue.Adopt(MimeGetStringByID(MIME_FORWARDED_MESSAGE_HTML_USER_WROTE)); + + nsString tmpRetString; + NS_GetLocalizedUnicharPreferenceWithDefault(nullptr, + "mailnews.forward_header_originalmessage", + NS_ConvertUTF8toUTF16(defaultValue), + tmpRetString); + + CopyUTF16toUTF8(tmpRetString, retString); +} + +/* given an address string passed though parameter "address", this one will be converted + and returned through the same parameter. The original string will be destroyed +*/ +static void UnquoteMimeAddress(nsACString &mimeHeader, const char *charset) +{ + if (!mimeHeader.IsEmpty()) + { + nsTArray<nsCString> addresses; + ExtractDisplayAddresses(EncodedHeader(mimeHeader, charset), + UTF16ArrayAdapter<>(addresses)); + mimeHeader.Truncate(); + + uint32_t count = addresses.Length(); + for (uint32_t i = 0; i < count; i++) + { + if (i != 0) + mimeHeader.AppendASCII(", "); + mimeHeader += addresses[i]; + } + } +} + +static void +mime_insert_all_headers(char **body, + MimeHeaders *headers, + MSG_ComposeFormat composeFormat, + char *mailcharset) +{ + bool htmlEdit = (composeFormat == nsIMsgCompFormat::HTML); + char *newBody = NULL; + char *html_tag = nullptr; + if (*body && PL_strncasecmp(*body, "<HTML", 5) == 0) + html_tag = PL_strchr(*body, '>') + 1; + int i; + + if (!headers->done_p) + { + MimeHeaders_build_heads_list(headers); + headers->done_p = true; + } + + nsCString replyHeader; + MimeGetForwardHeaderDelimiter(replyHeader); + if (htmlEdit) + { + NS_MsgSACopy(&(newBody), MIME_FORWARD_HTML_PREFIX); + NS_MsgSACat(&newBody, replyHeader.get()); + NS_MsgSACat(&newBody, MIME_HEADER_TABLE); + } + else + { + NS_MsgSACopy(&(newBody), MSG_LINEBREAK MSG_LINEBREAK); + NS_MsgSACat(&newBody, replyHeader.get()); + } + + for (i = 0; i < headers->heads_size; i++) + { + char *head = headers->heads[i]; + char *end = (i == headers->heads_size-1 + ? headers->all_headers + headers->all_headers_fp + : headers->heads[i+1]); + char *colon, *ocolon; + char *contents; + char *name = 0; + + // Hack for BSD Mailbox delimiter. + if (i == 0 && head[0] == 'F' && !strncmp(head, "From ", 5)) + { + colon = head + 4; + contents = colon + 1; + } + else + { + /* Find the colon. */ + for (colon = head; colon < end; colon++) + if (*colon == ':') break; + + if (colon >= end) continue; /* junk */ + + /* Back up over whitespace before the colon. */ + ocolon = colon; + for (; colon > head && IS_SPACE(colon[-1]); colon--) + ; + + contents = ocolon + 1; + } + + /* Skip over whitespace after colon. */ + while (contents <= end && IS_SPACE(*contents)) + contents++; + + /* Take off trailing whitespace... */ + while (end > contents && IS_SPACE(end[-1])) + end--; + + name = (char *)PR_MALLOC(colon - head + 1); + if (!name) + return /* MIME_OUT_OF_MEMORY */; + memcpy(name, head, colon - head); + name[colon - head] = 0; + + nsAutoCString headerValue; + headerValue.Assign(contents, end - contents); + + /* Do not reveal bcc recipients when forwarding a message! + See http://bugzilla.mozilla.org/show_bug.cgi?id=41150 + */ + if (PL_strcasecmp(name, "bcc") != 0) + { + if (!PL_strcasecmp(name, "resent-from") || !PL_strcasecmp(name, "from") || + !PL_strcasecmp(name, "resent-to") || !PL_strcasecmp(name, "to") || + !PL_strcasecmp(name, "resent-cc") || !PL_strcasecmp(name, "cc") || + !PL_strcasecmp(name, "reply-to")) + UnquoteMimeAddress(headerValue, mailcharset); + + mime_intl_insert_message_header_1(&newBody, headerValue.get(), name, name, + mailcharset, htmlEdit); + } + PR_Free(name); + } + + if (htmlEdit) + { + NS_MsgSACat(&newBody, "</TABLE>"); + NS_MsgSACat(&newBody, MSG_LINEBREAK "<BR><BR>"); + if (html_tag) + NS_MsgSACat(&newBody, html_tag); + else if (*body) + NS_MsgSACat(&newBody, *body); + } + else + { + NS_MsgSACat(&newBody, MSG_LINEBREAK MSG_LINEBREAK); + if (*body) + NS_MsgSACat(&newBody, *body); + } + + if (newBody) + { + PR_FREEIF(*body); + *body = newBody; + } +} + +static void +mime_insert_normal_headers(char **body, + MimeHeaders *headers, + MSG_ComposeFormat composeFormat, + char *mailcharset) +{ + char *newBody = nullptr; + char *subject = MimeHeaders_get(headers, HEADER_SUBJECT, false, false); + char *resent_comments = MimeHeaders_get(headers, HEADER_RESENT_COMMENTS, false, false); + char *resent_date = MimeHeaders_get(headers, HEADER_RESENT_DATE, false, true); + nsCString resent_from(MimeHeaders_get(headers, HEADER_RESENT_FROM, false, true)); + nsCString resent_to(MimeHeaders_get(headers, HEADER_RESENT_TO, false, true)); + nsCString resent_cc(MimeHeaders_get(headers, HEADER_RESENT_CC, false, true)); + char *date = MimeHeaders_get(headers, HEADER_DATE, false, true); + nsCString from(MimeHeaders_get(headers, HEADER_FROM, false, true)); + nsCString reply_to(MimeHeaders_get(headers, HEADER_REPLY_TO, false, true)); + char *organization = MimeHeaders_get(headers, HEADER_ORGANIZATION, false, false); + nsCString to(MimeHeaders_get(headers, HEADER_TO, false, true)); + nsCString cc(MimeHeaders_get(headers, HEADER_CC, false, true)); + char *newsgroups = MimeHeaders_get(headers, HEADER_NEWSGROUPS, false, true); + char *followup_to = MimeHeaders_get(headers, HEADER_FOLLOWUP_TO, false, true); + char *references = MimeHeaders_get(headers, HEADER_REFERENCES, false, true); + const char *html_tag = nullptr; + if (*body && PL_strncasecmp(*body, "<HTML", 5) == 0) + html_tag = PL_strchr(*body, '>') + 1; + bool htmlEdit = composeFormat == nsIMsgCompFormat::HTML; + + if (from.IsEmpty()) + from.Adopt(MimeHeaders_get(headers, HEADER_SENDER, false, true)); + if (resent_from.IsEmpty()) + resent_from.Adopt(MimeHeaders_get(headers, HEADER_RESENT_SENDER, false, + true)); + + UnquoteMimeAddress(resent_from, mailcharset); + UnquoteMimeAddress(resent_to, mailcharset); + UnquoteMimeAddress(resent_cc, mailcharset); + UnquoteMimeAddress(reply_to, mailcharset); + UnquoteMimeAddress(from, mailcharset); + UnquoteMimeAddress(to, mailcharset); + UnquoteMimeAddress(cc, mailcharset); + + nsCString replyHeader; + MimeGetForwardHeaderDelimiter(replyHeader); + if (htmlEdit) + { + NS_MsgSACopy(&(newBody), MIME_FORWARD_HTML_PREFIX); + NS_MsgSACat(&newBody, replyHeader.get()); + NS_MsgSACat(&newBody, MIME_HEADER_TABLE); + } + else + { + NS_MsgSACopy(&(newBody), MSG_LINEBREAK MSG_LINEBREAK); + NS_MsgSACat(&newBody, replyHeader.get()); + } + if (subject) + mime_intl_insert_message_header_1(&newBody, subject, HEADER_SUBJECT, + MimeGetNamedString(MIME_MHTML_SUBJECT), + mailcharset, htmlEdit); + if (resent_comments) + mime_intl_insert_message_header_1(&newBody, resent_comments, + HEADER_RESENT_COMMENTS, + MimeGetNamedString(MIME_MHTML_RESENT_COMMENTS), + mailcharset, htmlEdit); + if (resent_date) + mime_intl_insert_message_header_1(&newBody, resent_date, + HEADER_RESENT_DATE, + MimeGetNamedString(MIME_MHTML_RESENT_DATE), + mailcharset, htmlEdit); + if (!resent_from.IsEmpty()) + { + mime_intl_insert_message_header_1(&newBody, resent_from.get(), + HEADER_RESENT_FROM, + MimeGetNamedString(MIME_MHTML_RESENT_FROM), + mailcharset, htmlEdit); + } + if (!resent_to.IsEmpty()) + { + mime_intl_insert_message_header_1(&newBody, resent_to.get(), + HEADER_RESENT_TO, + MimeGetNamedString(MIME_MHTML_RESENT_TO), + mailcharset, htmlEdit); + } + if (!resent_cc.IsEmpty()) + { + mime_intl_insert_message_header_1(&newBody, resent_cc.get(), + HEADER_RESENT_CC, + MimeGetNamedString(MIME_MHTML_RESENT_CC), + mailcharset, htmlEdit); + } + if (date) + mime_intl_insert_message_header_1(&newBody, date, HEADER_DATE, + MimeGetNamedString(MIME_MHTML_DATE), + mailcharset, htmlEdit); + if (!from.IsEmpty()) + { + mime_intl_insert_message_header_1(&newBody, from.get(), HEADER_FROM, + MimeGetNamedString(MIME_MHTML_FROM), + mailcharset, htmlEdit); + } + if (!reply_to.IsEmpty()) + { + mime_intl_insert_message_header_1(&newBody, reply_to.get(), HEADER_REPLY_TO, + MimeGetNamedString(MIME_MHTML_REPLY_TO), + mailcharset, htmlEdit); + } + if (organization) + mime_intl_insert_message_header_1(&newBody, organization, + HEADER_ORGANIZATION, + MimeGetNamedString(MIME_MHTML_ORGANIZATION), + mailcharset, htmlEdit); + if (!to.IsEmpty()) + { + mime_intl_insert_message_header_1(&newBody, to.get(), HEADER_TO, + MimeGetNamedString(MIME_MHTML_TO), + mailcharset, htmlEdit); + } + if (!cc.IsEmpty()) + { + mime_intl_insert_message_header_1(&newBody, cc.get(), HEADER_CC, + MimeGetNamedString(MIME_MHTML_CC), + mailcharset, htmlEdit); + } + /* + Do not reveal bcc recipients when forwarding a message! + See http://bugzilla.mozilla.org/show_bug.cgi?id=41150 + */ + if (newsgroups) + mime_intl_insert_message_header_1(&newBody, newsgroups, HEADER_NEWSGROUPS, + MimeGetNamedString(MIME_MHTML_NEWSGROUPS), + mailcharset, htmlEdit); + if (followup_to) + { + mime_intl_insert_message_header_1(&newBody, followup_to, + HEADER_FOLLOWUP_TO, + MimeGetNamedString(MIME_MHTML_FOLLOWUP_TO), + mailcharset, htmlEdit); + } + // only show references for newsgroups + if (newsgroups && references) + { + mime_intl_insert_message_header_1(&newBody, references, + HEADER_REFERENCES, + MimeGetNamedString(MIME_MHTML_REFERENCES), + mailcharset, htmlEdit); + } + if (htmlEdit) + { + NS_MsgSACat(&newBody, "</TABLE>"); + NS_MsgSACat(&newBody, MSG_LINEBREAK "<BR><BR>"); + if (html_tag) + NS_MsgSACat(&newBody, html_tag); + else if (*body) + NS_MsgSACat(&newBody, *body); + } + else + { + NS_MsgSACat(&newBody, MSG_LINEBREAK MSG_LINEBREAK); + if (*body) + NS_MsgSACat(&newBody, *body); + } + if (newBody) + { + PR_FREEIF(*body); + *body = newBody; + } + PR_FREEIF(subject); + PR_FREEIF(resent_comments); + PR_FREEIF(resent_date); + PR_FREEIF(date); + PR_FREEIF(organization); + PR_FREEIF(newsgroups); + PR_FREEIF(followup_to); + PR_FREEIF(references); +} + +static void +mime_insert_micro_headers(char **body, + MimeHeaders *headers, + MSG_ComposeFormat composeFormat, + char *mailcharset) +{ + char *newBody = NULL; + char *subject = MimeHeaders_get(headers, HEADER_SUBJECT, false, false); + nsCString from(MimeHeaders_get(headers, HEADER_FROM, false, true)); + nsCString resent_from(MimeHeaders_get(headers, HEADER_RESENT_FROM, false, true)); + char *date = MimeHeaders_get(headers, HEADER_DATE, false, true); + nsCString to(MimeHeaders_get(headers, HEADER_TO, false, true)); + nsCString cc(MimeHeaders_get(headers, HEADER_CC, false, true)); + char *newsgroups = MimeHeaders_get(headers, HEADER_NEWSGROUPS, false, + true); + const char *html_tag = nullptr; + if (*body && PL_strncasecmp(*body, "<HTML", 5) == 0) + html_tag = PL_strchr(*body, '>') + 1; + bool htmlEdit = composeFormat == nsIMsgCompFormat::HTML; + + if (from.IsEmpty()) + from.Adopt(MimeHeaders_get(headers, HEADER_SENDER, false, true)); + if (resent_from.IsEmpty()) + resent_from.Adopt(MimeHeaders_get(headers, HEADER_RESENT_SENDER, false, true)); + if (!date) + date = MimeHeaders_get(headers, HEADER_RESENT_DATE, false, true); + + UnquoteMimeAddress(resent_from, mailcharset); + UnquoteMimeAddress(from, mailcharset); + UnquoteMimeAddress(to, mailcharset); + UnquoteMimeAddress(cc, mailcharset); + + nsCString replyHeader; + MimeGetForwardHeaderDelimiter(replyHeader); + if (htmlEdit) + { + NS_MsgSACopy(&(newBody), MIME_FORWARD_HTML_PREFIX); + NS_MsgSACat(&newBody, replyHeader.get()); + NS_MsgSACat(&newBody, MIME_HEADER_TABLE); + } + else + { + NS_MsgSACopy(&(newBody), MSG_LINEBREAK MSG_LINEBREAK); + NS_MsgSACat(&newBody, replyHeader.get()); + } + + if (!from.IsEmpty()) + { + mime_intl_insert_message_header_1(&newBody, from.get(), HEADER_FROM, + MimeGetNamedString(MIME_MHTML_FROM), + mailcharset, htmlEdit); + } + if (subject) + mime_intl_insert_message_header_1(&newBody, subject, HEADER_SUBJECT, + MimeGetNamedString(MIME_MHTML_SUBJECT), + mailcharset, htmlEdit); +/* + if (date) + mime_intl_insert_message_header_1(&newBody, date, HEADER_DATE, + MimeGetNamedString(MIME_MHTML_DATE), + mailcharset, htmlEdit); +*/ + if (!resent_from.IsEmpty()) + { + mime_intl_insert_message_header_1(&newBody, resent_from.get(), + HEADER_RESENT_FROM, + MimeGetNamedString(MIME_MHTML_RESENT_FROM), + mailcharset, htmlEdit); + } + if (!to.IsEmpty()) + { + mime_intl_insert_message_header_1(&newBody, to.get(), HEADER_TO, + MimeGetNamedString(MIME_MHTML_TO), + mailcharset, htmlEdit); + } + if (!cc.IsEmpty()) + { + mime_intl_insert_message_header_1(&newBody, cc.get(), HEADER_CC, + MimeGetNamedString(MIME_MHTML_CC), + mailcharset, htmlEdit); + } + /* + Do not reveal bcc recipients when forwarding a message! + See http://bugzilla.mozilla.org/show_bug.cgi?id=41150 + */ + if (newsgroups) + mime_intl_insert_message_header_1(&newBody, newsgroups, HEADER_NEWSGROUPS, + MimeGetNamedString(MIME_MHTML_NEWSGROUPS), + mailcharset, htmlEdit); + if (htmlEdit) + { + NS_MsgSACat(&newBody, "</TABLE>"); + NS_MsgSACat(&newBody, MSG_LINEBREAK "<BR><BR>"); + if (html_tag) + NS_MsgSACat(&newBody, html_tag); + else if (*body) + NS_MsgSACat(&newBody, *body); + } + else + { + NS_MsgSACat(&newBody, MSG_LINEBREAK MSG_LINEBREAK); + if (*body) + NS_MsgSACat(&newBody, *body); + } + if (newBody) + { + PR_FREEIF(*body); + *body = newBody; + } + PR_FREEIF(subject); + PR_FREEIF(date); + PR_FREEIF(newsgroups); + +} + +// body has to be encoded in UTF-8 +static void +mime_insert_forwarded_message_headers(char **body, + MimeHeaders *headers, + MSG_ComposeFormat composeFormat, + char *mailcharset) +{ + if (!body || !headers) + return; + + int32_t show_headers = 0; + nsresult res; + + nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &res)); + if (NS_SUCCEEDED(res)) + prefBranch->GetIntPref("mail.show_headers", &show_headers); + + switch (show_headers) + { + case 0: + mime_insert_micro_headers(body, headers, composeFormat, mailcharset); + break; + default: + case 1: + mime_insert_normal_headers(body, headers, composeFormat, mailcharset); + break; + case 2: + mime_insert_all_headers(body, headers, composeFormat, mailcharset); + break; + } +} + +static void +mime_parse_stream_complete(nsMIMESession *stream) +{ + mime_draft_data *mdd = (mime_draft_data *)stream->data_object; + nsCOMPtr<nsIMsgCompFields> fields; + int htmlAction = 0; + int lineWidth = 0; + + char *host = 0; + char *news_host = 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; + + bool forward_inline = false; + bool bodyAsAttachment = false; + bool charsetOverride = false; + + NS_ASSERTION(mdd, "null mime draft data"); + + if (!mdd) return; + + 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; + if ((!mdd->mailcharset || charsetOverride) && mdd->options->default_charset) + { + PR_Free(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((nsMIMESession *)mdd->stream->data_object); + 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", + getter_Copies(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); + + host = MimeHeaders_get(mdd->headers, HEADER_X_MOZILLA_NEWSHOST, false, false); + if (!host) + host = MimeHeaders_get(mdd->headers, HEADER_NNTP_POSTING_HOST, false, false); + + 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); + + + if (host) + { + char *secure = NULL; + + secure = PL_strcasestr(host, "secure"); + if (secure) + { + *secure = 0; + news_host = PR_smprintf ("snews://%s", host); + } + else + { + news_host = PR_smprintf ("news://%s", host); + } + } + } + + + CreateCompositionFields(from, repl, to, cc, bcc, fcc, grps, foll, + org, subj, refs, priority, news_host, + 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); + + // Keep the same message id when editing a draft unless we're + // editing a message "as new message" (template) or forwarding inline. + if (mdd->format_out != nsMimeOutput::nsMimeMessageEditorTemplate && + fields && !forward_inline) { + 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::AskUser; + 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(NS_MSGACCOUNTMANAGER_CONTRACTID, &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.Find("text/html", CaseInsensitiveCompare) != -1) + composeFormat = nsIMsgCompFormat::HTML; + else if (mdd->messageBody->m_type.Find("text/plain", CaseInsensitiveCompare) != -1 || + 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; + uint32_t bodyLen = 0; + + if (!bodyAsAttachment && mdd->messageBody->m_tmpFile) + { + int64_t fileSize; + nsCOMPtr<nsIFile> tempFileCopy; + mdd->messageBody->m_tmpFile->Clone(getter_AddRefs(tempFileCopy)); + mdd->messageBody->m_tmpFile = do_QueryInterface(tempFileCopy); + tempFileCopy = nullptr; + mdd->messageBody->m_tmpFile->GetFileSize(&fileSize); + + // 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)) + 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. + char *bodyCharset = mimeCharset ? mimeCharset : mdd->mailcharset; + if (bodyCharset) + { + nsAutoString tmpUnicodeBody; + rv = ConvertToUnicode(bodyCharset, 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; + } + } + PR_FREEIF(mimeCharset); + } + } + + 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. + + //We need to convert the plain/text to HTML in order to escape any HTML markup + char *escapedBody = MsgEscapeHTML(body); + if (escapedBody) + { + PR_Free(body); + body = escapedBody; + bodyLen = strlen(body); + } + + //+13 chars for <pre> & </pre> tags and CRLF + uint32_t newbodylen = bodyLen + 14; + char* newbody = (char *)PR_MALLOC (newbodylen); + if (newbody) + { + *newbody = 0; + PL_strcatn(newbody, newbodylen, "<PRE>"); + PL_strcatn(newbody, newbodylen, body); + PL_strcatn(newbody, newbodylen, "</PRE>" CRLF); + PR_Free(body); + body = newbody; + } + } + // 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); + + } + + // 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) + { + MSG_ComposeType msgComposeType = PL_strstr(mdd->url_name, + "&redirect=true") ? + nsIMsgCompType::Redirect : + nsIMsgCompType::Template; + 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 + { + fields->SetDraftId(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, nullptr, 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(mdd->url_name); + CreateTheComposeWindow(fields, newAttachData, nsIMsgCompType::Draft, nsIMsgCompFormat::Default, mdd->identity, nullptr, mdd->origMsgHdr); + } + } + } + } + else + { + CreateCompositionFields(from, repl, to, cc, bcc, fcc, grps, foll, + org, subj, refs, priority, news_host, + mdd->mailcharset, + getter_AddRefs(fields)); + if (fields) + CreateTheComposeWindow(fields, newAttachData, nsIMsgCompType::New, nsIMsgCompFormat::Default, mdd->identity, nullptr, 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); + PR_Free(mdd->originalMsgURI); + mdd->origMsgHdr = nullptr; + PR_Free(mdd); + + PR_FREEIF(host); + 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; +} + +static void +mime_parse_stream_abort(nsMIMESession *stream, int status) +{ + mime_draft_data *mdd = (mime_draft_data *)stream->data_object; + NS_ASSERTION(mdd, "null mime draft data"); + + if (!mdd) + return; + + if (mdd->obj) + { + int status=0; + + if (!mdd->obj->closed_p) + status = mdd->obj->clazz->parse_eof(mdd->obj, true); + if (!mdd->obj->parsed_p) + mdd->obj->clazz->parse_end(mdd->obj, true); + + NS_ASSERTION(mdd->options == mdd->obj->options, "draft display options not same as mime obj"); + mime_free (mdd->obj); + mdd->obj = 0; + if (mdd->options) + { + delete mdd->options; + mdd->options = 0; + } + + if (mdd->stream) + { + mdd->stream->abort((nsMIMESession *)mdd->stream->data_object, status); + PR_Free(mdd->stream); + mdd->stream = 0; + } + } + + if (mdd->headers) + MimeHeaders_free(mdd->headers); + + + mime_free_attachments(mdd->attachments); + + PR_FREEIF(mdd->mailcharset); + + PR_Free(mdd); +} + +static int +make_mime_headers_copy(void *closure, MimeHeaders *headers) +{ + mime_draft_data *mdd = (mime_draft_data *)closure; + + NS_ASSERTION(mdd && headers, "null mime draft data and/or headers"); + + if (!mdd || ! headers) + return 0; + + NS_ASSERTION(mdd->headers == NULL , "non null mime draft data headers"); + + mdd->headers = MimeHeaders_copy(headers); + mdd->options->done_parsing_outer_headers = true; + + return 0; +} + +int +mime_decompose_file_init_fn(void *stream_closure, MimeHeaders *headers) +{ + mime_draft_data *mdd = (mime_draft_data *)stream_closure; + nsMsgAttachedFile *newAttachment = 0; + int nAttachments = 0; + //char *hdr_value = NULL; + char *parm_value = NULL; + bool creatingMsgBody = true; + + NS_ASSERTION(mdd && headers, "null mime draft data and/or headers"); + if (!mdd || !headers) + return -1; + + if (mdd->options->decompose_init_count) + { + mdd->options->decompose_init_count++; + NS_ASSERTION(mdd->curAttachment, "missing attachment in mime_decompose_file_init_fn"); + if (mdd->curAttachment) + mdd->curAttachment->m_type.Adopt(MimeHeaders_get(headers, + HEADER_CONTENT_TYPE, + false, true)); + return 0; + } + else + mdd->options->decompose_init_count++; + + nAttachments = mdd->attachments.Length(); + + if (!nAttachments && !mdd->messageBody) + { + // if we've been told to use an override charset then do so....otherwise use the charset + // inside the message header... + if (mdd->options && mdd->options->override_charset) + mdd->mailcharset = strdup(mdd->options->default_charset); + else + { + char *contentType; + contentType = MimeHeaders_get(headers, HEADER_CONTENT_TYPE, false, false); + if (contentType) + { + mdd->mailcharset = MimeHeaders_get_parameter(contentType, "charset", NULL, NULL); + PR_FREEIF(contentType); + } + } + + mdd->messageBody = new nsMsgAttachedFile; + if (!mdd->messageBody) + return MIME_OUT_OF_MEMORY; + newAttachment = mdd->messageBody; + creatingMsgBody = true; + } + else + { + /* always allocate one more extra; don't ask me why */ + newAttachment = new nsMsgAttachedFile; + if (!newAttachment) + return MIME_OUT_OF_MEMORY; + mdd->attachments.AppendElement(newAttachment); + } + + char *workURLSpec = nullptr; + char *contLoc = nullptr; + + newAttachment->m_realName.Adopt(MimeHeaders_get_name(headers, mdd->options)); + contLoc = MimeHeaders_get(headers, HEADER_CONTENT_LOCATION, false, false); + if (!contLoc) + contLoc = MimeHeaders_get(headers, HEADER_CONTENT_BASE, false, false); + + if (!contLoc && !newAttachment->m_realName.IsEmpty()) + workURLSpec = ToNewCString(newAttachment->m_realName); + if (contLoc && !workURLSpec) + workURLSpec = strdup(contLoc); + + PR_FREEIF(contLoc); + + mdd->curAttachment = newAttachment; + newAttachment->m_type.Adopt(MimeHeaders_get(headers, HEADER_CONTENT_TYPE, false, false)); + + // + // This is to handle the degenerated Apple Double attachment. + // + parm_value = MimeHeaders_get(headers, HEADER_CONTENT_TYPE, false, false); + if (parm_value) + { + char *boundary = NULL; + char *tmp_value = NULL; + boundary = MimeHeaders_get_parameter(parm_value, "boundary", NULL, NULL); + if (boundary) + tmp_value = PR_smprintf("; boundary=\"%s\"", boundary); + if (tmp_value) + newAttachment->m_type = tmp_value; + newAttachment->m_xMacType.Adopt( + MimeHeaders_get_parameter(parm_value, "x-mac-type", NULL, NULL)); + newAttachment->m_xMacCreator.Adopt( + MimeHeaders_get_parameter(parm_value, "x-mac-creator", NULL, NULL)); + PR_FREEIF(parm_value); + PR_FREEIF(boundary); + PR_FREEIF(tmp_value); + } + + newAttachment->m_size = 0; + newAttachment->m_encoding.Adopt(MimeHeaders_get(headers, HEADER_CONTENT_TRANSFER_ENCODING, + false, false)); + newAttachment->m_description.Adopt(MimeHeaders_get(headers, HEADER_CONTENT_DESCRIPTION, + false, false)); + // + // If we came up empty for description or the orig URL, we should do something about it. + // + if (newAttachment->m_description.IsEmpty() && workURLSpec) + newAttachment->m_description = workURLSpec; + + PR_FREEIF(workURLSpec); // resource leak otherwise + + newAttachment->m_cloudPartInfo.Adopt(MimeHeaders_get(headers, + HEADER_X_MOZILLA_CLOUD_PART, + false, false)); + + // There's no file in the message if it's a cloud part. + if (!newAttachment->m_cloudPartInfo.IsEmpty()) + { + nsAutoCString fileURL; + fileURL.Adopt( + MimeHeaders_get_parameter(newAttachment->m_cloudPartInfo.get(), "file", + nullptr, nullptr)); + if (!fileURL.IsEmpty()) + nsMimeNewURI(getter_AddRefs(newAttachment->m_origUrl), fileURL.get(), + nullptr); + mdd->tmpFile = nullptr; + return 0; + } + + nsCOMPtr<nsIFile> tmpFile = nullptr; + { + // Let's build a temp file with an extension based on the content-type: nsmail.<extension> + + nsAutoCString newAttachName("nsmail"); + bool extensionAdded = false; + // the content type may contain a charset. i.e. text/html; ISO-2022-JP...we want to strip off the charset + // before we ask the mime service for a mime info for this content type. + nsAutoCString contentType(newAttachment->m_type); + int32_t pos = contentType.FindChar(';'); + if (pos > 0) + contentType.SetLength(pos); + nsresult rv = NS_OK; + nsCOMPtr<nsIMIMEService> mimeFinder(do_GetService(NS_MIMESERVICE_CONTRACTID, &rv)); + if (NS_SUCCEEDED(rv) && mimeFinder) + { + nsAutoCString fileExtension; + rv = mimeFinder->GetPrimaryExtension(contentType, EmptyCString(), fileExtension); + + if (NS_SUCCEEDED(rv) && !fileExtension.IsEmpty()) + { + newAttachName.Append("."); + newAttachName.Append(fileExtension); + extensionAdded = true; + } + } + + if (!extensionAdded) + { + newAttachName.Append(".tmp"); + } + + nsMsgCreateTempFile(newAttachName.get(), getter_AddRefs(tmpFile)); + } + nsresult rv; + + // This needs to be done so the attachment structure has a handle + // on the temp file for this attachment... + if (tmpFile) + { + nsAutoCString fileURL; + rv = NS_GetURLSpecFromFile(tmpFile, fileURL); + if (NS_SUCCEEDED(rv)) + nsMimeNewURI(getter_AddRefs(newAttachment->m_origUrl), + fileURL.get(), nullptr); + } + + if (!tmpFile) + return MIME_OUT_OF_MEMORY; + + mdd->tmpFile = do_QueryInterface(tmpFile); + + newAttachment->m_tmpFile = mdd->tmpFile; + + rv = MsgNewBufferedFileOutputStream(getter_AddRefs(mdd->tmpFileStream), tmpFile,PR_WRONLY | PR_CREATE_FILE, 00600); + if (NS_FAILED(rv)) + return MIME_UNABLE_TO_OPEN_TMP_FILE; + + // For now, we are always going to decode all of the attachments + // for the message. This way, we have native data + if (creatingMsgBody) + { + MimeDecoderData *(*fn) (MimeConverterOutputCallback, void*) = 0; + + // + // Initialize a decoder if necessary. + // + if (newAttachment->m_encoding.LowerCaseEqualsLiteral(ENCODING_BASE64)) + fn = &MimeB64DecoderInit; + else if (newAttachment->m_encoding.LowerCaseEqualsLiteral(ENCODING_QUOTED_PRINTABLE)) + { + mdd->decoder_data = MimeQPDecoderInit (/* The (MimeConverterOutputCallback) cast is to turn the `void' argument into `MimeObject'. */ + ((MimeConverterOutputCallback)dummy_file_write), + mdd->tmpFileStream); + if (!mdd->decoder_data) + return MIME_OUT_OF_MEMORY; + } + else if (newAttachment->m_encoding.LowerCaseEqualsLiteral(ENCODING_UUENCODE) || + newAttachment->m_encoding.LowerCaseEqualsLiteral(ENCODING_UUENCODE2) || + newAttachment->m_encoding.LowerCaseEqualsLiteral(ENCODING_UUENCODE3) || + newAttachment->m_encoding.LowerCaseEqualsLiteral(ENCODING_UUENCODE4)) + fn = &MimeUUDecoderInit; + else if (newAttachment->m_encoding.LowerCaseEqualsLiteral(ENCODING_YENCODE)) + fn = &MimeYDecoderInit; + + if (fn) + { + mdd->decoder_data = fn (/* The (MimeConverterOutputCallback) cast is to + turn the `void' argument into `MimeObject'. */ + ((MimeConverterOutputCallback) dummy_file_write), + mdd->tmpFileStream); + if (!mdd->decoder_data) + return MIME_OUT_OF_MEMORY; + } + } + + return 0; +} + +int +mime_decompose_file_output_fn(const char *buf, + int32_t size, + void *stream_closure) +{ + mime_draft_data *mdd = (mime_draft_data *)stream_closure; + int ret = 0; + + NS_ASSERTION(mdd && buf, "missing mime draft data and/or buf"); + if (!mdd || !buf) return -1; + if (!size) return 0; + + if (!mdd->tmpFileStream) + return 0; + + if (mdd->decoder_data) { + int32_t outsize; + ret = MimeDecoderWrite(mdd->decoder_data, buf, size, &outsize); + if (ret == -1) return -1; + mdd->curAttachment->m_size += outsize; + } + else + { + uint32_t bytesWritten; + mdd->tmpFileStream->Write(buf, size, &bytesWritten); + if ((int32_t)bytesWritten < size) + return MIME_ERROR_WRITING_FILE; + mdd->curAttachment->m_size += size; + } + + return 0; +} + +int +mime_decompose_file_close_fn(void *stream_closure) +{ + mime_draft_data *mdd = (mime_draft_data *)stream_closure; + + if (!mdd) + return -1; + + if (--mdd->options->decompose_init_count > 0) + return 0; + + if (mdd->decoder_data) { + MimeDecoderDestroy(mdd->decoder_data, false); + mdd->decoder_data = 0; + } + + if (!mdd->tmpFileStream) { + // it's ok to have a null tmpFileStream if there's no tmpFile. + // This happens for cloud file attachments. + NS_ASSERTION(!mdd->tmpFile, "shouldn't have a tmp file bu no stream"); + return 0; + } + mdd->tmpFileStream->Close(); + + mdd->tmpFileStream = nullptr; + + mdd->tmpFile = nullptr; + + return 0; +} + +extern "C" void * +mime_bridge_create_draft_stream( + nsIMimeEmitter *newEmitter, + nsStreamConverter *newPluginObj2, + nsIURI *uri, + nsMimeOutputType format_out) +{ + int status = 0; + nsMIMESession *stream = nullptr; + mime_draft_data *mdd = nullptr; + MimeObject *obj = nullptr; + + if (!uri) + return nullptr; + + mdd = new mime_draft_data; + if (!mdd) + return nullptr; + + nsAutoCString turl; + nsCOMPtr<nsIMsgMessageService> msgService; + nsCOMPtr<nsIURI> aURL; + nsAutoCString urlString; + nsresult rv; + + // first, convert the rdf msg uri into a url that represents the message... + if (NS_FAILED(uri->GetSpec(turl))) + goto FAIL; + + rv = GetMessageServiceFromURI(turl, getter_AddRefs(msgService)); + if (NS_FAILED(rv)) + goto FAIL; + + rv = msgService->GetUrlForUri(turl.get(), getter_AddRefs(aURL), nullptr); + if (NS_FAILED(rv)) + goto FAIL; + + if (NS_SUCCEEDED(aURL->GetSpec(urlString))) + { + int32_t typeIndex = urlString.Find("&type=application/x-message-display"); + if (typeIndex != -1) + urlString.Cut(typeIndex, sizeof("&type=application/x-message-display") - 1); + + mdd->url_name = ToNewCString(urlString); + if (!(mdd->url_name)) + goto FAIL; + } + + newPluginObj2->GetForwardInline(&mdd->forwardInline); + newPluginObj2->GetForwardInlineFilter(&mdd->forwardInlineFilter); + newPluginObj2->GetForwardToAddress(mdd->forwardToAddress); + newPluginObj2->GetOverrideComposeFormat(&mdd->overrideComposeFormat); + newPluginObj2->GetIdentity(getter_AddRefs(mdd->identity)); + newPluginObj2->GetOriginalMsgURI(&mdd->originalMsgURI); + newPluginObj2->GetOrigMsgHdr(getter_AddRefs(mdd->origMsgHdr)); + mdd->format_out = format_out; + mdd->options = new MimeDisplayOptions ; + if (!mdd->options) + goto FAIL; + + mdd->options->url = strdup(mdd->url_name); + mdd->options->format_out = format_out; // output format + mdd->options->decompose_file_p = true; /* new field in MimeDisplayOptions */ + mdd->options->stream_closure = mdd; + mdd->options->html_closure = mdd; + mdd->options->decompose_headers_info_fn = make_mime_headers_copy; + mdd->options->decompose_file_init_fn = mime_decompose_file_init_fn; + mdd->options->decompose_file_output_fn = mime_decompose_file_output_fn; + mdd->options->decompose_file_close_fn = mime_decompose_file_close_fn; + + mdd->options->m_prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); + if (NS_FAILED(rv)) + goto FAIL; + +#ifdef ENABLE_SMIME + /* If we're attaching a message (for forwarding) then we must eradicate all + traces of xlateion from it, since forwarding someone else a message + that wasn't xlated for them doesn't work. We have to dexlate it + before sending it. + */ + mdd->options->decrypt_p = true; +#endif /* ENABLE_SMIME */ + + obj = mime_new((MimeObjectClass *)&mimeMessageClass, (MimeHeaders *)NULL, MESSAGE_RFC822); + if (!obj) + goto FAIL; + + obj->options = mdd->options; + mdd->obj = obj; + + stream = PR_NEWZAP(nsMIMESession); + if (!stream) + goto FAIL; + + stream->name = "MIME To Draft Converter Stream"; + stream->complete = mime_parse_stream_complete; + stream->abort = mime_parse_stream_abort; + stream->put_block = mime_parse_stream_write; + stream->data_object = mdd; + + status = obj->clazz->initialize(obj); + if (status >= 0) + status = obj->clazz->parse_begin(obj); + if (status < 0) + goto FAIL; + + return stream; + +FAIL: + if (mdd) + { + PR_Free(mdd->url_name); + PR_Free(mdd->originalMsgURI); + if (mdd->options) + delete mdd->options; + PR_Free(mdd); + } + PR_Free(stream); + PR_Free(obj); + + return nullptr; +} |