diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /comm/mailnews/extensions/mdn | |
parent | Initial commit. (diff) | |
download | thunderbird-upstream.tar.xz thunderbird-upstream.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'comm/mailnews/extensions/mdn')
-rw-r--r-- | comm/mailnews/extensions/mdn/MDNService.jsm | 24 | ||||
-rw-r--r-- | comm/mailnews/extensions/mdn/am-mdn.js | 161 | ||||
-rw-r--r-- | comm/mailnews/extensions/mdn/am-mdn.xhtml | 231 | ||||
-rw-r--r-- | comm/mailnews/extensions/mdn/components.conf | 23 | ||||
-rw-r--r-- | comm/mailnews/extensions/mdn/jar.mn | 7 | ||||
-rw-r--r-- | comm/mailnews/extensions/mdn/mdn.js | 25 | ||||
-rw-r--r-- | comm/mailnews/extensions/mdn/moz.build | 26 | ||||
-rw-r--r-- | comm/mailnews/extensions/mdn/nsMsgMdnGenerator.cpp | 1040 | ||||
-rw-r--r-- | comm/mailnews/extensions/mdn/nsMsgMdnGenerator.h | 86 | ||||
-rw-r--r-- | comm/mailnews/extensions/mdn/test/unit/head_mdn.js | 18 | ||||
-rw-r--r-- | comm/mailnews/extensions/mdn/test/unit/test_askuser.js | 67 | ||||
-rw-r--r-- | comm/mailnews/extensions/mdn/test/unit/test_mdnFlags.js | 60 | ||||
-rw-r--r-- | comm/mailnews/extensions/mdn/test/unit/xpcshell.ini | 6 |
13 files changed, 1774 insertions, 0 deletions
diff --git a/comm/mailnews/extensions/mdn/MDNService.jsm b/comm/mailnews/extensions/mdn/MDNService.jsm new file mode 100644 index 0000000000..47f5257b6a --- /dev/null +++ b/comm/mailnews/extensions/mdn/MDNService.jsm @@ -0,0 +1,24 @@ +/* -*- 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/. */ + +var EXPORTED_SYMBOLS = ["MDNService"]; + +function MDNService() {} + +MDNService.prototype = { + name: "mdn", + chromePackageName: "messenger", + showPanel(server) { + // don't show the panel for news, rss, im or local accounts + return ( + server.type != "nntp" && + server.type != "rss" && + server.type != "im" && + server.type != "none" + ); + }, + + QueryInterface: ChromeUtils.generateQI(["nsIMsgAccountManagerExtension"]), +}; diff --git a/comm/mailnews/extensions/mdn/am-mdn.js b/comm/mailnews/extensions/mdn/am-mdn.js new file mode 100644 index 0000000000..7725339fbd --- /dev/null +++ b/comm/mailnews/extensions/mdn/am-mdn.js @@ -0,0 +1,161 @@ +/* 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/. */ + +/* import-globals-from ../../base/prefs/content/amUtils.js */ + +var useCustomPrefs; +var requestReceipt; +var leaveInInbox; +var moveToSent; +var receiptSend; +var neverReturn; +var returnSome; +var notInToCcPref; +var notInToCcLabel; +var outsideDomainPref; +var outsideDomainLabel; +var otherCasesPref; +var otherCasesLabel; +var receiptArriveLabel; +var receiptRequestLabel; +var gIdentity; +var gIncomingServer; +var gMdnPrefBranch; + +function onInit() { + useCustomPrefs = document.getElementById("identity.use_custom_prefs"); + requestReceipt = document.getElementById( + "identity.request_return_receipt_on" + ); + leaveInInbox = document.getElementById("leave_in_inbox"); + moveToSent = document.getElementById("move_to_sent"); + receiptSend = document.getElementById("server.mdn_report_enabled"); + neverReturn = document.getElementById("never_return"); + returnSome = document.getElementById("return_some"); + notInToCcPref = document.getElementById("server.mdn_not_in_to_cc"); + notInToCcLabel = document.getElementById("notInToCcLabel"); + outsideDomainPref = document.getElementById("server.mdn_outside_domain"); + outsideDomainLabel = document.getElementById("outsideDomainLabel"); + otherCasesPref = document.getElementById("server.mdn_other"); + otherCasesLabel = document.getElementById("otherCasesLabel"); + receiptArriveLabel = document.getElementById("receiptArriveLabel"); + receiptRequestLabel = document.getElementById("receiptRequestLabel"); + + EnableDisableCustomSettings(); + + return true; +} + +function onSave() {} + +function EnableDisableCustomSettings() { + if (useCustomPrefs && useCustomPrefs.getAttribute("value") == "false") { + requestReceipt.setAttribute("disabled", "true"); + leaveInInbox.setAttribute("disabled", "true"); + moveToSent.setAttribute("disabled", "true"); + neverReturn.setAttribute("disabled", "true"); + returnSome.setAttribute("disabled", "true"); + receiptArriveLabel.setAttribute("disabled", "true"); + receiptRequestLabel.setAttribute("disabled", "true"); + } else { + requestReceipt.removeAttribute("disabled"); + leaveInInbox.removeAttribute("disabled"); + moveToSent.removeAttribute("disabled"); + neverReturn.removeAttribute("disabled"); + returnSome.removeAttribute("disabled"); + receiptArriveLabel.removeAttribute("disabled"); + receiptRequestLabel.removeAttribute("disabled"); + } + EnableDisableAllowedReceipts(); + // Lock id based prefs + onLockPreference("mail.identity", gIdentity.key); + // Lock server based prefs + onLockPreference("mail.server", gIncomingServer.key); + return true; +} + +function EnableDisableAllowedReceipts() { + if (receiptSend) { + if ( + !neverReturn.getAttribute("disabled") && + receiptSend.getAttribute("value") != "false" + ) { + notInToCcPref.removeAttribute("disabled"); + notInToCcLabel.removeAttribute("disabled"); + outsideDomainPref.removeAttribute("disabled"); + outsideDomainLabel.removeAttribute("disabled"); + otherCasesPref.removeAttribute("disabled"); + otherCasesLabel.removeAttribute("disabled"); + } else { + notInToCcPref.setAttribute("disabled", "true"); + notInToCcLabel.setAttribute("disabled", "true"); + outsideDomainPref.setAttribute("disabled", "true"); + outsideDomainLabel.setAttribute("disabled", "true"); + otherCasesPref.setAttribute("disabled", "true"); + otherCasesLabel.setAttribute("disabled", "true"); + } + } + return true; +} + +function onPreInit(account, accountValues) { + gIdentity = account.defaultIdentity; + gIncomingServer = account.incomingServer; +} + +// Disables xul elements that have associated preferences locked. +function onLockPreference(initPrefString, keyString) { + var allPrefElements = [ + { + prefstring: "request_return_receipt_on", + id: "identity.request_return_receipt_on", + }, + { prefstring: "select_custom_prefs", id: "identity.select_custom_prefs" }, + { prefstring: "select_global_prefs", id: "identity.select_global_prefs" }, + { + prefstring: "incorporate_return_receipt", + id: "server.incorporate_return_receipt", + }, + { prefstring: "never_return", id: "never_return" }, + { prefstring: "return_some", id: "return_some" }, + { prefstring: "mdn_not_in_to_cc", id: "server.mdn_not_in_to_cc" }, + { prefstring: "mdn_outside_domain", id: "server.mdn_outside_domain" }, + { prefstring: "mdn_other", id: "server.mdn_other" }, + ]; + + var finalPrefString = initPrefString + "." + keyString + "."; + gMdnPrefBranch = Services.prefs.getBranch(finalPrefString); + + disableIfLocked(allPrefElements); +} + +function disableIfLocked(prefstrArray) { + for (let i = 0; i < prefstrArray.length; i++) { + var id = prefstrArray[i].id; + var element = document.getElementById(id); + if (gMdnPrefBranch.prefIsLocked(prefstrArray[i].prefstring)) { + if (id == "server.incorporate_return_receipt") { + document + .getElementById("leave_in_inbox") + .setAttribute("disabled", "true"); + document + .getElementById("move_to_sent") + .setAttribute("disabled", "true"); + } else { + element.setAttribute("disabled", "true"); + } + } + } +} + +/** + * Opens Preferences (Options) dialog on the pane and tab where + * the global receipts settings can be found. + */ +function showGlobalReceipts() { + parent.gSubDialog.open( + "chrome://messenger/content/preferences/receipts.xhtml", + { features: "resizable=no" } + ); +} diff --git a/comm/mailnews/extensions/mdn/am-mdn.xhtml b/comm/mailnews/extensions/mdn/am-mdn.xhtml new file mode 100644 index 0000000000..3cdf85d9e5 --- /dev/null +++ b/comm/mailnews/extensions/mdn/am-mdn.xhtml @@ -0,0 +1,231 @@ +<?xml version="1.0"?> +<!-- + 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/. --> + +<?xml-stylesheet href="chrome://messenger/skin/accountManage.css" type="text/css"?> + +<!DOCTYPE html SYSTEM "chrome://messenger/locale/am-mdn.dtd"> + +<html + xmlns="http://www.w3.org/1999/xhtml" + xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + xmlns:html="http://www.w3.org/1999/xhtml" +> + <head> + <title>&pane.title;</title> + <script + defer="defer" + src="chrome://messenger/content/AccountManager.js" + ></script> + <script defer="defer" src="chrome://messenger/content/amUtils.js"></script> + <script defer="defer" src="chrome://messenger/content/am-mdn.js"></script> + <script> + // FIXME: move to script file. + window.addEventListener("load", event => { + parent.onPanelLoaded("am-mdn.xhtml"); + }); + </script> + </head> + <html:body + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + > + <vbox id="containerBox" flex="1"> + <stringbundle + id="bundle_smime" + src="chrome://messenger/locale/am-mdn.properties" + /> + + <hbox class="dialogheader"> + <label class="dialogheader-title" value="&pane.title;" /> + </hbox> + + <separator class="thin" /> + + <html:div> + <html:fieldset> + <hbox id="prefChoices" align="center" flex="1"> + <radiogroup + id="identity.use_custom_prefs" + wsm_persist="true" + genericattr="true" + preftype="bool" + prefstring="mail.identity.%identitykey%.use_custom_prefs" + oncommand="EnableDisableCustomSettings();" + flex="1" + > + <radio + id="identity.select_global_prefs" + value="false" + label="&useGlobalPrefs.label;" + accesskey="&useGlobalPrefs.accesskey;" + /> + <hbox flex="1"> + <spacer flex="1" /> + <button + id="globalReceiptsLink" + label="&globalReceipts.label;" + accesskey="&globalReceipts.accesskey;" + oncommand="showGlobalReceipts();" + /> + </hbox> + <radio + id="identity.select_custom_prefs" + value="true" + label="&useCustomPrefs.label;" + accesskey="&useCustomPrefs.accesskey;" + /> + </radiogroup> + </hbox> + + <vbox id="returnReceiptSettings" class="indent" align="start"> + <checkbox + id="identity.request_return_receipt_on" + label="&requestReceipt.label;" + accesskey="&requestReceipt.accesskey;" + wsm_persist="true" + genericattr="true" + iscontrolcontainer="true" + preftype="bool" + prefstring="mail.identity.%identitykey%.request_return_receipt_on" + /> + + <separator /> + + <vbox id="receiptArrive"> + <label + id="receiptArriveLabel" + control="server.incorporate_return_receipt" + >&receiptArrive.label;</label + > + <radiogroup + id="server.incorporate_return_receipt" + wsm_persist="true" + genericattr="true" + preftype="int" + prefstring="mail.server.%serverkey%.incorporate_return_receipt" + class="indent" + > + <radio + id="leave_in_inbox" + value="0" + label="&leaveIt.label;" + accesskey="&leaveIt.accesskey;" + /> + <radio + id="move_to_sent" + value="1" + label="&moveToSent.label;" + accesskey="&moveToSent.accesskey;" + /> + </radiogroup> + </vbox> + + <separator /> + + <vbox id="receiptRequest"> + <label + id="receiptRequestLabel" + control="server.mdn_report_enabled" + >&requestMDN.label;</label + > + <radiogroup + id="server.mdn_report_enabled" + wsm_persist="true" + genericattr="true" + preftype="bool" + prefstring="mail.server.%serverkey%.mdn_report_enabled" + oncommand="EnableDisableAllowedReceipts();" + class="indent" + > + <radio + id="never_return" + value="false" + label="&never.label;" + accesskey="&never.accesskey;" + /> + <radio + id="return_some" + value="true" + label="&returnSome.label;" + accesskey="&returnSome.accesskey;" + /> + + <hbox id="receiptSendIf" class="indent"> + <vbox> + <hbox flex="1" align="center"> + <label + id="notInToCcLabel" + value="¬InToCc.label;" + accesskey="¬InToCc.accesskey;" + control="server.mdn_not_in_to_cc" + /> + </hbox> + <hbox flex="1" align="center"> + <label + id="outsideDomainLabel" + value="&outsideDomain.label;" + accesskey="&outsideDomain.accesskey;" + control="server.mdn_outside_domain" + /> + </hbox> + <hbox flex="1" align="center"> + <label + id="otherCasesLabel" + value="&otherCases.label;" + accesskey="&otherCases.accesskey;" + control="server.mdn_other" + /> + </hbox> + </vbox> + <vbox> + <menulist + id="server.mdn_not_in_to_cc" + wsm_persist="true" + genericattr="true" + preftype="int" + prefstring="mail.server.%serverkey%.mdn_not_in_to_cc" + > + <menupopup> + <menuitem value="0" label="&neverSend.label;" /> + <menuitem value="1" label="&alwaysSend.label;" /> + <menuitem value="2" label="&askMe.label;" /> + </menupopup> + </menulist> + <menulist + id="server.mdn_outside_domain" + wsm_persist="true" + genericattr="true" + preftype="int" + prefstring="mail.server.%serverkey%.mdn_outside_domain" + > + <menupopup> + <menuitem value="0" label="&neverSend.label;" /> + <menuitem value="1" label="&alwaysSend.label;" /> + <menuitem value="2" label="&askMe.label;" /> + </menupopup> + </menulist> + <menulist + id="server.mdn_other" + wsm_persist="true" + genericattr="true" + preftype="int" + prefstring="mail.server.%serverkey%.mdn_other" + > + <menupopup> + <menuitem value="0" label="&neverSend.label;" /> + <menuitem value="1" label="&alwaysSend.label;" /> + <menuitem value="2" label="&askMe.label;" /> + </menupopup> + </menulist> + </vbox> + </hbox> + </radiogroup> + </vbox> + </vbox> + </html:fieldset> + </html:div> + </vbox> + </html:body> +</html> diff --git a/comm/mailnews/extensions/mdn/components.conf b/comm/mailnews/extensions/mdn/components.conf new file mode 100644 index 0000000000..84f53febbf --- /dev/null +++ b/comm/mailnews/extensions/mdn/components.conf @@ -0,0 +1,23 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +Classes = [ + { + "cid": "{e007d92e-1dd1-11b2-a61e-dc962c9b8571}", + "contract_ids": ["@mozilla.org/accountmanager/extension;1?name=mdn"], + "jsm": "resource:///modules/MDNService.jsm", + "constructor": "MDNService", + "categories": { + "mailnews-accountmanager-extensions": "mdn-account-manager-extension" + }, + }, + { + "cid": "{ec917b13-8f73-4d4d-9146-d7f7aafe9076}", + "contract_ids": ["@mozilla.org/messenger-mdn/generator;1"], + "type": "nsMsgMdnGenerator", + "headers": ["/comm/mailnews/extensions/mdn/nsMsgMdnGenerator.h"], + }, +] diff --git a/comm/mailnews/extensions/mdn/jar.mn b/comm/mailnews/extensions/mdn/jar.mn new file mode 100644 index 0000000000..cb487cd969 --- /dev/null +++ b/comm/mailnews/extensions/mdn/jar.mn @@ -0,0 +1,7 @@ +# 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/. + +messenger.jar: + content/messenger/am-mdn.xhtml (am-mdn.xhtml) + content/messenger/am-mdn.js (am-mdn.js) diff --git a/comm/mailnews/extensions/mdn/mdn.js b/comm/mailnews/extensions/mdn/mdn.js new file mode 100644 index 0000000000..ae82382f39 --- /dev/null +++ b/comm/mailnews/extensions/mdn/mdn.js @@ -0,0 +1,25 @@ +#filter dumbComments emptyLines substitution + +// 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/. + +// +// default prefs for mdn +// + +// false: Use global true: Use custom +pref("mail.identity.default.use_custom_prefs", false); +pref("mail.identity.default.request_return_receipt_on", false); +// 0: Inbox/filter 1: Sent folder +pref("mail.server.default.incorporate_return_receipt", 0); +// false: Never return receipts true: Return some receipts +pref("mail.server.default.mdn_report_enabled", true); +// 0: Never 1: Always 2: Ask me 3: Denial +pref("mail.server.default.mdn_not_in_to_cc", 2); +pref("mail.server.default.mdn_outside_domain", 2); +pref("mail.server.default.mdn_other", 2); +// return receipt header type - 0: MDN-DNT 1: RRT 2: Both +pref("mail.identity.default.request_receipt_header_type", 0); + +pref("mail.server.default.mdn_report_enabled", true); diff --git a/comm/mailnews/extensions/mdn/moz.build b/comm/mailnews/extensions/mdn/moz.build new file mode 100644 index 0000000000..2d7b8bce5d --- /dev/null +++ b/comm/mailnews/extensions/mdn/moz.build @@ -0,0 +1,26 @@ +# vim: set filetype=python: +# 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/. + +JAR_MANIFESTS += ["jar.mn"] + +JS_PREFERENCE_PP_FILES += [ + "mdn.js", +] + +XPCSHELL_TESTS_MANIFESTS += ["test/unit/xpcshell.ini"] + +SOURCES += [ + "nsMsgMdnGenerator.cpp", +] + +EXTRA_JS_MODULES += [ + "MDNService.jsm", +] + +XPCOM_MANIFESTS += [ + "components.conf", +] + +FINAL_LIBRARY = "mail" 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; +} diff --git a/comm/mailnews/extensions/mdn/nsMsgMdnGenerator.h b/comm/mailnews/extensions/mdn/nsMsgMdnGenerator.h new file mode 100644 index 0000000000..74eea8bcad --- /dev/null +++ b/comm/mailnews/extensions/mdn/nsMsgMdnGenerator.h @@ -0,0 +1,86 @@ +/* -*- 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/. */ + +#ifndef _nsMsgMdnGenerator_H_ +#define _nsMsgMdnGenerator_H_ + +#include "nsIMsgMdnGenerator.h" +#include "nsCOMPtr.h" +#include "nsIUrlListener.h" +#include "nsIMsgIncomingServer.h" +#include "nsIOutputStream.h" +#include "nsIFile.h" +#include "nsIMsgIdentity.h" +#include "nsIMsgWindow.h" +#include "nsIMimeHeaders.h" +#include "nsString.h" +#include "MailNewsTypes2.h" + +#define eNeverSendOp ((int32_t)0) +#define eAutoSendOp ((int32_t)1) +#define eAskMeOp ((int32_t)2) +#define eDeniedOp ((int32_t)3) + +class nsMsgMdnGenerator : public nsIMsgMdnGenerator, public nsIUrlListener { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIMSGMDNGENERATOR + NS_DECL_NSIURLLISTENER + + nsMsgMdnGenerator(); + + private: + virtual ~nsMsgMdnGenerator(); + + // Sanity Check methods + bool ProcessSendMode(); // must called prior ValidateReturnPath + bool ValidateReturnPath(); + bool NotInToOrCc(); + bool MailAddrMatch(const char* addr1, const char* addr2); + + nsresult StoreMDNSentFlag(nsIMsgFolder* folder, nsMsgKey key); + nsresult ClearMDNNeededFlag(nsIMsgFolder* folder, nsMsgKey key); + nsresult NoteMDNRequestHandled(); + + nsresult CreateMdnMsg(); + nsresult CreateFirstPart(); + nsresult CreateSecondPart(); + nsresult CreateThirdPart(); + nsresult SendMdnMsg(); + + // string bundle helper methods + nsresult GetStringFromName(const char* aName, nsAString& aResultString); + nsresult FormatStringFromName(const char* aName, const nsString& aString, + nsAString& aResultString); + + // other helper methods + nsresult InitAndProcess(bool* needToAskUser); + nsresult OutputAllHeaders(); + nsresult WriteString(const char* str); + + private: + EDisposeType m_disposeType; + nsCOMPtr<nsIMsgWindow> m_window; + nsCOMPtr<nsIOutputStream> m_outputStream; + nsCOMPtr<nsIFile> m_file; + nsCOMPtr<nsIMsgIdentity> m_identity; + nsMsgKey m_key; + nsCString m_email; + nsCString m_mimeSeparator; + nsCString m_messageId; + nsCOMPtr<nsIMsgFolder> m_folder; + nsCOMPtr<nsIMsgIncomingServer> m_server; + nsCOMPtr<nsIMimeHeaders> m_headers; + nsCString m_dntRrt; + int32_t m_notInToCcOp; + int32_t m_outsideDomainOp; + int32_t m_otherOp; + bool m_reallySendMdn; + bool m_autoSend; + bool m_autoAction; + bool m_mdnEnabled; +}; + +#endif // _nsMsgMdnGenerator_H_ diff --git a/comm/mailnews/extensions/mdn/test/unit/head_mdn.js b/comm/mailnews/extensions/mdn/test/unit/head_mdn.js new file mode 100644 index 0000000000..0735341e30 --- /dev/null +++ b/comm/mailnews/extensions/mdn/test/unit/head_mdn.js @@ -0,0 +1,18 @@ +var { MailServices } = ChromeUtils.import( + "resource:///modules/MailServices.jsm" +); +var { mailTestUtils } = ChromeUtils.import( + "resource://testing-common/mailnews/MailTestUtils.jsm" +); +var { localAccountUtils } = ChromeUtils.import( + "resource://testing-common/mailnews/LocalAccountUtils.jsm" +); + +var CC = Components.Constructor; + +// Ensure the profile directory is set up +do_get_profile(); + +registerCleanupFunction(function () { + load("../../../../../mailnews/resources/mailShutdown.js"); +}); diff --git a/comm/mailnews/extensions/mdn/test/unit/test_askuser.js b/comm/mailnews/extensions/mdn/test/unit/test_askuser.js new file mode 100644 index 0000000000..b30ab49b0f --- /dev/null +++ b/comm/mailnews/extensions/mdn/test/unit/test_askuser.js @@ -0,0 +1,67 @@ +localAccountUtils.loadLocalMailAccount(); + +var localAccount = MailServices.accounts.FindAccountForServer( + localAccountUtils.incomingServer +); +var identity = MailServices.accounts.createIdentity(); +identity.email = "bob@t2.example.net"; +localAccount.addIdentity(identity); +localAccount.defaultIdentity = identity; + +function run_test() { + var headers = + "from: alice@t1.example.com\r\n" + + "to: bob@t2.example.net\r\n" + + "return-path: alice@t1.example.com\r\n" + + "Disposition-Notification-To: alice@t1.example.com\r\n"; + + let mimeHdr = Cc["@mozilla.org/messenger/mimeheaders;1"].createInstance( + Ci.nsIMimeHeaders + ); + mimeHdr.initialize(headers); + let receivedHeader = mimeHdr.extractHeader("To", false); + dump(receivedHeader + "\n"); + + localAccountUtils.inboxFolder.QueryInterface(Ci.nsIMsgLocalMailFolder); + localAccountUtils.inboxFolder.addMessage( + "From \r\n" + headers + "\r\nhello\r\n" + ); + // Need to setup some prefs + Services.prefs.setBoolPref("mail.mdn.report.enabled", true); + Services.prefs.setIntPref("mail.mdn.report.not_in_to_cc", 2); + Services.prefs.setIntPref("mail.mdn.report.other", 2); + Services.prefs.setIntPref("mail.mdn.report.outside_domain", 2); + + var msgFolder = localAccountUtils.inboxFolder; + + var msgWindow = {}; + + var msgHdr = mailTestUtils.firstMsgHdr(localAccountUtils.inboxFolder); + + // Everything looks good so far, let's generate the MDN response. + var mdnGenerator = Cc[ + "@mozilla.org/messenger-mdn/generator;1" + ].createInstance(Ci.nsIMsgMdnGenerator); + + Services.prefs.setIntPref("mail.mdn.report.outside_domain", 1); + var askUser = mdnGenerator.process( + Ci.nsIMsgMdnGenerator.eDisplayed, + msgWindow, + msgFolder, + msgHdr.messageKey, + mimeHdr, + false + ); + Assert.ok(!askUser); + + Services.prefs.setIntPref("mail.mdn.report.outside_domain", 2); + askUser = mdnGenerator.process( + Ci.nsIMsgMdnGenerator.eDisplayed, + msgWindow, + msgFolder, + msgHdr.messageKey, + mimeHdr, + false + ); + Assert.ok(askUser); +} diff --git a/comm/mailnews/extensions/mdn/test/unit/test_mdnFlags.js b/comm/mailnews/extensions/mdn/test/unit/test_mdnFlags.js new file mode 100644 index 0000000000..4d6094e89d --- /dev/null +++ b/comm/mailnews/extensions/mdn/test/unit/test_mdnFlags.js @@ -0,0 +1,60 @@ +/** + * This tests that setting mdn flags works correctly, so that we don't + * reprompt when the user re-selects a message. + */ + +localAccountUtils.loadLocalMailAccount(); + +var localAccount = MailServices.accounts.FindAccountForServer( + localAccountUtils.incomingServer +); +var identity = MailServices.accounts.createIdentity(); +identity.email = "bob@t2.example.net"; +localAccount.addIdentity(identity); +localAccount.defaultIdentity = identity; + +function run_test() { + var headers = + "from: alice@t1.example.com\r\n" + + "to: bob@t2.example.net\r\n" + + "return-path: alice@t1.example.com\r\n" + + "Disposition-Notification-To: alice@t1.example.com\r\n"; + + let mimeHdr = Cc["@mozilla.org/messenger/mimeheaders;1"].createInstance( + Ci.nsIMimeHeaders + ); + mimeHdr.initialize(headers); + mimeHdr.extractHeader("To", false); + + localAccountUtils.inboxFolder.QueryInterface(Ci.nsIMsgLocalMailFolder); + localAccountUtils.inboxFolder.addMessage( + "From \r\n" + headers + "\r\nhello\r\n" + ); + // Need to setup some prefs + Services.prefs.setBoolPref("mail.mdn.report.enabled", true); + Services.prefs.setIntPref("mail.mdn.report.not_in_to_cc", 2); + Services.prefs.setIntPref("mail.mdn.report.other", 2); + Services.prefs.setIntPref("mail.mdn.report.outside_domain", 2); + + var msgFolder = localAccountUtils.inboxFolder; + + var msgWindow = {}; + + var msgHdr = mailTestUtils.firstMsgHdr(localAccountUtils.inboxFolder); + + // Everything looks good so far, let's generate the MDN response. + var mdnGenerator = Cc[ + "@mozilla.org/messenger-mdn/generator;1" + ].createInstance(Ci.nsIMsgMdnGenerator); + mdnGenerator.process( + Ci.nsIMsgMdnGenerator.eDisplayed, + msgWindow, + msgFolder, + msgHdr.messageKey, + mimeHdr, + false + ); + mdnGenerator.userDeclined(); + Assert.notEqual(msgHdr.flags & Ci.nsMsgMessageFlags.MDNReportSent, 0); + Assert.equal(msgHdr.flags & Ci.nsMsgMessageFlags.MDNReportNeeded, 0); +} diff --git a/comm/mailnews/extensions/mdn/test/unit/xpcshell.ini b/comm/mailnews/extensions/mdn/test/unit/xpcshell.ini new file mode 100644 index 0000000000..243145b676 --- /dev/null +++ b/comm/mailnews/extensions/mdn/test/unit/xpcshell.ini @@ -0,0 +1,6 @@ +[DEFAULT] +head = head_mdn.js +tail = + +[test_askuser.js] +[test_mdnFlags.js] |