From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- comm/mailnews/imap/src/nsImapService.cpp | 3091 ++++++++++++++++++++++++++++++ 1 file changed, 3091 insertions(+) create mode 100644 comm/mailnews/imap/src/nsImapService.cpp (limited to 'comm/mailnews/imap/src/nsImapService.cpp') diff --git a/comm/mailnews/imap/src/nsImapService.cpp b/comm/mailnews/imap/src/nsImapService.cpp new file mode 100644 index 0000000000..8c02d02dcd --- /dev/null +++ b/comm/mailnews/imap/src/nsImapService.cpp @@ -0,0 +1,3091 @@ +/* -*- 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 "msgCore.h" // precompiled header... +#include "nsImapService.h" +#include "nsImapCore.h" +#include "netCore.h" + +#include "nsImapUrl.h" +#include "nsCOMPtr.h" +#include "nsIMsgFolder.h" +#include "nsIMsgImapMailFolder.h" +#include "nsIImapIncomingServer.h" +#include "nsIImapMailFolderSink.h" +#include "nsIImapMessageSink.h" +#include "nsIImapServerSink.h" +#include "nsIImapMockChannel.h" +#include "nsImapUtils.h" +#include "nsImapNamespace.h" +#include "nsIDocShell.h" +#include "nsIProgressEventSink.h" +#include "nsIPrefBranch.h" +#include "nsIPrefService.h" +#include "nsILoadGroup.h" +#include "nsIMsgAccountManager.h" +#include "nsMsgFolderFlags.h" +#include "nsMailDirServiceDefs.h" +#include "nsIWebNavigation.h" +#include "nsImapStringBundle.h" +#include "plbase64.h" +#include "nsImapOfflineSync.h" +#include "nsIMsgHdr.h" +#include "nsMsgUtils.h" +#include "nsICacheStorage.h" +#include "nsICacheStorageService.h" +#include "nsIStreamListener.h" +#include "nsIUrlListener.h" +#include "nsNetCID.h" +#include "nsMsgI18N.h" +#include "nsIOutputStream.h" +#include "nsIInputStream.h" +#include "nsMsgLineBuffer.h" +#include "nsIMsgParseMailMsgState.h" +#include "nsIOutputStream.h" +#include "nsIDocShell.h" +#include "nsIMessengerWindowService.h" +#include "nsIWindowMediator.h" +#include "nsIPrompt.h" +#include "nsIWindowWatcher.h" +#include "nsIMsgMailSession.h" +#include "nsIStreamConverterService.h" +#include "nsIAutoSyncManager.h" +#include "nsThreadUtils.h" +#include "nsNetUtil.h" +#include "nsMsgMessageFlags.h" +#include "nsIMsgPluggableStore.h" +#include "../../base/src/MailnewsLoadContextInfo.h" +#include "nsDocShellLoadState.h" +#include "nsContentUtils.h" +#include "mozilla/LoadInfo.h" + +#define PREF_MAIL_ROOT_IMAP_REL "mail.root.imap-rel" +// old - for backward compatibility only +#define PREF_MAIL_ROOT_IMAP "mail.root.imap" + +#define NS_IMAPURL_CID \ + { \ + 0x21a89611, 0xdc0d, 0x11d2, { \ + 0x80, 0x6c, 0x0, 0x60, 0x8, 0x12, 0x8c, 0x4e \ + } \ + } +static NS_DEFINE_CID(kImapUrlCID, NS_IMAPURL_CID); + +#define NS_IMAPMOCKCHANNEL_CID \ + { \ + 0x4eca51df, 0x6734, 0x11d3, { \ + 0x98, 0x9a, 0x0, 0x10, 0x83, 0x1, 0xe, 0x9b \ + } \ + } +static NS_DEFINE_CID(kCImapMockChannel, NS_IMAPMOCKCHANNEL_CID); + +static const char sequenceString[] = "SEQUENCE"; +static const char uidString[] = "UID"; + +static bool gInitialized = false; + +NS_IMPL_ISUPPORTS(nsImapService, nsIImapService, nsIMsgMessageService, + nsIProtocolHandler, nsIMsgProtocolInfo, + nsIMsgMessageFetchPartService, nsIContentHandler) + +nsImapService::nsImapService() { + if (!gInitialized) { + nsresult rv; + + nsCOMPtr ioServ = do_GetIOService(); + ioServ->RegisterProtocolHandler( + "imap"_ns, this, + nsIProtocolHandler::URI_NORELATIVE | + nsIProtocolHandler::URI_FORBIDS_AUTOMATIC_DOCUMENT_REPLACEMENT | + nsIProtocolHandler::URI_DANGEROUS_TO_LOAD | + nsIProtocolHandler::ALLOWS_PROXY | + nsIProtocolHandler::URI_FORBIDS_COOKIE_ACCESS | + nsIProtocolHandler::ORIGIN_IS_FULL_SPEC, + nsIImapUrl::DEFAULT_IMAP_PORT); + + // initialize auto-sync service + nsCOMPtr autoSyncMgr = + do_GetService(NS_AUTOSYNCMANAGER_CONTRACTID, &rv); + if (NS_SUCCEEDED(rv) && autoSyncMgr) { + // auto-sync manager initialization goes here + // assign new strategy objects here... + } + NS_ASSERTION(autoSyncMgr != nullptr, + "*** Cannot initialize nsAutoSyncManager service."); + + gInitialized = true; + } +} + +nsImapService::~nsImapService() {} + +char nsImapService::GetHierarchyDelimiter(nsIMsgFolder* aMsgFolder) { + char delimiter = '/'; + if (aMsgFolder) { + nsCOMPtr imapFolder = do_QueryInterface(aMsgFolder); + if (imapFolder) imapFolder->GetHierarchyDelimiter(&delimiter); + } + return delimiter; +} + +// N.B., this returns an escaped folder name, appropriate for putting in a url. +nsresult nsImapService::GetFolderName(nsIMsgFolder* aImapFolder, + nsACString& aFolderName) { + nsresult rv; + nsCOMPtr aFolder(do_QueryInterface(aImapFolder, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCString onlineName; + // Online name is in MUTF-7 or UTF-8. + rv = aFolder->GetOnlineName(onlineName); + NS_ENSURE_SUCCESS(rv, rv); + if (onlineName.IsEmpty()) { + nsCString uri; + rv = aImapFolder->GetURI(uri); + NS_ENSURE_SUCCESS(rv, rv); + nsCString hostname; + rv = aImapFolder->GetHostname(hostname); + NS_ENSURE_SUCCESS(rv, rv); + rv = nsImapURI2FullName(kImapRootURI, hostname.get(), uri.get(), + getter_Copies(onlineName)); + } + // if the hierarchy delimiter is not '/', then we want to escape slashes; + // otherwise, we do want to escape slashes. + // we want to escape slashes and '^' first, otherwise, nsEscape will lose them + bool escapeSlashes = (GetHierarchyDelimiter(aImapFolder) != '/'); + if (escapeSlashes && !onlineName.IsEmpty()) { + char* escapedOnlineName; + rv = nsImapUrl::EscapeSlashes(onlineName.get(), &escapedOnlineName); + if (NS_SUCCEEDED(rv)) onlineName.Adopt(escapedOnlineName); + } + // need to escape everything else + MsgEscapeString(onlineName, nsINetUtil::ESCAPE_URL_PATH, aFolderName); + return rv; +} + +NS_IMETHODIMP nsImapService::SelectFolder(nsIMsgFolder* aImapMailFolder, + nsIUrlListener* aUrlListener, + nsIMsgWindow* aMsgWindow, + nsIURI** aURL) { + NS_ENSURE_ARG_POINTER(aImapMailFolder); + + if (WeAreOffline()) return NS_MSG_ERROR_OFFLINE; + + bool canOpenThisFolder = true; + nsCOMPtr imapFolder = + do_QueryInterface(aImapMailFolder); + if (imapFolder) imapFolder->GetCanOpenFolder(&canOpenThisFolder); + + if (!canOpenThisFolder) return NS_OK; + + nsresult rv; + nsCOMPtr imapUrl; + nsAutoCString urlSpec; + char hierarchyDelimiter = GetHierarchyDelimiter(aImapMailFolder); + rv = CreateStartOfImapUrl(EmptyCString(), getter_AddRefs(imapUrl), + aImapMailFolder, aUrlListener, urlSpec, + hierarchyDelimiter); + + if (NS_SUCCEEDED(rv) && imapUrl) { + // nsImapUrl::SetSpec() will set the imap action properly + rv = imapUrl->SetImapAction(nsIImapUrl::nsImapSelectFolder); + + nsCOMPtr mailNewsUrl = do_QueryInterface(imapUrl); + // if no msg window, we won't put up error messages (this is almost + // certainly a biff-inspired get new msgs) + if (!aMsgWindow) mailNewsUrl->SetSuppressErrorMsgs(true); + mailNewsUrl->SetMsgWindow(aMsgWindow); + mailNewsUrl->SetUpdatingFolder(true); + rv = SetImapUrlSink(aImapMailFolder, imapUrl); + + if (NS_SUCCEEDED(rv)) { + nsAutoCString folderName; + GetFolderName(aImapMailFolder, folderName); + urlSpec.AppendLiteral("/select>"); + urlSpec.Append(hierarchyDelimiter); + urlSpec.Append(folderName); + rv = mailNewsUrl->SetSpecInternal(urlSpec); + if (NS_SUCCEEDED(rv)) + rv = GetImapConnectionAndLoadUrl(imapUrl, nullptr, aURL); + } + } // if we have a url to run.... + + return rv; +} + +// lite select, used to verify UIDVALIDITY while going on/offline +NS_IMETHODIMP nsImapService::LiteSelectFolder(nsIMsgFolder* aImapMailFolder, + nsIUrlListener* aUrlListener, + nsIMsgWindow* aMsgWindow, + nsIURI** aURL) { + NS_ENSURE_ARG_POINTER(aImapMailFolder); + + return FolderCommand(aImapMailFolder, aUrlListener, "/liteselect>", + nsIImapUrl::nsImapLiteSelectFolder, aMsgWindow, aURL); +} + +NS_IMETHODIMP nsImapService::GetUrlForUri(const nsACString& aMessageURI, + nsIMsgWindow* aMsgWindow, + nsIURI** aURL) { + nsAutoCString messageURI(aMessageURI); + + if (messageURI.Find("&type=application/x-message-display"_ns) != kNotFound) + return NS_NewURI(aURL, aMessageURI); + + nsCOMPtr folder; + nsAutoCString msgKey; + nsresult rv = DecomposeImapURI(messageURI, getter_AddRefs(folder), msgKey); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr imapUrl; + nsAutoCString urlSpec; + char hierarchyDelimiter = GetHierarchyDelimiter(folder); + rv = CreateStartOfImapUrl(messageURI, getter_AddRefs(imapUrl), folder, + nullptr, urlSpec, hierarchyDelimiter); + NS_ENSURE_SUCCESS(rv, rv); + rv = SetImapUrlSink(folder, imapUrl); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr mailnewsUrl = do_QueryInterface(imapUrl); + bool useLocalCache = false; + folder->HasMsgOffline(strtoul(msgKey.get(), nullptr, 10), &useLocalCache); + mailnewsUrl->SetMsgIsInLocalCache(useLocalCache); + + nsCOMPtr url = do_QueryInterface(imapUrl); + rv = url->GetSpec(urlSpec); + NS_ENSURE_SUCCESS(rv, rv); + urlSpec.AppendLiteral("fetch>UID>"); + urlSpec.Append(hierarchyDelimiter); + + nsAutoCString folderName; + GetFolderName(folder, folderName); + urlSpec.Append(folderName); + urlSpec.Append('>'); + urlSpec.Append(msgKey); + rv = mailnewsUrl->SetSpecInternal(urlSpec); + imapUrl->QueryInterface(NS_GET_IID(nsIURI), (void**)aURL); + } + + return rv; +} + +NS_IMETHODIMP nsImapService::FetchMimePart( + nsIURI* aURI, const nsACString& aMessageURI, nsISupports* aDisplayConsumer, + nsIMsgWindow* aMsgWindow, nsIUrlListener* aUrlListener, nsIURI** aURL) { + nsCOMPtr folder; + nsAutoCString messageURI(aMessageURI); + nsAutoCString msgKey; + nsAutoCString mimePart; + nsAutoCString folderURI; + nsMsgKey key; + + nsresult rv = DecomposeImapURI(messageURI, getter_AddRefs(folder), msgKey); + NS_ENSURE_SUCCESS(rv, rv); + rv = nsParseImapMessageURI(aMessageURI, folderURI, &key, + getter_Copies(mimePart)); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr imapMessageSink( + do_QueryInterface(folder, &rv)); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr imapUrl = do_QueryInterface(aURI); + nsCOMPtr msgurl(do_QueryInterface(aURI, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + msgurl->SetMsgWindow(aMsgWindow); + msgurl->RegisterListener(aUrlListener); + + if (!mimePart.IsEmpty()) { + return FetchMimePart(imapUrl, nsIImapUrl::nsImapMsgFetch, folder, + imapMessageSink, aURL, aDisplayConsumer, msgKey, + mimePart); + } + } + } + return rv; +} + +NS_IMETHODIMP nsImapService::LoadMessage(const nsACString& aMessageURI, + nsISupports* aDisplayConsumer, + nsIMsgWindow* aMsgWindow, + nsIUrlListener* aUrlListener, + bool aAutodetectCharset) { + nsresult rv; + + nsCOMPtr folder; + nsAutoCString msgKey; + nsAutoCString mimePart; + nsAutoCString folderURI; + nsMsgKey key; + nsAutoCString messageURI(aMessageURI); + + int32_t typeIndex = messageURI.Find("&type=application/x-message-display"); + if (typeIndex != kNotFound) { + // This happens with forward inline of a message/rfc822 attachment opened in + // a standalone msg window. + // So, just cut to the chase and call AsyncOpen on a channel. + nsCOMPtr uri; + messageURI.Cut(typeIndex, + sizeof("&type=application/x-message-display") - 1); + rv = NS_NewURI(getter_AddRefs(uri), messageURI.get()); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr aStreamListener = + do_QueryInterface(aDisplayConsumer, &rv); + if (NS_SUCCEEDED(rv) && aStreamListener) { + nsCOMPtr aChannel; + nsCOMPtr aLoadGroup; + nsCOMPtr mailnewsUrl = do_QueryInterface(uri, &rv); + if (NS_SUCCEEDED(rv) && mailnewsUrl) + mailnewsUrl->GetLoadGroup(getter_AddRefs(aLoadGroup)); + + nsCOMPtr loadInfo = new mozilla::net::LoadInfo( + nsContentUtils::GetSystemPrincipal(), nullptr, nullptr, + nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, + nsIContentPolicy::TYPE_OTHER); + rv = NewChannel(uri, loadInfo, getter_AddRefs(aChannel)); + NS_ENSURE_SUCCESS(rv, rv); + + // now try to open the channel passing in our display consumer as the + // listener + rv = aChannel->AsyncOpen(aStreamListener); + return rv; + } + } + + rv = DecomposeImapURI(messageURI, getter_AddRefs(folder), msgKey); + NS_ENSURE_SUCCESS(rv, rv); + if (msgKey.IsEmpty()) return NS_MSG_MESSAGE_NOT_FOUND; + + rv = nsParseImapMessageURI(aMessageURI, folderURI, &key, + getter_Copies(mimePart)); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr imapMessageSink( + do_QueryInterface(folder, &rv)); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr imapUrl; + nsAutoCString urlSpec; + char hierarchyDelimiter = GetHierarchyDelimiter(folder); + rv = CreateStartOfImapUrl(messageURI, getter_AddRefs(imapUrl), folder, + aUrlListener, urlSpec, hierarchyDelimiter); + NS_ENSURE_SUCCESS(rv, rv); + if (!mimePart.IsEmpty()) { + nsresult rv; + nsCOMPtr mailnewsurl = do_QueryInterface(imapUrl); + + rv = AddImapFetchToUrl(mailnewsurl, folder, msgKey + mimePart, + EmptyCString()); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr dummyURI; + return FetchMimePart(imapUrl, nsIImapUrl::nsImapMsgFetch, folder, + imapMessageSink, getter_AddRefs(dummyURI), + aDisplayConsumer, msgKey, mimePart); + } + + nsCOMPtr msgurl(do_QueryInterface(imapUrl)); + nsCOMPtr i18nurl(do_QueryInterface(imapUrl)); + i18nurl->SetAutodetectCharset(aAutodetectCharset); + + bool shouldStoreMsgOffline = false; + bool hasMsgOffline = false; + + msgurl->SetMsgWindow(aMsgWindow); + + if (folder) { + folder->ShouldStoreMsgOffline(key, &shouldStoreMsgOffline); + folder->HasMsgOffline(key, &hasMsgOffline); + } + imapUrl->SetStoreResultsOffline(shouldStoreMsgOffline); + + if (hasMsgOffline) msgurl->SetMsgIsInLocalCache(true); + + nsCOMPtr prefBranch( + do_GetService(NS_PREFSERVICE_CONTRACTID, &rv)); + // Should the message fetch force a peek or a traditional fetch? + // Force peek if there is a delay in marking read (or no auto-marking at + // all). This is because a FETCH (BODY[]) will implicitly set the \Seen + // flag on the msg, but a FETCH (BODY.PEEK[]) won't. + bool forcePeek = false; + if (NS_SUCCEEDED(rv) && prefBranch) { + nsAutoCString uriStr(aMessageURI); + int32_t dontMarkAsReadPos = uriStr.Find("&markRead=false"); + bool markReadAuto = true; + prefBranch->GetBoolPref("mailnews.mark_message_read.auto", + &markReadAuto); + bool markReadDelay = false; + prefBranch->GetBoolPref("mailnews.mark_message_read.delay", + &markReadDelay); + forcePeek = (!markReadAuto || markReadDelay || + (dontMarkAsReadPos != kNotFound)); + } + + if (!forcePeek) { + // If we're loading a message in an inactive docShell, don't let it + // be marked as read immediately. + nsCOMPtr docShell = + do_QueryInterface(aDisplayConsumer, &rv); + if (NS_SUCCEEDED(rv) && docShell) { + auto* bc = docShell->GetBrowsingContext(); + forcePeek = !bc->IsActive(); + } + } + + nsCOMPtr dummyURI; + rv = FetchMessage(imapUrl, + forcePeek ? nsIImapUrl::nsImapMsgFetchPeek + : nsIImapUrl::nsImapMsgFetch, + folder, imapMessageSink, aMsgWindow, aDisplayConsumer, + msgKey, false, getter_AddRefs(dummyURI)); + } + } + return rv; +} + +nsresult nsImapService::FetchMimePart( + nsIImapUrl* aImapUrl, nsImapAction aImapAction, + nsIMsgFolder* aImapMailFolder, nsIImapMessageSink* aImapMessage, + nsIURI** aURL, nsISupports* aDisplayConsumer, + const nsACString& messageIdentifierList, const nsACString& mimePart) { + NS_ENSURE_ARG_POINTER(aImapUrl); + NS_ENSURE_ARG_POINTER(aImapMailFolder); + NS_ENSURE_ARG_POINTER(aImapMessage); + + // create a protocol instance to handle the request. + // NOTE: once we start working with multiple connections, this step will be + // much more complicated...but for now just create a connection and process + // the request. + nsAutoCString urlSpec; + nsresult rv = SetImapUrlSink(aImapMailFolder, aImapUrl); + nsImapAction actionToUse = aImapAction; + if (actionToUse == nsImapUrl::nsImapOpenMimePart) + actionToUse = nsIImapUrl::nsImapMsgFetch; + + nsCOMPtr msgurl(do_QueryInterface(aImapUrl)); + if (aImapMailFolder && msgurl && !messageIdentifierList.IsEmpty()) { + bool useLocalCache = false; + aImapMailFolder->HasMsgOffline( + strtoul(PromiseFlatCString(messageIdentifierList).get(), nullptr, 10), + &useLocalCache); + msgurl->SetMsgIsInLocalCache(useLocalCache); + } + rv = aImapUrl->SetImapMessageSink(aImapMessage); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr url = do_QueryInterface(aImapUrl); + if (aURL) NS_IF_ADDREF(*aURL = url); + + rv = url->GetSpec(urlSpec); + NS_ENSURE_SUCCESS(rv, rv); + + rv = msgurl->SetSpecInternal(urlSpec); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aImapUrl->SetImapAction(actionToUse /* nsIImapUrl::nsImapMsgFetch */); + if (aImapMailFolder && aDisplayConsumer) { + nsCOMPtr aMsgIncomingServer; + rv = aImapMailFolder->GetServer(getter_AddRefs(aMsgIncomingServer)); + if (NS_SUCCEEDED(rv) && aMsgIncomingServer) { + bool interrupted; + nsCOMPtr aImapServer( + do_QueryInterface(aMsgIncomingServer, &rv)); + if (NS_SUCCEEDED(rv) && aImapServer) + aImapServer->PseudoInterruptMsgLoad(aImapMailFolder, nullptr, + &interrupted); + } + } + // if the display consumer is a docshell, then we should run the url in the + // docshell. otherwise, it should be a stream listener....so open a channel + // using AsyncRead and the provided stream listener.... + + nsCOMPtr docShell(do_QueryInterface(aDisplayConsumer, &rv)); + if (NS_SUCCEEDED(rv) && docShell) { + // DIRTY LITTLE HACK --> if we are opening an attachment we want the + // docshell to treat this load as if it were a user click event. Then the + // dispatching stuff will be much happier. + RefPtr loadState = new nsDocShellLoadState(url); + loadState->SetLoadFlags(aImapAction == nsImapUrl::nsImapOpenMimePart + ? nsIWebNavigation::LOAD_FLAGS_IS_LINK + : nsIWebNavigation::LOAD_FLAGS_NONE); + if (aImapAction == nsImapUrl::nsImapOpenMimePart) + loadState->SetLoadType(LOAD_LINK); + loadState->SetFirstParty(false); + loadState->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal()); + rv = docShell->LoadURI(loadState, false); + } else { + nsCOMPtr aStreamListener = + do_QueryInterface(aDisplayConsumer, &rv); + if (NS_SUCCEEDED(rv) && aStreamListener) { + nsCOMPtr aChannel; + nsCOMPtr loadGroup; + nsCOMPtr mailnewsUrl = + do_QueryInterface(aImapUrl, &rv); + if (NS_SUCCEEDED(rv) && mailnewsUrl) + mailnewsUrl->GetLoadGroup(getter_AddRefs(loadGroup)); + + nsCOMPtr loadInfo = new mozilla::net::LoadInfo( + nsContentUtils::GetSystemPrincipal(), nullptr, nullptr, + nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, + nsIContentPolicy::TYPE_OTHER); + rv = NewChannel(url, loadInfo, getter_AddRefs(aChannel)); + NS_ENSURE_SUCCESS(rv, rv); + + // we need a load group to hold onto the channel. When the request is + // finished, it'll get removed from the load group, and the channel will + // go away, which will free the load group. + if (!loadGroup) loadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID); + + aChannel->SetLoadGroup(loadGroup); + + // now try to open the channel passing in our display consumer as the + // listener + rv = aChannel->AsyncOpen(aStreamListener); + } else // do what we used to do before + { + // I'd like to get rid of this code as I believe that we always get a + // docshell or stream listener passed into us in this method but i'm not + // sure yet... I'm going to use an assert for now to figure out if this + // is ever getting called +#if defined(DEBUG_mscott) || defined(DEBUG_bienvenu) + NS_ERROR("oops...someone still is reaching this part of the code"); +#endif + rv = GetImapConnectionAndLoadUrl(aImapUrl, aDisplayConsumer, aURL); + } + } + } + return rv; +} + +NS_IMETHODIMP nsImapService::CopyMessage(const nsACString& aSrcMailboxURI, + nsIStreamListener* aMailboxCopy, + bool moveMessage, + nsIUrlListener* aUrlListener, + nsIMsgWindow* aMsgWindow) { + NS_ENSURE_ARG_POINTER(aMailboxCopy); + + nsresult rv; + nsCOMPtr folder; + nsAutoCString msgKey; + rv = DecomposeImapURI(aSrcMailboxURI, getter_AddRefs(folder), msgKey); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr imapMessageSink( + do_QueryInterface(folder, &rv)); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr imapUrl; + nsAutoCString urlSpec; + char hierarchyDelimiter = GetHierarchyDelimiter(folder); + bool hasMsgOffline = false; + nsMsgKey key = strtoul(msgKey.get(), nullptr, 10); + + rv = CreateStartOfImapUrl(aSrcMailboxURI, getter_AddRefs(imapUrl), folder, + aUrlListener, urlSpec, hierarchyDelimiter); + if (folder) { + nsCOMPtr msgurl(do_QueryInterface(imapUrl)); + folder->HasMsgOffline(key, &hasMsgOffline); + if (msgurl) msgurl->SetMsgIsInLocalCache(hasMsgOffline); + } + // now try to download the message + nsImapAction imapAction = nsIImapUrl::nsImapOnlineToOfflineCopy; + if (moveMessage) imapAction = nsIImapUrl::nsImapOnlineToOfflineMove; + nsCOMPtr dummyURI; + rv = + FetchMessage(imapUrl, imapAction, folder, imapMessageSink, aMsgWindow, + aMailboxCopy, msgKey, false, getter_AddRefs(dummyURI)); + } // if we got an imap message sink + } // if we decomposed the imap message + return rv; +} + +NS_IMETHODIMP nsImapService::CopyMessages( + const nsTArray& aKeys, nsIMsgFolder* srcFolder, + nsIStreamListener* aMailboxCopy, bool moveMessage, + nsIUrlListener* aUrlListener, nsIMsgWindow* aMsgWindow, nsIURI** aURL) { + NS_ENSURE_ARG_POINTER(aMailboxCopy); + NS_ENSURE_TRUE(!aKeys.IsEmpty(), NS_ERROR_INVALID_ARG); + + nsresult rv; + nsCOMPtr folder = srcFolder; + nsCOMPtr imapMessageSink(do_QueryInterface(folder, &rv)); + if (NS_SUCCEEDED(rv)) { + // we generate the uri for the first message so that way on down the line, + // GetMessage in nsCopyMessageStreamListener will get an unescaped + // username and be able to find the msg hdr. See bug 259656 for details + nsCString uri; + srcFolder->GenerateMessageURI(aKeys[0], uri); + + nsCString messageIds; + // TODO: AllocateImapUidString() maxes out at 950 keys or so... it + // updates the numKeys passed in, but here the resulting value is + // ignored. Does this need sorting out? + uint32_t numKeys = aKeys.Length(); + AllocateImapUidString(aKeys.Elements(), numKeys, nullptr, messageIds); + nsCOMPtr imapUrl; + nsAutoCString urlSpec; + char hierarchyDelimiter = GetHierarchyDelimiter(folder); + rv = CreateStartOfImapUrl(uri, getter_AddRefs(imapUrl), folder, + aUrlListener, urlSpec, hierarchyDelimiter); + nsImapAction action; + if (moveMessage) // don't use ?: syntax here, it seems to break the Mac. + action = nsIImapUrl::nsImapOnlineToOfflineMove; + else + action = nsIImapUrl::nsImapOnlineToOfflineCopy; + imapUrl->SetCopyState(aMailboxCopy); + // now try to display the message + rv = FetchMessage(imapUrl, action, folder, imapMessageSink, aMsgWindow, + aMailboxCopy, messageIds, false, aURL); + // ### end of copy operation should know how to do the delete.if this is a + // move + + } // if we got an imap message sink + return rv; +} + +NS_IMETHODIMP nsImapService::Search(nsIMsgSearchSession* aSearchSession, + nsIMsgWindow* aMsgWindow, + nsIMsgFolder* aMsgFolder, + const nsACString& aSearchUri) { + NS_ENSURE_ARG_POINTER(aMsgFolder); + nsresult rv; + + nsCOMPtr imapUrl; + nsCOMPtr urlListener = do_QueryInterface(aSearchSession, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoCString urlSpec; + char hierarchyDelimiter = GetHierarchyDelimiter(aMsgFolder); + rv = CreateStartOfImapUrl(EmptyCString(), getter_AddRefs(imapUrl), aMsgFolder, + urlListener, urlSpec, hierarchyDelimiter); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr msgurl(do_QueryInterface(imapUrl)); + + msgurl->SetMsgWindow(aMsgWindow); + msgurl->SetSearchSession(aSearchSession); + rv = SetImapUrlSink(aMsgFolder, imapUrl); + + if (NS_SUCCEEDED(rv)) { + nsCString folderName; + GetFolderName(aMsgFolder, folderName); + + nsCOMPtr mailNewsUrl = do_QueryInterface(imapUrl); + if (!aMsgWindow) mailNewsUrl->SetSuppressErrorMsgs(true); + + urlSpec.AppendLiteral("/search>UID>"); + urlSpec.Append(hierarchyDelimiter); + urlSpec.Append(folderName); + urlSpec.Append('>'); + // escape aSearchUri so that IMAP special characters (i.e. '\') + // won't be replaced with '/' in NECKO. + // it will be unescaped in nsImapUrl::ParseUrl(). + nsCString escapedSearchUri; + + MsgEscapeString(aSearchUri, nsINetUtil::ESCAPE_XALPHAS, escapedSearchUri); + urlSpec.Append(escapedSearchUri); + rv = mailNewsUrl->SetSpecInternal(urlSpec); + if (NS_SUCCEEDED(rv)) + rv = GetImapConnectionAndLoadUrl(imapUrl, nullptr, nullptr); + } + return rv; +} + +// just a helper method to break down imap message URIs.... +nsresult nsImapService::DecomposeImapURI(const nsACString& aMessageURI, + nsIMsgFolder** aFolder, + nsACString& aMsgKey) { + nsMsgKey msgKey; + nsresult rv = DecomposeImapURI(aMessageURI, aFolder, &msgKey); + NS_ENSURE_SUCCESS(rv, rv); + + if (msgKey) { + nsAutoCString messageIdString; + messageIdString.AppendInt(msgKey); + aMsgKey = messageIdString; + } + + return rv; +} + +// just a helper method to break down imap message URIs.... +nsresult nsImapService::DecomposeImapURI(const nsACString& aMessageURI, + nsIMsgFolder** aFolder, + nsMsgKey* aMsgKey) { + NS_ENSURE_ARG_POINTER(aFolder); + NS_ENSURE_ARG_POINTER(aMsgKey); + + nsAutoCString folderURI; + nsresult rv = nsParseImapMessageURI(aMessageURI, folderURI, aMsgKey, nullptr); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr folder; + rv = GetOrCreateFolder(folderURI, aFolder); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +NS_IMETHODIMP nsImapService::SaveMessageToDisk( + const nsACString& aMessageURI, nsIFile* aFile, bool aAddDummyEnvelope, + nsIUrlListener* aUrlListener, nsIURI** aURL, bool canonicalLineEnding, + nsIMsgWindow* aMsgWindow) { + nsCOMPtr folder; + nsCOMPtr imapUrl; + nsAutoCString msgKey; + + nsresult rv = DecomposeImapURI(aMessageURI, getter_AddRefs(folder), msgKey); + NS_ENSURE_SUCCESS(rv, rv); + + bool hasMsgOffline = false; + + if (folder) + folder->HasMsgOffline(strtoul(msgKey.get(), nullptr, 10), &hasMsgOffline); + + nsAutoCString urlSpec; + char hierarchyDelimiter = GetHierarchyDelimiter(folder); + rv = CreateStartOfImapUrl(aMessageURI, getter_AddRefs(imapUrl), folder, + aUrlListener, urlSpec, hierarchyDelimiter); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr imapMessageSink( + do_QueryInterface(folder, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr msgUrl = do_QueryInterface(imapUrl, &rv); + NS_ENSURE_SUCCESS(rv, rv); + msgUrl->SetMessageFile(aFile); + msgUrl->SetAddDummyEnvelope(aAddDummyEnvelope); + msgUrl->SetCanonicalLineEnding(canonicalLineEnding); + + nsCOMPtr mailnewsUrl = do_QueryInterface(msgUrl); + if (mailnewsUrl) mailnewsUrl->SetMsgIsInLocalCache(hasMsgOffline); + + nsCOMPtr saveAsListener; + mailnewsUrl->GetSaveAsListener(aAddDummyEnvelope, aFile, + getter_AddRefs(saveAsListener)); + + return FetchMessage(imapUrl, nsIImapUrl::nsImapSaveMessageToDisk, folder, + imapMessageSink, aMsgWindow, saveAsListener, msgKey, + false, aURL); + } + return rv; +} + +/* fetching RFC822 messages */ +/* imap4://HOST>fetch>>MAILBOXPATH>x */ +/* 'x' is the message UID */ +/* will set the 'SEEN' flag */ +NS_IMETHODIMP nsImapService::AddImapFetchToUrl( + nsIMsgMailNewsUrl* aUrl, nsIMsgFolder* aImapMailFolder, + const nsACString& aMessageIdentifierList, + const nsACString& aAdditionalHeader) { + NS_ENSURE_ARG_POINTER(aUrl); + + nsAutoCString urlSpec; + nsresult rv = aUrl->GetSpec(urlSpec); + NS_ENSURE_SUCCESS(rv, rv); + + char hierarchyDelimiter = GetHierarchyDelimiter(aImapMailFolder); + + urlSpec.AppendLiteral("fetch>UID>"); + urlSpec.Append(hierarchyDelimiter); + + nsAutoCString folderName; + GetFolderName(aImapMailFolder, folderName); + urlSpec.Append(folderName); + + urlSpec.Append('>'); + urlSpec.Append(aMessageIdentifierList); + + if (!aAdditionalHeader.IsEmpty()) { + urlSpec.AppendLiteral("?header="); + urlSpec.Append(aAdditionalHeader); + } + + return aUrl->SetSpecInternal(urlSpec); +} + +NS_IMETHODIMP nsImapService::FetchMessage( + nsIImapUrl* aImapUrl, nsImapAction aImapAction, + nsIMsgFolder* aImapMailFolder, nsIImapMessageSink* aImapMessage, + nsIMsgWindow* aMsgWindow, nsISupports* aDisplayConsumer, + const nsACString& messageIdentifierList, bool aConvertDataToText, + nsIURI** aURL) { + NS_ENSURE_ARG_POINTER(aImapUrl); + NS_ENSURE_ARG_POINTER(aImapMailFolder); + NS_ENSURE_ARG_POINTER(aImapMessage); + + nsresult rv; + nsCOMPtr mailnewsurl = do_QueryInterface(aImapUrl); + + rv = AddImapFetchToUrl(mailnewsurl, aImapMailFolder, messageIdentifierList, + ""_ns); + NS_ENSURE_SUCCESS(rv, rv); + + if (WeAreOffline()) { + bool msgIsInCache = false; + nsCOMPtr msgUrl(do_QueryInterface(aImapUrl)); + msgUrl->GetMsgIsInLocalCache(&msgIsInCache); + if (!msgIsInCache) + IsMsgInMemCache(mailnewsurl, aImapMailFolder, &msgIsInCache); + + // Display the "offline" message if we didn't find it in the memory cache + // either + if (!msgIsInCache) { + return NS_ERROR_OFFLINE; + } + } + + if (aURL) mailnewsurl.forget(aURL); + + return GetMessageFromUrl(aImapUrl, aImapAction, aImapMailFolder, aImapMessage, + aMsgWindow, aDisplayConsumer, aConvertDataToText, + aURL); +} + +nsresult nsImapService::GetMessageFromUrl( + nsIImapUrl* aImapUrl, nsImapAction aImapAction, + nsIMsgFolder* aImapMailFolder, nsIImapMessageSink* aImapMessage, + nsIMsgWindow* aMsgWindow, nsISupports* aDisplayConsumer, + bool aConvertDataToText, nsIURI** aURL) { + nsresult rv = SetImapUrlSink(aImapMailFolder, aImapUrl); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aImapUrl->SetImapMessageSink(aImapMessage); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aImapUrl->SetImapAction(aImapAction); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr url(do_QueryInterface(aImapUrl)); + + // if the display consumer is a docshell, then we should run the url in the + // docshell. otherwise, it should be a stream listener....so open a channel + // using AsyncRead and the provided stream listener.... + + nsCOMPtr docShell(do_QueryInterface(aDisplayConsumer, &rv)); + if (aImapMailFolder && docShell) { + nsCOMPtr aMsgIncomingServer; + rv = aImapMailFolder->GetServer(getter_AddRefs(aMsgIncomingServer)); + if (NS_SUCCEEDED(rv) && aMsgIncomingServer) { + bool interrupted; + nsCOMPtr aImapServer( + do_QueryInterface(aMsgIncomingServer, &rv)); + if (NS_SUCCEEDED(rv) && aImapServer) + aImapServer->PseudoInterruptMsgLoad(aImapMailFolder, aMsgWindow, + &interrupted); + } + } + if (NS_SUCCEEDED(rv) && docShell) { + NS_ASSERTION(!aConvertDataToText, + "can't convert to text when using docshell"); + RefPtr loadState = new nsDocShellLoadState(url); + loadState->SetLoadFlags(nsIWebNavigation::LOAD_FLAGS_NONE); + loadState->SetFirstParty(false); + loadState->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal()); + rv = docShell->LoadURI(loadState, false); + } else { + nsCOMPtr streamListener = + do_QueryInterface(aDisplayConsumer, &rv); + nsCOMPtr mailnewsUrl = do_QueryInterface(aImapUrl, &rv); + if (aMsgWindow && mailnewsUrl) mailnewsUrl->SetMsgWindow(aMsgWindow); + if (NS_SUCCEEDED(rv) && streamListener) { + nsCOMPtr channel; + nsCOMPtr loadGroup; + if (NS_SUCCEEDED(rv) && mailnewsUrl) + mailnewsUrl->GetLoadGroup(getter_AddRefs(loadGroup)); + + nsCOMPtr loadInfo = new mozilla::net::LoadInfo( + nsContentUtils::GetSystemPrincipal(), nullptr, nullptr, + nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, + nsIContentPolicy::TYPE_OTHER); + rv = NewChannel(url, loadInfo, getter_AddRefs(channel)); + NS_ENSURE_SUCCESS(rv, rv); + + // we need a load group to hold onto the channel. When the request is + // finished, it'll get removed from the load group, and the channel will + // go away, which will free the load group. + if (!loadGroup) loadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID); + + rv = channel->SetLoadGroup(loadGroup); + NS_ENSURE_SUCCESS(rv, rv); + + if (aConvertDataToText) { + nsCOMPtr conversionListener; + nsCOMPtr streamConverter = + do_GetService("@mozilla.org/streamConverters;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + rv = streamConverter->AsyncConvertData( + "message/rfc822", "*/*", streamListener, channel, + getter_AddRefs(conversionListener)); + NS_ENSURE_SUCCESS(rv, rv); + streamListener = conversionListener; // this is our new listener. + } + + // now try to open the channel passing in our display consumer as the + // listener + rv = channel->AsyncOpen(streamListener); + } else // do what we used to do before + { + // I'd like to get rid of this code as I believe that we always get a + // docshell or stream listener passed into us in this method but i'm not + // sure yet... I'm going to use an assert for now to figure out if this is + // ever getting called +#if defined(DEBUG_mscott) || defined(DEBUG_bienvenu) + NS_ERROR("oops...someone still is reaching this part of the code"); +#endif + rv = GetImapConnectionAndLoadUrl(aImapUrl, aDisplayConsumer, aURL); + } + } + return rv; +} + +// this method streams a message to the passed in consumer, with an optional +// stream converter and additional header (e.g., "header=filter") +NS_IMETHODIMP nsImapService::StreamMessage( + const nsACString& aMessageURI, nsISupports* aConsumer, + nsIMsgWindow* aMsgWindow, nsIUrlListener* aUrlListener, bool aConvertData, + const nsACString& aAdditionalHeader, bool aLocalOnly, nsIURI** aURL) { + nsCOMPtr folder; + nsAutoCString msgKey; + nsAutoCString mimePart; + nsAutoCString folderURI; + nsMsgKey key; + nsAutoCString messageURI(aMessageURI); + + int32_t typeIndex = messageURI.Find("&type=application/x-message-display"); + if (typeIndex != kNotFound) { + // This happens with forward inline of a message/rfc822 attachment opened in + // a standalone msg window. + // So, just cut to the chase and call AsyncOpen on a channel. + nsCOMPtr uri; + messageURI.Cut(typeIndex, + sizeof("&type=application/x-message-display") - 1); + nsresult rv = NS_NewURI(getter_AddRefs(uri), messageURI.get()); + NS_ENSURE_SUCCESS(rv, rv); + if (aURL) NS_IF_ADDREF(*aURL = uri); + nsCOMPtr aStreamListener = + do_QueryInterface(aConsumer, &rv); + if (NS_SUCCEEDED(rv) && aStreamListener) { + nsCOMPtr aChannel; + nsCOMPtr aLoadGroup; + nsCOMPtr mailnewsUrl = do_QueryInterface(uri, &rv); + if (NS_SUCCEEDED(rv) && mailnewsUrl) + mailnewsUrl->GetLoadGroup(getter_AddRefs(aLoadGroup)); + + nsCOMPtr loadInfo = new mozilla::net::LoadInfo( + nsContentUtils::GetSystemPrincipal(), nullptr, nullptr, + nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, + nsIContentPolicy::TYPE_OTHER); + rv = NewChannel(uri, loadInfo, getter_AddRefs(aChannel)); + NS_ENSURE_SUCCESS(rv, rv); + + // now try to open the channel passing in our display consumer as the + // listener + rv = aChannel->AsyncOpen(aStreamListener); + return rv; + } + } + + nsresult rv = DecomposeImapURI(aMessageURI, getter_AddRefs(folder), msgKey); + NS_ENSURE_SUCCESS(rv, rv); + + if (msgKey.IsEmpty()) return NS_MSG_MESSAGE_NOT_FOUND; + rv = nsParseImapMessageURI(aMessageURI, folderURI, &key, + getter_Copies(mimePart)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr imapMessageSink(do_QueryInterface(folder, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr imapUrl; + nsAutoCString urlSpec; + char hierarchyDelimiter = GetHierarchyDelimiter(folder); + rv = CreateStartOfImapUrl(aMessageURI, getter_AddRefs(imapUrl), folder, + aUrlListener, urlSpec, hierarchyDelimiter); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr mailnewsurl(do_QueryInterface(imapUrl)); + + // This option is used by the JS Mime Emitter, in case we want a cheap + // streaming, for example, if we just want a quick look at some header, + // without having to download all the attachments... + + // We need to add the fetch command here for the cache lookup to behave + // correctly + nsAutoCString additionalHeader(aAdditionalHeader); + rv = AddImapFetchToUrl(mailnewsurl, folder, msgKey, additionalHeader); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr aMsgIncomingServer; + + mailnewsurl->SetMsgWindow(aMsgWindow); + rv = mailnewsurl->GetServer(getter_AddRefs(aMsgIncomingServer)); + + // Try to check if the message is offline + bool hasMsgOffline = false; + folder->HasMsgOffline(key, &hasMsgOffline); + mailnewsurl->SetMsgIsInLocalCache(hasMsgOffline); + imapUrl->SetLocalFetchOnly(aLocalOnly); + + // If we don't have the message available locally, and we can't get it + // over the network, return with an error + if (aLocalOnly || WeAreOffline()) { + bool isMsgInMemCache = false; + if (!hasMsgOffline) { + rv = IsMsgInMemCache(mailnewsurl, folder, &isMsgInMemCache); + NS_ENSURE_SUCCESS(rv, rv); + + if (!isMsgInMemCache) return NS_ERROR_FAILURE; + } + } + + bool shouldStoreMsgOffline = false; + folder->ShouldStoreMsgOffline(key, &shouldStoreMsgOffline); + imapUrl->SetStoreResultsOffline(shouldStoreMsgOffline); + rv = GetMessageFromUrl(imapUrl, nsIImapUrl::nsImapMsgFetchPeek, folder, + imapMessageSink, aMsgWindow, aConsumer, aConvertData, + aURL); + return rv; +} + +// this method streams a message's headers to the passed in consumer. +NS_IMETHODIMP nsImapService::StreamHeaders(const nsACString& aMessageURI, + nsIStreamListener* aConsumer, + nsIUrlListener* aUrlListener, + bool aLocalOnly, nsIURI** aURL) { + NS_ENSURE_ARG_POINTER(aConsumer); + nsCOMPtr folder; + nsAutoCString msgKey; + nsAutoCString folderURI; + nsCString mimePart; + nsMsgKey key; + + nsresult rv = DecomposeImapURI(aMessageURI, getter_AddRefs(folder), msgKey); + NS_ENSURE_SUCCESS(rv, rv); + + if (msgKey.IsEmpty()) return NS_MSG_MESSAGE_NOT_FOUND; + rv = nsParseImapMessageURI(aMessageURI, folderURI, &key, + getter_Copies(mimePart)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr inputStream; + bool hasMsgOffline = false; + folder->HasMsgOffline(key, &hasMsgOffline); + if (hasMsgOffline) { + nsCOMPtr hdr; + rv = folder->GetMessageHeader(key, getter_AddRefs(hdr)); + NS_ENSURE_SUCCESS(rv, rv); + rv = folder->GetLocalMsgStream(hdr, getter_AddRefs(inputStream)); + NS_ENSURE_SUCCESS(rv, rv); + return MsgStreamMsgHeaders(inputStream, aConsumer); + } + + if (aLocalOnly) return NS_ERROR_FAILURE; + return NS_OK; +} + +NS_IMETHODIMP nsImapService::IsMsgInMemCache(nsIURI* aUrl, + nsIMsgFolder* aImapMailFolder, + bool* aResult) { + NS_ENSURE_ARG_POINTER(aUrl); + NS_ENSURE_ARG_POINTER(aImapMailFolder); + *aResult = false; + + // Poke around in the memory cache + if (mCacheStorage) { + nsAutoCString urlSpec; + aUrl->GetSpec(urlSpec); + + // Strip any query qualifiers. + bool truncated = false; + int32_t ind = urlSpec.FindChar('?'); + if (ind != kNotFound) { + urlSpec.SetLength(ind); + truncated = true; + } + ind = urlSpec.Find("/;"); + if (ind != kNotFound) { + urlSpec.SetLength(ind); + truncated = true; + } + + nsresult rv; + nsCOMPtr folderSink( + do_QueryInterface(aImapMailFolder, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + int32_t uidValidity = -1; + folderSink->GetUidValidity(&uidValidity); + // stick the uid validity in front of the url, so that if the uid validity + // changes, we won't re-use the wrong cache entries. + nsAutoCString extension; + extension.AppendInt(uidValidity, 16); + + bool exists; + if (truncated) { + nsCOMPtr newUri; + rv = NS_NewURI(getter_AddRefs(newUri), urlSpec); + NS_ENSURE_SUCCESS(rv, rv); + rv = mCacheStorage->Exists(newUri, extension, &exists); + } else { + rv = mCacheStorage->Exists(aUrl, extension, &exists); + } + if (NS_SUCCEEDED(rv) && exists) { + *aResult = true; + } + } + + return NS_OK; +} + +nsresult nsImapService::CreateStartOfImapUrl(const nsACString& aImapURI, + nsIImapUrl** imapUrl, + nsIMsgFolder* aImapMailFolder, + nsIUrlListener* aUrlListener, + nsACString& urlSpec, + char& hierarchyDelimiter) { + NS_ENSURE_ARG_POINTER(aImapMailFolder); + + nsCString hostname; + nsCString username; + nsCString escapedUsername; + + nsresult rv = aImapMailFolder->GetHostname(hostname); + NS_ENSURE_SUCCESS(rv, rv); + rv = aImapMailFolder->GetUsername(username); + NS_ENSURE_SUCCESS(rv, rv); + if (!username.IsEmpty()) + MsgEscapeString(username, nsINetUtil::ESCAPE_XALPHAS, escapedUsername); + + int32_t port = nsIImapUrl::DEFAULT_IMAP_PORT; + nsCOMPtr server; + rv = aImapMailFolder->GetServer(getter_AddRefs(server)); + if (NS_SUCCEEDED(rv)) { + server->GetPort(&port); + if (port == -1 || port == 0) port = nsIImapUrl::DEFAULT_IMAP_PORT; + } + + // now we need to create an imap url to load into the connection. The url + // needs to represent a select folder action. + rv = CallCreateInstance(kImapUrlCID, imapUrl); + if (NS_SUCCEEDED(rv) && *imapUrl) { + nsCOMPtr mailnewsUrl = do_QueryInterface(*imapUrl, &rv); + if (NS_SUCCEEDED(rv) && mailnewsUrl && aUrlListener) + mailnewsUrl->RegisterListener(aUrlListener); + nsCOMPtr msgurl(do_QueryInterface(*imapUrl)); + (*imapUrl)->SetExternalLinkUrl(false); + msgurl->SetUri(aImapURI); + + urlSpec = "imap://"; + urlSpec.Append(escapedUsername); + urlSpec.Append('@'); + urlSpec.Append(hostname); + urlSpec.Append(':'); + + nsAutoCString portStr; + portStr.AppendInt(port); + urlSpec.Append(portStr); + + // *** jefft - force to parse the urlSpec in order to search for + // the correct incoming server + rv = mailnewsUrl->SetSpecInternal(urlSpec); + NS_ENSURE_SUCCESS(rv, rv); + + hierarchyDelimiter = kOnlineHierarchySeparatorUnknown; + nsCOMPtr imapFolder = + do_QueryInterface(aImapMailFolder); + if (imapFolder) imapFolder->GetHierarchyDelimiter(&hierarchyDelimiter); + } + return rv; +} + +/* fetching the headers of RFC822 messages */ +/* imap4://HOST>header>>MAILBOXPATH>x */ +/* 'x' is the message UID or sequence number list */ +/* will not affect the 'SEEN' flag */ +NS_IMETHODIMP nsImapService::GetHeaders(nsIMsgFolder* aImapMailFolder, + nsIUrlListener* aUrlListener, + nsIURI** aURL, + const nsACString& messageIdentifierList, + bool messageIdsAreUID) { + // create a protocol instance to handle the request. + // NOTE: once we start working with multiple connections, this step will be + // much more complicated...but for now just create a connection and process + // the request. + NS_ENSURE_ARG_POINTER(aImapMailFolder); + + nsCOMPtr imapUrl; + nsAutoCString urlSpec; + char hierarchyDelimiter = GetHierarchyDelimiter(aImapMailFolder); + + nsresult rv = CreateStartOfImapUrl(EmptyCString(), getter_AddRefs(imapUrl), + aImapMailFolder, aUrlListener, urlSpec, + hierarchyDelimiter); + if (NS_SUCCEEDED(rv) && imapUrl) { + nsCOMPtr mailnewsUrl = do_QueryInterface(imapUrl); + + rv = imapUrl->SetImapAction(nsIImapUrl::nsImapMsgFetch); + rv = SetImapUrlSink(aImapMailFolder, imapUrl); + + if (NS_SUCCEEDED(rv)) { + urlSpec.AppendLiteral("/header>"); + urlSpec.Append(messageIdsAreUID ? uidString : sequenceString); + urlSpec.Append('>'); + urlSpec.Append(char(hierarchyDelimiter)); + + nsCString folderName; + + GetFolderName(aImapMailFolder, folderName); + urlSpec.Append(folderName); + urlSpec.Append('>'); + urlSpec.Append(messageIdentifierList); + rv = mailnewsUrl->SetSpecInternal(urlSpec); + + if (NS_SUCCEEDED(rv)) + rv = GetImapConnectionAndLoadUrl(imapUrl, nullptr, aURL); + } + } + return rv; +} + +/* peeking at the start of msg bodies */ +/* imap4://HOST>header>>MAILBOXPATH>x>n */ +/* 'x' is the message UID */ +/* 'n' is the number of bytes to fetch */ +/* will not affect the 'SEEN' flag */ +NS_IMETHODIMP nsImapService::GetBodyStart( + nsIMsgFolder* aImapMailFolder, nsIUrlListener* aUrlListener, + const nsACString& messageIdentifierList, int32_t numBytes, nsIURI** aURL) { + NS_ENSURE_ARG_POINTER(aImapMailFolder); + + nsresult rv; + nsCOMPtr imapUrl; + nsAutoCString urlSpec; + + char hierarchyDelimiter = GetHierarchyDelimiter(aImapMailFolder); + rv = CreateStartOfImapUrl(EmptyCString(), getter_AddRefs(imapUrl), + aImapMailFolder, aUrlListener, urlSpec, + hierarchyDelimiter); + if (NS_SUCCEEDED(rv) && imapUrl) { + rv = imapUrl->SetImapAction(nsIImapUrl::nsImapMsgPreview); + rv = SetImapUrlSink(aImapMailFolder, imapUrl); + + if (NS_SUCCEEDED(rv)) { + nsCOMPtr mailnewsUrl = do_QueryInterface(imapUrl); + + urlSpec.AppendLiteral("/previewBody>"); + urlSpec.Append(uidString); + urlSpec.Append('>'); + urlSpec.Append(hierarchyDelimiter); + + nsCString folderName; + GetFolderName(aImapMailFolder, folderName); + urlSpec.Append(folderName); + urlSpec.Append('>'); + urlSpec.Append(messageIdentifierList); + urlSpec.Append('>'); + urlSpec.AppendInt(numBytes); + rv = mailnewsUrl->SetSpecInternal(urlSpec); + if (NS_SUCCEEDED(rv)) + rv = GetImapConnectionAndLoadUrl(imapUrl, nullptr, aURL); + } + } + return rv; +} + +nsresult nsImapService::FolderCommand(nsIMsgFolder* imapMailFolder, + nsIUrlListener* urlListener, + const char* aCommand, + nsImapAction imapAction, + nsIMsgWindow* msgWindow, nsIURI** url) { + NS_ENSURE_ARG_POINTER(imapMailFolder); + + nsCOMPtr imapUrl; + nsAutoCString urlSpec; + + char hierarchyDelimiter = GetHierarchyDelimiter(imapMailFolder); + nsresult rv = CreateStartOfImapUrl(EmptyCString(), getter_AddRefs(imapUrl), + imapMailFolder, urlListener, urlSpec, + hierarchyDelimiter); + if (NS_SUCCEEDED(rv) && imapUrl) { + rv = imapUrl->SetImapAction(imapAction); + rv = SetImapUrlSink(imapMailFolder, imapUrl); + nsCOMPtr mailnewsurl = do_QueryInterface(imapUrl); + if (mailnewsurl) mailnewsurl->SetMsgWindow(msgWindow); + + if (NS_SUCCEEDED(rv)) { + urlSpec.Append(aCommand); + urlSpec.Append(hierarchyDelimiter); + + nsCString folderName; + GetFolderName(imapMailFolder, folderName); + urlSpec.Append(folderName); + rv = mailnewsurl->SetSpecInternal(urlSpec); + if (NS_SUCCEEDED(rv)) + rv = GetImapConnectionAndLoadUrl(imapUrl, nullptr, url); + } + } + return rv; +} + +NS_IMETHODIMP +nsImapService::VerifyLogon(nsIMsgFolder* aFolder, nsIUrlListener* aUrlListener, + nsIMsgWindow* aMsgWindow, nsIURI** aURL) { + nsCOMPtr imapUrl; + nsAutoCString urlSpec; + + char delimiter = '/'; // shouldn't matter what is is. + nsresult rv = CreateStartOfImapUrl(EmptyCString(), getter_AddRefs(imapUrl), + aFolder, aUrlListener, urlSpec, delimiter); + if (NS_SUCCEEDED(rv) && imapUrl) { + nsCOMPtr mailnewsurl = do_QueryInterface(imapUrl); + + nsCOMPtr mailNewsUrl = do_QueryInterface(imapUrl); + mailNewsUrl->SetSuppressErrorMsgs(true); + mailNewsUrl->SetMsgWindow(aMsgWindow); + rv = SetImapUrlSink(aFolder, imapUrl); + urlSpec.AppendLiteral("/verifyLogon"); + rv = mailnewsurl->SetSpecInternal(urlSpec); + if (NS_SUCCEEDED(rv)) + rv = GetImapConnectionAndLoadUrl(imapUrl, nullptr, nullptr); + if (aURL) mailnewsurl.forget(aURL); + } + return rv; +} + +// Noop, used to update a folder (causes server to send changes). +NS_IMETHODIMP nsImapService::Noop(nsIMsgFolder* aImapMailFolder, + nsIUrlListener* aUrlListener, nsIURI** aURL) { + NS_ENSURE_ARG_POINTER(aImapMailFolder); + + return FolderCommand(aImapMailFolder, aUrlListener, "/selectnoop>", + nsIImapUrl::nsImapSelectNoopFolder, nullptr, aURL); +} + +// FolderStatus, used to update message counts +NS_IMETHODIMP nsImapService::UpdateFolderStatus(nsIMsgFolder* aImapMailFolder, + nsIUrlListener* aUrlListener, + nsIURI** aURL) { + NS_ENSURE_ARG_POINTER(aImapMailFolder); + + return FolderCommand(aImapMailFolder, aUrlListener, "/folderstatus>", + nsIImapUrl::nsImapFolderStatus, nullptr, aURL); +} + +// Expunge, used to "compress" an imap folder,removes deleted messages. +NS_IMETHODIMP nsImapService::Expunge(nsIMsgFolder* aImapMailFolder, + nsIUrlListener* aUrlListener, + nsIMsgWindow* aMsgWindow) { + NS_ENSURE_ARG_POINTER(aImapMailFolder); + + return FolderCommand(aImapMailFolder, aUrlListener, "/Expunge>", + nsIImapUrl::nsImapExpungeFolder, aMsgWindow, nullptr); +} + +/* old-stle biff that doesn't download headers */ +NS_IMETHODIMP nsImapService::Biff(nsIMsgFolder* aImapMailFolder, + nsIUrlListener* aUrlListener, nsIURI** aURL, + uint32_t uidHighWater) { + NS_ENSURE_ARG_POINTER(aImapMailFolder); + + // static const char *formatString = "biff>%c%s>%ld"; + nsCOMPtr imapUrl; + nsAutoCString urlSpec; + + char hierarchyDelimiter = GetHierarchyDelimiter(aImapMailFolder); + nsresult rv = CreateStartOfImapUrl(EmptyCString(), getter_AddRefs(imapUrl), + aImapMailFolder, aUrlListener, urlSpec, + hierarchyDelimiter); + if (NS_SUCCEEDED(rv) && imapUrl) { + rv = imapUrl->SetImapAction(nsIImapUrl::nsImapExpungeFolder); + rv = SetImapUrlSink(aImapMailFolder, imapUrl); + + nsCOMPtr mailnewsurl = do_QueryInterface(imapUrl); + if (NS_SUCCEEDED(rv)) { + urlSpec.AppendLiteral("/Biff>"); + urlSpec.Append(hierarchyDelimiter); + + nsCString folderName; + GetFolderName(aImapMailFolder, folderName); + urlSpec.Append(folderName); + urlSpec.Append('>'); + urlSpec.AppendInt(uidHighWater); + rv = mailnewsurl->SetSpecInternal(urlSpec); + if (NS_SUCCEEDED(rv)) + rv = GetImapConnectionAndLoadUrl(imapUrl, nullptr, aURL); + } + } + return rv; +} + +NS_IMETHODIMP nsImapService::DeleteFolder(nsIMsgFolder* aImapMailFolder, + nsIUrlListener* aUrlListener, + nsIMsgWindow* aMsgWindow) { + NS_ENSURE_ARG_POINTER(aImapMailFolder); + + // If it's an aol server then use 'deletefolder' url to + // remove all msgs first and then remove the folder itself. + bool removeFolderAndMsgs = false; + nsCOMPtr server; + if (NS_SUCCEEDED(aImapMailFolder->GetServer(getter_AddRefs(server))) && + server) { + nsCOMPtr imapServer = do_QueryInterface(server); + if (imapServer) imapServer->GetIsAOLServer(&removeFolderAndMsgs); + } + + return FolderCommand(aImapMailFolder, aUrlListener, + removeFolderAndMsgs ? "/deletefolder>" : "/delete>", + nsIImapUrl::nsImapDeleteFolder, aMsgWindow, nullptr); +} + +NS_IMETHODIMP nsImapService::DeleteMessages( + nsIMsgFolder* aImapMailFolder, nsIUrlListener* aUrlListener, nsIURI** aURL, + const nsACString& messageIdentifierList, bool messageIdsAreUID) { + NS_ENSURE_ARG_POINTER(aImapMailFolder); + + // create a protocol instance to handle the request. + // NOTE: once we start working with multiple connections, this step will be + // much more complicated...but for now just create a connection and process + // the request. + nsresult rv; + nsCOMPtr imapUrl; + nsAutoCString urlSpec; + + char hierarchyDelimiter = GetHierarchyDelimiter(aImapMailFolder); + rv = CreateStartOfImapUrl(EmptyCString(), getter_AddRefs(imapUrl), + aImapMailFolder, aUrlListener, urlSpec, + hierarchyDelimiter); + if (NS_SUCCEEDED(rv) && imapUrl) { + rv = imapUrl->SetImapAction(nsIImapUrl::nsImapMsgFetch); + rv = SetImapUrlSink(aImapMailFolder, imapUrl); + + if (NS_SUCCEEDED(rv)) { + nsCOMPtr mailnewsurl = do_QueryInterface(imapUrl); + + urlSpec.AppendLiteral("/deletemsg>"); + urlSpec.Append(messageIdsAreUID ? uidString : sequenceString); + urlSpec.Append('>'); + urlSpec.Append(hierarchyDelimiter); + + nsCString folderName; + GetFolderName(aImapMailFolder, folderName); + urlSpec.Append(folderName); + urlSpec.Append('>'); + urlSpec.Append(messageIdentifierList); + rv = mailnewsurl->SetSpecInternal(urlSpec); + if (NS_SUCCEEDED(rv)) + rv = GetImapConnectionAndLoadUrl(imapUrl, nullptr, aURL); + } + } + return rv; +} + +// Delete all messages in a folder, used to empty trash +NS_IMETHODIMP nsImapService::DeleteAllMessages(nsIMsgFolder* aImapMailFolder, + nsIUrlListener* aUrlListener) { + NS_ENSURE_ARG_POINTER(aImapMailFolder); + + return FolderCommand(aImapMailFolder, aUrlListener, "/deleteallmsgs>", + nsIImapUrl::nsImapSelectNoopFolder, nullptr, nullptr); +} + +NS_IMETHODIMP nsImapService::AddMessageFlags( + nsIMsgFolder* aImapMailFolder, nsIUrlListener* aUrlListener, + const nsACString& messageIdentifierList, imapMessageFlagsType flags, + bool messageIdsAreUID) { + NS_ENSURE_ARG_POINTER(aImapMailFolder); + + return DiddleFlags(aImapMailFolder, aUrlListener, nullptr, + messageIdentifierList, "addmsgflags", flags, + messageIdsAreUID); +} + +NS_IMETHODIMP nsImapService::SubtractMessageFlags( + nsIMsgFolder* aImapMailFolder, nsIUrlListener* aUrlListener, + const nsACString& messageIdentifierList, imapMessageFlagsType flags, + bool messageIdsAreUID) { + NS_ENSURE_ARG_POINTER(aImapMailFolder); + + return DiddleFlags(aImapMailFolder, aUrlListener, nullptr, + messageIdentifierList, "subtractmsgflags", flags, + messageIdsAreUID); +} + +NS_IMETHODIMP nsImapService::SetMessageFlags( + nsIMsgFolder* aImapMailFolder, nsIUrlListener* aUrlListener, nsIURI** aURL, + const nsACString& messageIdentifierList, imapMessageFlagsType flags, + bool messageIdsAreUID) { + NS_ENSURE_ARG_POINTER(aImapMailFolder); + + return DiddleFlags(aImapMailFolder, aUrlListener, aURL, messageIdentifierList, + "setmsgflags", flags, messageIdsAreUID); +} + +nsresult nsImapService::DiddleFlags(nsIMsgFolder* aImapMailFolder, + nsIUrlListener* aUrlListener, nsIURI** aURL, + const nsACString& messageIdentifierList, + const char* howToDiddle, + imapMessageFlagsType flags, + bool messageIdsAreUID) { + NS_ENSURE_ARG_POINTER(aImapMailFolder); + + // create a protocol instance to handle the request. + // NOTE: once we start working with multiple connections, + // this step will be much more complicated...but for now + // just create a connection and process the request. + nsCOMPtr imapUrl; + nsAutoCString urlSpec; + + char hierarchyDelimiter = GetHierarchyDelimiter(aImapMailFolder); + nsresult rv = CreateStartOfImapUrl(EmptyCString(), getter_AddRefs(imapUrl), + aImapMailFolder, aUrlListener, urlSpec, + hierarchyDelimiter); + if (NS_SUCCEEDED(rv) && imapUrl) { + rv = imapUrl->SetImapAction(nsIImapUrl::nsImapMsgFetch); + rv = SetImapUrlSink(aImapMailFolder, imapUrl); + + if (NS_SUCCEEDED(rv)) { + nsCOMPtr mailnewsurl = do_QueryInterface(imapUrl); + + urlSpec.Append('/'); + urlSpec.Append(howToDiddle); + urlSpec.Append('>'); + urlSpec.Append(messageIdsAreUID ? uidString : sequenceString); + urlSpec.Append('>'); + urlSpec.Append(hierarchyDelimiter); + nsCString folderName; + GetFolderName(aImapMailFolder, folderName); + urlSpec.Append(folderName); + urlSpec.Append('>'); + urlSpec.Append(messageIdentifierList); + urlSpec.Append('>'); + urlSpec.AppendInt(flags); + rv = mailnewsurl->SetSpecInternal(urlSpec); + if (NS_SUCCEEDED(rv)) + rv = GetImapConnectionAndLoadUrl(imapUrl, nullptr, aURL); + } + } + return rv; +} + +nsresult nsImapService::SetImapUrlSink(nsIMsgFolder* aMsgFolder, + nsIImapUrl* aImapUrl) { + NS_ENSURE_ARG_POINTER(aMsgFolder); + NS_ENSURE_ARG_POINTER(aImapUrl); + + nsresult rv; + nsCOMPtr incomingServer; + nsCOMPtr imapServerSink; + + rv = aMsgFolder->GetServer(getter_AddRefs(incomingServer)); + if (NS_SUCCEEDED(rv) && incomingServer) { + imapServerSink = do_QueryInterface(incomingServer); + if (imapServerSink) aImapUrl->SetImapServerSink(imapServerSink); + } + + nsCOMPtr imapMailFolderSink = + do_QueryInterface(aMsgFolder); + if (NS_SUCCEEDED(rv) && imapMailFolderSink) + aImapUrl->SetImapMailFolderSink(imapMailFolderSink); + + nsCOMPtr imapMessageSink = do_QueryInterface(aMsgFolder); + if (NS_SUCCEEDED(rv) && imapMessageSink) + aImapUrl->SetImapMessageSink(imapMessageSink); + + nsCOMPtr mailnewsUrl = do_QueryInterface(aImapUrl); + mailnewsUrl->SetFolder(aMsgFolder); + + return NS_OK; +} + +NS_IMETHODIMP nsImapService::DiscoverAllFolders(nsIMsgFolder* aImapMailFolder, + nsIUrlListener* aUrlListener, + nsIMsgWindow* aMsgWindow) { + NS_ENSURE_ARG_POINTER(aImapMailFolder); + + nsCOMPtr imapUrl; + nsAutoCString urlSpec; + + char hierarchyDelimiter = GetHierarchyDelimiter(aImapMailFolder); + nsresult rv = CreateStartOfImapUrl(EmptyCString(), getter_AddRefs(imapUrl), + aImapMailFolder, aUrlListener, urlSpec, + hierarchyDelimiter); + if (NS_SUCCEEDED(rv)) { + rv = SetImapUrlSink(aImapMailFolder, imapUrl); + + if (NS_SUCCEEDED(rv)) { + nsCOMPtr mailnewsurl = do_QueryInterface(imapUrl); + mailnewsurl->SetMsgWindow(aMsgWindow); + urlSpec.AppendLiteral("/discoverallboxes"); + rv = mailnewsurl->SetSpecInternal(urlSpec); + if (NS_SUCCEEDED(rv)) + rv = GetImapConnectionAndLoadUrl(imapUrl, nullptr, nullptr); + } + } + return rv; +} + +NS_IMETHODIMP nsImapService::DiscoverAllAndSubscribedFolders( + nsIMsgFolder* aImapMailFolder, nsIUrlListener* aUrlListener, + nsIMsgWindow* aMsgWindow) { + NS_ENSURE_ARG_POINTER(aImapMailFolder); + + nsCOMPtr aImapUrl; + nsAutoCString urlSpec; + + char hierarchyDelimiter = GetHierarchyDelimiter(aImapMailFolder); + nsresult rv = CreateStartOfImapUrl(EmptyCString(), getter_AddRefs(aImapUrl), + aImapMailFolder, aUrlListener, urlSpec, + hierarchyDelimiter); + if (NS_SUCCEEDED(rv) && aImapUrl) { + rv = SetImapUrlSink(aImapMailFolder, aImapUrl); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr mailnewsurl = do_QueryInterface(aImapUrl); + urlSpec.AppendLiteral("/discoverallandsubscribedboxes"); + rv = mailnewsurl->SetSpecInternal(urlSpec); + + if (aMsgWindow) mailnewsurl->SetMsgWindow(aMsgWindow); + + if (NS_SUCCEEDED(rv)) + rv = GetImapConnectionAndLoadUrl(aImapUrl, nullptr, nullptr); + } + } + return rv; +} + +NS_IMETHODIMP nsImapService::DiscoverChildren(nsIMsgFolder* aImapMailFolder, + nsIUrlListener* aUrlListener, + const nsACString& folderPath) { + NS_ENSURE_ARG_POINTER(aImapMailFolder); + + nsCOMPtr aImapUrl; + nsAutoCString urlSpec; + + char hierarchyDelimiter = GetHierarchyDelimiter(aImapMailFolder); + nsresult rv = CreateStartOfImapUrl(EmptyCString(), getter_AddRefs(aImapUrl), + aImapMailFolder, aUrlListener, urlSpec, + hierarchyDelimiter); + if (NS_SUCCEEDED(rv)) { + rv = SetImapUrlSink(aImapMailFolder, aImapUrl); + if (NS_SUCCEEDED(rv)) { + if (!folderPath.IsEmpty()) { + nsCOMPtr mailnewsurl = do_QueryInterface(aImapUrl); + urlSpec.AppendLiteral("/discoverchildren>"); + urlSpec.Append(hierarchyDelimiter); + urlSpec.Append(folderPath); + rv = mailnewsurl->SetSpecInternal(urlSpec); + + // Make sure the uri has the same hierarchy separator as the one in msg + // folder obj if it's not kOnlineHierarchySeparatorUnknown (ie, '^'). + char uriDelimiter; + nsresult rv1 = aImapUrl->GetOnlineSubDirSeparator(&uriDelimiter); + if (NS_SUCCEEDED(rv1) && + hierarchyDelimiter != kOnlineHierarchySeparatorUnknown && + uriDelimiter != hierarchyDelimiter) + aImapUrl->SetOnlineSubDirSeparator(hierarchyDelimiter); + + if (NS_SUCCEEDED(rv)) + rv = GetImapConnectionAndLoadUrl(aImapUrl, nullptr, nullptr); + } else + rv = NS_ERROR_FAILURE; + } + } + return rv; +} + +NS_IMETHODIMP nsImapService::OnlineMessageCopy( + nsIMsgFolder* aSrcFolder, const nsACString& messageIds, + nsIMsgFolder* aDstFolder, bool idsAreUids, bool isMove, + nsIUrlListener* aUrlListener, nsIURI** aURL, nsISupports* copyState, + nsIMsgWindow* aMsgWindow) { + NS_ENSURE_ARG_POINTER(aSrcFolder); + NS_ENSURE_ARG_POINTER(aDstFolder); + + nsresult rv; + nsCOMPtr srcServer; + nsCOMPtr dstServer; + + rv = aSrcFolder->GetServer(getter_AddRefs(srcServer)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aDstFolder->GetServer(getter_AddRefs(dstServer)); + NS_ENSURE_SUCCESS(rv, rv); + + bool sameServer; + rv = dstServer->Equals(srcServer, &sameServer); + NS_ENSURE_SUCCESS(rv, rv); + + if (!sameServer) { + NS_ASSERTION(false, "can't use this method to copy across servers"); + // *** can only take message from the same imap host and user accnt + return NS_ERROR_FAILURE; + } + + nsCOMPtr imapUrl; + nsAutoCString urlSpec; + + char hierarchyDelimiter = GetHierarchyDelimiter(aSrcFolder); + rv = CreateStartOfImapUrl(EmptyCString(), getter_AddRefs(imapUrl), aSrcFolder, + aUrlListener, urlSpec, hierarchyDelimiter); + if (NS_SUCCEEDED(rv)) { + SetImapUrlSink(aSrcFolder, imapUrl); + imapUrl->SetCopyState(copyState); + + nsCOMPtr mailnewsurl(do_QueryInterface(imapUrl)); + mailnewsurl->SetMsgWindow(aMsgWindow); + + if (isMove) + urlSpec.AppendLiteral("/onlinemove>"); + else + urlSpec.AppendLiteral("/onlinecopy>"); + if (idsAreUids) + urlSpec.Append(uidString); + else + urlSpec.Append(sequenceString); + urlSpec.Append('>'); + urlSpec.Append(hierarchyDelimiter); + + nsCString folderName; + GetFolderName(aSrcFolder, folderName); + urlSpec.Append(folderName); + urlSpec.Append('>'); + urlSpec.Append(messageIds); + urlSpec.Append('>'); + urlSpec.Append(hierarchyDelimiter); + folderName.Adopt(strdup("")); + GetFolderName(aDstFolder, folderName); + urlSpec.Append(folderName); + + rv = mailnewsurl->SetSpecInternal(urlSpec); + if (NS_SUCCEEDED(rv)) + rv = GetImapConnectionAndLoadUrl(imapUrl, nullptr, aURL); + } + return rv; +} + +nsresult nsImapService::OfflineAppendFromFile( + nsIFile* aFile, nsIURI* aUrl, nsIMsgFolder* aDstFolder, + const nsACString& messageId, // to be replaced + bool inSelectedState, // needs to be in + nsIUrlListener* aListener, nsIURI** aURL, nsISupports* aCopyState) { + nsCOMPtr destDB; + nsresult rv = aDstFolder->GetMsgDatabase(getter_AddRefs(destDB)); + // ### might need to send some notifications instead of just returning + + bool isLocked; + aDstFolder->GetLocked(&isLocked); + if (isLocked) return NS_MSG_FOLDER_BUSY; + + if (NS_SUCCEEDED(rv) && destDB) { + nsMsgKey fakeKey; + destDB->GetNextFakeOfflineMsgKey(&fakeKey); + + nsCOMPtr op; + nsCOMPtr opsDb = do_QueryInterface(destDB, &rv); + NS_ENSURE_SUCCESS(rv, rv); + rv = opsDb->GetOfflineOpForKey(fakeKey, true, getter_AddRefs(op)); + if (NS_SUCCEEDED(rv) && op) { + nsCString destFolderUri; + aDstFolder->GetURI(destFolderUri); + op->SetOperation( + nsIMsgOfflineImapOperation::kAppendDraft); // ### do we care if it's + // a template? + op->SetDestinationFolderURI(destFolderUri); + nsCOMPtr outputStream; + nsCOMPtr msgStore; + nsCOMPtr dstServer; + nsCOMPtr newMsgHdr; + + aDstFolder->GetServer(getter_AddRefs(dstServer)); + rv = dstServer->GetMsgStore(getter_AddRefs(msgStore)); + NS_ENSURE_SUCCESS(rv, rv); + rv = destDB->CreateNewHdr(fakeKey, getter_AddRefs(newMsgHdr)); + NS_ENSURE_SUCCESS(rv, rv); + rv = aDstFolder->GetOfflineStoreOutputStream( + newMsgHdr, getter_AddRefs(outputStream)); + + if (NS_SUCCEEDED(rv) && outputStream) { + nsCOMPtr inputStream; + nsCOMPtr msgParser = do_CreateInstance( + "@mozilla.org/messenger/messagestateparser;1", &rv); + msgParser->SetMailDB(destDB); + + rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), aFile); + if (NS_SUCCEEDED(rv) && inputStream) { + // now, copy the temp file to the offline store for the dest folder. + RefPtr inputStreamBuffer = + new nsMsgLineStreamBuffer( + FILE_IO_BUFFER_SIZE, + true, // allocate new lines + false); // leave CRLFs on the returned string + int64_t fileSize; + aFile->GetFileSize(&fileSize); + uint32_t bytesWritten; + rv = NS_OK; + // rv = inputStream->Read(inputBuffer, inputBufferSize, &bytesRead); + // if (NS_SUCCEEDED(rv) && bytesRead > 0) + msgParser->SetState(nsIMsgParseMailMsgState::ParseHeadersState); + msgParser->SetNewMsgHdr(newMsgHdr); + // set the new key to fake key so the msg hdr will have that for a key + msgParser->SetNewKey(fakeKey); + bool needMoreData = false; + char* newLine = nullptr; + uint32_t numBytesInLine = 0; + do { + newLine = inputStreamBuffer->ReadNextLine( + inputStream, numBytesInLine, needMoreData); + if (newLine) { + msgParser->ParseAFolderLine(newLine, numBytesInLine); + rv = outputStream->Write(newLine, numBytesInLine, &bytesWritten); + free(newLine); + } + } while (newLine); + msgParser->FinishHeader(); + + if (NS_SUCCEEDED(rv)) { + uint32_t resultFlags; + newMsgHdr->OrFlags( + nsMsgMessageFlags::Offline | nsMsgMessageFlags::Read, + &resultFlags); + newMsgHdr->SetOfflineMessageSize(fileSize); + destDB->AddNewHdrToDB(newMsgHdr, true /* notify */); + aDstFolder->SetFlag(nsMsgFolderFlags::OfflineEvents); + if (msgStore) msgStore->FinishNewMessage(outputStream, newMsgHdr); + } + // tell the listener we're done. + inputStream->Close(); + inputStream = nullptr; + aListener->OnStopRunningUrl(aUrl, NS_OK); + } + outputStream->Close(); + } + } + } + + if (destDB) destDB->Close(true); + return rv; +} + +/* append message from file url */ +/* imap://HOST>appendmsgfromfile>DESTINATIONMAILBOXPATH */ +/* imap://HOST>appenddraftfromfile>DESTINATIONMAILBOXPATH>UID>messageId */ +NS_IMETHODIMP nsImapService::AppendMessageFromFile( + nsIFile* aFile, nsIMsgFolder* aDstFolder, + const nsACString& messageId, // to be replaced + bool idsAreUids, + bool inSelectedState, // needs to be in + nsIUrlListener* aListener, nsISupports* aCopyState, + nsIMsgWindow* aMsgWindow) { + NS_ENSURE_ARG_POINTER(aFile); + NS_ENSURE_ARG_POINTER(aDstFolder); + + nsresult rv; + nsCOMPtr imapUrl; + nsAutoCString urlSpec; + + char hierarchyDelimiter = GetHierarchyDelimiter(aDstFolder); + rv = CreateStartOfImapUrl(EmptyCString(), getter_AddRefs(imapUrl), aDstFolder, + aListener, urlSpec, hierarchyDelimiter); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr msgUrl = do_QueryInterface(imapUrl); + if (msgUrl && aMsgWindow) { + // we get the loadGroup from msgWindow + msgUrl->SetMsgWindow(aMsgWindow); + } + + SetImapUrlSink(aDstFolder, imapUrl); + imapUrl->SetMsgFile(aFile); + imapUrl->SetCopyState(aCopyState); + + nsCOMPtr mailnewsurl = do_QueryInterface(imapUrl); + + if (inSelectedState) + urlSpec.AppendLiteral("/appenddraftfromfile>"); + else + urlSpec.AppendLiteral("/appendmsgfromfile>"); + + urlSpec.Append(hierarchyDelimiter); + + nsCString folderName; + GetFolderName(aDstFolder, folderName); + urlSpec.Append(folderName); + + if (inSelectedState) { + urlSpec.Append('>'); + if (idsAreUids) + urlSpec.Append(uidString); + else + urlSpec.Append(sequenceString); + urlSpec.Append('>'); + if (!messageId.IsEmpty()) urlSpec.Append(messageId); + } + + rv = mailnewsurl->SetSpecInternal(urlSpec); + if (WeAreOffline()) { + // handle offline append to drafts or templates folder here. + return OfflineAppendFromFile(aFile, mailnewsurl, aDstFolder, messageId, + inSelectedState, aListener, nullptr, + aCopyState); + } + if (NS_SUCCEEDED(rv)) + rv = GetImapConnectionAndLoadUrl(imapUrl, nullptr, nullptr); + } + return rv; +} + +nsresult nsImapService::GetImapConnectionAndLoadUrl(nsIImapUrl* aImapUrl, + nsISupports* aConsumer, + nsIURI** aURL) { + NS_ENSURE_ARG_POINTER(aImapUrl); + + bool isValidUrl; + aImapUrl->GetValidUrl(&isValidUrl); + if (!isValidUrl) return NS_ERROR_FAILURE; + + if (WeAreOffline()) { + nsImapAction imapAction; + + // the only thing we can do offline is fetch messages. + // ### TODO - need to look at msg copy, save attachment, etc. when we + // have offline message bodies. + aImapUrl->GetImapAction(&imapAction); + if (imapAction != nsIImapUrl::nsImapMsgFetch && + imapAction != nsIImapUrl::nsImapSaveMessageToDisk) + return NS_MSG_ERROR_OFFLINE; + } + + nsCOMPtr aMsgIncomingServer; + nsCOMPtr msgUrl = do_QueryInterface(aImapUrl); + nsresult rv = msgUrl->GetServer(getter_AddRefs(aMsgIncomingServer)); + + if (aURL) { + msgUrl.forget(aURL); + } + + if (NS_SUCCEEDED(rv) && aMsgIncomingServer) { + nsCOMPtr aImapServer( + do_QueryInterface(aMsgIncomingServer, &rv)); + if (NS_SUCCEEDED(rv) && aImapServer) + rv = aImapServer->GetImapConnectionAndLoadUrl(aImapUrl, aConsumer); + } + return rv; +} + +NS_IMETHODIMP nsImapService::MoveFolder(nsIMsgFolder* srcFolder, + nsIMsgFolder* dstFolder, + nsIUrlListener* urlListener, + nsIMsgWindow* msgWindow) { + NS_ENSURE_ARG_POINTER(srcFolder); + NS_ENSURE_ARG_POINTER(dstFolder); + + nsCOMPtr imapUrl; + nsAutoCString urlSpec; + nsresult rv; + + char default_hierarchyDelimiter = GetHierarchyDelimiter(dstFolder); + rv = CreateStartOfImapUrl(EmptyCString(), getter_AddRefs(imapUrl), dstFolder, + urlListener, urlSpec, default_hierarchyDelimiter); + if (NS_SUCCEEDED(rv) && imapUrl) { + rv = SetImapUrlSink(dstFolder, imapUrl); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr mailNewsUrl = do_QueryInterface(imapUrl); + if (mailNewsUrl) mailNewsUrl->SetMsgWindow(msgWindow); + char hierarchyDelimiter = kOnlineHierarchySeparatorUnknown; + nsCString folderName; + + nsCOMPtr mailnewsurl = do_QueryInterface(imapUrl); + GetFolderName(srcFolder, folderName); + urlSpec.AppendLiteral("/movefolderhierarchy>"); + urlSpec.Append(hierarchyDelimiter); + urlSpec.Append(folderName); + urlSpec.Append('>'); + GetFolderName(dstFolder, folderName); + if (!folderName.IsEmpty()) { + urlSpec.Append(hierarchyDelimiter); + urlSpec.Append(folderName); + } + rv = mailnewsurl->SetSpecInternal(urlSpec); + if (NS_SUCCEEDED(rv)) { + GetFolderName(srcFolder, folderName); + rv = GetImapConnectionAndLoadUrl(imapUrl, nullptr, nullptr); + } + } + } + return rv; +} + +NS_IMETHODIMP nsImapService::RenameLeaf(nsIMsgFolder* srcFolder, + const nsAString& newLeafName, + nsIUrlListener* urlListener, + nsIMsgWindow* msgWindow) { + NS_ENSURE_ARG_POINTER(srcFolder); + + nsCOMPtr imapUrl; + nsAutoCString urlSpec; + + char hierarchyDelimiter = GetHierarchyDelimiter(srcFolder); + nsresult rv = + CreateStartOfImapUrl(EmptyCString(), getter_AddRefs(imapUrl), srcFolder, + urlListener, urlSpec, hierarchyDelimiter); + if (NS_SUCCEEDED(rv)) { + rv = SetImapUrlSink(srcFolder, imapUrl); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr mailNewsUrl = do_QueryInterface(imapUrl); + mailNewsUrl->SetMsgWindow(msgWindow); + nsCString folderName; + GetFolderName(srcFolder, folderName); + urlSpec.AppendLiteral("/rename>"); + urlSpec.Append(hierarchyDelimiter); + urlSpec.Append(folderName); + urlSpec.Append('>'); + urlSpec.Append(hierarchyDelimiter); + nsAutoCString cStrFolderName; + // Unescape the name before looking for parent path + MsgUnescapeString(folderName, 0, cStrFolderName); + int32_t leafNameStart = cStrFolderName.RFindChar(hierarchyDelimiter); + if (leafNameStart != -1) { + cStrFolderName.SetLength(leafNameStart + 1); + urlSpec.Append(cStrFolderName); + } + + nsAutoCString utfNewName; + bool utf8AcceptEnabled; + nsCOMPtr imapFolder = do_QueryInterface(srcFolder); + rv = imapFolder->GetShouldUseUtf8FolderName(&utf8AcceptEnabled); + NS_ENSURE_SUCCESS(rv, rv); + if (utf8AcceptEnabled) { + CopyUTF16toUTF8(newLeafName, utfNewName); + } else { + CopyUTF16toMUTF7(newLeafName, utfNewName); + } + nsCString escapedNewName; + MsgEscapeString(utfNewName, nsINetUtil::ESCAPE_URL_PATH, escapedNewName); + nsCString escapedSlashName; + rv = nsImapUrl::EscapeSlashes(escapedNewName.get(), + getter_Copies(escapedSlashName)); + NS_ENSURE_SUCCESS(rv, rv); + urlSpec.Append(escapedSlashName); + + rv = mailNewsUrl->SetSpecInternal(urlSpec); + if (NS_SUCCEEDED(rv)) + rv = GetImapConnectionAndLoadUrl(imapUrl, nullptr, nullptr); + } + } + return rv; +} + +NS_IMETHODIMP nsImapService::CreateFolder(nsIMsgFolder* parent, + const nsAString& newFolderName, + nsIUrlListener* urlListener, + nsIURI** url) { + NS_ENSURE_ARG_POINTER(parent); + + nsCOMPtr imapUrl; + nsAutoCString urlSpec; + nsresult rv; + + char hierarchyDelimiter = GetHierarchyDelimiter(parent); + rv = CreateStartOfImapUrl(EmptyCString(), getter_AddRefs(imapUrl), parent, + urlListener, urlSpec, hierarchyDelimiter); + if (NS_SUCCEEDED(rv) && imapUrl) { + rv = SetImapUrlSink(parent, imapUrl); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr mailnewsurl = do_QueryInterface(imapUrl); + + nsCString folderName; + GetFolderName(parent, folderName); + urlSpec.AppendLiteral("/create>"); + urlSpec.Append(hierarchyDelimiter); + if (!folderName.IsEmpty()) { + nsCString canonicalName; + nsImapUrl::ConvertToCanonicalFormat( + folderName.get(), hierarchyDelimiter, getter_Copies(canonicalName)); + urlSpec.Append(canonicalName); + urlSpec.Append(hierarchyDelimiter); + } + + nsAutoCString utfNewName; + bool utf8AcceptEnabled; + nsCOMPtr imapFolder = do_QueryInterface(parent); + rv = imapFolder->GetShouldUseUtf8FolderName(&utf8AcceptEnabled); + NS_ENSURE_SUCCESS(rv, rv); + if (utf8AcceptEnabled) { + CopyUTF16toUTF8(newFolderName, utfNewName); + } else { + CopyUTF16toMUTF7(newFolderName, utfNewName); + } + nsCString escapedFolderName; + MsgEscapeString(utfNewName, nsINetUtil::ESCAPE_URL_PATH, + escapedFolderName); + urlSpec.Append(escapedFolderName); + + rv = mailnewsurl->SetSpecInternal(urlSpec); + if (NS_SUCCEEDED(rv)) + rv = GetImapConnectionAndLoadUrl(imapUrl, nullptr, url); + } + } + return rv; +} + +NS_IMETHODIMP nsImapService::EnsureFolderExists(nsIMsgFolder* parent, + const nsAString& newFolderName, + nsIMsgWindow* msgWindow, + nsIUrlListener* urlListener) { + NS_ENSURE_ARG_POINTER(parent); + + nsCOMPtr imapUrl; + nsAutoCString urlSpec; + nsresult rv; + + char hierarchyDelimiter = GetHierarchyDelimiter(parent); + rv = CreateStartOfImapUrl(EmptyCString(), getter_AddRefs(imapUrl), parent, + urlListener, urlSpec, hierarchyDelimiter); + if (NS_SUCCEEDED(rv) && imapUrl) { + rv = SetImapUrlSink(parent, imapUrl); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr mailnewsurl = do_QueryInterface(imapUrl); + + nsCString folderName; + GetFolderName(parent, folderName); + urlSpec.AppendLiteral("/ensureExists>"); + urlSpec.Append(hierarchyDelimiter); + if (!folderName.IsEmpty()) { + urlSpec.Append(folderName); + urlSpec.Append(hierarchyDelimiter); + } + nsAutoCString utfNewName; + bool utf8AcceptEnabled; + nsCOMPtr imapFolder = do_QueryInterface(parent); + rv = imapFolder->GetShouldUseUtf8FolderName(&utf8AcceptEnabled); + NS_ENSURE_SUCCESS(rv, rv); + if (utf8AcceptEnabled) { + CopyUTF16toUTF8(newFolderName, utfNewName); + } else { + CopyUTF16toMUTF7(newFolderName, utfNewName); + } + nsCString escapedFolderName; + MsgEscapeString(utfNewName, nsINetUtil::ESCAPE_URL_PATH, + escapedFolderName); + urlSpec.Append(escapedFolderName); + + rv = mailnewsurl->SetSpecInternal(urlSpec); + + if (msgWindow) mailnewsurl->SetMsgWindow(msgWindow); + + if (NS_SUCCEEDED(rv)) + rv = GetImapConnectionAndLoadUrl(imapUrl, nullptr, nullptr); + } + } + return rv; +} + +NS_IMETHODIMP nsImapService::ListFolder(nsIMsgFolder* aImapMailFolder, + nsIUrlListener* aUrlListener) { + NS_ENSURE_ARG_POINTER(aImapMailFolder); + + return FolderCommand(aImapMailFolder, aUrlListener, "/listfolder>", + nsIImapUrl::nsImapListFolder, nullptr, nullptr); +} + +NS_IMETHODIMP nsImapService::GetScheme(nsACString& aScheme) { + aScheme.AssignLiteral("imap"); + return NS_OK; +} + +NS_IMETHODIMP nsImapService::AllowPort(int32_t port, const char* scheme, + bool* aRetVal) { + // allow imap to run on any port + *aRetVal = true; + return NS_OK; +} + +NS_IMETHODIMP nsImapService::GetDefaultDoBiff(bool* aDoBiff) { + NS_ENSURE_ARG_POINTER(aDoBiff); + // by default, do biff for IMAP servers + *aDoBiff = true; + return NS_OK; +} + +NS_IMETHODIMP nsImapService::GetDefaultServerPort(bool isSecure, + int32_t* aDefaultPort) { + // Return Secure IMAP Port if secure option chosen i.e., if isSecure is TRUE + if (isSecure) + *aDefaultPort = nsIImapUrl::DEFAULT_IMAPS_PORT; + else + *aDefaultPort = nsIImapUrl::DEFAULT_IMAP_PORT; + + return NS_OK; +} + +// this method first tries to find an exact username and hostname match with the +// given url then, tries to find any account on the passed in imap host in case +// this is a url to a shared imap folder. +nsresult nsImapService::GetServerFromUrl(nsIImapUrl* aImapUrl, + nsIMsgIncomingServer** aServer) { + nsresult rv; + nsCString folderName; + nsAutoCString userPass; + nsAutoCString hostName; + nsCOMPtr mailnewsUrl = do_QueryInterface(aImapUrl); + + // if we can't get a folder name out of the url then I think this is an error + aImapUrl->CreateCanonicalSourceFolderPathString(getter_Copies(folderName)); + if (folderName.IsEmpty()) { + rv = mailnewsUrl->GetFileName(folderName); + NS_ENSURE_SUCCESS(rv, rv); + } + + nsCOMPtr accountManager = + do_GetService("@mozilla.org/messenger/account-manager;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + rv = accountManager->FindServerByURI(mailnewsUrl, aServer); + + // look for server with any user name, in case we're trying to subscribe + // to a folder with some one else's user name like the following + // "IMAP://userSharingFolder@server1/SharedFolderName" + if (NS_FAILED(rv) || !aServer) { + nsAutoCString turl; + rv = mailnewsUrl->GetSpec(turl); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr url; + rv = NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID) + .SetSpec(turl) + .SetUserPass(EmptyCString()) + .Finalize(url); + NS_ENSURE_SUCCESS(rv, rv); + + rv = accountManager->FindServerByURI(url, aServer); + if (*aServer) aImapUrl->SetExternalLinkUrl(true); + } + + // if we can't extract the imap server from this url then give up!!! + NS_ENSURE_TRUE(*aServer, NS_ERROR_FAILURE); + return rv; +} + +nsresult nsImapService::NewURI(const nsACString& aSpec, + const char* aOriginCharset, // ignored + nsIURI* aBaseURI, nsIURI** aRetVal) { + NS_ENSURE_ARG_POINTER(aRetVal); + + nsresult rv; + nsCOMPtr aImapUrl = do_CreateInstance(kImapUrlCID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + // now extract lots of fun information... + nsCOMPtr mailnewsUrl = do_QueryInterface(aImapUrl); + // nsAutoCString unescapedSpec(aSpec); + // nsUnescape(unescapedSpec.BeginWriting()); + + // set the spec + if (aBaseURI) { + nsAutoCString newSpec; + aBaseURI->Resolve(aSpec, newSpec); + rv = mailnewsUrl->SetSpecInternal(newSpec); + } else { + rv = mailnewsUrl->SetSpecInternal(aSpec); + } + + NS_ENSURE_SUCCESS(rv, rv); + + nsCString folderName; + // if we can't get a folder name out of the url then I think this is an error + aImapUrl->CreateCanonicalSourceFolderPathString(getter_Copies(folderName)); + if (folderName.IsEmpty()) { + rv = mailnewsUrl->GetFileName(folderName); + NS_ENSURE_SUCCESS(rv, rv); + } + + nsCOMPtr server; + rv = GetServerFromUrl(aImapUrl, getter_AddRefs(server)); + // if we can't extract the imap server from this url then give up!!! + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(server, NS_ERROR_FAILURE); + + // now try to get the folder in question... + nsCOMPtr rootFolder; + server->GetRootFolder(getter_AddRefs(rootFolder)); + bool ready; + if (rootFolder && !folderName.IsEmpty() && + // Skip folder processing if folder names aren't ready yet. + // They may not be available during early initialization. + // XXX TODO: This hack can be removed when the localization system gets + // initialized in M-C code before, for example, the permission manager + // which creates all sorts of URIs incl. imap: URIs. + NS_SUCCEEDED(rootFolder->FolderNamesReady(&ready)) && ready) { + nsCOMPtr folder; + nsCOMPtr imapRoot = do_QueryInterface(rootFolder); + nsCOMPtr subFolder; + if (imapRoot) { + imapRoot->FindOnlineSubFolder(folderName, getter_AddRefs(subFolder)); + folder = do_QueryInterface(subFolder); + } + + // If we can't find the folder, we can still create the URI + // in this low-level service. Cloning URIs where the folder + // isn't found is common when folders are renamed or moved. + // We also ignore return statuses here. + if (folder) { + nsCOMPtr msgSink = do_QueryInterface(folder); + (void)aImapUrl->SetImapMessageSink(msgSink); + + (void)SetImapUrlSink(folder, aImapUrl); + + nsCString messageIdString; + aImapUrl->GetListOfMessageIds(messageIdString); + if (!messageIdString.IsEmpty()) { + bool useLocalCache = false; + folder->HasMsgOffline(strtoul(messageIdString.get(), nullptr, 10), + &useLocalCache); + mailnewsUrl->SetMsgIsInLocalCache(useLocalCache); + } + } + } + + // we got an imap url, so be sure to return it... + nsCOMPtr imapUri = do_QueryInterface(aImapUrl); + + imapUri.forget(aRetVal); + + return rv; +} + +NS_IMETHODIMP nsImapService::NewChannel(nsIURI* aURI, nsILoadInfo* aLoadInfo, + nsIChannel** aRetVal) { + NS_ENSURE_ARG_POINTER(aURI); + NS_ENSURE_ARG_POINTER(aRetVal); + MOZ_ASSERT(aLoadInfo); + *aRetVal = nullptr; + + nsresult rv; + nsCOMPtr imapUrl = do_QueryInterface(aURI, &rv); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr mailnewsUrl = do_QueryInterface(imapUrl, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + // imap can't open and return a channel right away...the url needs to go in + // the imap url queue until we find a connection which can run the url..in + // order to satisfy necko, we're going to return a mock imap channel.... + nsCOMPtr channel = + do_CreateInstance(kCImapMockChannel, &rv); + NS_ENSURE_SUCCESS(rv, rv); + channel->SetURI(aURI); + + rv = channel->SetLoadInfo(aLoadInfo); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoCString spec; + rv = aURI->GetSpec(spec); + NS_ENSURE_SUCCESS(rv, rv); + + // Add the attachment disposition. This forces docShell to open the + // attachment instead of displaying it. Content types we have special + // handlers for are white-listed. This white list also exists in + // nsMailboxService::NewChannel and nsNntpService::NewChannel, so if you're + // changing this, update those too. + if (spec.Find("part=") >= 0 && spec.Find("type=message/rfc822") < 0 && + spec.Find("type=application/x-message-display") < 0 && + spec.Find("type=application/pdf") < 0) { + rv = channel->SetContentDisposition(nsIChannel::DISPOSITION_ATTACHMENT); + NS_ENSURE_SUCCESS(rv, rv); + } + + nsCOMPtr msgWindow; + mailnewsUrl->GetMsgWindow(getter_AddRefs(msgWindow)); + if (msgWindow) { + nsCOMPtr msgDocShell; + msgWindow->GetRootDocShell(getter_AddRefs(msgDocShell)); + if (msgDocShell) { + nsCOMPtr prevEventSink; + channel->GetProgressEventSink(getter_AddRefs(prevEventSink)); + nsCOMPtr docIR(do_QueryInterface(msgDocShell)); + channel->SetNotificationCallbacks(docIR); + // we want to use our existing event sink. + if (prevEventSink) channel->SetProgressEventSink(prevEventSink); + } + } else { + // This might not be a call resulting from user action (e.g. we might be + // getting a new message via nsImapMailFolder::OnNewIdleMessages(), or via + // nsAutoSyncManager, etc). In this case, try to retrieve the top-most + // message window to update its status feedback. + nsCOMPtr mailSession = + do_GetService("@mozilla.org/messenger/services/session;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr msgWindow; + rv = mailSession->GetTopmostMsgWindow(getter_AddRefs(msgWindow)); + if (NS_SUCCEEDED(rv) && msgWindow) { + // If we could retrieve a window, get its nsIMsgStatusFeedback and set it + // to the URL so that other components interacting with it can correctly + // feed status updates to the UI. + nsCOMPtr statusFeedback; + msgWindow->GetStatusFeedback(getter_AddRefs(statusFeedback)); + mailnewsUrl->SetStatusFeedback(statusFeedback); + // We also need to set the status feedback as the channel's progress event + // sink, since that's how nsImapProtocol feeds some of the progress + // changes (e.g. downloading incoming messages) to the UI. + nsCOMPtr eventSink = + do_QueryInterface(statusFeedback); + channel->SetProgressEventSink(eventSink); + } + + // This function ends by checking the final value of rv and deciding whether + // to set aRetVal to our channel according to it. We don't want this to be + // impacted if we fail to retrieve a window (which might not work if we're + // being called through the command line, or through a test), so let's just + // reset rv to an OK value. + rv = NS_OK; + } + + // the imap url holds a weak reference so we can pass the channel into the + // imap protocol when we actually run the url. + imapUrl->SetMockChannel(channel); + + bool externalLinkUrl; + imapUrl->GetExternalLinkUrl(&externalLinkUrl); + + // Only external imap links with no action are supported. Ignore links that + // attempt to cause an effect such as fetching a mime part. This avoids + // spurious prompts to subscribe to folders due to "imap://...Fetch..." links + // residing in legacy emails residing in an imap mailbox. + if (externalLinkUrl) { + nsImapAction imapAction; + imapUrl->GetImapAction(&imapAction); + if (imapAction != 0) externalLinkUrl = false; + } + + if (externalLinkUrl) { + // Everything after here is to handle clicking on an external link. We only + // want to do this if we didn't run the url through the various + // nsImapService methods, which we can tell by seeing if the sinks have been + // setup on the url or not. + nsCOMPtr server; + rv = GetServerFromUrl(imapUrl, getter_AddRefs(server)); + NS_ENSURE_SUCCESS(rv, rv); + nsCString folderName; + imapUrl->CreateCanonicalSourceFolderPathString(getter_Copies(folderName)); + if (folderName.IsEmpty()) { + nsCString escapedFolderName; + rv = mailnewsUrl->GetFileName(escapedFolderName); + if (!escapedFolderName.IsEmpty()) { + MsgUnescapeString(escapedFolderName, 0, folderName); + } + } + // if the parent is null, then the folder doesn't really exist, so see if + // the user wants to subscribe to it./ + nsCOMPtr urlFolder; + // now try to get the folder in question... + nsCOMPtr rootFolder; + server->GetRootFolder(getter_AddRefs(rootFolder)); + nsCOMPtr imapRoot = do_QueryInterface(rootFolder); + nsCOMPtr subFolder; + if (imapRoot) { + imapRoot->FindOnlineSubFolder(folderName, getter_AddRefs(subFolder)); + urlFolder = do_QueryInterface(subFolder); + } + nsCOMPtr parent; + if (urlFolder) urlFolder->GetParent(getter_AddRefs(parent)); + nsCString serverKey; + nsAutoCString userPass; + rv = mailnewsUrl->GetUserPass(userPass); + server->GetKey(serverKey); + nsCString fullFolderName; + if (parent) fullFolderName = folderName; + if (!parent && !folderName.IsEmpty() && imapRoot) { + // Check if this folder is another user's folder. + fullFolderName = + nsImapNamespaceList::GenerateFullFolderNameWithDefaultNamespace( + serverKey.get(), folderName.get(), userPass.get(), + kOtherUsersNamespace, nullptr); + // if this is another user's folder, let's see if we're already subscribed + // to it. + rv = imapRoot->FindOnlineSubFolder(fullFolderName, + getter_AddRefs(subFolder)); + urlFolder = do_QueryInterface(subFolder); + if (urlFolder) urlFolder->GetParent(getter_AddRefs(parent)); + } + // if we couldn't get the fullFolderName, then we probably couldn't find + // the other user's namespace, in which case, we shouldn't try to subscribe + // to it. + if (!parent && !folderName.IsEmpty() && !fullFolderName.IsEmpty()) { + // this folder doesn't exist - check if the user wants to subscribe to + // this folder. + nsCOMPtr dialog; + nsCOMPtr wwatch( + do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + wwatch->GetNewPrompter(nullptr, getter_AddRefs(dialog)); + + nsString statusString, confirmText; + nsCOMPtr bundle; + rv = IMAPGetStringBundle(getter_AddRefs(bundle)); + NS_ENSURE_SUCCESS(rv, rv); + // Need to convert folder name, can be MUTF-7 or UTF-8 depending on the + // server. + nsAutoString unescapedName; + if (NS_FAILED(CopyFolderNameToUTF16(fullFolderName, unescapedName))) + CopyASCIItoUTF16(fullFolderName, unescapedName); + AutoTArray formatStrings = {unescapedName}; + + rv = bundle->FormatStringFromName("imapSubscribePrompt", formatStrings, + confirmText); + NS_ENSURE_SUCCESS(rv, rv); + + bool confirmResult = false; + rv = dialog->Confirm(nullptr, confirmText.get(), &confirmResult); + NS_ENSURE_SUCCESS(rv, rv); + + if (confirmResult) { + nsCOMPtr imapServer = do_QueryInterface(server); + if (imapServer) { + nsCOMPtr subscribeURI; + // Now we have the real folder name to try to subscribe to. Let's try + // running a subscribe url and returning that as the uri we've + // created. We need to convert this to unicode because that's what + // subscribe wants. + nsAutoString unicodeName; + CopyFolderNameToUTF16(fullFolderName, unicodeName); + rv = imapServer->SubscribeToFolder(unicodeName, true, + getter_AddRefs(subscribeURI)); + if (NS_SUCCEEDED(rv) && subscribeURI) { + nsCOMPtr imapSubscribeUrl = + do_QueryInterface(subscribeURI); + if (imapSubscribeUrl) imapSubscribeUrl->SetExternalLinkUrl(true); + nsCOMPtr mailnewsUrl = + do_QueryInterface(subscribeURI); + if (mailnewsUrl) { + nsCOMPtr mailSession = do_GetService( + "@mozilla.org/messenger/services/session;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr msgWindow; + rv = mailSession->GetTopmostMsgWindow(getter_AddRefs(msgWindow)); + if (NS_SUCCEEDED(rv) && msgWindow) { + mailnewsUrl->SetMsgWindow(msgWindow); + nsCOMPtr listener = + do_QueryInterface(rootFolder); + if (listener) mailnewsUrl->RegisterListener(listener); + } + } + } + } + } + // error out this channel, so it'll stop trying to run the url. + rv = NS_ERROR_FAILURE; + *aRetVal = nullptr; + } + // this folder exists - check if this is a click on a link to the folder + // in which case, we'll select it. + else if (!fullFolderName.IsEmpty()) { + nsCOMPtr imapFolder; + mailnewsUrl->GetFolder(getter_AddRefs(imapFolder)); + NS_ASSERTION( + imapFolder, + nsPrintfCString("No folder for imap url: %s", spec.get()).get()); + + nsCOMPtr mailSession = + do_GetService("@mozilla.org/messenger/services/session;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr msgWindow; + rv = mailSession->GetTopmostMsgWindow(getter_AddRefs(msgWindow)); + if (NS_SUCCEEDED(rv) && msgWindow) { + // Clicked IMAP folder URL in the window. + nsCOMPtr obsServ = + mozilla::services::GetObserverService(); + obsServ->NotifyObservers(imapFolder, "folder-attention", nullptr); + // null out this channel, so it'll stop trying to run the url. + *aRetVal = nullptr; + rv = NS_OK; + } else { + // Got IMAP folder URL from command line (most likely). + // Set action to nsImapSelectFolder (x-application-imapfolder), so + // ::HandleContent will handle it. + imapUrl->SetImapAction(nsIImapUrl::nsImapSelectFolder); + HandleContent("x-application-imapfolder", nullptr, channel); + } + } + } + if (NS_SUCCEEDED(rv)) channel.forget(aRetVal); + return rv; +} + +NS_IMETHODIMP nsImapService::SetDefaultLocalPath(nsIFile* aPath) { + NS_ENSURE_ARG_POINTER(aPath); + + return NS_SetPersistentFile(PREF_MAIL_ROOT_IMAP_REL, PREF_MAIL_ROOT_IMAP, + aPath); +} + +NS_IMETHODIMP nsImapService::GetDefaultLocalPath(nsIFile** aResult) { + NS_ENSURE_ARG_POINTER(aResult); + *aResult = nullptr; + + bool havePref; + nsCOMPtr localFile; + nsresult rv = NS_GetPersistentFile( + PREF_MAIL_ROOT_IMAP_REL, PREF_MAIL_ROOT_IMAP, NS_APP_IMAP_MAIL_50_DIR, + havePref, getter_AddRefs(localFile)); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(localFile, NS_ERROR_FAILURE); + + bool exists; + rv = localFile->Exists(&exists); + if (NS_SUCCEEDED(rv) && !exists) + rv = localFile->Create(nsIFile::DIRECTORY_TYPE, 0775); + NS_ENSURE_SUCCESS(rv, rv); + + if (!havePref || !exists) { + rv = NS_SetPersistentFile(PREF_MAIL_ROOT_IMAP_REL, PREF_MAIL_ROOT_IMAP, + localFile); + NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to set root dir pref."); + } + + localFile.forget(aResult); + return NS_OK; +} + +NS_IMETHODIMP nsImapService::GetServerIID(nsIID** aServerIID) { + *aServerIID = new nsIID(NS_GET_IID(nsIImapIncomingServer)); + return NS_OK; +} + +NS_IMETHODIMP nsImapService::GetRequiresUsername(bool* aRequiresUsername) { + NS_ENSURE_ARG_POINTER(aRequiresUsername); + + *aRequiresUsername = true; + return NS_OK; +} + +NS_IMETHODIMP nsImapService::GetPreflightPrettyNameWithEmailAddress( + bool* aPreflightPrettyNameWithEmailAddress) { + NS_ENSURE_ARG_POINTER(aPreflightPrettyNameWithEmailAddress); + + *aPreflightPrettyNameWithEmailAddress = true; + return NS_OK; +} + +NS_IMETHODIMP nsImapService::GetCanLoginAtStartUp(bool* aCanLoginAtStartUp) { + NS_ENSURE_ARG_POINTER(aCanLoginAtStartUp); + *aCanLoginAtStartUp = true; + return NS_OK; +} + +NS_IMETHODIMP nsImapService::GetCanDelete(bool* aCanDelete) { + NS_ENSURE_ARG_POINTER(aCanDelete); + *aCanDelete = true; + return NS_OK; +} + +NS_IMETHODIMP nsImapService::GetCanDuplicate(bool* aCanDuplicate) { + NS_ENSURE_ARG_POINTER(aCanDuplicate); + *aCanDuplicate = true; + return NS_OK; +} + +NS_IMETHODIMP nsImapService::GetCanGetMessages(bool* aCanGetMessages) { + NS_ENSURE_ARG_POINTER(aCanGetMessages); + *aCanGetMessages = true; + return NS_OK; +} + +NS_IMETHODIMP nsImapService::GetCanGetIncomingMessages( + bool* aCanGetIncomingMessages) { + NS_ENSURE_ARG_POINTER(aCanGetIncomingMessages); + *aCanGetIncomingMessages = true; + return NS_OK; +} + +NS_IMETHODIMP nsImapService::GetShowComposeMsgLink(bool* showComposeMsgLink) { + NS_ENSURE_ARG_POINTER(showComposeMsgLink); + *showComposeMsgLink = true; + return NS_OK; +} + +NS_IMETHODIMP nsImapService::GetFoldersCreatedAsync(bool* aAsyncCreation) { + NS_ENSURE_ARG_POINTER(aAsyncCreation); + *aAsyncCreation = true; + return NS_OK; +} + +NS_IMETHODIMP nsImapService::GetListOfFoldersWithPath( + nsIImapIncomingServer* aServer, nsIMsgWindow* aMsgWindow, + const nsACString& folderPath) { + nsresult rv; + nsCOMPtr server = do_QueryInterface(aServer); + if (!server) return NS_ERROR_FAILURE; + + nsCOMPtr rootMsgFolder; + rv = server->GetRootMsgFolder(getter_AddRefs(rootMsgFolder)); + + NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && rootMsgFolder, NS_ERROR_FAILURE); + + nsCOMPtr listener = do_QueryInterface(aServer, &rv); + NS_ENSURE_SUCCESS(rv, rv); + if (!listener) return NS_ERROR_FAILURE; + + // Locate the folder so that the correct hierarchical delimiter is used in the + // folder pathnames, otherwise root's (ie, '^') is used and this is wrong. + nsCOMPtr msgFolder; + if (rootMsgFolder && !folderPath.IsEmpty()) { + // If the folder path contains 'INBOX' of any forms, we need to convert it + // to uppercase before finding it under the root folder. We do the same in + // PossibleImapMailbox(). + nsAutoCString tempFolderName(folderPath); + nsAutoCString tokenStr, remStr, changedStr; + int32_t slashPos = tempFolderName.FindChar('/'); + if (slashPos > 0) { + tokenStr = StringHead(tempFolderName, slashPos); + remStr = Substring(tempFolderName, slashPos); + } else + tokenStr.Assign(tempFolderName); + + if (tokenStr.LowerCaseEqualsLiteral("inbox") && + !tokenStr.EqualsLiteral("INBOX")) + changedStr.AppendLiteral("INBOX"); + else + changedStr.Append(tokenStr); + + if (slashPos > 0) changedStr.Append(remStr); + + rv = rootMsgFolder->FindSubFolder(changedStr, getter_AddRefs(msgFolder)); + } + return DiscoverChildren(msgFolder, listener, folderPath); +} + +NS_IMETHODIMP nsImapService::GetListOfFoldersOnServer( + nsIImapIncomingServer* aServer, nsIMsgWindow* aMsgWindow) { + nsresult rv; + + nsCOMPtr server = do_QueryInterface(aServer); + if (!server) return NS_ERROR_FAILURE; + + nsCOMPtr rootMsgFolder; + rv = server->GetRootMsgFolder(getter_AddRefs(rootMsgFolder)); + + NS_ENSURE_SUCCESS(rv, rv); + if (!rootMsgFolder) return NS_ERROR_FAILURE; + + nsCOMPtr listener = do_QueryInterface(aServer, &rv); + NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && listener, NS_ERROR_FAILURE); + + return DiscoverAllAndSubscribedFolders(rootMsgFolder, listener, aMsgWindow); +} + +NS_IMETHODIMP nsImapService::SubscribeFolder(nsIMsgFolder* aFolder, + const nsAString& aFolderName, + nsIUrlListener* urlListener, + nsIURI** url) { + return ChangeFolderSubscription(aFolder, aFolderName, "/subscribe>", + urlListener, url); +} + +nsresult nsImapService::ChangeFolderSubscription(nsIMsgFolder* folder, + const nsAString& folderName, + const char* command, + nsIUrlListener* urlListener, + nsIURI** url) { + NS_ENSURE_ARG_POINTER(folder); + + nsCOMPtr imapUrl; + nsAutoCString urlSpec; + nsresult rv; + char hierarchyDelimiter = GetHierarchyDelimiter(folder); + rv = CreateStartOfImapUrl(EmptyCString(), getter_AddRefs(imapUrl), folder, + urlListener, urlSpec, hierarchyDelimiter); + if (NS_SUCCEEDED(rv) && imapUrl) { + rv = SetImapUrlSink(folder, imapUrl); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr mailnewsurl = do_QueryInterface(imapUrl); + urlSpec.Append(command); + urlSpec.Append(hierarchyDelimiter); + // `folderName` contains MUFT-7 or UTF-8 as required by the server here. + NS_ConvertUTF16toUTF8 utfFolderName(folderName); + nsCString escapedFolderName; + MsgEscapeString(utfFolderName, nsINetUtil::ESCAPE_URL_PATH, + escapedFolderName); + urlSpec.Append(escapedFolderName); + rv = mailnewsurl->SetSpecInternal(urlSpec); + if (NS_SUCCEEDED(rv)) + rv = GetImapConnectionAndLoadUrl(imapUrl, nullptr, url); + } + } + return rv; +} + +NS_IMETHODIMP nsImapService::UnsubscribeFolder(nsIMsgFolder* aFolder, + const nsAString& aFolderName, + nsIUrlListener* aUrlListener, + nsIURI** aUrl) { + return ChangeFolderSubscription(aFolder, aFolderName, "/unsubscribe>", + aUrlListener, aUrl); +} + +NS_IMETHODIMP nsImapService::GetFolderAdminUrl(nsIMsgFolder* aImapMailFolder, + nsIMsgWindow* aMsgWindow, + nsIUrlListener* aUrlListener, + nsIURI** aURL) { + NS_ENSURE_ARG_POINTER(aImapMailFolder); + + return FolderCommand(aImapMailFolder, aUrlListener, "/refreshfolderurls>", + nsIImapUrl::nsImapRefreshFolderUrls, aMsgWindow, aURL); +} + +NS_IMETHODIMP nsImapService::IssueCommandOnMsgs(nsIMsgFolder* anImapFolder, + nsIMsgWindow* aMsgWindow, + const nsACString& aCommand, + const nsACString& uids, + nsIURI** aURL) { + NS_ENSURE_ARG_POINTER(anImapFolder); + NS_ENSURE_ARG_POINTER(aMsgWindow); + + nsCOMPtr imapUrl; + nsAutoCString urlSpec; + nsresult rv; + char hierarchyDelimiter = GetHierarchyDelimiter(anImapFolder); + rv = CreateStartOfImapUrl(EmptyCString(), getter_AddRefs(imapUrl), + anImapFolder, nullptr, urlSpec, hierarchyDelimiter); + + if (NS_SUCCEEDED(rv) && imapUrl) { + // nsImapUrl::SetSpec() will set the imap action properly + rv = imapUrl->SetImapAction(nsIImapUrl::nsImapUserDefinedMsgCommand); + + nsCOMPtr mailNewsUrl = do_QueryInterface(imapUrl); + mailNewsUrl->SetMsgWindow(aMsgWindow); + mailNewsUrl->SetUpdatingFolder(true); + rv = SetImapUrlSink(anImapFolder, imapUrl); + + if (NS_SUCCEEDED(rv)) { + nsCString folderName; + GetFolderName(anImapFolder, folderName); + urlSpec.Append('/'); + urlSpec.Append(aCommand); + urlSpec.Append('>'); + urlSpec.Append(uidString); + urlSpec.Append('>'); + urlSpec.Append(hierarchyDelimiter); + urlSpec.Append(folderName); + urlSpec.Append('>'); + urlSpec.Append(uids); + rv = mailNewsUrl->SetSpecInternal(urlSpec); + if (NS_SUCCEEDED(rv)) + rv = GetImapConnectionAndLoadUrl(imapUrl, nullptr, aURL); + } + } // if we have a url to run.... + + return rv; +} + +NS_IMETHODIMP nsImapService::FetchCustomMsgAttribute( + nsIMsgFolder* anImapFolder, nsIMsgWindow* aMsgWindow, + const nsACString& aAttribute, const nsACString& uids, nsIURI** aURL) { + NS_ENSURE_ARG_POINTER(anImapFolder); + NS_ENSURE_ARG_POINTER(aMsgWindow); + + nsCOMPtr imapUrl; + nsAutoCString urlSpec; + nsresult rv; + char hierarchyDelimiter = GetHierarchyDelimiter(anImapFolder); + rv = CreateStartOfImapUrl(EmptyCString(), getter_AddRefs(imapUrl), + anImapFolder, nullptr, urlSpec, hierarchyDelimiter); + if (NS_SUCCEEDED(rv) && imapUrl) { + // nsImapUrl::SetSpec() will set the imap action properly + rv = imapUrl->SetImapAction(nsIImapUrl::nsImapUserDefinedFetchAttribute); + + nsCOMPtr mailNewsUrl = do_QueryInterface(imapUrl); + mailNewsUrl->SetMsgWindow(aMsgWindow); + mailNewsUrl->SetUpdatingFolder(true); + rv = SetImapUrlSink(anImapFolder, imapUrl); + + if (NS_SUCCEEDED(rv)) { + nsCString folderName; + GetFolderName(anImapFolder, folderName); + urlSpec.AppendLiteral("/customFetch>UID>"); + urlSpec.Append(hierarchyDelimiter); + urlSpec.Append(folderName); + urlSpec.Append('>'); + urlSpec.Append(uids); + urlSpec.Append('>'); + urlSpec.Append(aAttribute); + rv = mailNewsUrl->SetSpecInternal(urlSpec); + if (NS_SUCCEEDED(rv)) + rv = GetImapConnectionAndLoadUrl(imapUrl, nullptr, aURL); + } + } // if we have a url to run.... + + return rv; +} + +NS_IMETHODIMP nsImapService::StoreCustomKeywords( + nsIMsgFolder* anImapFolder, nsIMsgWindow* aMsgWindow, + const nsACString& flagsToAdd, const nsACString& flagsToSubtract, + const nsACString& uids, nsIURI** aURL) { + NS_ENSURE_ARG_POINTER(anImapFolder); + + nsCOMPtr imapUrl; + nsAutoCString urlSpec; + nsresult rv; + char hierarchyDelimiter = GetHierarchyDelimiter(anImapFolder); + rv = CreateStartOfImapUrl(EmptyCString(), getter_AddRefs(imapUrl), + anImapFolder, nullptr, urlSpec, hierarchyDelimiter); + + if (NS_SUCCEEDED(rv) && imapUrl) { + // nsImapUrl::SetSpec() will set the imap action properly + rv = imapUrl->SetImapAction(nsIImapUrl::nsImapMsgStoreCustomKeywords); + + nsCOMPtr mailNewsUrl = do_QueryInterface(imapUrl); + mailNewsUrl->SetMsgWindow(aMsgWindow); + mailNewsUrl->SetUpdatingFolder(true); + rv = SetImapUrlSink(anImapFolder, imapUrl); + + if (NS_SUCCEEDED(rv)) { + nsCString folderName; + GetFolderName(anImapFolder, folderName); + urlSpec.AppendLiteral("/customKeywords>UID>"); + urlSpec.Append(hierarchyDelimiter); + urlSpec.Append(folderName); + urlSpec.Append('>'); + urlSpec.Append(uids); + urlSpec.Append('>'); + urlSpec.Append(flagsToAdd); + urlSpec.Append('>'); + urlSpec.Append(flagsToSubtract); + rv = mailNewsUrl->SetSpecInternal(urlSpec); + if (NS_SUCCEEDED(rv)) + rv = GetImapConnectionAndLoadUrl(imapUrl, nullptr, aURL); + } + } // if we have a url to run.... + + return rv; +} + +NS_IMETHODIMP nsImapService::DownloadMessagesForOffline( + const nsACString& messageIds, nsIMsgFolder* aFolder, + nsIUrlListener* aUrlListener, nsIMsgWindow* aMsgWindow) { + NS_ENSURE_ARG_POINTER(aFolder); + + nsCOMPtr imapUrl; + nsAutoCString urlSpec; + nsresult rv; + char hierarchyDelimiter = GetHierarchyDelimiter(aFolder); + rv = CreateStartOfImapUrl(EmptyCString(), getter_AddRefs(imapUrl), aFolder, + nullptr, urlSpec, hierarchyDelimiter); + if (NS_SUCCEEDED(rv) && imapUrl) { + nsCOMPtr runningURI; + // need to pass in stream listener in order to get the channel created + // correctly + nsCOMPtr imapMessageSink( + do_QueryInterface(aFolder, &rv)); + rv = FetchMessage(imapUrl, nsImapUrl::nsImapMsgDownloadForOffline, aFolder, + imapMessageSink, aMsgWindow, nullptr, messageIds, false, + getter_AddRefs(runningURI)); + if (runningURI && aUrlListener) { + nsCOMPtr msgurl(do_QueryInterface(runningURI)); + nsCOMPtr imapUrl(do_QueryInterface(runningURI)); + if (msgurl) msgurl->RegisterListener(aUrlListener); + if (imapUrl) imapUrl->SetStoreResultsOffline(true); + } + } + return rv; +} + +NS_IMETHODIMP nsImapService::MessageURIToMsgHdr(const nsACString& uri, + nsIMsgDBHdr** aRetVal) { + NS_ENSURE_ARG_POINTER(aRetVal); + + nsCOMPtr folder; + nsMsgKey msgKey; + nsresult rv = DecomposeImapURI(uri, getter_AddRefs(folder), &msgKey); + NS_ENSURE_SUCCESS(rv, rv); + rv = folder->GetMessageHeader(msgKey, aRetVal); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +NS_IMETHODIMP nsImapService::PlaybackAllOfflineOperations( + nsIMsgWindow* aMsgWindow, nsIUrlListener* aListener, + nsISupports** aResult) { + NS_ENSURE_ARG_POINTER(aResult); + + nsresult rv; + nsImapOfflineSync* goOnline = new nsImapOfflineSync(); + goOnline->Init(aMsgWindow, aListener, nullptr, false); + rv = goOnline->QueryInterface(NS_GET_IID(nsISupports), (void**)aResult); + NS_ENSURE_SUCCESS(rv, rv); + if (NS_SUCCEEDED(rv) && *aResult) return goOnline->ProcessNextOperation(); + return NS_ERROR_OUT_OF_MEMORY; +} + +NS_IMETHODIMP nsImapService::DownloadAllOffineImapFolders( + nsIMsgWindow* aMsgWindow, nsIUrlListener* aListener) { + RefPtr downloadForOffline = + new nsImapOfflineDownloader(aMsgWindow, aListener); + if (downloadForOffline) { + // hold reference to this so it won't get deleted out from under itself. + nsresult rv = downloadForOffline->ProcessNextOperation(); + return rv; + } + return NS_ERROR_OUT_OF_MEMORY; +} + +NS_IMETHODIMP nsImapService::GetCacheStorage(nsICacheStorage** result) { + nsresult rv = NS_OK; + if (!mCacheStorage) { + nsCOMPtr cacheStorageService = + do_GetService("@mozilla.org/netwerk/cache-storage-service;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + RefPtr lci = + new MailnewsLoadContextInfo(false, false, mozilla::OriginAttributes()); + + // Determine if disk cache or memory cache is in use. + // Note: This is mozilla system cache, not offline storage (mbox, maildir) + // which is also sometimes referred to as cache at places in the code. + if (mozilla::Preferences::GetBool("mail.imap.use_disk_cache2", true)) + rv = cacheStorageService->DiskCacheStorage(lci, + getter_AddRefs(mCacheStorage)); + else + rv = cacheStorageService->MemoryCacheStorage( + lci, getter_AddRefs(mCacheStorage)); + NS_ENSURE_SUCCESS(rv, rv); + } + + NS_IF_ADDREF(*result = mCacheStorage); + return rv; +} + +NS_IMETHODIMP nsImapService::HandleContent( + const char* aContentType, nsIInterfaceRequestor* aWindowContext, + nsIRequest* request) { + NS_ENSURE_ARG_POINTER(request); + + nsresult rv; + nsCOMPtr aChannel = do_QueryInterface(request, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + if (PL_strcasecmp(aContentType, "x-application-imapfolder") == 0) { + nsCOMPtr uri; + rv = aChannel->GetURI(getter_AddRefs(uri)); + NS_ENSURE_SUCCESS(rv, rv); + + if (uri) { + request->Cancel(NS_BINDING_ABORTED); + nsCOMPtr mediator( + do_GetService(NS_WINDOWMEDIATOR_CONTRACTID, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoCString uriStr; + rv = uri->GetSpec(uriStr); + NS_ENSURE_SUCCESS(rv, rv); + + // imap uri's are unescaped, so unescape the url. + nsCString unescapedUriStr; + MsgUnescapeString(uriStr, 0, unescapedUriStr); + nsCOMPtr messengerWindowService = + do_GetService("@mozilla.org/messenger/windowservice;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + rv = messengerWindowService->OpenMessengerWindowWithUri( + "mail:3pane", unescapedUriStr, nsMsgKey_None); + NS_ENSURE_SUCCESS(rv, rv); + } + } else { + // The content-type was not x-application-imapfolder + return NS_ERROR_WONT_HANDLE_CONTENT; + } + + return rv; +} -- cgit v1.2.3