diff options
Diffstat (limited to 'mailnews/compose/src/nsMsgComposeService.cpp')
-rw-r--r-- | mailnews/compose/src/nsMsgComposeService.cpp | 1479 |
1 files changed, 1479 insertions, 0 deletions
diff --git a/mailnews/compose/src/nsMsgComposeService.cpp b/mailnews/compose/src/nsMsgComposeService.cpp new file mode 100644 index 000000000..944f7dbe3 --- /dev/null +++ b/mailnews/compose/src/nsMsgComposeService.cpp @@ -0,0 +1,1479 @@ +/* -*- 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 "nsMsgComposeService.h" +#include "nsMsgCompCID.h" +#include "nsIMsgSend.h" +#include "nsIServiceManager.h" +#include "nsIObserverService.h" +#include "nsIMsgIdentity.h" +#include "nsISmtpUrl.h" +#include "nsIURI.h" +#include "nsMsgI18N.h" +#include "nsIMsgComposeParams.h" +#include "nsXPCOM.h" +#include "nsISupportsPrimitives.h" +#include "nsIWindowWatcher.h" +#include "mozIDOMWindow.h" +#include "nsIContentViewer.h" +#include "nsIMsgWindow.h" +#include "nsIDocShell.h" +#include "nsPIDOMWindow.h" +#include "nsIDOMDocument.h" +#include "nsIDOMHTMLDocument.h" +#include "nsIDOMElement.h" +#include "nsIXULWindow.h" +#include "nsIWindowMediator.h" +#include "nsIDocShellTreeItem.h" +#include "nsIDocShellTreeOwner.h" +#include "nsIBaseWindow.h" +#include "nsIPrefService.h" +#include "nsIPrefBranch.h" +#include "nsMsgBaseCID.h" +#include "nsIMsgAccountManager.h" +#include "nsIMimeMiscStatus.h" +#include "nsIStreamConverter.h" +#include "nsMsgMimeCID.h" +#include "nsToolkitCompsCID.h" +#include "nsNetUtil.h" +#include "nsIMsgMailNewsUrl.h" +#include "nsIInterfaceRequestorUtils.h" +#include "nsIMsgDatabase.h" +#include "nsIDocumentEncoder.h" +#include "nsContentCID.h" +#include "nsISelection.h" +#include "nsUTF8Utils.h" +#include "nsILineBreaker.h" +#include "nsLWBrkCIID.h" +#include "mozilla/Services.h" +#include "mimemoz2.h" +#include "nsIArray.h" +#include "nsArrayUtils.h" + +#ifdef MSGCOMP_TRACE_PERFORMANCE +#include "mozilla/Logging.h" +#include "nsIMsgHdr.h" +#include "nsIMsgMessageService.h" +#include "nsMsgUtils.h" +#endif + +#include "nsICommandLine.h" +#include "nsIAppStartup.h" +#include "nsMsgUtils.h" +#include "nsIPrincipal.h" + +#ifdef XP_WIN32 +#include <windows.h> +#include <shellapi.h> +#include "nsIWidget.h" +#endif + +#define DEFAULT_CHROME "chrome://messenger/content/messengercompose/messengercompose.xul" + +#define PREF_MAILNEWS_REPLY_QUOTING_SELECTION "mailnews.reply_quoting_selection" +#define PREF_MAILNEWS_REPLY_QUOTING_SELECTION_MULTI_WORD "mailnews.reply_quoting_selection.multi_word" +#define PREF_MAILNEWS_REPLY_QUOTING_SELECTION_ONLY_IF "mailnews.reply_quoting_selection.only_if_chars" + +#define MAIL_ROOT_PREF "mail." +#define MAILNEWS_ROOT_PREF "mailnews." +#define HTMLDOMAINUPDATE_VERSION_PREF_NAME "global_html_domains.version" +#define HTMLDOMAINUPDATE_DOMAINLIST_PREF_NAME "global_html_domains" +#define USER_CURRENT_HTMLDOMAINLIST_PREF_NAME "html_domains" +#define USER_CURRENT_PLAINTEXTDOMAINLIST_PREF_NAME "plaintext_domains" +#define DOMAIN_DELIMITER ',' + +#ifdef MSGCOMP_TRACE_PERFORMANCE +static PRLogModuleInfo *MsgComposeLogModule = nullptr; + +static uint32_t GetMessageSizeFromURI(const char * originalMsgURI) +{ + uint32_t msgSize = 0; + + if (originalMsgURI && *originalMsgURI) + { + nsCOMPtr <nsIMsgDBHdr> originalMsgHdr; + GetMsgDBHdrFromURI(originalMsgURI, getter_AddRefs(originalMsgHdr)); + if (originalMsgHdr) + originalMsgHdr->GetMessageSize(&msgSize); + } + + return msgSize; +} +#endif + +nsMsgComposeService::nsMsgComposeService() +{ + +// Defaulting the value of mLogComposePerformance to FALSE to prevent logging. + mLogComposePerformance = false; +#ifdef MSGCOMP_TRACE_PERFORMANCE + if (!MsgComposeLogModule) + MsgComposeLogModule = PR_NewLogModule("msgcompose"); + + mStartTime = PR_IntervalNow(); + mPreviousTime = mStartTime; +#endif + +} + +NS_IMPL_ISUPPORTS(nsMsgComposeService, + nsIMsgComposeService, + ICOMMANDLINEHANDLER, + nsISupportsWeakReference) + +nsMsgComposeService::~nsMsgComposeService() +{ + mOpenComposeWindows.Clear(); +} + +nsresult nsMsgComposeService::Init() +{ + nsresult rv = NS_OK; + + Reset(); + + AddGlobalHtmlDomains(); + // Since the compose service should only be initialized once, we can + // be pretty sure there aren't any existing compose windows open. + MsgCleanupTempFiles("nsmail", "tmp"); + MsgCleanupTempFiles("nsemail", "html"); + MsgCleanupTempFiles("nscopy", "tmp"); + return rv; +} + +void nsMsgComposeService::Reset() +{ + mOpenComposeWindows.Clear(); + + nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID)); + if (prefs) + prefs->GetBoolPref("mailnews.logComposePerformance", &mLogComposePerformance); +} + +// Function to open a message compose window and pass an nsIMsgComposeParams +// parameter to it. +NS_IMETHODIMP +nsMsgComposeService::OpenComposeWindowWithParams(const char *chrome, + nsIMsgComposeParams *params) +{ + NS_ENSURE_ARG_POINTER(params); +#ifdef MSGCOMP_TRACE_PERFORMANCE + if(mLogComposePerformance) + { + TimeStamp("Start opening the window", true); + } +#endif + + nsresult rv; + + NS_ENSURE_ARG_POINTER(params); + + //Use default identity if no identity has been specified + nsCOMPtr<nsIMsgIdentity> identity; + params->GetIdentity(getter_AddRefs(identity)); + if (!identity) + { + GetDefaultIdentity(getter_AddRefs(identity)); + params->SetIdentity(identity); + } + + // Create a new window. + nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID)); + if (!wwatch) + return NS_ERROR_FAILURE; + + nsCOMPtr<nsISupportsInterfacePointer> msgParamsWrapper = + do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + msgParamsWrapper->SetData(params); + msgParamsWrapper->SetDataIID(&NS_GET_IID(nsIMsgComposeParams)); + + nsCOMPtr<mozIDOMWindowProxy> newWindow; + rv = wwatch->OpenWindow(0, chrome && *chrome ? chrome : DEFAULT_CHROME, + "_blank", "all,chrome,dialog=no,status,toolbar", msgParamsWrapper, + getter_AddRefs(newWindow)); + + return rv; +} + +NS_IMETHODIMP +nsMsgComposeService::DetermineComposeHTML(nsIMsgIdentity *aIdentity, MSG_ComposeFormat aFormat, bool *aComposeHTML) +{ + NS_ENSURE_ARG_POINTER(aComposeHTML); + + *aComposeHTML = true; + switch (aFormat) + { + case nsIMsgCompFormat::HTML: + *aComposeHTML = true; + break; + case nsIMsgCompFormat::PlainText: + *aComposeHTML = false; + break; + + default: + nsCOMPtr<nsIMsgIdentity> identity = aIdentity; + if (!identity) + GetDefaultIdentity(getter_AddRefs(identity)); + + if (identity) + { + identity->GetComposeHtml(aComposeHTML); + if (aFormat == nsIMsgCompFormat::OppositeOfDefault) + *aComposeHTML = !*aComposeHTML; + } + else + { + // default identity not found. Use the mail.html_compose pref to determine + // message compose type (HTML or PlainText). + nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID)); + if (prefs) + { + nsresult rv; + bool useHTMLCompose; + rv = prefs->GetBoolPref(MAIL_ROOT_PREF "html_compose", &useHTMLCompose); + if (NS_SUCCEEDED(rv)) + *aComposeHTML = useHTMLCompose; + } + } + break; + } + + return NS_OK; +} + +nsresult +nsMsgComposeService::GetOrigWindowSelection(MSG_ComposeType type, nsIMsgWindow *aMsgWindow, nsACString& aSelHTML) +{ + nsresult rv; + + // Good hygiene + aSelHTML.Truncate(); + + // Get the pref to see if we even should do reply quoting selection + nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + bool replyQuotingSelection; + rv = prefs->GetBoolPref(PREF_MAILNEWS_REPLY_QUOTING_SELECTION, &replyQuotingSelection); + NS_ENSURE_SUCCESS(rv, rv); + if (!replyQuotingSelection) + return NS_ERROR_ABORT; + + // Now delve down in to the message to get the HTML representation of the selection + nsCOMPtr<nsIDocShell> rootDocShell; + rv = aMsgWindow->GetRootDocShell(getter_AddRefs(rootDocShell)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIDocShellTreeItem> childAsItem; + rv = rootDocShell->FindChildWithName(NS_LITERAL_STRING("messagepane"), + true, false, nullptr, nullptr, getter_AddRefs(childAsItem)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(childAsItem, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<mozIDOMWindowProxy> domWindow(do_GetInterface(childAsItem)); + NS_ENSURE_TRUE(domWindow, NS_ERROR_FAILURE); + nsCOMPtr<nsPIDOMWindowOuter> privateWindow = nsPIDOMWindowOuter::From(domWindow); + nsCOMPtr<nsISelection> sel = privateWindow->GetSelection(); + NS_ENSURE_TRUE(sel, NS_ERROR_FAILURE); + + bool requireMultipleWords = true; + nsAutoCString charsOnlyIf; + prefs->GetBoolPref(PREF_MAILNEWS_REPLY_QUOTING_SELECTION_MULTI_WORD, &requireMultipleWords); + prefs->GetCharPref(PREF_MAILNEWS_REPLY_QUOTING_SELECTION_ONLY_IF, getter_Copies(charsOnlyIf)); + if (sel && (requireMultipleWords || !charsOnlyIf.IsEmpty())) + { + nsAutoString selPlain; + rv = sel->ToString(selPlain); + NS_ENSURE_SUCCESS(rv, rv); + + // If "mailnews.reply_quoting_selection.multi_word" is on, then there must be at least + // two words selected in order to quote just the selected text + if (requireMultipleWords) + { + if (selPlain.IsEmpty()) + return NS_ERROR_ABORT; + + nsCOMPtr<nsILineBreaker> lineBreaker = do_GetService(NS_LBRK_CONTRACTID, &rv); + + if (NS_SUCCEEDED(rv)) + { + const uint32_t length = selPlain.Length(); + const char16_t* unicodeStr = selPlain.get(); + int32_t endWordPos = lineBreaker->Next(unicodeStr, length, 0); + + // If there's not even one word, then there's not multiple words + if (endWordPos == NS_LINEBREAKER_NEED_MORE_TEXT) + return NS_ERROR_ABORT; + + // If after the first word is only space, then there's not multiple words + const char16_t* end; + for (end = unicodeStr + endWordPos; NS_IsSpace(*end); end++) + ; + if (!*end) + return NS_ERROR_ABORT; + } + } + + if (!charsOnlyIf.IsEmpty()) + { + if (MsgFindCharInSet(selPlain, charsOnlyIf.get()) < 0) + return NS_ERROR_ABORT; + } + } + + nsCOMPtr<nsIContentViewer> contentViewer; + rv = docShell->GetContentViewer(getter_AddRefs(contentViewer)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIDOMDocument> domDocument; + rv = contentViewer->GetDOMDocument(getter_AddRefs(domDocument)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIDocumentEncoder> docEncoder(do_CreateInstance(NS_HTMLCOPY_ENCODER_CONTRACTID, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = docEncoder->Init(domDocument, NS_LITERAL_STRING("text/html"), 0); + NS_ENSURE_SUCCESS(rv, rv); + + rv = docEncoder->SetSelection(sel); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoString selHTML; + rv = docEncoder->EncodeToString(selHTML); + NS_ENSURE_SUCCESS(rv, rv); + + // Now remove <span class="moz-txt-citetags">> </span>. + nsAutoCString html(NS_ConvertUTF16toUTF8(selHTML).get()); + int32_t spanInd = html.Find("<span class=\"moz-txt-citetags\">"); + while (spanInd != kNotFound) { + nsAutoCString right0(Substring(html, spanInd)); + int32_t endInd = right0.Find("</span>"); + if (endInd == kNotFound) + break; // oops, where is the closing tag gone? + nsAutoCString right1(Substring(html, spanInd + endInd + 7)); + html.SetLength(spanInd); + html.Append(right1); + spanInd = html.Find("<span class=\"moz-txt-citetags\">"); + } + + aSelHTML.Assign(html); + + return rv; +} + +NS_IMETHODIMP +nsMsgComposeService::OpenComposeWindow(const char *msgComposeWindowURL, nsIMsgDBHdr *origMsgHdr, const char *originalMsgURI, + MSG_ComposeType type, MSG_ComposeFormat format, nsIMsgIdentity * aIdentity, nsIMsgWindow *aMsgWindow) +{ + nsresult rv; + + // Check for any reply type that wants to ignore the quote. + bool ignoreQuote = false; + if (type >= nsIMsgCompType::ReplyIgnoreQuote) { + type -= nsIMsgCompType::ReplyIgnoreQuote; + ignoreQuote = true; + } + + nsCOMPtr<nsIMsgIdentity> identity = aIdentity; + if (!identity) + GetDefaultIdentity(getter_AddRefs(identity)); + + /* Actually, the only way to implement forward inline is to simulate a template message. + Maybe one day when we will have more time we can change that + */ + if (type == nsIMsgCompType::ForwardInline || type == nsIMsgCompType::Draft || type == nsIMsgCompType::Template + || type == nsIMsgCompType::ReplyWithTemplate || type == nsIMsgCompType::Redirect) + { + nsAutoCString uriToOpen(originalMsgURI); + uriToOpen += (uriToOpen.FindChar('?') == kNotFound) ? '?' : '&'; + uriToOpen.Append("fetchCompleteMessage=true"); + if (type == nsIMsgCompType::Redirect) + uriToOpen.Append("&redirect=true"); + + return LoadDraftOrTemplate(uriToOpen, type == nsIMsgCompType::ForwardInline || type == nsIMsgCompType::Draft ? + nsMimeOutput::nsMimeMessageDraftOrTemplate : nsMimeOutput::nsMimeMessageEditorTemplate, + identity, originalMsgURI, origMsgHdr, type == nsIMsgCompType::ForwardInline, + format == nsIMsgCompFormat::OppositeOfDefault, aMsgWindow); + } + + nsCOMPtr<nsIMsgComposeParams> pMsgComposeParams (do_CreateInstance(NS_MSGCOMPOSEPARAMS_CONTRACTID, &rv)); + if (NS_SUCCEEDED(rv) && pMsgComposeParams) + { + nsCOMPtr<nsIMsgCompFields> pMsgCompFields (do_CreateInstance(NS_MSGCOMPFIELDS_CONTRACTID, &rv)); + if (NS_SUCCEEDED(rv) && pMsgCompFields) + { + pMsgComposeParams->SetType(type); + pMsgComposeParams->SetFormat(format); + pMsgComposeParams->SetIdentity(identity); + + // When doing a reply (except with a template) see if there's a selection that we should quote + if (!ignoreQuote && + (type == nsIMsgCompType::Reply || + type == nsIMsgCompType::ReplyAll || + type == nsIMsgCompType::ReplyToSender || + type == nsIMsgCompType::ReplyToGroup || + type == nsIMsgCompType::ReplyToSenderAndGroup || + type == nsIMsgCompType::ReplyToList)) + { + nsAutoCString selHTML; + if (NS_SUCCEEDED(GetOrigWindowSelection(type, aMsgWindow, selHTML))) + pMsgComposeParams->SetHtmlToQuote(selHTML); + } + + if (originalMsgURI && *originalMsgURI) + { + if (type == nsIMsgCompType::NewsPost) + { + nsAutoCString newsURI(originalMsgURI); + nsAutoCString group; + nsAutoCString host; + + int32_t slashpos = newsURI.RFindChar('/'); + if (slashpos > 0 ) + { + // uri is "[s]news://host[:port]/group" + host = StringHead(newsURI, slashpos); + group = Substring(newsURI, slashpos + 1); + + } + else + group = originalMsgURI; + + nsAutoCString unescapedName; + MsgUnescapeString(group, + nsINetUtil::ESCAPE_URL_FILE_BASENAME | nsINetUtil::ESCAPE_URL_FORCED, + unescapedName); + pMsgCompFields->SetNewsgroups(NS_ConvertUTF8toUTF16(unescapedName)); + pMsgCompFields->SetNewspostUrl(host.get()); + } + else + { + pMsgComposeParams->SetOriginalMsgURI(originalMsgURI); + pMsgComposeParams->SetOrigMsgHdr(origMsgHdr); + } + } + + pMsgComposeParams->SetComposeFields(pMsgCompFields); + + if(mLogComposePerformance) + { +#ifdef MSGCOMP_TRACE_PERFORMANCE + // ducarroz, properly fix this in the case of new message (not a reply) + if (type != nsIMsgCompType::NewsPost) { + char buff[256]; + sprintf(buff, "Start opening the window, message size = %d", GetMessageSizeFromURI(originalMsgURI)); + TimeStamp(buff, true); + } +#endif + }//end if(mLogComposePerformance) + + rv = OpenComposeWindowWithParams(msgComposeWindowURL, pMsgComposeParams); + } + } + return rv; +} + +NS_IMETHODIMP nsMsgComposeService::GetParamsForMailto(nsIURI * aURI, nsIMsgComposeParams ** aParams) +{ + nsresult rv = NS_OK; + if (aURI) + { + nsCOMPtr<nsIMailtoUrl> aMailtoUrl; + rv = aURI->QueryInterface(NS_GET_IID(nsIMailtoUrl), getter_AddRefs(aMailtoUrl)); + if (NS_SUCCEEDED(rv)) + { + MSG_ComposeFormat requestedComposeFormat = nsIMsgCompFormat::Default; + nsCString toPart; + nsCString ccPart; + nsCString bccPart; + nsCString subjectPart; + nsCString bodyPart; + nsCString newsgroup; + nsCString refPart; + nsCString HTMLBodyPart; + + aMailtoUrl->GetMessageContents(toPart, ccPart, bccPart, subjectPart, + bodyPart, HTMLBodyPart, refPart, + newsgroup, &requestedComposeFormat); + + nsAutoString sanitizedBody; + + bool composeHTMLFormat; + DetermineComposeHTML(NULL, requestedComposeFormat, &composeHTMLFormat); + + // If there was an 'html-body' param, finding it will have requested + // HTML format in GetMessageContents, so we try to use it first. If it's + // empty, but we are composing in HTML because of the user's prefs, the + // 'body' param needs to be escaped, since it's supposed to be plain + // text, but it then doesn't need to sanitized. + nsString rawBody; + if (HTMLBodyPart.IsEmpty()) + { + if (composeHTMLFormat) + { + char *escaped = MsgEscapeHTML(bodyPart.get()); + if (!escaped) + return NS_ERROR_OUT_OF_MEMORY; + + CopyUTF8toUTF16(nsDependentCString(escaped), sanitizedBody); + free(escaped); + } + else + CopyUTF8toUTF16(bodyPart, rawBody); + } + else + CopyUTF8toUTF16(HTMLBodyPart, rawBody); + + if (!rawBody.IsEmpty() && composeHTMLFormat) + { + //For security reason, we must sanitize the message body before accepting any html... + + rv = HTMLSanitize(rawBody, sanitizedBody); // from mimemoz2.h + + if (NS_FAILED(rv)) + { + // Something went horribly wrong with parsing for html format + // in the body. Set composeHTMLFormat to false so we show the + // plain text mail compose. + composeHTMLFormat = false; + } + } + + nsCOMPtr<nsIMsgComposeParams> pMsgComposeParams (do_CreateInstance(NS_MSGCOMPOSEPARAMS_CONTRACTID, &rv)); + if (NS_SUCCEEDED(rv) && pMsgComposeParams) + { + pMsgComposeParams->SetType(nsIMsgCompType::MailToUrl); + pMsgComposeParams->SetFormat(composeHTMLFormat ? nsIMsgCompFormat::HTML : nsIMsgCompFormat::PlainText); + + + nsCOMPtr<nsIMsgCompFields> pMsgCompFields (do_CreateInstance(NS_MSGCOMPFIELDS_CONTRACTID, &rv)); + if (pMsgCompFields) + { + //ugghh more conversion work!!!! + pMsgCompFields->SetTo(NS_ConvertUTF8toUTF16(toPart)); + pMsgCompFields->SetCc(NS_ConvertUTF8toUTF16(ccPart)); + pMsgCompFields->SetBcc(NS_ConvertUTF8toUTF16(bccPart)); + pMsgCompFields->SetNewsgroups(NS_ConvertUTF8toUTF16(newsgroup)); + pMsgCompFields->SetReferences(refPart.get()); + pMsgCompFields->SetSubject(NS_ConvertUTF8toUTF16(subjectPart)); + pMsgCompFields->SetBody(composeHTMLFormat ? sanitizedBody : rawBody); + pMsgComposeParams->SetComposeFields(pMsgCompFields); + + NS_ADDREF(*aParams = pMsgComposeParams); + return NS_OK; + } + } // if we created msg compose params.... + } // if we had a mailto url + } // if we had a url... + + // if we got here we must have encountered an error + *aParams = nullptr; + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP nsMsgComposeService::OpenComposeWindowWithURI(const char * aMsgComposeWindowURL, nsIURI * aURI, nsIMsgIdentity *identity) +{ + nsCOMPtr<nsIMsgComposeParams> pMsgComposeParams; + nsresult rv = GetParamsForMailto(aURI, getter_AddRefs(pMsgComposeParams)); + if (NS_SUCCEEDED(rv)) { + pMsgComposeParams->SetIdentity(identity); + rv = OpenComposeWindowWithParams(aMsgComposeWindowURL, pMsgComposeParams); + } + return rv; +} + +NS_IMETHODIMP nsMsgComposeService::InitCompose(nsIMsgComposeParams *aParams, + mozIDOMWindowProxy *aWindow, + nsIDocShell *aDocShell, + nsIMsgCompose **_retval) +{ + nsresult rv; + nsCOMPtr<nsIMsgCompose> msgCompose = + do_CreateInstance(NS_MSGCOMPOSE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv,rv); + + rv = msgCompose->Initialize(aParams, aWindow, aDocShell); + NS_ENSURE_SUCCESS(rv,rv); + + NS_IF_ADDREF(*_retval = msgCompose); + return rv; +} + +NS_IMETHODIMP +nsMsgComposeService::GetDefaultIdentity(nsIMsgIdentity **_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + *_retval = nullptr; + + nsresult rv; + nsCOMPtr<nsIMsgAccountManager> accountManager = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIMsgAccount> defaultAccount; + rv = accountManager->GetDefaultAccount(getter_AddRefs(defaultAccount)); + NS_ENSURE_SUCCESS(rv, rv); + + return defaultAccount->GetDefaultIdentity(_retval); +} + +/* readonly attribute boolean logComposePerformance; */ +NS_IMETHODIMP nsMsgComposeService::GetLogComposePerformance(bool *aLogComposePerformance) +{ + *aLogComposePerformance = mLogComposePerformance; + return NS_OK; +} + +NS_IMETHODIMP nsMsgComposeService::TimeStamp(const char * label, bool resetTime) +{ + if (!mLogComposePerformance) + return NS_OK; + +#ifdef MSGCOMP_TRACE_PERFORMANCE + + PRIntervalTime now; + + if (resetTime) + { + MOZ_LOG(MsgComposeLogModule, mozilla::LogLevel::Info, ("\n[process]: [totalTime][deltaTime]\n--------------------\n")); + + mStartTime = PR_IntervalNow(); + mPreviousTime = mStartTime; + now = mStartTime; + } + else + now = PR_IntervalNow(); + + PRIntervalTime totalTime = PR_IntervalToMilliseconds(now - mStartTime); + PRIntervalTime deltaTime = PR_IntervalToMilliseconds(now - mPreviousTime); + + MOZ_LOG(MsgComposeLogModule, mozilla::LogLevel::Info, ("[%3.2f][%3.2f] - %s\n", +((double)totalTime/1000.0) + 0.005, ((double)deltaTime/1000.0) + 0.005, label)); + + mPreviousTime = now; +#endif + return NS_OK; +} + +class nsMsgTemplateReplyHelper final: public nsIStreamListener, + public nsIUrlListener +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIURLLISTENER + NS_DECL_NSISTREAMLISTENER + NS_DECL_NSIREQUESTOBSERVER + + nsMsgTemplateReplyHelper(); + + nsCOMPtr<nsIMsgDBHdr> mHdrToReplyTo; + nsCOMPtr<nsIMsgDBHdr> mTemplateHdr; + nsCOMPtr<nsIMsgWindow> mMsgWindow; + nsCOMPtr<nsIMsgIdentity> mIdentity; + nsCString mTemplateBody; + bool mInMsgBody; + char mLastBlockChars[3]; + +private: + ~nsMsgTemplateReplyHelper(); +}; + +NS_IMPL_ISUPPORTS(nsMsgTemplateReplyHelper, + nsIStreamListener, + nsIRequestObserver, + nsIUrlListener) + +nsMsgTemplateReplyHelper::nsMsgTemplateReplyHelper() +{ + mInMsgBody = false; + memset(mLastBlockChars, 0, sizeof(mLastBlockChars)); +} + +nsMsgTemplateReplyHelper::~nsMsgTemplateReplyHelper() +{ +} + + +NS_IMETHODIMP nsMsgTemplateReplyHelper::OnStartRunningUrl(nsIURI *aUrl) +{ + return NS_OK; +} + +NS_IMETHODIMP nsMsgTemplateReplyHelper::OnStopRunningUrl(nsIURI *aUrl, nsresult aExitCode) +{ + NS_ENSURE_SUCCESS(aExitCode, aExitCode); + nsresult rv; + nsCOMPtr<nsPIDOMWindowOuter> parentWindow; + if (mMsgWindow) + { + nsCOMPtr<nsIDocShell> docShell; + rv = mMsgWindow->GetRootDocShell(getter_AddRefs(docShell)); + NS_ENSURE_SUCCESS(rv, rv); + parentWindow = do_GetInterface(docShell); + NS_ENSURE_TRUE(parentWindow, NS_ERROR_FAILURE); + } + + // create the compose params object + nsCOMPtr<nsIMsgComposeParams> pMsgComposeParams (do_CreateInstance(NS_MSGCOMPOSEPARAMS_CONTRACTID, &rv)); + if (NS_FAILED(rv) || (!pMsgComposeParams) ) return rv ; + nsCOMPtr<nsIMsgCompFields> compFields = do_CreateInstance(NS_MSGCOMPFIELDS_CONTRACTID, &rv) ; + + nsCString replyTo; + mHdrToReplyTo->GetStringProperty("replyTo", getter_Copies(replyTo)); + if (replyTo.IsEmpty()) + mHdrToReplyTo->GetAuthor(getter_Copies(replyTo)); + compFields->SetTo(NS_ConvertUTF8toUTF16(replyTo)); + + nsString body; + nsString templateSubject, replySubject; + + mHdrToReplyTo->GetMime2DecodedSubject(replySubject); + mTemplateHdr->GetMime2DecodedSubject(templateSubject); + nsString subject(NS_LITERAL_STRING("Auto: ")); // RFC 3834 3.1.5. + subject.Append(templateSubject); + if (!replySubject.IsEmpty()) + { + subject.Append(NS_LITERAL_STRING(" (was: ")); + subject.Append(replySubject); + subject.Append(NS_LITERAL_STRING(")")); + } + + compFields->SetSubject(subject); + compFields->SetRawHeader("Auto-Submitted", NS_LITERAL_CSTRING("auto-replied"), nullptr); + + nsCString charset; + rv = mTemplateHdr->GetCharset(getter_Copies(charset)); + NS_ENSURE_SUCCESS(rv, rv); + compFields->SetCharacterSet(charset.get()); + rv = nsMsgI18NConvertToUnicode(charset.get(), mTemplateBody, body); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "couldn't convert templ body to unicode"); + compFields->SetBody(body); + + nsCString msgUri; + nsCOMPtr <nsIMsgFolder> folder; + mHdrToReplyTo->GetFolder(getter_AddRefs(folder)); + folder->GetUriForMsg(mHdrToReplyTo, msgUri); + // populate the compose params + pMsgComposeParams->SetType(nsIMsgCompType::ReplyWithTemplate); + pMsgComposeParams->SetFormat(nsIMsgCompFormat::Default); + pMsgComposeParams->SetIdentity(mIdentity); + pMsgComposeParams->SetComposeFields(compFields); + pMsgComposeParams->SetOriginalMsgURI(msgUri.get()); + + // create the nsIMsgCompose object to send the object + nsCOMPtr<nsIMsgCompose> pMsgCompose (do_CreateInstance(NS_MSGCOMPOSE_CONTRACTID, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + /** initialize nsIMsgCompose, Send the message, wait for send completion response **/ + + rv = pMsgCompose->Initialize(pMsgComposeParams, parentWindow, nullptr); + NS_ENSURE_SUCCESS(rv,rv); + + return pMsgCompose->SendMsg(nsIMsgSend::nsMsgDeliverNow, mIdentity, nullptr, nullptr, nullptr) ; +} + +NS_IMETHODIMP +nsMsgTemplateReplyHelper::OnStartRequest(nsIRequest* request, nsISupports* aSupport) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsMsgTemplateReplyHelper::OnStopRequest(nsIRequest* request, nsISupports* aSupport, + nsresult status) +{ + if (NS_SUCCEEDED(status)) + { + // now we've got the message body in mTemplateBody - + // need to set body in compose params and send the reply. + } + return NS_OK; +} + +NS_IMETHODIMP +nsMsgTemplateReplyHelper::OnDataAvailable(nsIRequest* request, + nsISupports* aSupport, + nsIInputStream* inStream, + uint64_t srcOffset, + uint32_t count) +{ + nsresult rv = NS_OK; + + char readBuf[1024]; + + uint64_t available; + uint32_t readCount; + uint32_t maxReadCount = sizeof(readBuf) - 1; + + rv = inStream->Available(&available); + while (NS_SUCCEEDED(rv) && available > 0) + { + uint32_t bodyOffset = 0, readOffset = 0; + if (!mInMsgBody && mLastBlockChars[0]) + { + memcpy(readBuf, mLastBlockChars, 3); + readOffset = 3; + maxReadCount -= 3; + } + if (maxReadCount > available) + maxReadCount = (uint32_t)available; + memset(readBuf, 0, sizeof(readBuf)); + rv = inStream->Read(readBuf + readOffset, maxReadCount, &readCount); + available -= readCount; + readCount += readOffset; + // we're mainly interested in the msg body, so we need to + // find the header/body delimiter of a blank line. A blank line + // looks like <CR><CR>, <LF><LF>, or <CRLF><CRLF> + if (!mInMsgBody) + { + for (uint32_t charIndex = 0; charIndex < readCount && !bodyOffset; charIndex++) + { + if (readBuf[charIndex] == '\r' || readBuf[charIndex] == '\n') + { + if (charIndex + 1 < readCount) + { + if (readBuf[charIndex] == readBuf[charIndex + 1]) + { + // got header+body separator + bodyOffset = charIndex + 2; + break; + } + else if ((charIndex + 3 < readCount) && !strncmp(readBuf + charIndex, "\r\n\r\n", 4)) + { + bodyOffset = charIndex + 4; + break; + } + } + } + } + mInMsgBody = bodyOffset != 0; + if (!mInMsgBody && readCount > 3) // still in msg hdrs + strncpy(mLastBlockChars, readBuf + readCount - 3, 3); + } + mTemplateBody.Append(readBuf + bodyOffset); + } + return NS_OK; +} + + +NS_IMETHODIMP nsMsgComposeService::ReplyWithTemplate(nsIMsgDBHdr *aMsgHdr, const char *templateUri, + nsIMsgWindow *aMsgWindow, nsIMsgIncomingServer *aServer) +{ + // To reply with template, we need the message body of the template. + // I think we're going to need to stream the template message to ourselves, + // and construct the body, and call setBody on the compFields. + nsresult rv; + nsCOMPtr <nsIMsgAccountManager> accountManager = + do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIMsgAccount> account; + rv = accountManager->FindAccountForServer(aServer, getter_AddRefs(account)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIArray> identities; + rv = account->GetIdentities(getter_AddRefs(identities)); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoCString recipients; + aMsgHdr->GetRecipients(getter_Copies(recipients)); + + nsAutoCString ccList; + aMsgHdr->GetCcList(getter_Copies(ccList)); + + // Go through the identities to see to whom this was addressed. + // In case we get no match, this is likely a list/bulk/bcc/spam mail and we + // shouldn't reply. RFC 3834 2. + nsCOMPtr<nsIMsgIdentity> identity; // identity to reply from + uint32_t count = 0; + identities->GetLength(&count); + for (uint32_t i = 0; i < count; i++) + { + nsCOMPtr<nsIMsgIdentity> anIdentity(do_QueryElementAt(identities, i, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoCString identityEmail; + anIdentity->GetEmail(identityEmail); + + if (recipients.Find(identityEmail, CaseInsensitiveCompare) != kNotFound || + ccList.Find(identityEmail, CaseInsensitiveCompare) != kNotFound) + { + identity = anIdentity; + break; + } + } + if (!identity) // Found no match -> don't reply. + return NS_ERROR_ABORT; + + RefPtr<nsMsgTemplateReplyHelper> helper = new nsMsgTemplateReplyHelper; + + helper->mHdrToReplyTo = aMsgHdr; + helper->mMsgWindow = aMsgWindow; + helper->mIdentity = identity; + + nsAutoCString replyTo; + aMsgHdr->GetStringProperty("replyTo", getter_Copies(replyTo)); + if (replyTo.IsEmpty()) + aMsgHdr->GetAuthor(getter_Copies(replyTo)); + if (replyTo.IsEmpty()) + return NS_ERROR_FAILURE; // nowhere to send the reply + + nsCOMPtr <nsIMsgFolder> templateFolder; + nsCOMPtr <nsIMsgDatabase> templateDB; + nsCString templateMsgHdrUri; + const char * query = PL_strstr(templateUri, "?messageId="); + if (!query) + return NS_ERROR_FAILURE; + + nsAutoCString folderUri(Substring(templateUri, query)); + rv = GetExistingFolder(folderUri, getter_AddRefs(templateFolder)); + NS_ENSURE_SUCCESS(rv, rv); + rv = templateFolder->GetMsgDatabase(getter_AddRefs(templateDB)); + NS_ENSURE_SUCCESS(rv, rv); + + const char *subject = PL_strstr(templateUri, "&subject="); + if (subject) + { + const char *subjectEnd = subject + strlen(subject); + nsAutoCString messageId(Substring(query + 11, subject)); + nsAutoCString subjectString(Substring(subject + 9, subjectEnd)); + templateDB->GetMsgHdrForMessageID(messageId.get(), getter_AddRefs(helper->mTemplateHdr)); + if (helper->mTemplateHdr) + templateFolder->GetUriForMsg(helper->mTemplateHdr, templateMsgHdrUri); + // to use the subject, we'd need to expose a method to find a message by subject, + // or painfully iterate through messages...We'll try to make the message-id + // not change when saving a template first. + } + if (templateMsgHdrUri.IsEmpty()) + { + // ### probably want to return a specific error and + // have the calling code disable the filter. + NS_ASSERTION(false, "failed to get msg hdr"); + return NS_ERROR_FAILURE; + } + // we need to convert the template uri, which is of the form + // <folder uri>?messageId=<messageId>&subject=<subject> + nsCOMPtr <nsIMsgMessageService> msgService; + rv = GetMessageServiceFromURI(templateMsgHdrUri, getter_AddRefs(msgService)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsISupports> listenerSupports; + helper->QueryInterface(NS_GET_IID(nsISupports), getter_AddRefs(listenerSupports)); + + nsCOMPtr<nsIURI> dummyNull; + rv = msgService->StreamMessage(templateMsgHdrUri.get(), listenerSupports, + aMsgWindow, helper, + false, // convert data + EmptyCString(), false, getter_AddRefs(dummyNull)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIMsgFolder> folder; + aMsgHdr->GetFolder(getter_AddRefs(folder)); + if (!folder) + return NS_ERROR_NULL_POINTER; + + // We're sending a new message. Conceptually it's a reply though, so mark the + // original message as replied. + return folder->AddMessageDispositionState(aMsgHdr, nsIMsgFolder::nsMsgDispositionState_Replied); +} + +NS_IMETHODIMP +nsMsgComposeService::ForwardMessage(const nsAString &forwardTo, + nsIMsgDBHdr *aMsgHdr, + nsIMsgWindow *aMsgWindow, + nsIMsgIncomingServer *aServer, + uint32_t aForwardType) +{ + NS_ENSURE_ARG_POINTER(aMsgHdr); + + nsresult rv; + if (aForwardType == nsIMsgComposeService::kForwardAsDefault) + { + int32_t forwardPref = 0; + nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + prefBranch->GetIntPref("mail.forward_message_mode", &forwardPref); + // 0=default as attachment 2=forward as inline with attachments, + // (obsolete 4.x value)1=forward as quoted (mapped to 2 in mozilla) + aForwardType = forwardPref == 0 ? nsIMsgComposeService::kForwardAsAttachment : + nsIMsgComposeService::kForwardInline; + } + nsCString msgUri; + + nsCOMPtr<nsIMsgFolder> folder; + aMsgHdr->GetFolder(getter_AddRefs(folder)); + NS_ENSURE_TRUE(folder, NS_ERROR_NULL_POINTER); + + folder->GetUriForMsg(aMsgHdr, msgUri); + + nsAutoCString uriToOpen(msgUri); + uriToOpen += (uriToOpen.FindChar('?') == kNotFound) ? '?' : '&'; + uriToOpen.Append("fetchCompleteMessage=true"); + + // get the MsgIdentity for the above key using AccountManager + nsCOMPtr<nsIMsgAccountManager> accountManager = + do_GetService (NS_MSGACCOUNTMANAGER_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIMsgAccount> account; + nsCOMPtr<nsIMsgIdentity> identity; + + rv = accountManager->FindAccountForServer(aServer, getter_AddRefs(account)); + NS_ENSURE_SUCCESS(rv, rv); + rv = account->GetDefaultIdentity(getter_AddRefs(identity)); + // Use default identity if no identity has been found on this account + if (NS_FAILED(rv) || !identity) + { + rv = GetDefaultIdentity(getter_AddRefs(identity)); + NS_ENSURE_SUCCESS(rv, rv); + } + + if (aForwardType == nsIMsgComposeService::kForwardInline) + return RunMessageThroughMimeDraft(uriToOpen, + nsMimeOutput::nsMimeMessageDraftOrTemplate, + identity, + uriToOpen.get(), aMsgHdr, + true, forwardTo, + false, aMsgWindow); + + nsCOMPtr<mozIDOMWindowProxy> parentWindow; + if (aMsgWindow) + { + nsCOMPtr<nsIDocShell> docShell; + rv = aMsgWindow->GetRootDocShell(getter_AddRefs(docShell)); + NS_ENSURE_SUCCESS(rv, rv); + parentWindow = do_GetInterface(docShell); + NS_ENSURE_TRUE(parentWindow, NS_ERROR_FAILURE); + } + // create the compose params object + nsCOMPtr<nsIMsgComposeParams> pMsgComposeParams (do_CreateInstance(NS_MSGCOMPOSEPARAMS_CONTRACTID, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr<nsIMsgCompFields> compFields = do_CreateInstance(NS_MSGCOMPFIELDS_CONTRACTID, &rv); + + compFields->SetTo(forwardTo); + // populate the compose params + pMsgComposeParams->SetType(nsIMsgCompType::ForwardAsAttachment); + pMsgComposeParams->SetFormat(nsIMsgCompFormat::Default); + pMsgComposeParams->SetIdentity(identity); + pMsgComposeParams->SetComposeFields(compFields); + pMsgComposeParams->SetOriginalMsgURI(uriToOpen.get()); + // create the nsIMsgCompose object to send the object + nsCOMPtr<nsIMsgCompose> pMsgCompose (do_CreateInstance(NS_MSGCOMPOSE_CONTRACTID, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + /** initialize nsIMsgCompose, Send the message, wait for send completion response **/ + rv = pMsgCompose->Initialize(pMsgComposeParams, parentWindow, nullptr); + NS_ENSURE_SUCCESS(rv, rv); + + rv = pMsgCompose->SendMsg(nsIMsgSend::nsMsgDeliverNow, identity, nullptr, nullptr, nullptr); + NS_ENSURE_SUCCESS(rv, rv); + + // nsMsgCompose::ProcessReplyFlags usually takes care of marking messages + // as forwarded. ProcessReplyFlags is normally called from + // nsMsgComposeSendListener::OnStopSending but for this case the msgCompose + // object is not set so ProcessReplyFlags won't get called. + // Therefore, let's just mark it here instead. + return folder->AddMessageDispositionState(aMsgHdr, nsIMsgFolder::nsMsgDispositionState_Forwarded); +} + +nsresult nsMsgComposeService::AddGlobalHtmlDomains() +{ + + nsresult rv; + nsCOMPtr<nsIPrefService> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv,rv); + + nsCOMPtr<nsIPrefBranch> prefBranch; + rv = prefs->GetBranch(MAILNEWS_ROOT_PREF, getter_AddRefs(prefBranch)); + NS_ENSURE_SUCCESS(rv,rv); + + nsCOMPtr<nsIPrefBranch> defaultsPrefBranch; + rv = prefs->GetDefaultBranch(MAILNEWS_ROOT_PREF, getter_AddRefs(defaultsPrefBranch)); + NS_ENSURE_SUCCESS(rv,rv); + + /** + * Check to see if we need to add any global domains. + * If so, make sure the following prefs are added to mailnews.js + * + * 1. pref("mailnews.global_html_domains.version", version number); + * This pref registers the current version in the user prefs file. A default value is stored + * in mailnews file. Depending the changes we plan to make we can move the default version number. + * Comparing version number from user's prefs file and the default one from mailnews.js, we + * can effect ppropriate changes. + * + * 2. pref("mailnews.global_html_domains", <comma separated domain list>); + * This pref contains the list of html domains that ISP can add to make that user's contain all + * of these under the HTML domains in the Mail&NewsGrpus|Send Format under global preferences. + */ + int32_t htmlDomainListCurrentVersion, htmlDomainListDefaultVersion; + rv = prefBranch->GetIntPref(HTMLDOMAINUPDATE_VERSION_PREF_NAME, &htmlDomainListCurrentVersion); + NS_ENSURE_SUCCESS(rv,rv); + + rv = defaultsPrefBranch->GetIntPref(HTMLDOMAINUPDATE_VERSION_PREF_NAME, &htmlDomainListDefaultVersion); + NS_ENSURE_SUCCESS(rv,rv); + + // Update the list as needed + if (htmlDomainListCurrentVersion <= htmlDomainListDefaultVersion) { + // Get list of global domains need to be added + nsCString globalHtmlDomainList; + rv = prefBranch->GetCharPref(HTMLDOMAINUPDATE_DOMAINLIST_PREF_NAME, getter_Copies(globalHtmlDomainList)); + + if (NS_SUCCEEDED(rv) && !globalHtmlDomainList.IsEmpty()) { + nsTArray<nsCString> domainArray; + + // Get user's current HTML domain set for send format + nsCString currentHtmlDomainList; + rv = prefBranch->GetCharPref(USER_CURRENT_HTMLDOMAINLIST_PREF_NAME, getter_Copies(currentHtmlDomainList)); + NS_ENSURE_SUCCESS(rv,rv); + + nsAutoCString newHtmlDomainList(currentHtmlDomainList); + // Get the current html domain list into new list var + ParseString(currentHtmlDomainList, DOMAIN_DELIMITER, domainArray); + + // Get user's current Plaintext domain set for send format + nsCString currentPlaintextDomainList; + rv = prefBranch->GetCharPref(USER_CURRENT_PLAINTEXTDOMAINLIST_PREF_NAME, getter_Copies(currentPlaintextDomainList)); + NS_ENSURE_SUCCESS(rv,rv); + + // Get the current plaintext domain list into new list var + ParseString(currentPlaintextDomainList, DOMAIN_DELIMITER, domainArray); + + size_t i = domainArray.Length(); + if (i > 0) { + // Append each domain in the preconfigured html domain list + globalHtmlDomainList.StripWhitespace(); + ParseString(globalHtmlDomainList, DOMAIN_DELIMITER, domainArray); + + // Now add each domain that does not already appear in + // the user's current html or plaintext domain lists + for (; i < domainArray.Length(); i++) { + if (domainArray.IndexOf(domainArray[i]) == i) { + if (!newHtmlDomainList.IsEmpty()) + newHtmlDomainList += DOMAIN_DELIMITER; + newHtmlDomainList += domainArray[i]; + } + } + } + else + { + // User has no domains listed either in html or plain text category. + // Assign the global list to be the user's current html domain list + newHtmlDomainList = globalHtmlDomainList; + } + + // Set user's html domain pref with the updated list + rv = prefBranch->SetCharPref(USER_CURRENT_HTMLDOMAINLIST_PREF_NAME, newHtmlDomainList.get()); + NS_ENSURE_SUCCESS(rv,rv); + + // Increase the version to avoid running the update code unless needed (based on default version) + rv = prefBranch->SetIntPref(HTMLDOMAINUPDATE_VERSION_PREF_NAME, htmlDomainListCurrentVersion + 1); + NS_ENSURE_SUCCESS(rv,rv); + } + } + return NS_OK; +} + +NS_IMETHODIMP +nsMsgComposeService::RegisterComposeDocShell(nsIDocShell *aDocShell, + nsIMsgCompose *aComposeObject) +{ + NS_ENSURE_ARG_POINTER(aDocShell); + NS_ENSURE_ARG_POINTER(aComposeObject); + + nsresult rv; + + // add the msg compose / dom window mapping to our hash table + nsCOMPtr<nsIWeakReference> weakDocShell = do_GetWeakReference(aDocShell, &rv); + NS_ENSURE_SUCCESS(rv,rv); + nsCOMPtr<nsIWeakReference> weakMsgComposePtr = do_GetWeakReference(aComposeObject); + NS_ENSURE_SUCCESS(rv,rv); + mOpenComposeWindows.Put(weakDocShell, weakMsgComposePtr); + + return rv; +} + +NS_IMETHODIMP +nsMsgComposeService::UnregisterComposeDocShell(nsIDocShell *aDocShell) +{ + NS_ENSURE_ARG_POINTER(aDocShell); + + nsresult rv; + nsCOMPtr<nsIWeakReference> weakDocShell = do_GetWeakReference(aDocShell, &rv); + NS_ENSURE_SUCCESS(rv,rv); + + mOpenComposeWindows.Remove(weakDocShell); + + return rv; +} + +NS_IMETHODIMP +nsMsgComposeService::GetMsgComposeForDocShell(nsIDocShell *aDocShell, + nsIMsgCompose **aComposeObject) +{ + NS_ENSURE_ARG_POINTER(aDocShell); + NS_ENSURE_ARG_POINTER(aComposeObject); + + if (!mOpenComposeWindows.Count()) + return NS_ERROR_FAILURE; + + // get the weak reference for our dom window + nsresult rv; + nsCOMPtr<nsIWeakReference> weakDocShell = do_GetWeakReference(aDocShell, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIWeakReference> weakMsgComposePtr; + + if (!mOpenComposeWindows.Get(weakDocShell, + getter_AddRefs(weakMsgComposePtr))) + return NS_ERROR_FAILURE; + + nsCOMPtr<nsIMsgCompose> msgCompose = do_QueryReferent(weakMsgComposePtr, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + NS_IF_ADDREF(*aComposeObject = msgCompose); + return rv; +} + +/** + * LoadDraftOrTemplate + * Helper routine used to run msgURI through libmime in order to fetch the contents for a + * draft or template. + */ +nsresult +nsMsgComposeService::LoadDraftOrTemplate(const nsACString& aMsgURI, nsMimeOutputType aOutType, + nsIMsgIdentity * aIdentity, const char * aOriginalMsgURI, + nsIMsgDBHdr * aOrigMsgHdr, + bool aForwardInline, + bool overrideComposeFormat, + nsIMsgWindow *aMsgWindow) +{ + return RunMessageThroughMimeDraft(aMsgURI, aOutType, aIdentity, + aOriginalMsgURI, aOrigMsgHdr, + aForwardInline, EmptyString(), + overrideComposeFormat, aMsgWindow); +} + +/** + * Run the aMsgURI message through libmime. We set various attributes of the + * nsIMimeStreamConverter so mimedrft.cpp will know what to do with the message + * when its done streaming. Usually that will be opening a compose window + * with the contents of the message, but if forwardTo is non-empty, mimedrft.cpp + * will forward the contents directly. + * + * @param aMsgURI URI to stream, which is the msgUri + any extra terms, e.g., + * "redirect=true". + * @param aOutType nsMimeOutput::nsMimeMessageDraftOrTemplate or + * nsMimeOutput::nsMimeMessageEditorTemplate + * @param aIdentity identity to use for the new message + * @param aOriginalMsgURI msgURI w/o any extra terms + * @param aOrigMsgHdr nsIMsgDBHdr corresponding to aOriginalMsgURI + * @param aForwardInline true if doing a forward inline + * @param aForwardTo e-mail address to forward msg to. This is used for + * forward inline message filter actions. + * @param aOverrideComposeFormat True if the user had shift key down when + doing a command that opens the compose window, + * which means we switch the compose window used + * from the default. + * @param aMsgWindow msgWindow to pass into DisplayMessage. + */ +nsresult +nsMsgComposeService::RunMessageThroughMimeDraft( + const nsACString& aMsgURI, nsMimeOutputType aOutType, + nsIMsgIdentity * aIdentity, const char * aOriginalMsgURI, + nsIMsgDBHdr * aOrigMsgHdr, + bool aForwardInline, + const nsAString &aForwardTo, + bool aOverrideComposeFormat, + nsIMsgWindow *aMsgWindow) +{ + nsCOMPtr <nsIMsgMessageService> messageService; + nsresult rv = GetMessageServiceFromURI(aMsgURI, getter_AddRefs(messageService)); + NS_ENSURE_SUCCESS(rv, rv); + + // Create a mime parser (nsIMimeStreamConverter)to do the conversion. + nsCOMPtr<nsIMimeStreamConverter> mimeConverter = + do_CreateInstance(NS_MAILNEWS_MIME_STREAM_CONVERTER_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + mimeConverter->SetMimeOutputType(aOutType); // Set the type of output for libmime + mimeConverter->SetForwardInline(aForwardInline); + if (!aForwardTo.IsEmpty()) + { + mimeConverter->SetForwardInlineFilter(true); + mimeConverter->SetForwardToAddress(aForwardTo); + } + mimeConverter->SetOverrideComposeFormat(aOverrideComposeFormat); + mimeConverter->SetIdentity(aIdentity); + mimeConverter->SetOriginalMsgURI(aOriginalMsgURI); + mimeConverter->SetOrigMsgHdr(aOrigMsgHdr); + + nsCOMPtr<nsIURI> url; + bool fileUrl = StringBeginsWith(aMsgURI, NS_LITERAL_CSTRING("file:")); + nsCString mailboxUri(aMsgURI); + if (fileUrl) + { + // We loaded a .eml file from a file: url. Construct equivalent mailbox url. + mailboxUri.Replace(0, 5, NS_LITERAL_CSTRING("mailbox:")); + mailboxUri.Append(NS_LITERAL_CSTRING("&number=0")); + // Need this to prevent nsMsgCompose::TagEmbeddedObjects from setting + // inline images as moz-do-not-send. + mimeConverter->SetOriginalMsgURI(mailboxUri.get()); + } + if (fileUrl || PromiseFlatCString(aMsgURI).Find("&type=application/x-message-display") >= 0) + rv = NS_NewURI(getter_AddRefs(url), mailboxUri); + else + rv = messageService->GetUrlForUri(PromiseFlatCString(aMsgURI).get(), getter_AddRefs(url), aMsgWindow); + NS_ENSURE_SUCCESS(rv, rv); + + // ignore errors here - it's not fatal, and in the case of mailbox messages, + // we're always passing in an invalid spec... + (void )url->SetSpec(mailboxUri); + + // if we are forwarding a message and that message used a charset over ride + // then use that over ride charset instead of the charset specified in the message + nsCString mailCharset; + if (aMsgWindow) + { + bool charsetOverride; + if (NS_SUCCEEDED(aMsgWindow->GetCharsetOverride(&charsetOverride)) && charsetOverride) + { + if (NS_SUCCEEDED(aMsgWindow->GetMailCharacterSet(mailCharset))) + { + nsCOMPtr<nsIMsgI18NUrl> i18nUrl(do_QueryInterface(url)); + if (i18nUrl) + (void) i18nUrl->SetCharsetOverRide(mailCharset.get()); + } + } + } + + nsCOMPtr<nsIPrincipal> nullPrincipal = + do_CreateInstance("@mozilla.org/nullprincipal;1", &rv); + NS_ASSERTION(NS_SUCCEEDED(rv), "CreateInstance of nullprincipal failed"); + if (NS_FAILED(rv)) + return rv; + + nsCOMPtr<nsIChannel> channel; + rv = NS_NewInputStreamChannel(getter_AddRefs(channel), + url, + nullptr, + nullPrincipal, + nsILoadInfo::SEC_NORMAL, + nsIContentPolicy::TYPE_OTHER); + NS_ASSERTION(NS_SUCCEEDED(rv), "NS_NewChannel failed."); + if (NS_FAILED(rv)) + return rv; + + nsCOMPtr<nsIStreamConverter> converter = do_QueryInterface(mimeConverter); + rv = converter->AsyncConvertData(nullptr, nullptr, nullptr, channel); + NS_ENSURE_SUCCESS(rv, rv); + + // Now, just plug the two together and get the hell out of the way! + nsCOMPtr<nsIStreamListener> streamListener = do_QueryInterface(mimeConverter); + nsCOMPtr<nsIURI> dummyNull; + return messageService->DisplayMessage(PromiseFlatCString(aMsgURI).get(), streamListener, + aMsgWindow, nullptr, mailCharset.get(), + getter_AddRefs(dummyNull)); +} + +NS_IMETHODIMP +nsMsgComposeService::Handle(nsICommandLine* aCmdLine) +{ + NS_ENSURE_ARG_POINTER(aCmdLine); + + nsresult rv; + int32_t found, end, count; + nsAutoString uristr; + bool composeShouldHandle = true; + + rv = aCmdLine->FindFlag(NS_LITERAL_STRING("compose"), false, &found); + NS_ENSURE_SUCCESS(rv, rv); + +#ifndef MOZ_SUITE + // MAC OS X passes in -url mailto:mscott@mozilla.org into the command line + // instead of -compose. + if (found == -1) + { + rv = aCmdLine->FindFlag(NS_LITERAL_STRING("url"), false, &found); + // we don't want to consume the argument for -url unless we're sure it is a mailto url and we'll + // figure that out shortly. + composeShouldHandle = false; + } +#endif + + if (found == -1) + return NS_OK; + + end = found; + + rv = aCmdLine->GetLength(&count); + NS_ENSURE_SUCCESS(rv, rv); + + if (count > found + 1) { + aCmdLine->GetArgument(found + 1, uristr); + if (StringBeginsWith(uristr, NS_LITERAL_STRING("mailto:")) || + StringBeginsWith(uristr, NS_LITERAL_STRING("preselectid=")) || + StringBeginsWith(uristr, NS_LITERAL_STRING("to=")) || + StringBeginsWith(uristr, NS_LITERAL_STRING("cc=")) || + StringBeginsWith(uristr, NS_LITERAL_STRING("bcc=")) || + StringBeginsWith(uristr, NS_LITERAL_STRING("newsgroups=")) || + StringBeginsWith(uristr, NS_LITERAL_STRING("subject=")) || + StringBeginsWith(uristr, NS_LITERAL_STRING("format=")) || + StringBeginsWith(uristr, NS_LITERAL_STRING("body=")) || + StringBeginsWith(uristr, NS_LITERAL_STRING("attachment=")) || + StringBeginsWith(uristr, NS_LITERAL_STRING("message=")) || + StringBeginsWith(uristr, NS_LITERAL_STRING("from="))) { + composeShouldHandle = true; // the -url argument looks like mailto + end++; + // mailto: URIs are frequently passed with spaces in them. They should be + // escaped with %20, but we hack around broken clients. See bug 231032. + while (end + 1 < count) { + nsAutoString curarg; + aCmdLine->GetArgument(end + 1, curarg); + if (curarg.First() == '-') + break; + + uristr.Append(' '); + uristr.Append(curarg); + ++end; + } + } + else { + uristr.Truncate(); + } + } + if (composeShouldHandle) + { + aCmdLine->RemoveArguments(found, end); + + nsCOMPtr<nsIWindowWatcher> wwatch (do_GetService(NS_WINDOWWATCHER_CONTRACTID)); + NS_ENSURE_TRUE(wwatch, NS_ERROR_FAILURE); + + nsCOMPtr<nsISupportsString> arg(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID)); + if (arg) + arg->SetData(uristr); + + nsCOMPtr<mozIDOMWindowProxy> opened; + wwatch->OpenWindow(nullptr, DEFAULT_CHROME, "_blank", + "chrome,dialog=no,all", arg, getter_AddRefs(opened)); + + aCmdLine->SetPreventDefault(true); + } + return NS_OK; +} + +NS_IMETHODIMP +nsMsgComposeService::GetHelpInfo(nsACString& aResult) +{ + aResult.AssignLiteral( + " -compose [ <options> ] Compose a mail or news message. Options are specified\n" + " as string \"option='value,...',option=value,...\" and\n" + " include: from, to, cc, bcc, newsgroups, subject, body,\n" + " message (file), attachment (file), format (html | text).\n" + " Example: \"to=john@example.com,subject='Dinner tonight?'\"\n"); + return NS_OK; +} |