diff options
Diffstat (limited to 'mailnews/extensions/mdn/src/nsMsgMdnGenerator.cpp')
-rw-r--r-- | mailnews/extensions/mdn/src/nsMsgMdnGenerator.cpp | 1139 |
1 files changed, 1139 insertions, 0 deletions
diff --git a/mailnews/extensions/mdn/src/nsMsgMdnGenerator.cpp b/mailnews/extensions/mdn/src/nsMsgMdnGenerator.cpp new file mode 100644 index 000000000..31b55fe2f --- /dev/null +++ b/mailnews/extensions/mdn/src/nsMsgMdnGenerator.cpp @@ -0,0 +1,1139 @@ +/* -*- 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 "nsMsgBaseCID.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 "nsMsgCompCID.h" +#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/Services.h" +#include "nsIArray.h" +#include "nsArrayUtils.h" +#include "mozilla/mailnews/MimeHeaderParser.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_outputStream = nullptr; + m_reallySendMdn = false; + m_autoSend = false; + m_autoAction = false; + m_mdnEnabled = false; + m_notInToCcOp = eNeverSendOp; + m_outsideDomainOp = eNeverSendOp; + m_otherOp = eNeverSendOp; +} + +nsMsgMdnGenerator::~nsMsgMdnGenerator() +{ +} + +nsresult nsMsgMdnGenerator::FormatStringFromName(const char16_t *aName, + const char16_t *aString, + char16_t **aResultString) +{ + DEBUG_MDN("nsMsgMdnGenerator::FormatStringFromName"); + + nsCOMPtr<nsIStringBundleService> bundleService = + mozilla::services::GetStringBundleService(); + 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); + + const char16_t *formatStrings[1] = { aString }; + rv = bundle->FormatStringFromName(aName, + formatStrings, 1, aResultString); + NS_ENSURE_SUCCESS(rv,rv); + return rv; +} + +nsresult nsMsgMdnGenerator::GetStringFromName(const char16_t *aName, + char16_t **aResultString) +{ + DEBUG_MDN("nsMsgMdnGenerator::GetStringFromName"); + + nsCOMPtr<nsIStringBundleService> bundleService = + mozilla::services::GetStringBundleService(); + 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, 1, 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 + // thev 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(NS_MSGCOMPUTILS_CONTRACTID, &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 (superceding 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, m_charset.get(), 0, conformToStandard); + + parm = PR_smprintf("From: %s" CRLF, convbuf ? convbuf : m_email.get()); + + rv = FormatStringFromName(u"MsgMdnMsgSentTo", NS_ConvertASCIItoUTF16(m_email).get(), + getter_Copies(firstPart1)); + if (NS_FAILED(rv)) + return rv; + + PUSH_N_FREE_STRING (parm); + + PR_Free(convbuf); + + if (compUtils) + { + nsCString msgId; + rv = compUtils->MsgGenerateMessageId(m_identity, getter_Copies(msgId)); + 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( + u"MdnDisplayedReceipt", + getter_Copies(receipt_string)); + break; + case nsIMsgMdnGenerator::eDispatched: + rv = GetStringFromName( + u"MdnDispatchedReceipt", + getter_Copies(receipt_string)); + break; + case nsIMsgMdnGenerator::eProcessed: + rv = GetStringFromName( + u"MdnProcessedReceipt", + getter_Copies(receipt_string)); + break; + case nsIMsgMdnGenerator::eDeleted: + rv = GetStringFromName( + u"MdnDeletedReceipt", + getter_Copies(receipt_string)); + break; + case nsIMsgMdnGenerator::eDenied: + rv = GetStringFromName( + u"MdnDeniedReceipt", + getter_Copies(receipt_string)); + break; + case nsIMsgMdnGenerator::eFailed: + rv = GetStringFromName( + u"MdnFailedReceipt", + getter_Copies(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, m_charset.get(), 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, m_charset.get(), 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( + u"MsgMdnDisplayed", + getter_Copies(firstPart2)); + break; + case nsIMsgMdnGenerator::eDispatched: + rv = GetStringFromName( + u"MsgMdnDispatched", + getter_Copies(firstPart2)); + break; + case nsIMsgMdnGenerator::eProcessed: + rv = GetStringFromName( + u"MsgMdnProcessed", + getter_Copies(firstPart2)); + break; + case nsIMsgMdnGenerator::eDeleted: + rv = GetStringFromName( + u"MsgMdnDeleted", + getter_Copies(firstPart2)); + break; + case nsIMsgMdnGenerator::eDenied: + rv = GetStringFromName( + u"MsgMdnDenied", + getter_Copies(firstPart2)); + break; + case nsIMsgMdnGenerator::eFailed: + rv = GetStringFromName( + u"MsgMdnFailed", + getter_Copies(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) + { + nsAutoCString userAgentString; + 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.Insert("; ", 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(NS_MSGCOMPUTILS_CONTRACTID, &rv); + if (compUtils) + compUtils->GetMsgMimeConformToStandard(&conformToStandard); + + convbuf = nsMsgI18NEncodeMimePartIIStr( + m_email.get(), true, m_charset.get(), 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); + if (NS_FAILED(rv)) + return rv; + rv = WriteString(CRLF); + 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(NS_SMTPSERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv,rv); + + nsCOMPtr<nsIRequest> aRequest; + smtpService->SendMailMessage(m_file, m_dntRrt.get(), m_identity, + nullptr, this, nullptr, nullptr, false, nullptr, + 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(NS_MSGACCOUNTMANAGER_CONTRACTID, &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); + nsCOMPtr<nsIArray> servIdentities; + accountManager->GetIdentitiesForServer(m_server, getter_AddRefs(servIdentities)); + if (servIdentities) + { + nsCOMPtr<nsIMsgIdentity> ident; + nsCString identEmail; + uint32_t count = 0; + servIdentities->GetLength(&count); + // First check in the "To:" header + for (uint32_t i = 0; i < count; i++) + { + ident = do_QueryElementAt(servIdentities, i, &rv); + if (NS_FAILED(rv)) + continue; + ident->GetEmail(identEmail); + if (!mailTo.IsEmpty() && !identEmail.IsEmpty() && + mailTo.Find(identEmail, CaseInsensitiveCompare) != kNotFound) + { + m_identity = ident; + break; + } + } + // If no match, check the "Cc:" header + if (!m_identity) + { + for (uint32_t i = 0; i < count; i++) + { + rv = servIdentities->QueryElementAt(i, NS_GET_IID(nsIMsgIdentity),getter_AddRefs(ident)); + if (NS_FAILED(rv)) + continue; + ident->GetEmail(identEmail); + if (!mailCC.IsEmpty() && !identEmail.IsEmpty() && + mailCC.Find(identEmail, CaseInsensitiveCompare) != kNotFound) + { + m_identity = ident; + break; + } + } + } + } + + // If no match again, 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); + } + } + } + } + + rv = m_folder->GetCharset(m_charset); + 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 char16_t* exitString; + + switch (aExitCode) + { + case NS_ERROR_UNKNOWN_HOST: + case NS_ERROR_UNKNOWN_PROXY_HOST: + exitString = u"smtpSendFailedUnknownServer"; + break; + case NS_ERROR_CONNECTION_REFUSED: + case NS_ERROR_PROXY_CONNECTION_REFUSED: + exitString = u"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 = u"smtpSendInterrupted"; + break; + case NS_ERROR_NET_TIMEOUT: + case NS_ERROR_NET_RESET: + exitString = u"smtpSendTimeout"; + break; + default: + exitString = errorStringNameForErrorCode(aExitCode); + break; + } + + nsCOMPtr<nsISmtpService> smtpService(do_GetService(NS_SMTPSERVICE_CONTRACTID, &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); + + nsAutoString hostStr; + CopyASCIItoUTF16(smtpHostName, hostStr); + const char16_t *params[] = { hostStr.get() }; + + nsCOMPtr<nsIStringBundle> bundle; + nsCOMPtr<nsIStringBundleService> bundleService = + mozilla::services::GetStringBundleService(); + 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, 1, getter_Copies(failed_msg)); + bundle->GetStringFromName(u"sendMessageErrorTitle", getter_Copies(dialogTitle)); + + nsCOMPtr<nsIPrompt> dialog; + rv = m_window->GetPromptDialog(getter_AddRefs(dialog)); + if (NS_SUCCEEDED(rv)) + dialog->Alert(dialogTitle.get(),failed_msg.get()); + + return NS_OK; +} |