summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/extensions/mdn/nsMsgMdnGenerator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mailnews/extensions/mdn/nsMsgMdnGenerator.cpp')
-rw-r--r--comm/mailnews/extensions/mdn/nsMsgMdnGenerator.cpp1040
1 files changed, 1040 insertions, 0 deletions
diff --git a/comm/mailnews/extensions/mdn/nsMsgMdnGenerator.cpp b/comm/mailnews/extensions/mdn/nsMsgMdnGenerator.cpp
new file mode 100644
index 0000000000..76fc6d5214
--- /dev/null
+++ b/comm/mailnews/extensions/mdn/nsMsgMdnGenerator.cpp
@@ -0,0 +1,1040 @@
+/* -*- 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 "nsMsgMdnGenerator.h"
+#include "nsImapCore.h"
+#include "nsIMsgImapMailFolder.h"
+#include "nsIMsgAccountManager.h"
+#include "nsMimeTypes.h"
+#include "prprf.h"
+#include "prmem.h"
+#include "prsystem.h"
+#include "nsMsgI18N.h"
+#include "nsMailHeaders.h"
+#include "nsMsgLocalFolderHdrs.h"
+#include "nsIHttpProtocolHandler.h"
+#include "nsISmtpService.h" // for actually sending the message...
+#include "nsComposeStrings.h"
+#include "nsISmtpServer.h"
+#include "nsIPrompt.h"
+#include "nsIMsgCompUtils.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "nsIStringBundle.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsMsgUtils.h"
+#include "nsNetUtil.h"
+#include "nsIMsgDatabase.h"
+#include "mozilla/Components.h"
+#include "mozilla/mailnews/MimeHeaderParser.h"
+#include "mozilla/Unused.h"
+#include "nsIPromptService.h"
+#include "nsEmbedCID.h"
+
+using namespace mozilla::mailnews;
+
+#define MDN_NOT_IN_TO_CC ((int)0x0001)
+#define MDN_OUTSIDE_DOMAIN ((int)0x0002)
+
+#define HEADER_RETURN_PATH "Return-Path"
+#define HEADER_DISPOSITION_NOTIFICATION_TO "Disposition-Notification-To"
+#define HEADER_APPARENTLY_TO "Apparently-To"
+#define HEADER_ORIGINAL_RECIPIENT "Original-Recipient"
+#define HEADER_REPORTING_UA "Reporting-UA"
+#define HEADER_MDN_GATEWAY "MDN-Gateway"
+#define HEADER_FINAL_RECIPIENT "Final-Recipient"
+#define HEADER_DISPOSITION "Disposition"
+#define HEADER_ORIGINAL_MESSAGE_ID "Original-Message-ID"
+#define HEADER_FAILURE "Failure"
+#define HEADER_ERROR "Error"
+#define HEADER_WARNING "Warning"
+#define HEADER_RETURN_RECEIPT_TO "Return-Receipt-To"
+#define HEADER_X_ACCEPT_LANGUAGE "X-Accept-Language"
+
+#define PUSH_N_FREE_STRING(p) \
+ do { \
+ if (p) { \
+ rv = WriteString(p); \
+ PR_smprintf_free(p); \
+ p = 0; \
+ if (NS_FAILED(rv)) return rv; \
+ } else { \
+ return NS_ERROR_OUT_OF_MEMORY; \
+ } \
+ } while (0)
+
+// String bundle for mdn. Class static.
+#define MDN_STRINGBUNDLE_URL "chrome://messenger/locale/msgmdn.properties"
+
+#if defined(DEBUG_jefft)
+# define DEBUG_MDN(s) printf("%s\n", s)
+#else
+# define DEBUG_MDN(s)
+#endif
+
+// machine parsible string; should not be localized
+char DispositionTypes[7][16] = {
+ "displayed", "dispatched", "processed", "deleted", "denied", "failed", ""};
+
+NS_IMPL_ISUPPORTS(nsMsgMdnGenerator, nsIMsgMdnGenerator, nsIUrlListener)
+
+nsMsgMdnGenerator::nsMsgMdnGenerator()
+ : m_disposeType(eDisplayed),
+ m_key(nsMsgKey_None),
+ m_notInToCcOp(eNeverSendOp),
+ m_outsideDomainOp(eNeverSendOp),
+ m_otherOp(eNeverSendOp),
+ m_reallySendMdn(false),
+ m_autoSend(false),
+ m_autoAction(false),
+ m_mdnEnabled(false) {}
+
+nsMsgMdnGenerator::~nsMsgMdnGenerator() {}
+
+nsresult nsMsgMdnGenerator::FormatStringFromName(const char* aName,
+ const nsString& aString,
+ nsAString& aResultString) {
+ DEBUG_MDN("nsMsgMdnGenerator::FormatStringFromName");
+
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::components::StringBundle::Service();
+ NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED);
+
+ nsCOMPtr<nsIStringBundle> bundle;
+ nsresult rv =
+ bundleService->CreateBundle(MDN_STRINGBUNDLE_URL, getter_AddRefs(bundle));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ AutoTArray<nsString, 1> formatStrings = {aString};
+ rv = bundle->FormatStringFromName(aName, formatStrings, aResultString);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return rv;
+}
+
+nsresult nsMsgMdnGenerator::GetStringFromName(const char* aName,
+ nsAString& aResultString) {
+ DEBUG_MDN("nsMsgMdnGenerator::GetStringFromName");
+
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::components::StringBundle::Service();
+ NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED);
+
+ nsCOMPtr<nsIStringBundle> bundle;
+ nsresult rv =
+ bundleService->CreateBundle(MDN_STRINGBUNDLE_URL, getter_AddRefs(bundle));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = bundle->GetStringFromName(aName, aResultString);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return rv;
+}
+
+nsresult nsMsgMdnGenerator::StoreMDNSentFlag(nsIMsgFolder* folder,
+ nsMsgKey key) {
+ DEBUG_MDN("nsMsgMdnGenerator::StoreMDNSentFlag");
+
+ nsCOMPtr<nsIMsgDatabase> msgDB;
+ nsresult rv = folder->GetMsgDatabase(getter_AddRefs(msgDB));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = msgDB->MarkMDNSent(key, true, nullptr);
+
+ nsCOMPtr<nsIMsgImapMailFolder> imapFolder = do_QueryInterface(folder);
+ // Store the $MDNSent flag if the folder is an Imap Mail Folder
+ if (imapFolder)
+ return imapFolder->StoreImapFlags(kImapMsgMDNSentFlag, true, {key},
+ nullptr);
+ return rv;
+}
+
+nsresult nsMsgMdnGenerator::ClearMDNNeededFlag(nsIMsgFolder* folder,
+ nsMsgKey key) {
+ DEBUG_MDN("nsMsgMdnGenerator::ClearMDNNeededFlag");
+
+ nsCOMPtr<nsIMsgDatabase> msgDB;
+ nsresult rv = folder->GetMsgDatabase(getter_AddRefs(msgDB));
+ NS_ENSURE_SUCCESS(rv, rv);
+ return msgDB->MarkMDNNeeded(key, false, nullptr);
+}
+
+bool nsMsgMdnGenerator::ProcessSendMode() {
+ DEBUG_MDN("nsMsgMdnGenerator::ProcessSendMode");
+ int32_t miscState = 0;
+
+ if (m_identity) {
+ m_identity->GetEmail(m_email);
+ if (m_email.IsEmpty()) return m_reallySendMdn;
+
+ const char* accountDomain = strchr(m_email.get(), '@');
+ if (!accountDomain) return m_reallySendMdn;
+
+ if (MailAddrMatch(m_email.get(),
+ m_dntRrt.get())) // return address is self, don't send
+ return false;
+
+ // *** fix me see Bug 132504 for more information
+ // *** what if the message has been filtered to different account
+ if (!PL_strcasestr(m_dntRrt.get(), accountDomain))
+ miscState |= MDN_OUTSIDE_DOMAIN;
+ if (NotInToOrCc()) miscState |= MDN_NOT_IN_TO_CC;
+ m_reallySendMdn = true;
+ // *********
+ // How are we gona deal with the auto forwarding issues? Some server
+ // didn't bother to add addition header or modify existing header to
+ // the message when forwarding. They simply copy the exact same
+ // message to another user's mailbox. Some change To: to
+ // Apparently-To:
+ // Unfortunately, there is nothing we can do. It's out of our control.
+ // *********
+ // starting from lowest denominator to highest
+ if (!miscState) { // under normal situation: recipent is in to and cc list,
+ // and the sender is from the same domain
+ switch (m_otherOp) {
+ default:
+ case eNeverSendOp:
+ m_reallySendMdn = false;
+ break;
+ case eAutoSendOp:
+ m_autoSend = true;
+ break;
+ case eAskMeOp:
+ m_autoSend = false;
+ break;
+ case eDeniedOp:
+ m_autoSend = true;
+ m_disposeType = eDenied;
+ break;
+ }
+ } else if (miscState == (MDN_OUTSIDE_DOMAIN | MDN_NOT_IN_TO_CC)) {
+ if (m_outsideDomainOp != m_notInToCcOp) {
+ m_autoSend = false; // ambiguous; always ask user
+ } else {
+ switch (m_outsideDomainOp) {
+ default:
+ case eNeverSendOp:
+ m_reallySendMdn = false;
+ break;
+ case eAutoSendOp:
+ m_autoSend = true;
+ break;
+ case eAskMeOp:
+ m_autoSend = false;
+ break;
+ }
+ }
+ } else if (miscState & MDN_OUTSIDE_DOMAIN) {
+ switch (m_outsideDomainOp) {
+ default:
+ case eNeverSendOp:
+ m_reallySendMdn = false;
+ break;
+ case eAutoSendOp:
+ m_autoSend = true;
+ break;
+ case eAskMeOp:
+ m_autoSend = false;
+ break;
+ }
+ } else if (miscState & MDN_NOT_IN_TO_CC) {
+ switch (m_notInToCcOp) {
+ default:
+ case eNeverSendOp:
+ m_reallySendMdn = false;
+ break;
+ case eAutoSendOp:
+ m_autoSend = true;
+ break;
+ case eAskMeOp:
+ m_autoSend = false;
+ break;
+ }
+ }
+ }
+ return m_reallySendMdn;
+}
+
+bool nsMsgMdnGenerator::MailAddrMatch(const char* addr1, const char* addr2) {
+ // Comparing two email addresses returns true if matched; local/account
+ // part comparison is case sensitive; domain part comparison is case
+ // insensitive
+ DEBUG_MDN("nsMsgMdnGenerator::MailAddrMatch");
+ bool isMatched = true;
+ const char *atSign1 = nullptr, *atSign2 = nullptr;
+ const char *lt = nullptr, *local1 = nullptr, *local2 = nullptr;
+ const char *end1 = nullptr, *end2 = nullptr;
+
+ if (!addr1 || !addr2) return false;
+
+ lt = strchr(addr1, '<');
+ local1 = !lt ? addr1 : lt + 1;
+ lt = strchr(addr2, '<');
+ local2 = !lt ? addr2 : lt + 1;
+ end1 = strchr(local1, '>');
+ if (!end1) end1 = addr1 + strlen(addr1);
+ end2 = strchr(local2, '>');
+ if (!end2) end2 = addr2 + strlen(addr2);
+ atSign1 = strchr(local1, '@');
+ atSign2 = strchr(local2, '@');
+ if (!atSign1 || !atSign2 // ill formed addr spec
+ || (atSign1 - local1) != (atSign2 - local2))
+ isMatched = false;
+ else if (strncmp(local1, local2, (atSign1 - local1))) // case sensitive
+ // compare for local part
+ isMatched = false;
+ else if ((end1 - atSign1) != (end2 - atSign2) ||
+ PL_strncasecmp(atSign1, atSign2, (end1 - atSign1))) // case
+ // insensitive compare for domain part
+ isMatched = false;
+ return isMatched;
+}
+
+bool nsMsgMdnGenerator::NotInToOrCc() {
+ DEBUG_MDN("nsMsgMdnGenerator::NotInToOrCc");
+ nsCString reply_to;
+ nsCString to;
+ nsCString cc;
+
+ m_identity->GetReplyTo(reply_to);
+ m_headers->ExtractHeader(HEADER_TO, true, to);
+ m_headers->ExtractHeader(HEADER_CC, true, cc);
+
+ // start with a simple check
+ if ((!to.IsEmpty() && PL_strcasestr(to.get(), m_email.get())) ||
+ (!cc.IsEmpty() && PL_strcasestr(cc.get(), m_email.get()))) {
+ return false;
+ }
+
+ if ((!reply_to.IsEmpty() && !to.IsEmpty() &&
+ PL_strcasestr(to.get(), reply_to.get())) ||
+ (!reply_to.IsEmpty() && !cc.IsEmpty() &&
+ PL_strcasestr(cc.get(), reply_to.get()))) {
+ return false;
+ }
+ return true;
+}
+
+bool nsMsgMdnGenerator::ValidateReturnPath() {
+ DEBUG_MDN("nsMsgMdnGenerator::ValidateReturnPath");
+ // ValidateReturnPath applies to Automatic Send Mode only. If we were not
+ // in auto send mode we simply by passing the check
+ if (!m_autoSend) return m_reallySendMdn;
+
+ nsCString returnPath;
+ m_headers->ExtractHeader(HEADER_RETURN_PATH, false, returnPath);
+ if (returnPath.IsEmpty()) {
+ m_autoSend = false;
+ return m_reallySendMdn;
+ }
+ m_autoSend = MailAddrMatch(returnPath.get(), m_dntRrt.get());
+ return m_reallySendMdn;
+}
+
+nsresult nsMsgMdnGenerator::CreateMdnMsg() {
+ DEBUG_MDN("nsMsgMdnGenerator::CreateMdnMsg");
+ nsresult rv;
+
+ nsCOMPtr<nsIFile> tmpFile;
+ rv = GetSpecialDirectoryWithFileName(NS_OS_TEMP_DIR, "mdnmsg",
+ getter_AddRefs(m_file));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = m_file->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 00600);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = MsgNewBufferedFileOutputStream(getter_AddRefs(m_outputStream), m_file,
+ PR_CREATE_FILE | PR_WRONLY | PR_TRUNCATE,
+ 0664);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "creating mdn: failed to output stream");
+ if (NS_FAILED(rv)) return NS_OK;
+
+ rv = CreateFirstPart();
+ if (NS_SUCCEEDED(rv)) {
+ rv = CreateSecondPart();
+ if (NS_SUCCEEDED(rv)) rv = CreateThirdPart();
+ }
+
+ if (m_outputStream) {
+ m_outputStream->Flush();
+ m_outputStream->Close();
+ }
+ if (NS_FAILED(rv))
+ m_file->Remove(false);
+ else
+ rv = SendMdnMsg();
+
+ return NS_OK;
+}
+
+nsresult nsMsgMdnGenerator::CreateFirstPart() {
+ DEBUG_MDN("nsMsgMdnGenerator::CreateFirstPart");
+ char *convbuf = nullptr, *tmpBuffer = nullptr;
+ char* parm = nullptr;
+ nsString firstPart1;
+ nsString firstPart2;
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIMsgCompUtils> compUtils;
+
+ if (m_mimeSeparator.IsEmpty()) {
+ compUtils = do_GetService("@mozilla.org/messengercompose/computils;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = compUtils->MimeMakeSeparator("mdn", getter_Copies(m_mimeSeparator));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ if (m_mimeSeparator.IsEmpty()) return NS_ERROR_OUT_OF_MEMORY;
+
+ tmpBuffer = (char*)PR_CALLOC(256);
+
+ if (!tmpBuffer) return NS_ERROR_OUT_OF_MEMORY;
+
+ PRExplodedTime now;
+ PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &now);
+
+ int gmtoffset =
+ (now.tm_params.tp_gmt_offset + now.tm_params.tp_dst_offset) / 60;
+ /* Use PR_FormatTimeUSEnglish() to format the date in US English format,
+ then figure out what our local GMT offset is, and append it (since
+ PR_FormatTimeUSEnglish() can't do that.) Generate four digit years as
+ per RFC 1123 (superseding RFC 822.)
+ */
+ PR_FormatTimeUSEnglish(tmpBuffer, 100, "Date: %a, %d %b %Y %H:%M:%S ", &now);
+
+ PR_snprintf(tmpBuffer + strlen(tmpBuffer), 100, "%c%02d%02d" CRLF,
+ (gmtoffset >= 0 ? '+' : '-'),
+ ((gmtoffset >= 0 ? gmtoffset : -gmtoffset) / 60),
+ ((gmtoffset >= 0 ? gmtoffset : -gmtoffset) % 60));
+
+ rv = WriteString(tmpBuffer);
+ PR_Free(tmpBuffer);
+ if (NS_FAILED(rv)) return rv;
+
+ bool conformToStandard = false;
+ if (compUtils) compUtils->GetMsgMimeConformToStandard(&conformToStandard);
+
+ nsString fullName;
+ m_identity->GetFullName(fullName);
+
+ nsCString fullAddress;
+ // convert fullName to UTF8 before passing it to MakeMimeAddress
+ MakeMimeAddress(NS_ConvertUTF16toUTF8(fullName), m_email, fullAddress);
+
+ convbuf = nsMsgI18NEncodeMimePartIIStr(fullAddress.get(), true, "UTF-8", 0,
+ conformToStandard);
+
+ parm = PR_smprintf("From: %s" CRLF, convbuf ? convbuf : m_email.get());
+
+ rv = FormatStringFromName("MsgMdnMsgSentTo", NS_ConvertASCIItoUTF16(m_email),
+ firstPart1);
+ if (NS_FAILED(rv)) return rv;
+
+ PUSH_N_FREE_STRING(parm);
+
+ PR_Free(convbuf);
+
+ if (compUtils) {
+ nsCString msgId;
+ rv = compUtils->MsgGenerateMessageId(m_identity, ""_ns, msgId);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ tmpBuffer = PR_smprintf("Message-ID: %s" CRLF, msgId.get());
+ PUSH_N_FREE_STRING(tmpBuffer);
+ }
+
+ nsString receipt_string;
+ switch (m_disposeType) {
+ case nsIMsgMdnGenerator::eDisplayed:
+ rv = GetStringFromName("MdnDisplayedReceipt", receipt_string);
+ break;
+ case nsIMsgMdnGenerator::eDispatched:
+ rv = GetStringFromName("MdnDispatchedReceipt", receipt_string);
+ break;
+ case nsIMsgMdnGenerator::eProcessed:
+ rv = GetStringFromName("MdnProcessedReceipt", receipt_string);
+ break;
+ case nsIMsgMdnGenerator::eDeleted:
+ rv = GetStringFromName("MdnDeletedReceipt", receipt_string);
+ break;
+ case nsIMsgMdnGenerator::eDenied:
+ rv = GetStringFromName("MdnDeniedReceipt", receipt_string);
+ break;
+ case nsIMsgMdnGenerator::eFailed:
+ rv = GetStringFromName("MdnFailedReceipt", receipt_string);
+ break;
+ default:
+ rv = NS_ERROR_INVALID_ARG;
+ break;
+ }
+
+ if (NS_FAILED(rv)) return rv;
+
+ receipt_string.AppendLiteral(" - ");
+
+ char* encodedReceiptString =
+ nsMsgI18NEncodeMimePartIIStr(NS_ConvertUTF16toUTF8(receipt_string).get(),
+ false, "UTF-8", 0, conformToStandard);
+
+ nsCString subject;
+ m_headers->ExtractHeader(HEADER_SUBJECT, false, subject);
+ convbuf = nsMsgI18NEncodeMimePartIIStr(
+ subject.Length() ? subject.get() : "[no subject]", false, "UTF-8", 0,
+ conformToStandard);
+ tmpBuffer = PR_smprintf(
+ "Subject: %s%s" CRLF, encodedReceiptString,
+ (convbuf ? convbuf
+ : (subject.Length() ? subject.get() : "[no subject]")));
+
+ PUSH_N_FREE_STRING(tmpBuffer);
+ PR_Free(convbuf);
+ PR_Free(encodedReceiptString);
+
+ convbuf = nsMsgI18NEncodeMimePartIIStr(m_dntRrt.get(), true, "UTF-8", 0,
+ conformToStandard);
+ tmpBuffer = PR_smprintf("To: %s" CRLF, convbuf ? convbuf : m_dntRrt.get());
+ PUSH_N_FREE_STRING(tmpBuffer);
+
+ PR_Free(convbuf);
+
+ // *** This is not in the spec. I am adding this so we could do
+ // threading
+ m_headers->ExtractHeader(HEADER_MESSAGE_ID, false, m_messageId);
+
+ if (!m_messageId.IsEmpty()) {
+ if (*m_messageId.get() == '<')
+ tmpBuffer = PR_smprintf("References: %s" CRLF, m_messageId.get());
+ else
+ tmpBuffer = PR_smprintf("References: <%s>" CRLF, m_messageId.get());
+ PUSH_N_FREE_STRING(tmpBuffer);
+ }
+ tmpBuffer = PR_smprintf("%s" CRLF, "MIME-Version: 1.0");
+ PUSH_N_FREE_STRING(tmpBuffer);
+
+ tmpBuffer = PR_smprintf(
+ "Content-Type: multipart/report; \
+report-type=disposition-notification;\r\n\tboundary=\"%s\"" CRLF CRLF,
+ m_mimeSeparator.get());
+ PUSH_N_FREE_STRING(tmpBuffer);
+
+ tmpBuffer = PR_smprintf("--%s" CRLF, m_mimeSeparator.get());
+ PUSH_N_FREE_STRING(tmpBuffer);
+
+ tmpBuffer = PR_smprintf("Content-Type: text/plain; charset=UTF-8" CRLF);
+ PUSH_N_FREE_STRING(tmpBuffer);
+
+ tmpBuffer =
+ PR_smprintf("Content-Transfer-Encoding: %s" CRLF CRLF, ENCODING_8BIT);
+ PUSH_N_FREE_STRING(tmpBuffer);
+
+ if (!firstPart1.IsEmpty()) {
+ tmpBuffer =
+ PR_smprintf("%s" CRLF CRLF, NS_ConvertUTF16toUTF8(firstPart1).get());
+ PUSH_N_FREE_STRING(tmpBuffer);
+ }
+
+ switch (m_disposeType) {
+ case nsIMsgMdnGenerator::eDisplayed:
+ rv = GetStringFromName("MsgMdnDisplayed", firstPart2);
+ break;
+ case nsIMsgMdnGenerator::eDispatched:
+ rv = GetStringFromName("MsgMdnDispatched", firstPart2);
+ break;
+ case nsIMsgMdnGenerator::eProcessed:
+ rv = GetStringFromName("MsgMdnProcessed", firstPart2);
+ break;
+ case nsIMsgMdnGenerator::eDeleted:
+ rv = GetStringFromName("MsgMdnDeleted", firstPart2);
+ break;
+ case nsIMsgMdnGenerator::eDenied:
+ rv = GetStringFromName("MsgMdnDenied", firstPart2);
+ break;
+ case nsIMsgMdnGenerator::eFailed:
+ rv = GetStringFromName("MsgMdnFailed", firstPart2);
+ break;
+ default:
+ rv = NS_ERROR_INVALID_ARG;
+ break;
+ }
+
+ if (NS_FAILED(rv)) return rv;
+
+ if (!firstPart2.IsEmpty()) {
+ tmpBuffer =
+ PR_smprintf("%s" CRLF CRLF, NS_ConvertUTF16toUTF8(firstPart2).get());
+ PUSH_N_FREE_STRING(tmpBuffer);
+ }
+
+ return rv;
+}
+
+nsresult nsMsgMdnGenerator::CreateSecondPart() {
+ DEBUG_MDN("nsMsgMdnGenerator::CreateSecondPart");
+ char* tmpBuffer = nullptr;
+ char* convbuf = nullptr;
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIMsgCompUtils> compUtils;
+ bool conformToStandard = false;
+
+ tmpBuffer = PR_smprintf("--%s" CRLF, m_mimeSeparator.get());
+ PUSH_N_FREE_STRING(tmpBuffer);
+
+ tmpBuffer = PR_smprintf("%s" CRLF,
+ "Content-Type: message/disposition-notification; "
+ "name=\042MDNPart2.txt\042");
+ PUSH_N_FREE_STRING(tmpBuffer);
+
+ tmpBuffer = PR_smprintf("%s" CRLF, "Content-Disposition: inline");
+ PUSH_N_FREE_STRING(tmpBuffer);
+
+ tmpBuffer =
+ PR_smprintf("Content-Transfer-Encoding: %s" CRLF CRLF, ENCODING_7BIT);
+ PUSH_N_FREE_STRING(tmpBuffer);
+
+ nsCOMPtr<nsIHttpProtocolHandler> pHTTPHandler =
+ do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv);
+ if (NS_SUCCEEDED(rv) && pHTTPHandler) {
+ bool sendUserAgent = false;
+ nsCOMPtr<nsIPrefBranch> prefBranch(
+ do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv) && prefBranch) {
+ prefBranch->GetBoolPref("mailnews.headers.sendUserAgent", &sendUserAgent);
+ }
+
+ if (sendUserAgent) {
+ bool useMinimalUserAgent = false;
+ if (prefBranch) {
+ prefBranch->GetBoolPref("mailnews.headers.useMinimalUserAgent",
+ &useMinimalUserAgent);
+ }
+ if (useMinimalUserAgent) {
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::components::StringBundle::Service();
+ if (bundleService) {
+ nsCOMPtr<nsIStringBundle> brandBundle;
+ rv = bundleService->CreateBundle(
+ "chrome://branding/locale/brand.properties",
+ getter_AddRefs(brandBundle));
+ if (NS_SUCCEEDED(rv)) {
+ nsString brandName;
+ brandBundle->GetStringFromName("brandFullName", brandName);
+ if (!brandName.IsEmpty()) {
+ NS_ConvertUTF16toUTF8 ua8(brandName);
+ tmpBuffer = PR_smprintf("Reporting-UA: %s" CRLF, ua8.get());
+ PUSH_N_FREE_STRING(tmpBuffer);
+ }
+ }
+ }
+ } else {
+ nsAutoCString userAgentString;
+ // Ignore error since we're testing the return value.
+ mozilla::Unused << pHTTPHandler->GetUserAgent(userAgentString);
+
+ if (!userAgentString.IsEmpty()) {
+ // Prepend the product name with the dns name according to RFC 3798.
+ char hostName[256];
+ PR_GetSystemInfo(PR_SI_HOSTNAME_UNTRUNCATED, hostName,
+ sizeof hostName);
+ if ((hostName[0] != '\0') && (strchr(hostName, '.') != NULL)) {
+ userAgentString.InsertLiteral("; ", 0);
+ userAgentString.Insert(nsDependentCString(hostName), 0);
+ }
+
+ tmpBuffer =
+ PR_smprintf("Reporting-UA: %s" CRLF, userAgentString.get());
+ PUSH_N_FREE_STRING(tmpBuffer);
+ }
+ }
+ }
+ }
+
+ nsCString originalRecipient;
+ m_headers->ExtractHeader(HEADER_ORIGINAL_RECIPIENT, false, originalRecipient);
+
+ if (!originalRecipient.IsEmpty()) {
+ tmpBuffer =
+ PR_smprintf("Original-Recipient: %s" CRLF, originalRecipient.get());
+ PUSH_N_FREE_STRING(tmpBuffer);
+ }
+
+ compUtils = do_GetService("@mozilla.org/messengercompose/computils;1", &rv);
+ if (compUtils) compUtils->GetMsgMimeConformToStandard(&conformToStandard);
+
+ convbuf = nsMsgI18NEncodeMimePartIIStr(m_email.get(), true, "UTF-8", 0,
+ conformToStandard);
+ tmpBuffer = PR_smprintf("Final-Recipient: rfc822;%s" CRLF,
+ convbuf ? convbuf : m_email.get());
+ PUSH_N_FREE_STRING(tmpBuffer);
+
+ PR_Free(convbuf);
+
+ if (*m_messageId.get() == '<')
+ tmpBuffer = PR_smprintf("Original-Message-ID: %s" CRLF, m_messageId.get());
+ else
+ tmpBuffer =
+ PR_smprintf("Original-Message-ID: <%s>" CRLF, m_messageId.get());
+ PUSH_N_FREE_STRING(tmpBuffer);
+
+ tmpBuffer =
+ PR_smprintf("Disposition: %s/%s; %s" CRLF CRLF,
+ (m_autoAction ? "automatic-action" : "manual-action"),
+ (m_autoSend ? "MDN-sent-automatically" : "MDN-sent-manually"),
+ DispositionTypes[(int)m_disposeType]);
+ PUSH_N_FREE_STRING(tmpBuffer);
+
+ return rv;
+}
+
+nsresult nsMsgMdnGenerator::CreateThirdPart() {
+ DEBUG_MDN("nsMsgMdnGenerator::CreateThirdPart");
+ char* tmpBuffer = nullptr;
+ nsresult rv = NS_OK;
+
+ tmpBuffer = PR_smprintf("--%s" CRLF, m_mimeSeparator.get());
+ PUSH_N_FREE_STRING(tmpBuffer);
+
+ tmpBuffer = PR_smprintf(
+ "%s" CRLF,
+ "Content-Type: text/rfc822-headers; name=\042MDNPart3.txt\042");
+ PUSH_N_FREE_STRING(tmpBuffer);
+
+ tmpBuffer = PR_smprintf("%s" CRLF, "Content-Transfer-Encoding: 7bit");
+ PUSH_N_FREE_STRING(tmpBuffer);
+
+ tmpBuffer = PR_smprintf("%s" CRLF CRLF, "Content-Disposition: inline");
+ PUSH_N_FREE_STRING(tmpBuffer);
+
+ rv = OutputAllHeaders();
+
+ if (NS_FAILED(rv)) return rv;
+
+ rv = WriteString(CRLF);
+ if (NS_FAILED(rv)) return rv;
+
+ tmpBuffer = PR_smprintf("--%s--" CRLF, m_mimeSeparator.get());
+ PUSH_N_FREE_STRING(tmpBuffer);
+
+ return rv;
+}
+
+nsresult nsMsgMdnGenerator::OutputAllHeaders() {
+ DEBUG_MDN("nsMsgMdnGenerator::OutputAllHeaders");
+ nsCString all_headers;
+ int32_t all_headers_size = 0;
+ nsresult rv = NS_OK;
+
+ rv = m_headers->GetAllHeaders(all_headers);
+ if (NS_FAILED(rv)) return rv;
+ all_headers_size = all_headers.Length();
+ char *buf = (char*)all_headers.get(),
+ *buf_end = (char*)all_headers.get() + all_headers_size;
+ char *start = buf, *end = buf;
+
+ while (buf < buf_end) {
+ switch (*buf) {
+ case 0:
+ if (*(buf + 1) == '\n') {
+ // *buf = '\r';
+ end = buf;
+ } else if (*(buf + 1) == 0) {
+ // the case of message id
+ *buf = '>';
+ }
+ break;
+ case '\r':
+ end = buf;
+ *buf = 0;
+ break;
+ case '\n':
+ if (buf > start && *(buf - 1) == 0) {
+ start = buf + 1;
+ end = start;
+ } else {
+ end = buf;
+ }
+ *buf = 0;
+ break;
+ default:
+ break;
+ }
+ buf++;
+
+ if (end > start && *end == 0) {
+ // strip out private X-Mozilla-Status header & X-Mozilla-Draft-Info &&
+ // envelope header
+ if (!PL_strncasecmp(start, X_MOZILLA_STATUS, X_MOZILLA_STATUS_LEN) ||
+ !PL_strncasecmp(start, X_MOZILLA_DRAFT_INFO,
+ X_MOZILLA_DRAFT_INFO_LEN) ||
+ !PL_strncasecmp(start, "From ", 5)) {
+ while (end < buf_end && (*end == '\n' || *end == '\r' || *end == 0))
+ end++;
+ start = end;
+ } else {
+ NS_ASSERTION(*end == 0, "content of end should be null");
+ rv = WriteString(start);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = WriteString(CRLF);
+ NS_ENSURE_SUCCESS(rv, rv);
+ while (end < buf_end && (*end == '\n' || *end == '\r' || *end == 0))
+ end++;
+ start = end;
+ }
+ buf = start;
+ }
+ }
+ return NS_OK;
+}
+
+nsresult nsMsgMdnGenerator::SendMdnMsg() {
+ DEBUG_MDN("nsMsgMdnGenerator::SendMdnMsg");
+ nsresult rv;
+ nsCOMPtr<nsISmtpService> smtpService =
+ do_GetService("@mozilla.org/messengercompose/smtp;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIURI> aUri;
+ nsCOMPtr<nsIRequest> aRequest;
+ nsCString identEmail;
+ m_identity->GetEmail(identEmail);
+ smtpService->SendMailMessage(m_file, m_dntRrt.get(), m_identity,
+ identEmail.get(), EmptyString(), this, nullptr,
+ nullptr, false, ""_ns, getter_AddRefs(aUri),
+ getter_AddRefs(aRequest));
+
+ return NS_OK;
+}
+
+nsresult nsMsgMdnGenerator::WriteString(const char* str) {
+ NS_ENSURE_ARG(str);
+ uint32_t len = strlen(str);
+ uint32_t wLen = 0;
+
+ return m_outputStream->Write(str, len, &wLen);
+}
+
+nsresult nsMsgMdnGenerator::InitAndProcess(bool* needToAskUser) {
+ DEBUG_MDN("nsMsgMdnGenerator::InitAndProcess");
+ nsresult rv = m_folder->GetServer(getter_AddRefs(m_server));
+ nsCOMPtr<nsIMsgAccountManager> accountManager =
+ do_GetService("@mozilla.org/messenger/account-manager;1", &rv);
+ if (accountManager && m_server) {
+ if (!m_identity) {
+ // check if this is a message delivered to the global inbox,
+ // in which case we find the originating account's identity.
+ nsCString accountKey;
+ m_headers->ExtractHeader(HEADER_X_MOZILLA_ACCOUNT_KEY, false, accountKey);
+ nsCOMPtr<nsIMsgAccount> account;
+ if (!accountKey.IsEmpty())
+ accountManager->GetAccount(accountKey, getter_AddRefs(account));
+ if (account) account->GetIncomingServer(getter_AddRefs(m_server));
+
+ if (m_server) {
+ // Find the correct identity based on the "To:" and "Cc:" header
+ nsCString mailTo;
+ nsCString mailCC;
+ m_headers->ExtractHeader(HEADER_TO, true, mailTo);
+ m_headers->ExtractHeader(HEADER_CC, true, mailCC);
+ nsTArray<RefPtr<nsIMsgIdentity>> servIdentities;
+ accountManager->GetIdentitiesForServer(m_server, servIdentities);
+
+ // First check in the "To:" header
+ for (auto ident : servIdentities) {
+ nsCString identEmail;
+ ident->GetEmail(identEmail);
+ if (!mailTo.IsEmpty() && !identEmail.IsEmpty() &&
+ FindInReadable(identEmail, mailTo,
+ nsCaseInsensitiveCStringComparator)) {
+ m_identity = ident;
+ break;
+ }
+ }
+ // If no match, check the "Cc:" header
+ if (!m_identity) {
+ for (auto ident : servIdentities) {
+ nsCString identEmail;
+ ident->GetEmail(identEmail);
+ if (!mailCC.IsEmpty() && !identEmail.IsEmpty() &&
+ FindInReadable(identEmail, mailCC,
+ nsCaseInsensitiveCStringComparator)) {
+ m_identity = ident;
+ break;
+ }
+ }
+ }
+
+ // If still no match, use the first identity
+ if (!m_identity) {
+ rv = accountManager->GetFirstIdentityForServer(
+ m_server, getter_AddRefs(m_identity));
+ }
+ }
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (m_identity) {
+ bool useCustomPrefs = false;
+ m_identity->GetBoolAttribute("use_custom_prefs", &useCustomPrefs);
+ if (useCustomPrefs) {
+ bool bVal = false;
+ m_server->GetBoolValue("mdn_report_enabled", &bVal);
+ m_mdnEnabled = bVal;
+ m_server->GetIntValue("mdn_not_in_to_cc", &m_notInToCcOp);
+ m_server->GetIntValue("mdn_outside_domain", &m_outsideDomainOp);
+ m_server->GetIntValue("mdn_other", &m_otherOp);
+ } else {
+ bool bVal = false;
+
+ nsCOMPtr<nsIPrefBranch> prefBranch(
+ do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ if (NS_FAILED(rv)) return rv;
+
+ if (prefBranch) {
+ prefBranch->GetBoolPref("mail.mdn.report.enabled", &bVal);
+ m_mdnEnabled = bVal;
+ prefBranch->GetIntPref("mail.mdn.report.not_in_to_cc",
+ &m_notInToCcOp);
+ prefBranch->GetIntPref("mail.mdn.report.outside_domain",
+ &m_outsideDomainOp);
+ prefBranch->GetIntPref("mail.mdn.report.other", &m_otherOp);
+ }
+ }
+ }
+ }
+
+ if (m_mdnEnabled) {
+ m_headers->ExtractHeader(HEADER_DISPOSITION_NOTIFICATION_TO, false,
+ m_dntRrt);
+ if (m_dntRrt.IsEmpty())
+ m_headers->ExtractHeader(HEADER_RETURN_RECEIPT_TO, false, m_dntRrt);
+ if (!m_dntRrt.IsEmpty() && ProcessSendMode() && ValidateReturnPath()) {
+ if (!m_autoSend) {
+ *needToAskUser = true;
+ rv = NS_OK;
+ } else {
+ *needToAskUser = false;
+ rv = UserAgreed();
+ }
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsMsgMdnGenerator::Process(EDisposeType type,
+ nsIMsgWindow* aWindow,
+ nsIMsgFolder* folder, nsMsgKey key,
+ nsIMimeHeaders* headers,
+ bool autoAction, bool* _retval) {
+ DEBUG_MDN("nsMsgMdnGenerator::Process");
+ NS_ENSURE_ARG_POINTER(folder);
+ NS_ENSURE_ARG_POINTER(headers);
+ NS_ENSURE_ARG_POINTER(aWindow);
+ NS_ENSURE_TRUE(key != nsMsgKey_None, NS_ERROR_INVALID_ARG);
+ m_disposeType = type;
+ m_autoAction = autoAction;
+ m_window = aWindow;
+ m_folder = folder;
+ m_headers = headers;
+ m_key = key;
+
+ mozilla::DebugOnly<nsresult> rv = InitAndProcess(_retval);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "InitAndProcess failed");
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgMdnGenerator::UserAgreed() {
+ DEBUG_MDN("nsMsgMdnGenerator::UserAgreed");
+ (void)NoteMDNRequestHandled();
+ return CreateMdnMsg();
+}
+
+NS_IMETHODIMP nsMsgMdnGenerator::UserDeclined() {
+ DEBUG_MDN("nsMsgMdnGenerator::UserDeclined");
+ return NoteMDNRequestHandled();
+}
+
+/**
+ * Set/clear flags appropriately so we won't ask user again about MDN
+ * request for this message.
+ */
+nsresult nsMsgMdnGenerator::NoteMDNRequestHandled() {
+ nsresult rv = StoreMDNSentFlag(m_folder, m_key);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "StoreMDNSentFlag failed");
+ rv = ClearMDNNeededFlag(m_folder, m_key);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "ClearMDNNeededFlag failed");
+ return rv;
+}
+
+NS_IMETHODIMP nsMsgMdnGenerator::OnStartRunningUrl(nsIURI* url) {
+ DEBUG_MDN("nsMsgMdnGenerator::OnStartRunningUrl");
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgMdnGenerator::OnStopRunningUrl(nsIURI* url,
+ nsresult aExitCode) {
+ nsresult rv;
+
+ DEBUG_MDN("nsMsgMdnGenerator::OnStopRunningUrl");
+ if (m_file) m_file->Remove(false);
+
+ if (NS_SUCCEEDED(aExitCode)) return NS_OK;
+
+ const char* exitString;
+
+ switch (aExitCode) {
+ case NS_ERROR_UNKNOWN_HOST:
+ case NS_ERROR_UNKNOWN_PROXY_HOST:
+ exitString = "smtpSendFailedUnknownServer";
+ break;
+ case NS_ERROR_CONNECTION_REFUSED:
+ case NS_ERROR_PROXY_CONNECTION_REFUSED:
+ exitString = "smtpSendRequestRefused";
+ break;
+ case NS_ERROR_NET_INTERRUPT:
+ case NS_ERROR_ABORT: // we have no proper string for error code
+ // NS_ERROR_ABORT in compose bundle
+ exitString = "smtpSendInterrupted";
+ break;
+ case NS_ERROR_NET_TIMEOUT:
+ case NS_ERROR_NET_RESET:
+ exitString = "smtpSendTimeout";
+ break;
+ default:
+ exitString = errorStringNameForErrorCode(aExitCode);
+ break;
+ }
+
+ nsCOMPtr<nsISmtpService> smtpService(
+ do_GetService("@mozilla.org/messengercompose/smtp;1", &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Get the smtp hostname and format the string.
+ nsCString smtpHostName;
+ nsCOMPtr<nsISmtpServer> smtpServer;
+ rv = smtpService->GetServerByIdentity(m_identity, getter_AddRefs(smtpServer));
+ if (NS_SUCCEEDED(rv)) smtpServer->GetHostname(smtpHostName);
+
+ AutoTArray<nsString, 1> params;
+ CopyASCIItoUTF16(smtpHostName, *params.AppendElement());
+
+ nsCOMPtr<nsIStringBundle> bundle;
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::components::StringBundle::Service();
+ NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED);
+
+ rv = bundleService->CreateBundle(
+ "chrome://messenger/locale/messengercompose/composeMsgs.properties",
+ getter_AddRefs(bundle));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsString failed_msg, dialogTitle;
+
+ bundle->FormatStringFromName(exitString, params, failed_msg);
+ bundle->GetStringFromName("sendMessageErrorTitle", dialogTitle);
+
+ nsCOMPtr<mozIDOMWindowProxy> domWindow;
+ m_window->GetDomWindow(getter_AddRefs(domWindow));
+
+ nsCOMPtr<nsIPromptService> dlgService(
+ do_GetService(NS_PROMPTSERVICE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ dlgService->Alert(domWindow, dialogTitle.get(), failed_msg.get());
+
+ return NS_OK;
+}