summaryrefslogtreecommitdiffstats
path: root/mailnews/imap/src/nsImapIncomingServer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mailnews/imap/src/nsImapIncomingServer.cpp')
-rw-r--r--mailnews/imap/src/nsImapIncomingServer.cpp3382
1 files changed, 3382 insertions, 0 deletions
diff --git a/mailnews/imap/src/nsImapIncomingServer.cpp b/mailnews/imap/src/nsImapIncomingServer.cpp
new file mode 100644
index 000000000..77a34985b
--- /dev/null
+++ b/mailnews/imap/src/nsImapIncomingServer.cpp
@@ -0,0 +1,3382 @@
+/* -*- 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"
+#include "nsMsgImapCID.h"
+
+#include "netCore.h"
+#include "nsIMAPHostSessionList.h"
+#include "nsImapIncomingServer.h"
+#include "nsIMsgAccountManager.h"
+#include "nsIMsgIdentity.h"
+#include "nsIImapUrl.h"
+#include "nsIUrlListener.h"
+#include "nsThreadUtils.h"
+#include "nsImapProtocol.h"
+#include "nsCOMPtr.h"
+#include "nsIPrefBranch.h"
+#include "nsIPrefService.h"
+#include "nsMsgFolderFlags.h"
+#include "prmem.h"
+#include "plstr.h"
+#include "nsIMsgFolder.h"
+#include "nsIMsgWindow.h"
+#include "nsImapMailFolder.h"
+#include "nsImapUtils.h"
+#include "nsIRDFService.h"
+#include "nsRDFCID.h"
+#include "nsIMsgMailNewsUrl.h"
+#include "nsIImapService.h"
+#include "nsMsgI18N.h"
+#include "nsIImapMockChannel.h"
+// for the memory cache...
+#include "nsICacheEntry.h"
+#include "nsImapUrl.h"
+#include "nsIMsgProtocolInfo.h"
+#include "nsIMsgMailSession.h"
+#include "nsIMAPNamespace.h"
+#include "nsArrayUtils.h"
+#include "nsITimer.h"
+#include "nsMsgUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsCRTGlue.h"
+#include "mozilla/Services.h"
+
+using namespace mozilla;
+
+#define PREF_TRASH_FOLDER_NAME "trash_folder_name"
+#define DEFAULT_TRASH_FOLDER_NAME "Trash"
+
+static NS_DEFINE_CID(kImapProtocolCID, NS_IMAPPROTOCOL_CID);
+static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
+static NS_DEFINE_CID(kSubscribableServerCID, NS_SUBSCRIBABLESERVER_CID);
+static NS_DEFINE_CID(kCImapHostSessionListCID, NS_IIMAPHOSTSESSIONLIST_CID);
+
+NS_IMPL_ADDREF_INHERITED(nsImapIncomingServer, nsMsgIncomingServer)
+NS_IMPL_RELEASE_INHERITED(nsImapIncomingServer, nsMsgIncomingServer)
+
+NS_INTERFACE_MAP_BEGIN(nsImapIncomingServer)
+ NS_INTERFACE_MAP_ENTRY(nsIImapServerSink)
+ NS_INTERFACE_MAP_ENTRY(nsIImapIncomingServer)
+ NS_INTERFACE_MAP_ENTRY(nsISubscribableServer)
+ NS_INTERFACE_MAP_ENTRY(nsIUrlListener)
+NS_INTERFACE_MAP_END_INHERITING(nsMsgIncomingServer)
+
+nsImapIncomingServer::nsImapIncomingServer()
+ : mLock("nsImapIncomingServer.mLock")
+{
+ m_capability = kCapabilityUndefined;
+ mDoingSubscribeDialog = false;
+ mDoingLsub = false;
+ m_canHaveFilters = true;
+ m_userAuthenticated = false;
+ m_shuttingDown = false;
+}
+
+nsImapIncomingServer::~nsImapIncomingServer()
+{
+ mozilla::DebugOnly<nsresult> rv = ClearInner();
+ NS_ASSERTION(NS_SUCCEEDED(rv), "ClearInner failed");
+ CloseCachedConnections();
+}
+
+NS_IMETHODIMP nsImapIncomingServer::SetKey(const nsACString& aKey) // override nsMsgIncomingServer's implementation...
+{
+ nsMsgIncomingServer::SetKey(aKey);
+
+ // okay now that the key has been set, we need to add ourselves to the
+ // host session list...
+
+ // every time we create an imap incoming server, we need to add it to the
+ // host session list!!
+
+ nsresult rv;
+ nsCOMPtr<nsIImapHostSessionList> hostSession = do_GetService(kCImapHostSessionListCID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCString key(aKey);
+ hostSession->AddHostToList(key.get(), this);
+ nsMsgImapDeleteModel deleteModel = nsMsgImapDeleteModels::MoveToTrash; // default to trash
+ GetDeleteModel(&deleteModel);
+ hostSession->SetDeleteIsMoveToTrashForHost(key.get(), deleteModel == nsMsgImapDeleteModels::MoveToTrash);
+ hostSession->SetShowDeletedMessagesForHost(key.get(), deleteModel == nsMsgImapDeleteModels::IMAPDelete);
+
+ nsAutoCString onlineDir;
+ rv = GetServerDirectory(onlineDir);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!onlineDir.IsEmpty())
+ hostSession->SetOnlineDirForHost(key.get(), onlineDir.get());
+
+ nsCString personalNamespace;
+ nsCString publicNamespace;
+ nsCString otherUsersNamespace;
+
+ rv = GetPersonalNamespace(personalNamespace);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = GetPublicNamespace(publicNamespace);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = GetOtherUsersNamespace(otherUsersNamespace);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (personalNamespace.IsEmpty() && publicNamespace.IsEmpty() && otherUsersNamespace.IsEmpty())
+ personalNamespace.AssignLiteral("\"\"");
+
+ hostSession->SetNamespaceFromPrefForHost(key.get(), personalNamespace.get(),
+ kPersonalNamespace);
+
+ if (!publicNamespace.IsEmpty())
+ hostSession->SetNamespaceFromPrefForHost(key.get(), publicNamespace.get(),
+ kPublicNamespace);
+
+ if (!otherUsersNamespace.IsEmpty())
+ hostSession->SetNamespaceFromPrefForHost(key.get(), otherUsersNamespace.get(),
+ kOtherUsersNamespace);
+ return rv;
+}
+
+// construct the pretty name to show to the user if they haven't
+// specified one. This should be overridden for news and mail.
+NS_IMETHODIMP
+nsImapIncomingServer::GetConstructedPrettyName(nsAString& retval)
+{
+ nsAutoCString username;
+ nsAutoCString hostName;
+ nsresult rv;
+
+ nsCOMPtr<nsIMsgAccountManager> accountManager =
+ do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr<nsIMsgIdentity> identity;
+ rv = accountManager->GetFirstIdentityForServer(this, getter_AddRefs(identity));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsAutoString emailAddress;
+
+ if (NS_SUCCEEDED(rv) && identity)
+ {
+ nsCString identityEmailAddress;
+ identity->GetEmail(identityEmailAddress);
+ CopyASCIItoUTF16(identityEmailAddress, emailAddress);
+ }
+ else
+ {
+ rv = GetRealUsername(username);
+ NS_ENSURE_SUCCESS(rv,rv);
+ rv = GetRealHostName(hostName);
+ NS_ENSURE_SUCCESS(rv,rv);
+ if (!username.IsEmpty() && !hostName.IsEmpty()) {
+ CopyASCIItoUTF16(username, emailAddress);
+ emailAddress.Append('@');
+ emailAddress.Append(NS_ConvertASCIItoUTF16(hostName));
+ }
+ }
+
+ return GetFormattedStringFromName(emailAddress, "imapDefaultAccountName", retval);
+}
+
+
+NS_IMETHODIMP nsImapIncomingServer::GetLocalStoreType(nsACString& type)
+{
+ type.AssignLiteral("imap");
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsImapIncomingServer::GetLocalDatabaseType(nsACString& type)
+{
+ type.AssignLiteral("imap");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::GetServerDirectory(nsACString& serverDirectory)
+{
+ return GetCharValue("server_sub_directory", serverDirectory);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::SetServerDirectory(const nsACString& serverDirectory)
+{
+ nsCString serverKey;
+ nsresult rv = GetKey(serverKey);
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsIImapHostSessionList> hostSession = do_GetService(kCImapHostSessionListCID, &rv);
+ if (NS_SUCCEEDED(rv))
+ hostSession->SetOnlineDirForHost(serverKey.get(), PromiseFlatCString(serverDirectory).get());
+ }
+ return SetCharValue("server_sub_directory", serverDirectory);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::GetOverrideNamespaces(bool *bVal)
+{
+ return GetBoolValue("override_namespaces", bVal);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::SetOverrideNamespaces(bool bVal)
+{
+ nsCString serverKey;
+ GetKey(serverKey);
+ if (!serverKey.IsEmpty())
+ {
+ nsresult rv;
+ nsCOMPtr<nsIImapHostSessionList> hostSession = do_GetService(kCImapHostSessionListCID, &rv);
+ if (NS_SUCCEEDED(rv))
+ hostSession->SetNamespacesOverridableForHost(serverKey.get(), bVal);
+ }
+ return SetBoolValue("override_namespaces", bVal);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::GetUsingSubscription(bool *bVal)
+{
+ return GetBoolValue("using_subscription", bVal);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::SetUsingSubscription(bool bVal)
+{
+ nsCString serverKey;
+ GetKey(serverKey);
+ if (!serverKey.IsEmpty())
+ {
+ nsresult rv;
+ nsCOMPtr<nsIImapHostSessionList> hostSession = do_GetService(kCImapHostSessionListCID, &rv);
+ if (NS_SUCCEEDED(rv))
+ hostSession->SetHostIsUsingSubscription(serverKey.get(), bVal);
+ }
+ return SetBoolValue("using_subscription", bVal);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::GetMaximumConnectionsNumber(int32_t *aMaxConnections)
+{
+ NS_ENSURE_ARG_POINTER(aMaxConnections);
+
+ nsresult rv = GetIntValue("max_cached_connections", aMaxConnections);
+ // Get our maximum connection count. We need at least 1. If the value is 0,
+ // we use the default of 5. If it's negative, we treat that as 1.
+ if (NS_SUCCEEDED(rv) && *aMaxConnections > 0)
+ return NS_OK;
+
+ *aMaxConnections = (NS_FAILED(rv) || (*aMaxConnections == 0)) ? 5 : 1;
+ (void)SetMaximumConnectionsNumber(*aMaxConnections);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::SetMaximumConnectionsNumber(int32_t aMaxConnections)
+{
+ return SetIntValue("max_cached_connections", aMaxConnections);
+}
+
+NS_IMPL_SERVERPREF_STR(nsImapIncomingServer, ForceSelect,
+ "force_select")
+
+NS_IMPL_SERVERPREF_BOOL(nsImapIncomingServer, DualUseFolders,
+ "dual_use_folders")
+
+NS_IMPL_SERVERPREF_STR(nsImapIncomingServer, AdminUrl,
+ "admin_url")
+
+NS_IMPL_SERVERPREF_BOOL(nsImapIncomingServer, CleanupInboxOnExit,
+ "cleanup_inbox_on_exit")
+
+NS_IMPL_SERVERPREF_BOOL(nsImapIncomingServer, OfflineDownload,
+ "offline_download")
+
+NS_IMPL_SERVERPREF_INT(nsImapIncomingServer, EmptyTrashThreshhold,
+ "empty_trash_threshhold")
+
+NS_IMPL_SERVERPREF_BOOL(nsImapIncomingServer, DownloadBodiesOnGetNewMail,
+ "download_bodies_on_get_new_mail")
+
+NS_IMPL_SERVERPREF_BOOL(nsImapIncomingServer, AutoSyncOfflineStores,
+ "autosync_offline_stores")
+
+NS_IMPL_SERVERPREF_BOOL(nsImapIncomingServer, UseIdle,
+ "use_idle")
+
+NS_IMPL_SERVERPREF_BOOL(nsImapIncomingServer, CheckAllFoldersForNew,
+ "check_all_folders_for_new")
+
+NS_IMPL_SERVERPREF_BOOL(nsImapIncomingServer, UseCondStore,
+ "use_condstore")
+
+NS_IMPL_SERVERPREF_BOOL(nsImapIncomingServer, IsGMailServer,
+ "is_gmail")
+
+NS_IMPL_SERVERPREF_BOOL(nsImapIncomingServer, UseCompressDeflate,
+ "use_compress_deflate")
+
+NS_IMPL_SERVERPREF_INT(nsImapIncomingServer, AutoSyncMaxAgeDays,
+ "autosync_max_age_days")
+
+NS_IMETHODIMP
+nsImapIncomingServer::GetShuttingDown(bool *retval)
+{
+ NS_ENSURE_ARG_POINTER(retval);
+ *retval = m_shuttingDown;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::SetShuttingDown(bool val)
+{
+ m_shuttingDown = val;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::GetDeleteModel(int32_t *retval)
+{
+ NS_ENSURE_ARG(retval);
+ return GetIntValue("delete_model", retval);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::SetDeleteModel(int32_t ivalue)
+{
+ nsresult rv = SetIntValue("delete_model", ivalue);
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsIImapHostSessionList> hostSession =
+ do_GetService(kCImapHostSessionListCID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+ hostSession->SetDeleteIsMoveToTrashForHost(m_serverKey.get(), ivalue == nsMsgImapDeleteModels::MoveToTrash);
+ hostSession->SetShowDeletedMessagesForHost(m_serverKey.get(), ivalue == nsMsgImapDeleteModels::IMAPDelete);
+
+ nsAutoString trashFolderName;
+ nsresult rv = GetTrashFolderName(trashFolderName);
+ if (NS_SUCCEEDED(rv))
+ {
+ nsAutoCString trashFolderNameUtf7;
+ rv = CopyUTF16toMUTF7(trashFolderName, trashFolderNameUtf7);
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsIMsgFolder> trashFolder;
+ // XXX GetFolder only returns folders one level below root.
+ // trashFolderName is a leaf name. So this will not find INBOX.Trash
+ rv = GetFolder(trashFolderNameUtf7, getter_AddRefs(trashFolder));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCString trashURI;
+ trashFolder->GetURI(trashURI);
+ GetMsgFolderFromURI(trashFolder, trashURI, getter_AddRefs(trashFolder));
+ if (NS_SUCCEEDED(rv) && trashFolder)
+ {
+ // If the trash folder is used, set the flag, otherwise clear it.
+ if (ivalue == nsMsgImapDeleteModels::MoveToTrash)
+ trashFolder->SetFlag(nsMsgFolderFlags::Trash);
+ else
+ trashFolder->ClearFlag(nsMsgFolderFlags::Trash);
+ }
+ }
+ }
+ }
+ return rv;
+}
+
+NS_IMPL_SERVERPREF_INT(nsImapIncomingServer, TimeOutLimits,
+ "timeout")
+
+NS_IMPL_SERVERPREF_STR(nsImapIncomingServer, ServerIDPref,
+ "serverIDResponse")
+
+NS_IMPL_SERVERPREF_STR(nsImapIncomingServer, PersonalNamespace,
+ "namespace.personal")
+
+NS_IMPL_SERVERPREF_STR(nsImapIncomingServer, PublicNamespace,
+ "namespace.public")
+
+NS_IMPL_SERVERPREF_STR(nsImapIncomingServer, OtherUsersNamespace,
+ "namespace.other_users")
+
+NS_IMPL_SERVERPREF_BOOL(nsImapIncomingServer, FetchByChunks,
+ "fetch_by_chunks")
+
+NS_IMPL_SERVERPREF_BOOL(nsImapIncomingServer, MimePartsOnDemand,
+ "mime_parts_on_demand")
+
+NS_IMPL_SERVERPREF_BOOL(nsImapIncomingServer, SendID,
+ "send_client_info")
+
+NS_IMPL_SERVERPREF_BOOL(nsImapIncomingServer, CapabilityACL,
+ "cacheCapa.acl")
+NS_IMPL_SERVERPREF_BOOL(nsImapIncomingServer, CapabilityQuota,
+ "cacheCapa.quota")
+
+NS_IMETHODIMP
+nsImapIncomingServer::GetIsAOLServer(bool *aBool)
+{
+ NS_ENSURE_ARG_POINTER(aBool);
+ *aBool = ((m_capability & kAOLImapCapability) != 0);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::SetIsAOLServer(bool aBool)
+{
+ if (aBool)
+ m_capability |= kAOLImapCapability;
+ else
+ m_capability &= ~kAOLImapCapability;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::UpdateTrySTARTTLSPref(bool aStartTLSSucceeded)
+{
+ SetSocketType(aStartTLSSucceeded ? nsMsgSocketType::alwaysSTARTTLS :
+ nsMsgSocketType::plain);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::GetImapConnectionAndLoadUrl(nsIImapUrl* aImapUrl,
+ nsISupports* aConsumer)
+{
+ nsCOMPtr<nsIImapProtocol> aProtocol;
+
+ nsresult rv = GetImapConnection(aImapUrl, getter_AddRefs(aProtocol));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(aImapUrl, &rv);
+ if (aProtocol)
+ {
+ rv = aProtocol->LoadImapUrl(mailnewsurl, aConsumer);
+ // *** jt - in case of the time out situation or the connection gets
+ // terminated by some unforseen problems let's give it a second chance
+ // to run the url
+ if (NS_FAILED(rv) && rv != NS_ERROR_ILLEGAL_VALUE)
+ {
+ NS_ASSERTION(false, "shouldn't get an error loading url");
+ rv = aProtocol->LoadImapUrl(mailnewsurl, aConsumer);
+ }
+ }
+ else
+ { // unable to get an imap connection to run the url; add to the url
+ // queue
+ nsImapProtocol::LogImapUrl("queuing url", aImapUrl);
+ PR_CEnterMonitor(this);
+ m_urlQueue.AppendObject(aImapUrl);
+ m_urlConsumers.AppendElement(aConsumer);
+ NS_IF_ADDREF(aConsumer);
+ PR_CExitMonitor(this);
+ // let's try running it now - maybe the connection is free now.
+ bool urlRun;
+ rv = LoadNextQueuedUrl(nullptr, &urlRun);
+ }
+
+ return rv;
+}
+
+
+NS_IMETHODIMP
+nsImapIncomingServer::PrepareToRetryUrl(nsIImapUrl *aImapUrl, nsIImapMockChannel **aChannel)
+{
+ NS_ENSURE_ARG_POINTER(aChannel);
+ NS_ENSURE_ARG_POINTER(aImapUrl);
+ // maybe there's more we could do here, but this is all we need now.
+ return aImapUrl->GetMockChannel(aChannel);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::SuspendUrl(nsIImapUrl *aImapUrl)
+{
+ NS_ENSURE_ARG_POINTER(aImapUrl);
+ nsImapProtocol::LogImapUrl("suspending url", aImapUrl);
+ PR_CEnterMonitor(this);
+ m_urlQueue.AppendObject(aImapUrl);
+ m_urlConsumers.AppendElement(nullptr);
+ PR_CExitMonitor(this);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::RetryUrl(nsIImapUrl *aImapUrl, nsIImapMockChannel *aChannel)
+{
+ nsresult rv;
+ // Get current thread envent queue
+ aImapUrl->SetMockChannel(aChannel);
+ nsCOMPtr <nsIImapProtocol> protocolInstance;
+ nsImapProtocol::LogImapUrl("creating protocol instance to retry queued url", aImapUrl);
+ nsCOMPtr<nsIThread> thread(do_GetCurrentThread());
+ rv = GetImapConnection(aImapUrl, getter_AddRefs(protocolInstance));
+ if (NS_SUCCEEDED(rv) && protocolInstance)
+ {
+ nsCOMPtr<nsIURI> url = do_QueryInterface(aImapUrl, &rv);
+ if (NS_SUCCEEDED(rv) && url)
+ {
+ nsImapProtocol::LogImapUrl("retrying url", aImapUrl);
+ rv = protocolInstance->LoadImapUrl(url, nullptr); // ### need to save the display consumer.
+ NS_ASSERTION(NS_SUCCEEDED(rv), "failed running queued url");
+ }
+ }
+ return rv;
+}
+
+// checks to see if there are any queued urls on this incoming server,
+// and if so, tries to run the oldest one. Returns true if the url is run
+// on the passed in protocol connection.
+NS_IMETHODIMP
+nsImapIncomingServer::LoadNextQueuedUrl(nsIImapProtocol *aProtocol, bool *aResult)
+{
+ if (WeAreOffline())
+ return NS_MSG_ERROR_OFFLINE;
+
+ nsresult rv = NS_OK;
+ bool urlRun = false;
+ bool keepGoing = true;
+ nsCOMPtr <nsIImapProtocol> protocolInstance ;
+
+ MutexAutoLock mon(mLock);
+ int32_t cnt = m_urlQueue.Count();
+
+ while (cnt > 0 && !urlRun && keepGoing)
+ {
+ nsCOMPtr<nsIImapUrl> aImapUrl(m_urlQueue[0]);
+ nsCOMPtr<nsIMsgMailNewsUrl> aMailNewsUrl(do_QueryInterface(aImapUrl, &rv));
+
+ bool removeUrlFromQueue = false;
+ if (aImapUrl)
+ {
+ nsImapProtocol::LogImapUrl("considering playing queued url", aImapUrl);
+ rv = DoomUrlIfChannelHasError(aImapUrl, &removeUrlFromQueue);
+ NS_ENSURE_SUCCESS(rv, rv);
+ // if we didn't doom the url, lets run it.
+ if (!removeUrlFromQueue)
+ {
+ nsISupports *aConsumer = m_urlConsumers.ElementAt(0);
+ NS_IF_ADDREF(aConsumer);
+
+ nsImapProtocol::LogImapUrl("creating protocol instance to play queued url", aImapUrl);
+ rv = GetImapConnection(aImapUrl, getter_AddRefs(protocolInstance));
+ if (NS_SUCCEEDED(rv) && protocolInstance)
+ {
+ nsCOMPtr<nsIURI> url = do_QueryInterface(aImapUrl, &rv);
+ if (NS_SUCCEEDED(rv) && url)
+ {
+ nsImapProtocol::LogImapUrl("playing queued url", aImapUrl);
+ rv = protocolInstance->LoadImapUrl(url, aConsumer);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "failed running queued url");
+ bool isInbox;
+ protocolInstance->IsBusy(&urlRun, &isInbox);
+ if (!urlRun)
+ nsImapProtocol::LogImapUrl("didn't need to run", aImapUrl);
+ removeUrlFromQueue = true;
+ }
+ }
+ else
+ {
+ nsImapProtocol::LogImapUrl("failed creating protocol instance to play queued url", aImapUrl);
+ keepGoing = false;
+ }
+ NS_IF_RELEASE(aConsumer);
+ }
+ if (removeUrlFromQueue)
+ {
+ m_urlQueue.RemoveObjectAt(0);
+ m_urlConsumers.RemoveElementAt(0);
+ }
+ }
+ cnt = m_urlQueue.Count();
+ }
+ if (aResult)
+ *aResult = urlRun && aProtocol && aProtocol == protocolInstance;
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::AbortQueuedUrls()
+{
+ nsresult rv = NS_OK;
+
+ MutexAutoLock mon(mLock);
+ int32_t cnt = m_urlQueue.Count();
+
+ while (cnt > 0)
+ {
+ nsCOMPtr<nsIImapUrl> aImapUrl(m_urlQueue[cnt - 1]);
+ bool removeUrlFromQueue = false;
+
+ if (aImapUrl)
+ {
+ rv = DoomUrlIfChannelHasError(aImapUrl, &removeUrlFromQueue);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (removeUrlFromQueue)
+ {
+ m_urlQueue.RemoveObjectAt(cnt - 1);
+ m_urlConsumers.RemoveElementAt(cnt - 1);
+ }
+ }
+ cnt--;
+ }
+
+ return rv;
+}
+
+// if this url has a channel with an error, doom it and its mem cache entries,
+// and notify url listeners.
+nsresult nsImapIncomingServer::DoomUrlIfChannelHasError(nsIImapUrl *aImapUrl, bool *urlDoomed)
+{
+ nsresult rv = NS_OK;
+
+ nsCOMPtr<nsIMsgMailNewsUrl> aMailNewsUrl(do_QueryInterface(aImapUrl, &rv));
+
+ if (aMailNewsUrl && aImapUrl)
+ {
+ nsCOMPtr <nsIImapMockChannel> mockChannel;
+
+ if (NS_SUCCEEDED(aImapUrl->GetMockChannel(getter_AddRefs(mockChannel))) && mockChannel)
+ {
+ nsresult requestStatus;
+ nsCOMPtr<nsIRequest> request = do_QueryInterface(mockChannel);
+ if (!request)
+ return NS_ERROR_FAILURE;
+ request->GetStatus(&requestStatus);
+ if (NS_FAILED(requestStatus))
+ {
+ nsresult res;
+ *urlDoomed = true;
+ nsImapProtocol::LogImapUrl("dooming url", aImapUrl);
+
+ mockChannel->Close(); // try closing it to get channel listener nulled out.
+
+ if (aMailNewsUrl)
+ {
+ nsCOMPtr<nsICacheEntry> cacheEntry;
+ res = aMailNewsUrl->GetMemCacheEntry(getter_AddRefs(cacheEntry));
+ if (NS_SUCCEEDED(res) && cacheEntry)
+ cacheEntry->AsyncDoom(nullptr);
+ // we're aborting this url - tell listeners
+ aMailNewsUrl->SetUrlState(false, NS_MSG_ERROR_URL_ABORTED);
+ }
+ }
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::RemoveConnection(nsIImapProtocol* aImapConnection)
+{
+ PR_CEnterMonitor(this);
+ if (aImapConnection)
+ m_connectionCache.RemoveObject(aImapConnection);
+
+ PR_CExitMonitor(this);
+ return NS_OK;
+}
+
+bool
+nsImapIncomingServer::ConnectionTimeOut(nsIImapProtocol* aConnection)
+{
+ bool retVal = false;
+ if (!aConnection) return retVal;
+ nsresult rv;
+
+ int32_t timeoutInMinutes = 0;
+ rv = GetTimeOutLimits(&timeoutInMinutes);
+ if (NS_FAILED(rv) || timeoutInMinutes <= 0 || timeoutInMinutes > 29)
+ {
+ timeoutInMinutes = 29;
+ SetTimeOutLimits(timeoutInMinutes);
+ }
+
+ PRTime cacheTimeoutLimits = timeoutInMinutes * 60 * PR_USEC_PER_SEC;
+ PRTime lastActiveTimeStamp;
+ rv = aConnection->GetLastActiveTimeStamp(&lastActiveTimeStamp);
+
+ if (PR_Now() - lastActiveTimeStamp >= cacheTimeoutLimits)
+ {
+ nsCOMPtr<nsIImapProtocol> aProtocol(do_QueryInterface(aConnection,
+ &rv));
+ if (NS_SUCCEEDED(rv) && aProtocol)
+ {
+ RemoveConnection(aConnection);
+ aProtocol->TellThreadToDie(false);
+ retVal = true;
+ }
+ }
+ return retVal;
+}
+
+nsresult
+nsImapIncomingServer::GetImapConnection(nsIImapUrl * aImapUrl,
+ nsIImapProtocol ** aImapConnection)
+{
+ NS_ENSURE_ARG_POINTER(aImapUrl);
+
+ nsresult rv = NS_OK;
+ bool canRunUrlImmediately = false;
+ bool canRunButBusy = false;
+ nsCOMPtr<nsIImapProtocol> connection;
+ nsCOMPtr<nsIImapProtocol> freeConnection;
+ bool isBusy = false;
+ bool isInboxConnection = false;
+
+ PR_CEnterMonitor(this);
+
+ int32_t maxConnections;
+ (void)GetMaximumConnectionsNumber(&maxConnections);
+
+ int32_t cnt = m_connectionCache.Count();
+
+ *aImapConnection = nullptr;
+ // iterate through the connection cache for a connection that can handle this url.
+ bool userCancelled = false;
+
+ // loop until we find a connection that can run the url, or doesn't have to wait?
+ for (int32_t i = cnt - 1; i >= 0 && !canRunUrlImmediately && !canRunButBusy; i--)
+ {
+ connection = m_connectionCache[i];
+ if (connection)
+ {
+ bool badConnection = ConnectionTimeOut(connection);
+ if (!badConnection)
+ {
+ badConnection = NS_FAILED(connection->CanHandleUrl(aImapUrl,
+ &canRunUrlImmediately,
+ &canRunButBusy));
+#ifdef DEBUG_bienvenu
+ nsAutoCString curSelectedFolderName;
+ if (connection)
+ connection->GetSelectedMailboxName(getter_Copies(curSelectedFolderName));
+ // check that no other connection is in the same selected state.
+ if (!curSelectedFolderName.IsEmpty())
+ {
+ for (uint32_t j = 0; j < cnt; j++)
+ {
+ if (j != i)
+ {
+ nsCOMPtr<nsIImapProtocol> otherConnection = do_QueryElementAt(m_connectionCache, j);
+ if (otherConnection)
+ {
+ nsAutoCString otherSelectedFolderName;
+ otherConnection->GetSelectedMailboxName(getter_Copies(otherSelectedFolderName));
+ NS_ASSERTION(!curSelectedFolderName.Equals(otherSelectedFolderName), "two connections selected on same folder");
+ }
+
+ }
+ }
+ }
+#endif // DEBUG_bienvenu
+ }
+ if (badConnection)
+ {
+ connection = nullptr;
+ continue;
+ }
+ }
+
+ // if this connection is wrong, but it's not busy, check if we should designate
+ // it as the free connection.
+ if (!canRunUrlImmediately && !canRunButBusy && connection)
+ {
+ rv = connection->IsBusy(&isBusy, &isInboxConnection);
+ if (NS_FAILED(rv))
+ continue;
+ // if max connections is <= 1, we have to re-use the inbox connection.
+ if (!isBusy && (!isInboxConnection || maxConnections <= 1))
+ {
+ if (!freeConnection)
+ freeConnection = connection;
+ else // check which is the better free connection to use.
+ { // We prefer one not in the selected state.
+ nsAutoCString selectedFolderName;
+ connection->GetSelectedMailboxName(getter_Copies(selectedFolderName));
+ if (selectedFolderName.IsEmpty())
+ freeConnection = connection;
+ }
+ }
+ }
+ // don't leave this loop with connection set if we can't use it!
+ if (!canRunButBusy && !canRunUrlImmediately)
+ connection = nullptr;
+ }
+
+ nsImapState requiredState;
+ aImapUrl->GetRequiredImapState(&requiredState);
+ // refresh cnt in case we killed one or more dead connections. This
+ // will prevent us from not spinning up a new connection when all
+ // connections were dead.
+ cnt = m_connectionCache.Count();
+ // if we got here and we have a connection, then we should return it!
+ if (canRunUrlImmediately && connection)
+ {
+ *aImapConnection = connection;
+ NS_IF_ADDREF(*aImapConnection);
+ }
+ else if (canRunButBusy)
+ {
+ // do nothing; return NS_OK; for queuing
+ }
+ else if (userCancelled)
+ {
+ rv = NS_BINDING_ABORTED; // user cancelled
+ }
+ // CanHandleUrl will pretend that some types of urls require a selected state url
+ // (e.g., a folder delete or msg append) but we shouldn't create new connections
+ // for these types of urls if we have a free connection. So we check the actual
+ // required state here.
+ else if (cnt < maxConnections
+ && (!freeConnection || requiredState == nsIImapUrl::nsImapSelectedState))
+ rv = CreateProtocolInstance(aImapConnection);
+ else if (freeConnection)
+ {
+ *aImapConnection = freeConnection;
+ NS_IF_ADDREF(*aImapConnection);
+ }
+ else // cannot get anyone to handle the url queue it
+ {
+ if (cnt >= maxConnections)
+ nsImapProtocol::LogImapUrl("exceeded connection cache limit", aImapUrl);
+ // caller will queue the url
+ }
+
+ PR_CExitMonitor(this);
+ return rv;
+}
+
+nsresult
+nsImapIncomingServer::CreateProtocolInstance(nsIImapProtocol ** aImapConnection)
+{
+ // create a new connection and add it to the connection cache
+ // we may need to flag the protocol connection as busy so we don't get
+ // a race condition where someone else goes through this code
+
+ int32_t authMethod;
+ GetAuthMethod(&authMethod);
+ nsresult rv;
+ // pre-flight that we have nss - on the ui thread - for MD5 etc.
+ switch (authMethod)
+ {
+ case nsMsgAuthMethod::passwordEncrypted:
+ case nsMsgAuthMethod::secure:
+ case nsMsgAuthMethod::anything:
+ {
+ nsCOMPtr<nsISupports> dummyUsedToEnsureNSSIsInitialized =
+ do_GetService("@mozilla.org/psm;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ break;
+ default:
+ break;
+ }
+ nsIImapProtocol * protocolInstance;
+ rv = CallCreateInstance(kImapProtocolCID, &protocolInstance);
+ if (NS_SUCCEEDED(rv) && protocolInstance)
+ {
+ nsCOMPtr<nsIImapHostSessionList> hostSession =
+ do_GetService(kCImapHostSessionListCID, &rv);
+ if (NS_SUCCEEDED(rv))
+ rv = protocolInstance->Initialize(hostSession, this);
+ }
+
+ // take the protocol instance and add it to the connectionCache
+ if (protocolInstance)
+ m_connectionCache.AppendObject(protocolInstance);
+ *aImapConnection = protocolInstance; // this is already ref counted.
+ return rv;
+}
+
+NS_IMETHODIMP nsImapIncomingServer::CloseConnectionForFolder(nsIMsgFolder *aMsgFolder)
+{
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIImapProtocol> connection;
+ bool isBusy = false, isInbox = false;
+ nsCString inFolderName;
+ nsCString connectionFolderName;
+ nsCOMPtr <nsIMsgImapMailFolder> imapFolder = do_QueryInterface(aMsgFolder);
+
+ if (!imapFolder)
+ return NS_ERROR_NULL_POINTER;
+
+ int32_t cnt = m_connectionCache.Count();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ imapFolder->GetOnlineName(inFolderName);
+ PR_CEnterMonitor(this);
+
+ for (int32_t i = 0; i < cnt; ++i)
+ {
+ connection = m_connectionCache[i];
+ if (connection)
+ {
+ rv = connection->GetSelectedMailboxName(getter_Copies(connectionFolderName));
+ if (connectionFolderName.Equals(inFolderName))
+ {
+ rv = connection->IsBusy(&isBusy, &isInbox);
+ if (!isBusy)
+ rv = connection->TellThreadToDie(true);
+ break; // found it, end of the loop
+ }
+ }
+ }
+
+ PR_CExitMonitor(this);
+ return rv;
+}
+
+NS_IMETHODIMP nsImapIncomingServer::ResetConnection(const nsACString& folderName)
+{
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIImapProtocol> connection;
+ bool isBusy = false, isInbox = false;
+ nsCString curFolderName;
+
+ int32_t cnt = m_connectionCache.Count();
+
+ PR_CEnterMonitor(this);
+
+ for (int32_t i = 0; i < cnt; ++i)
+ {
+ connection = m_connectionCache[i];
+ if (connection)
+ {
+ rv = connection->GetSelectedMailboxName(getter_Copies(curFolderName));
+ if (curFolderName.Equals(folderName))
+ {
+ rv = connection->IsBusy(&isBusy, &isInbox);
+ if (!isBusy)
+ rv = connection->ResetToAuthenticatedState();
+ break; // found it, end of the loop
+ }
+ }
+ }
+
+ PR_CExitMonitor(this);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::PerformExpand(nsIMsgWindow *aMsgWindow)
+{
+ nsCString password;
+ nsresult rv;
+ rv = GetPassword(password);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (password.IsEmpty())
+ return NS_OK;
+
+ rv = ResetFoldersToUnverified(nullptr);
+
+ nsCOMPtr<nsIMsgFolder> rootMsgFolder;
+ rv = GetRootFolder(getter_AddRefs(rootMsgFolder));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!rootMsgFolder) return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIThread> thread(do_GetCurrentThread());
+ rv = imapService->DiscoverAllFolders(rootMsgFolder,
+ this, aMsgWindow, nullptr);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::VerifyLogon(nsIUrlListener *aUrlListener,
+ nsIMsgWindow *aMsgWindow, nsIURI **aURL)
+{
+ nsresult rv;
+
+ nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIMsgFolder> rootFolder;
+ // this will create the resource if it doesn't exist, but it shouldn't
+ // do anything on disk.
+ rv = GetRootFolder(getter_AddRefs(rootFolder));
+ NS_ENSURE_SUCCESS(rv, rv);
+ return imapService->VerifyLogon(rootFolder, aUrlListener, aMsgWindow, aURL);
+}
+
+NS_IMETHODIMP nsImapIncomingServer::PerformBiff(nsIMsgWindow* aMsgWindow)
+{
+ nsCOMPtr<nsIMsgFolder> rootMsgFolder;
+ nsresult rv = GetRootMsgFolder(getter_AddRefs(rootMsgFolder));
+ if(NS_SUCCEEDED(rv))
+ {
+ SetPerformingBiff(true);
+ rv = rootMsgFolder->GetNewMessages(aMsgWindow, nullptr);
+ }
+ return rv;
+}
+
+
+NS_IMETHODIMP
+nsImapIncomingServer::CloseCachedConnections()
+{
+ nsCOMPtr<nsIImapProtocol> connection;
+ PR_CEnterMonitor(this);
+
+ // iterate through the connection cache closing open connections.
+ int32_t cnt = m_connectionCache.Count();
+
+ for (int32_t i = cnt; i > 0; --i)
+ {
+ connection = m_connectionCache[i - 1];
+ if (connection)
+ connection->TellThreadToDie(true);
+ }
+
+ PR_CExitMonitor(this);
+ return NS_OK;
+}
+
+nsresult
+nsImapIncomingServer::CreateRootFolderFromUri(const nsCString &serverUri,
+ nsIMsgFolder **rootFolder)
+{
+ nsImapMailFolder *newRootFolder = new nsImapMailFolder;
+ if (!newRootFolder)
+ return NS_ERROR_OUT_OF_MEMORY;
+ newRootFolder->Init(serverUri.get());
+ NS_ADDREF(*rootFolder = newRootFolder);
+ return NS_OK;
+}
+
+// nsIImapServerSink impl
+// aNewFolder will not be set if we're listing for the subscribe UI, since that's the way 4.x worked.
+NS_IMETHODIMP nsImapIncomingServer::PossibleImapMailbox(const nsACString& folderPath,
+ char hierarchyDelimiter,
+ int32_t boxFlags, bool *aNewFolder)
+{
+ NS_ENSURE_ARG_POINTER(aNewFolder);
+ NS_ENSURE_TRUE(!folderPath.IsEmpty(), NS_ERROR_FAILURE);
+
+ // folderPath is in canonical format, i.e., hierarchy separator has been replaced with '/'
+ nsresult rv;
+ bool found = false;
+ bool haveParent = false;
+ nsCOMPtr<nsIMsgImapMailFolder> hostFolder;
+ nsCOMPtr<nsIMsgFolder> aFolder;
+ bool explicitlyVerify = false;
+
+ *aNewFolder = false;
+ nsCOMPtr<nsIMsgFolder> a_nsIFolder;
+ rv = GetRootFolder(getter_AddRefs(a_nsIFolder));
+
+ if(NS_FAILED(rv))
+ return rv;
+
+ nsAutoCString dupFolderPath(folderPath);
+ if (dupFolderPath.Last() == '/')
+ {
+ dupFolderPath.SetLength(dupFolderPath.Length()-1);
+ if (dupFolderPath.IsEmpty())
+ return NS_ERROR_FAILURE;
+ // *** this is what we did in 4.x in order to list uw folder only
+ // mailbox in order to get the \NoSelect flag
+ explicitlyVerify = !(boxFlags & kNameSpace);
+ }
+ if (mDoingSubscribeDialog)
+ {
+ // Make sure the imapmailfolder object has the right delimiter because the unsubscribed
+ // folders (those not in the 'lsub' list) have the delimiter set to the default ('^').
+ if (a_nsIFolder && !dupFolderPath.IsEmpty())
+ {
+ nsCOMPtr<nsIMsgFolder> msgFolder;
+ bool isNamespace = false;
+ bool noSelect = false;
+
+ rv = a_nsIFolder->FindSubFolder(dupFolderPath, getter_AddRefs(msgFolder));
+ NS_ENSURE_SUCCESS(rv,rv);
+ m_subscribeFolders.AppendObject(msgFolder);
+ noSelect = (boxFlags & kNoselect) != 0;
+ nsCOMPtr<nsIMsgImapMailFolder> imapFolder = do_QueryInterface(msgFolder, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+ imapFolder->SetHierarchyDelimiter(hierarchyDelimiter);
+ isNamespace = (boxFlags & kNameSpace) != 0;
+ if (!isNamespace)
+ rv = AddTo(dupFolderPath, mDoingLsub && !noSelect/* add as subscribed */,
+ !noSelect, mDoingLsub /* change if exists */);
+ NS_ENSURE_SUCCESS(rv,rv);
+ return rv;
+ }
+ }
+
+ hostFolder = do_QueryInterface(a_nsIFolder, &rv);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsAutoCString tempFolderName(dupFolderPath);
+ 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 ((int32_t(PL_strcasecmp(tokenStr.get(), "INBOX"))==0) && (strcmp(tokenStr.get(), "INBOX") != 0))
+ changedStr.Append("INBOX");
+ else
+ changedStr.Append(tokenStr);
+
+ if (slashPos > 0 )
+ changedStr.Append(remStr);
+
+ dupFolderPath.Assign(changedStr);
+ nsAutoCString folderName(dupFolderPath);
+
+ nsAutoCString uri;
+ nsCString serverUri;
+ GetServerURI(serverUri);
+ uri.Assign(serverUri);
+ int32_t leafPos = folderName.RFindChar('/');
+ nsAutoCString parentName(folderName);
+ nsAutoCString parentUri(uri);
+
+ if (leafPos > 0)
+ {
+ // If there is a hierarchy, there is a parent.
+ // Don't strip off slash if it's the first character
+ parentName.SetLength(leafPos);
+ folderName.Cut(0, leafPos + 1); // get rid of the parent name
+ haveParent = true;
+ parentUri.Append('/');
+ parentUri.Append(parentName);
+ }
+ if (MsgLowerCaseEqualsLiteral(folderPath, "inbox") &&
+ hierarchyDelimiter == kOnlineHierarchySeparatorNil)
+ {
+ hierarchyDelimiter = '/'; // set to default in this case (as in 4.x)
+ hostFolder->SetHierarchyDelimiter(hierarchyDelimiter);
+ }
+
+ nsCOMPtr <nsIMsgFolder> child;
+
+ // nsCString possibleName(aSpec->allocatedPathName);
+ uri.Append('/');
+ uri.Append(dupFolderPath);
+ bool caseInsensitive = MsgLowerCaseEqualsLiteral(dupFolderPath, "inbox");
+ a_nsIFolder->GetChildWithURI(uri, true, caseInsensitive, getter_AddRefs(child));
+ // if we couldn't find this folder by URI, tell the imap code it's a new folder to us
+ *aNewFolder = !child;
+ if (child)
+ found = true;
+ if (!found)
+ {
+ // trying to find/discover the parent
+ if (haveParent)
+ {
+ nsCOMPtr <nsIMsgFolder> parent;
+ bool parentIsNew;
+ caseInsensitive = MsgLowerCaseEqualsLiteral(parentName, "inbox");
+ a_nsIFolder->GetChildWithURI(parentUri, true, caseInsensitive, getter_AddRefs(parent));
+ if (!parent /* || parentFolder->GetFolderNeedsAdded()*/)
+ {
+ PossibleImapMailbox(parentName, hierarchyDelimiter, kNoselect | // be defensive
+ ((boxFlags & //only inherit certain flags from the child
+ (kPublicMailbox | kOtherUsersMailbox | kPersonalMailbox))), &parentIsNew);
+ }
+ }
+ rv = hostFolder->CreateClientSubfolderInfo(dupFolderPath, hierarchyDelimiter,boxFlags, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+ caseInsensitive = MsgLowerCaseEqualsLiteral(dupFolderPath, "inbox");
+ a_nsIFolder->GetChildWithURI(uri, true, caseInsensitive, getter_AddRefs(child));
+ }
+ if (child)
+ {
+ nsCOMPtr <nsIMsgImapMailFolder> imapFolder = do_QueryInterface(child);
+ if (imapFolder)
+ {
+ nsAutoCString onlineName;
+ nsAutoString unicodeName;
+ imapFolder->SetVerifiedAsOnlineFolder(true);
+ imapFolder->SetHierarchyDelimiter(hierarchyDelimiter);
+ if (boxFlags & kImapTrash)
+ {
+ int32_t deleteModel;
+ GetDeleteModel(&deleteModel);
+ if (deleteModel == nsMsgImapDeleteModels::MoveToTrash)
+ child->SetFlag(nsMsgFolderFlags::Trash);
+ }
+
+ imapFolder->SetBoxFlags(boxFlags);
+ imapFolder->SetExplicitlyVerify(explicitlyVerify);
+ imapFolder->GetOnlineName(onlineName);
+
+ // online name needs to use the correct hierarchy delimiter (I think...)
+ // or the canonical path - one or the other, but be consistent.
+ MsgReplaceChar(dupFolderPath, '/', hierarchyDelimiter);
+ if (hierarchyDelimiter != '/')
+ nsImapUrl::UnescapeSlashes(dupFolderPath.BeginWriting());
+
+ // GMail gives us a localized name for the inbox but doesn't let
+ // us select that localized name.
+ if (boxFlags & kImapInbox)
+ imapFolder->SetOnlineName(NS_LITERAL_CSTRING("INBOX"));
+ else if (onlineName.IsEmpty() || !onlineName.Equals(dupFolderPath))
+ imapFolder->SetOnlineName(dupFolderPath);
+
+ if (hierarchyDelimiter != '/')
+ nsImapUrl::UnescapeSlashes(folderName.BeginWriting());
+ if (NS_SUCCEEDED(CopyMUTF7toUTF16(folderName, unicodeName)))
+ child->SetPrettyName(unicodeName);
+ }
+ }
+ if (!found && child)
+ child->SetMsgDatabase(nullptr); // close the db, so we don't hold open all the .msf files for new folders
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsImapIncomingServer::AddFolderRights(const nsACString& mailboxName, const nsACString& userName,
+ const nsACString& rights)
+{
+ nsCOMPtr <nsIMsgFolder> rootFolder;
+ nsresult rv = GetRootFolder(getter_AddRefs(rootFolder));
+ if(NS_SUCCEEDED(rv) && rootFolder)
+ {
+ nsCOMPtr <nsIMsgImapMailFolder> imapRoot = do_QueryInterface(rootFolder);
+ if (imapRoot)
+ {
+ nsCOMPtr <nsIMsgImapMailFolder> foundFolder;
+ rv = imapRoot->FindOnlineSubFolder(mailboxName, getter_AddRefs(foundFolder));
+ if (NS_SUCCEEDED(rv) && foundFolder)
+ return foundFolder->AddFolderRights(userName, rights);
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsImapIncomingServer::FolderNeedsACLInitialized(const nsACString& folderPath,
+ bool *aNeedsACLInitialized)
+{
+ NS_ENSURE_ARG_POINTER(aNeedsACLInitialized);
+ nsCOMPtr <nsIMsgFolder> rootFolder;
+ nsresult rv = GetRootFolder(getter_AddRefs(rootFolder));
+ if(NS_SUCCEEDED(rv) && rootFolder)
+ {
+ nsCOMPtr <nsIMsgImapMailFolder> imapRoot = do_QueryInterface(rootFolder);
+ if (imapRoot)
+ {
+ nsCOMPtr <nsIMsgImapMailFolder> foundFolder;
+ rv = imapRoot->FindOnlineSubFolder(folderPath, getter_AddRefs(foundFolder));
+ if (NS_SUCCEEDED(rv) && foundFolder)
+ {
+ nsCOMPtr <nsIImapMailFolderSink> folderSink = do_QueryInterface(foundFolder);
+ if (folderSink)
+ return folderSink->GetFolderNeedsACLListed(aNeedsACLInitialized);
+ }
+ }
+ }
+ *aNeedsACLInitialized = false; // maybe we want to say TRUE here...
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsImapIncomingServer::RefreshFolderRights(const nsACString& folderPath)
+{
+ nsCOMPtr <nsIMsgFolder> rootFolder;
+ nsresult rv = GetRootFolder(getter_AddRefs(rootFolder));
+ if(NS_SUCCEEDED(rv) && rootFolder)
+ {
+ nsCOMPtr <nsIMsgImapMailFolder> imapRoot = do_QueryInterface(rootFolder);
+ if (imapRoot)
+ {
+ nsCOMPtr <nsIMsgImapMailFolder> foundFolder;
+ rv = imapRoot->FindOnlineSubFolder(folderPath, getter_AddRefs(foundFolder));
+ if (NS_SUCCEEDED(rv) && foundFolder)
+ return foundFolder->RefreshFolderRights();
+ }
+ }
+ return rv;
+}
+
+nsresult nsImapIncomingServer::GetFolder(const nsACString& name, nsIMsgFolder** pFolder)
+{
+ NS_ENSURE_ARG_POINTER(pFolder);
+ NS_ENSURE_TRUE(!name.IsEmpty(), NS_ERROR_FAILURE);
+ nsresult rv;
+ *pFolder = nullptr;
+
+ nsCOMPtr<nsIMsgFolder> rootFolder;
+ rv = GetRootFolder(getter_AddRefs(rootFolder));
+ if (NS_SUCCEEDED(rv) && rootFolder)
+ {
+ nsCString uri;
+ rv = rootFolder->GetURI(uri);
+ if (NS_SUCCEEDED(rv) && !uri.IsEmpty())
+ {
+ nsAutoCString uriString(uri);
+ uriString.Append('/');
+ uriString.Append(name);
+ nsCOMPtr<nsIRDFService> rdf(do_GetService(kRDFServiceCID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIRDFResource> res;
+ rv = rdf->GetResource(uriString, getter_AddRefs(res));
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsIMsgFolder> folder(do_QueryInterface(res, &rv));
+ if (NS_SUCCEEDED(rv) && folder)
+ folder.swap(*pFolder);
+ }
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsImapIncomingServer::OnlineFolderDelete(const nsACString& aFolderName)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsImapIncomingServer::OnlineFolderCreateFailed(const nsACString& aFolderName)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsImapIncomingServer::OnlineFolderRename(nsIMsgWindow *msgWindow, const nsACString& oldName,
+ const nsACString& newName)
+{
+ nsresult rv = NS_ERROR_FAILURE;
+ if (!newName.IsEmpty())
+ {
+ nsCOMPtr<nsIMsgFolder> me;
+ rv = GetFolder(oldName, getter_AddRefs(me));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsIMsgFolder> parent;
+ nsCString tmpNewName (newName);
+ int32_t folderStart = tmpNewName.RFindChar('/');
+ if (folderStart > 0)
+ {
+ rv = GetFolder(StringHead(tmpNewName, folderStart), getter_AddRefs(parent));
+ }
+ else // root is the parent
+ rv = GetRootFolder(getter_AddRefs(parent));
+ if (NS_SUCCEEDED(rv) && parent)
+ {
+ nsCOMPtr<nsIMsgImapMailFolder> folder;
+ folder = do_QueryInterface(me, &rv);
+ if (NS_SUCCEEDED(rv))
+ {
+ folder->RenameLocal(tmpNewName, parent);
+ nsCOMPtr<nsIMsgImapMailFolder> parentImapFolder = do_QueryInterface(parent);
+
+ if (parentImapFolder)
+ parentImapFolder->RenameClient(msgWindow, me, oldName, tmpNewName);
+
+ nsCOMPtr <nsIMsgFolder> newFolder;
+ nsString unicodeNewName;
+ // tmpNewName is imap mod utf7. It needs to be convert to utf8.
+ CopyMUTF7toUTF16(tmpNewName, unicodeNewName);
+ CopyUTF16toUTF8(unicodeNewName, tmpNewName);
+ rv = GetFolder(tmpNewName, getter_AddRefs(newFolder));
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr <nsIAtom> folderRenameAtom;
+ folderRenameAtom = MsgGetAtom("RenameCompleted");
+ newFolder->NotifyFolderEvent(folderRenameAtom);
+ }
+ }
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsImapIncomingServer::FolderIsNoSelect(const nsACString& aFolderName, bool *result)
+{
+ NS_ENSURE_ARG_POINTER(result);
+ nsCOMPtr<nsIMsgFolder> msgFolder;
+ nsresult rv = GetFolder(aFolderName, getter_AddRefs(msgFolder));
+ if (NS_SUCCEEDED(rv) && msgFolder)
+ {
+ uint32_t flags;
+ msgFolder->GetFlags(&flags);
+ *result = ((flags & nsMsgFolderFlags::ImapNoselect) != 0);
+ }
+ else
+ *result = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsImapIncomingServer::SetFolderAdminURL(const nsACString& aFolderName, const nsACString& aFolderAdminUrl)
+{
+ nsCOMPtr <nsIMsgFolder> rootFolder;
+ nsresult rv = GetRootFolder(getter_AddRefs(rootFolder));
+ if(NS_SUCCEEDED(rv) && rootFolder)
+ {
+ nsCOMPtr <nsIMsgImapMailFolder> imapRoot = do_QueryInterface(rootFolder);
+ if (imapRoot)
+ {
+ nsCOMPtr <nsIMsgImapMailFolder> foundFolder;
+ rv = imapRoot->FindOnlineSubFolder(aFolderName, getter_AddRefs(foundFolder));
+ if (NS_SUCCEEDED(rv) && foundFolder)
+ return foundFolder->SetAdminUrl(aFolderAdminUrl);
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsImapIncomingServer::FolderVerifiedOnline(const nsACString& folderName, bool *aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = false;
+ nsCOMPtr<nsIMsgFolder> rootFolder;
+ nsresult rv = GetRootFolder(getter_AddRefs(rootFolder));
+ if (NS_SUCCEEDED(rv) && rootFolder)
+ {
+ nsCOMPtr<nsIMsgFolder> folder;
+ rv = rootFolder->FindSubFolder(folderName, getter_AddRefs(folder));
+ if (NS_SUCCEEDED(rv) && folder)
+ {
+ nsCOMPtr<nsIMsgImapMailFolder> imapFolder = do_QueryInterface(folder);
+ if (imapFolder)
+ imapFolder->GetVerifiedAsOnlineFolder(aResult);
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsImapIncomingServer::DiscoveryDone()
+{
+ if (mDoingSubscribeDialog)
+ return NS_OK;
+
+ nsCOMPtr<nsIMsgFolder> rootMsgFolder;
+ nsresult rv = GetRootFolder(getter_AddRefs(rootMsgFolder));
+ if (NS_SUCCEEDED(rv) && rootMsgFolder)
+ {
+ // GetResource() may return a node which is not in the folder
+ // tree hierarchy but in the rdf cache in case of the non-existing default
+ // Sent, Drafts, and Templates folders. The resouce will be eventually
+ // released when the rdf service shuts down. When we create the default
+ // folders later on in the imap server, the subsequent GetResource() of the
+ // same uri will get us the cached rdf resource which should have the folder
+ // flag set appropriately.
+ nsCOMPtr<nsIRDFService> rdf(do_GetService("@mozilla.org/rdf/rdf-service;1",
+ &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgAccountManager> accountMgr = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgIdentity> identity;
+ rv = accountMgr->GetFirstIdentityForServer(this, getter_AddRefs(identity));
+ if (NS_SUCCEEDED(rv) && identity)
+ {
+ nsCString folderUri;
+ identity->GetFccFolder(folderUri);
+ nsCString existingUri;
+
+ if (CheckSpecialFolder(rdf, folderUri, nsMsgFolderFlags::SentMail,
+ existingUri))
+ {
+ identity->SetFccFolder(existingUri);
+ identity->SetFccFolderPickerMode(NS_LITERAL_CSTRING("1"));
+ }
+ identity->GetDraftFolder(folderUri);
+ if (CheckSpecialFolder(rdf, folderUri, nsMsgFolderFlags::Drafts,
+ existingUri))
+ {
+ identity->SetDraftFolder(existingUri);
+ identity->SetDraftsFolderPickerMode(NS_LITERAL_CSTRING("1"));
+ }
+ bool archiveEnabled;
+ identity->GetArchiveEnabled(&archiveEnabled);
+ if (archiveEnabled)
+ {
+ identity->GetArchiveFolder(folderUri);
+ if (CheckSpecialFolder(rdf, folderUri, nsMsgFolderFlags::Archive,
+ existingUri))
+ {
+ identity->SetArchiveFolder(existingUri);
+ identity->SetArchivesFolderPickerMode(NS_LITERAL_CSTRING("1"));
+ }
+ }
+ identity->GetStationeryFolder(folderUri);
+ nsCOMPtr<nsIRDFResource> res;
+ if (!folderUri.IsEmpty() && NS_SUCCEEDED(rdf->GetResource(folderUri, getter_AddRefs(res))))
+ {
+ nsCOMPtr<nsIMsgFolder> folder(do_QueryInterface(res, &rv));
+ if (NS_SUCCEEDED(rv))
+ rv = folder->SetFlag(nsMsgFolderFlags::Templates);
+ }
+ }
+
+ nsCOMPtr<nsISpamSettings> spamSettings;
+ rv = GetSpamSettings(getter_AddRefs(spamSettings));
+ if (NS_SUCCEEDED(rv) && spamSettings)
+ {
+ nsCString spamFolderUri, existingUri;
+ spamSettings->GetSpamFolderURI(getter_Copies(spamFolderUri));
+ if (CheckSpecialFolder(rdf, spamFolderUri, nsMsgFolderFlags::Junk,
+ existingUri))
+ {
+ // This only sets the cached values in the spam settings object.
+ spamSettings->SetActionTargetFolder(existingUri.get());
+ spamSettings->SetMoveTargetMode(nsISpamSettings::MOVE_TARGET_MODE_FOLDER);
+ // Set the preferences too so that the values persist.
+ SetCharValue("spamActionTargetFolder", existingUri);
+ SetIntValue("moveTargetMode", nsISpamSettings::MOVE_TARGET_MODE_FOLDER);
+ }
+ }
+
+ bool isGMailServer;
+ GetIsGMailServer(&isGMailServer);
+
+ // Verify there is only one trash folder. Another might be present if
+ // the trash name has been changed. Or we might be a gmail server and
+ // want to switch to gmail's trash folder.
+ nsCOMPtr<nsIArray> trashFolders;
+ rv = rootMsgFolder->GetFoldersWithFlags(nsMsgFolderFlags::Trash,
+ getter_AddRefs(trashFolders));
+
+ if (NS_SUCCEEDED(rv) && trashFolders)
+ {
+ uint32_t numFolders;
+ trashFolders->GetLength(&numFolders);
+ nsAutoString trashName;
+ if (NS_SUCCEEDED(GetTrashFolderName(trashName)))
+ {
+ for (uint32_t i = 0; i < numFolders; i++)
+ {
+ nsCOMPtr<nsIMsgFolder> trashFolder(do_QueryElementAt(trashFolders, i));
+ if (trashFolder)
+ {
+ // If we're a gmail server, we clear the trash flags from folder(s)
+ // without the kImapXListTrash flag. For normal servers, we clear
+ // the trash folder flag if the folder name doesn't match the
+ // pref trash folder name.
+ if (isGMailServer)
+ {
+ nsCOMPtr<nsIMsgImapMailFolder> imapFolder(do_QueryInterface(trashFolder));
+ int32_t boxFlags;
+ imapFolder->GetBoxFlags(&boxFlags);
+ if (boxFlags & kImapXListTrash)
+ {
+ continue;
+ }
+ }
+ else
+ {
+ // trashName is the leaf name on the folder URI, which will be
+ // different from the folder GetName if the trash name is
+ // localized.
+ nsAutoCString trashURL;
+ trashFolder->GetFolderURL(trashURL);
+ int32_t leafPos = trashURL.RFindChar('/');
+ nsAutoCString unescapedName;
+ MsgUnescapeString(Substring(trashURL, leafPos + 1),
+ nsINetUtil::ESCAPE_URL_PATH, unescapedName);
+ nsAutoString nameUnicode;
+ if (NS_FAILED(CopyMUTF7toUTF16(unescapedName, nameUnicode)) ||
+ trashName.Equals(nameUnicode))
+ {
+ continue;
+ }
+ if (numFolders == 1)
+ {
+ // We got here because the preferred trash folder does not
+ // exist, but a folder got discovered to be the trash folder.
+ SetUnicharValue(PREF_TRASH_FOLDER_NAME, nameUnicode);
+ continue;
+ }
+ }
+ trashFolder->ClearFlag(nsMsgFolderFlags::Trash);
+ }
+ }
+ }
+ }
+ }
+
+ bool usingSubscription = true;
+ GetUsingSubscription(&usingSubscription);
+
+ nsCOMArray<nsIMsgImapMailFolder> unverifiedFolders;
+ GetUnverifiedFolders(unverifiedFolders);
+
+ int32_t count = unverifiedFolders.Count();
+ for (int32_t k = 0; k < count; ++k)
+ {
+ bool explicitlyVerify = false;
+ bool hasSubFolders = false;
+ uint32_t folderFlags;
+ nsCOMPtr<nsIMsgImapMailFolder> currentImapFolder(unverifiedFolders[k]);
+ nsCOMPtr<nsIMsgFolder> currentFolder(do_QueryInterface(currentImapFolder, &rv));
+ if (NS_FAILED(rv))
+ continue;
+
+ currentFolder->GetFlags(&folderFlags);
+ if (folderFlags & nsMsgFolderFlags::Virtual) // don't remove virtual folders
+ continue;
+
+ if ((!usingSubscription ||
+ (NS_SUCCEEDED(currentImapFolder->GetExplicitlyVerify(&explicitlyVerify)) &&
+ explicitlyVerify)) ||
+ ((NS_SUCCEEDED(currentFolder->GetHasSubFolders(&hasSubFolders)) &&
+ hasSubFolders) &&
+ !NoDescendentsAreVerified(currentFolder)))
+ {
+ bool isNamespace;
+ currentImapFolder->GetIsNamespace(&isNamespace);
+ if (!isNamespace) // don't list namespaces explicitly
+ {
+ // If there are no subfolders and this is unverified, we don't want to
+ // run this url. That is, we want to undiscover the folder.
+ // If there are subfolders and no descendants are verified, we want to
+ // undiscover all of the folders.
+ // Only if there are subfolders and at least one of them is verified
+ // do we want to refresh that folder's flags, because it won't be going
+ // away.
+ currentImapFolder->SetExplicitlyVerify(false);
+ currentImapFolder->List();
+ }
+ }
+ else
+ DeleteNonVerifiedFolders(currentFolder);
+ }
+
+ return rv;
+}
+
+// Check if the special folder corresponding to the uri exists. If not, check
+// if there already exists a folder with the special folder flag (the server may
+// have told us about a folder to use through XLIST). If so, return the uri of
+// the existing special folder. If not, set the special flag on the folder so
+// it will be there if and when the folder is created.
+// Return true if we found an existing special folder different than
+// the one specified in prefs, and the one specified by prefs doesn't exist.
+bool nsImapIncomingServer::CheckSpecialFolder(nsIRDFService *rdf,
+ nsCString &folderUri,
+ uint32_t folderFlag,
+ nsCString &existingUri)
+{
+ nsCOMPtr<nsIRDFResource> res;
+ nsCOMPtr<nsIMsgFolder> folder;
+ nsCOMPtr<nsIMsgFolder> rootMsgFolder;
+ nsresult rv = GetRootFolder(getter_AddRefs(rootMsgFolder));
+ NS_ENSURE_SUCCESS(rv, false);
+ nsCOMPtr<nsIMsgFolder> existingFolder;
+ rootMsgFolder->GetFolderWithFlags(folderFlag, getter_AddRefs(existingFolder));
+
+ if (!folderUri.IsEmpty() && NS_SUCCEEDED(rdf->GetResource(folderUri, getter_AddRefs(res))))
+ {
+ folder = do_QueryInterface(res, &rv);
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsIMsgFolder> parent;
+ folder->GetParent(getter_AddRefs(parent));
+ if (parent)
+ {
+ existingFolder = nullptr;
+ }
+ if (!existingFolder)
+ {
+ folder->SetFlag(folderFlag);
+ }
+
+ nsString folderName;
+ folder->GetPrettyName(folderName);
+ // this will set the localized name based on the folder flag.
+ folder->SetPrettyName(folderName);
+ }
+ }
+
+ if (existingFolder)
+ {
+ existingFolder->GetURI(existingUri);
+ return true;
+ }
+
+ return false;
+}
+
+nsresult nsImapIncomingServer::DeleteNonVerifiedFolders(nsIMsgFolder *curFolder)
+{
+ bool autoUnsubscribeFromNoSelectFolders = true;
+ nsresult rv;
+ nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv))
+ prefBranch->GetBoolPref("mail.imap.auto_unsubscribe_from_noselect_folders", &autoUnsubscribeFromNoSelectFolders);
+
+ nsCOMPtr<nsISimpleEnumerator> subFolders;
+
+ rv = curFolder->GetSubFolders(getter_AddRefs(subFolders));
+ if(NS_SUCCEEDED(rv))
+ {
+ bool moreFolders;
+
+ while (NS_SUCCEEDED(subFolders->HasMoreElements(&moreFolders)) && moreFolders)
+ {
+ nsCOMPtr<nsISupports> child;
+ rv = subFolders->GetNext(getter_AddRefs(child));
+ if (NS_SUCCEEDED(rv) && child)
+ {
+ bool childVerified = false;
+ nsCOMPtr <nsIMsgImapMailFolder> childImapFolder = do_QueryInterface(child, &rv);
+ if (NS_SUCCEEDED(rv) && childImapFolder)
+ {
+ uint32_t flags;
+
+ nsCOMPtr <nsIMsgFolder> childFolder = do_QueryInterface(child, &rv);
+ rv = childImapFolder->GetVerifiedAsOnlineFolder(&childVerified);
+
+ rv = childFolder->GetFlags(&flags);
+ bool folderIsNoSelectFolder = NS_SUCCEEDED(rv) && ((flags & nsMsgFolderFlags::ImapNoselect) != 0);
+
+ bool usingSubscription = true;
+ GetUsingSubscription(&usingSubscription);
+ if (usingSubscription)
+ {
+ bool folderIsNameSpace = false;
+ bool noDescendentsAreVerified = NoDescendentsAreVerified(childFolder);
+ bool shouldDieBecauseNoSelect = (folderIsNoSelectFolder ?
+ ((noDescendentsAreVerified || AllDescendentsAreNoSelect(childFolder)) && !folderIsNameSpace)
+ : false);
+ if (!childVerified && (noDescendentsAreVerified || shouldDieBecauseNoSelect))
+ {
+ }
+ }
+ else
+ {
+ }
+ }
+ }
+ }
+ }
+
+ nsCOMPtr<nsIMsgFolder> parent;
+ rv = curFolder->GetParent(getter_AddRefs(parent));
+
+ if (NS_SUCCEEDED(rv) && parent)
+ {
+ nsCOMPtr<nsIMsgImapMailFolder> imapParent = do_QueryInterface(parent);
+ if (imapParent)
+ imapParent->RemoveSubFolder(curFolder);
+ }
+
+ return rv;
+}
+
+bool nsImapIncomingServer::NoDescendentsAreVerified(nsIMsgFolder *parentFolder)
+{
+ bool nobodyIsVerified = true;
+ nsCOMPtr<nsISimpleEnumerator> subFolders;
+ nsresult rv = parentFolder->GetSubFolders(getter_AddRefs(subFolders));
+ if(NS_SUCCEEDED(rv))
+ {
+ bool moreFolders;
+ while (NS_SUCCEEDED(subFolders->HasMoreElements(&moreFolders)) &&
+ moreFolders && nobodyIsVerified)
+ {
+ nsCOMPtr<nsISupports> child;
+ rv = subFolders->GetNext(getter_AddRefs(child));
+ if (NS_SUCCEEDED(rv) && child)
+ {
+ bool childVerified = false;
+ nsCOMPtr <nsIMsgImapMailFolder> childImapFolder = do_QueryInterface(child, &rv);
+ if (NS_SUCCEEDED(rv) && childImapFolder)
+ {
+ nsCOMPtr <nsIMsgFolder> childFolder = do_QueryInterface(child, &rv);
+ rv = childImapFolder->GetVerifiedAsOnlineFolder(&childVerified);
+ nobodyIsVerified = !childVerified && NoDescendentsAreVerified(childFolder);
+ }
+ }
+ }
+ }
+ return nobodyIsVerified;
+}
+
+
+bool nsImapIncomingServer::AllDescendentsAreNoSelect(nsIMsgFolder *parentFolder)
+{
+ bool allDescendentsAreNoSelect = true;
+ nsCOMPtr<nsISimpleEnumerator> subFolders;
+ nsresult rv = parentFolder->GetSubFolders(getter_AddRefs(subFolders));
+ if(NS_SUCCEEDED(rv))
+ {
+ bool moreFolders;
+ while (NS_SUCCEEDED(subFolders->HasMoreElements(&moreFolders)) &&
+ moreFolders && allDescendentsAreNoSelect)
+ {
+ nsCOMPtr<nsISupports> child;
+ rv = subFolders->GetNext(getter_AddRefs(child));
+ if (NS_SUCCEEDED(rv) && child)
+ {
+ bool childIsNoSelect = false;
+ nsCOMPtr <nsIMsgImapMailFolder> childImapFolder = do_QueryInterface(child, &rv);
+ if (NS_SUCCEEDED(rv) && childImapFolder)
+ {
+ uint32_t flags;
+ nsCOMPtr <nsIMsgFolder> childFolder = do_QueryInterface(child, &rv);
+ rv = childFolder->GetFlags(&flags);
+ childIsNoSelect = NS_SUCCEEDED(rv) && (flags & nsMsgFolderFlags::ImapNoselect);
+ allDescendentsAreNoSelect = !childIsNoSelect && AllDescendentsAreNoSelect(childFolder);
+ }
+ }
+ }
+ }
+#if 0
+ int numberOfSubfolders = parentFolder->GetNumSubFolders();
+
+ for (int childIndex=0; allDescendantsAreNoSelect && (childIndex < numberOfSubfolders); childIndex++)
+ {
+ MSG_IMAPFolderInfoMail *currentChild = (MSG_IMAPFolderInfoMail *) parentFolder->GetSubFolder(childIndex);
+ allDescendentsAreNoSelect = (currentChild->GetFolderPrefFlags() & MSG_FOLDER_PREF_IMAPNOSELECT) &&
+ AllDescendentsAreNoSelect(currentChild);
+ }
+#endif // 0
+ return allDescendentsAreNoSelect;
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::PromptLoginFailed(nsIMsgWindow *aMsgWindow,
+ int32_t *aResult)
+{
+ nsAutoCString hostName;
+ GetRealHostName(hostName);
+
+ return MsgPromptLoginFailed(aMsgWindow, hostName, aResult);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::FEAlert(const nsAString& aAlertString,
+ nsIMsgMailNewsUrl *aUrl)
+{
+ GetStringBundle();
+
+ if (m_stringBundle)
+ {
+ nsAutoString hostName;
+ nsresult rv = GetPrettyName(hostName);
+ if (NS_SUCCEEDED(rv))
+ {
+ nsString message;
+ nsString tempString(aAlertString);
+ const char16_t *params[] = { hostName.get(), tempString.get() };
+
+ rv = m_stringBundle->FormatStringFromName(
+ u"imapServerAlert",
+ params, 2, getter_Copies(message));
+ if (NS_SUCCEEDED(rv))
+ return AlertUser(message, aUrl);
+ }
+ }
+ return AlertUser(aAlertString, aUrl);
+}
+
+nsresult nsImapIncomingServer::AlertUser(const nsAString& aString,
+ nsIMsgMailNewsUrl *aUrl)
+{
+ nsresult rv;
+ nsCOMPtr <nsIMsgMailSession> mailSession =
+ do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return mailSession->AlertUser(aString, aUrl);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::FEAlertWithName(const char* aMsgName, nsIMsgMailNewsUrl *aUrl)
+{
+ // don't bother the user if we're shutting down.
+ if (m_shuttingDown)
+ return NS_OK;
+
+ GetStringBundle();
+
+ nsString message;
+
+ if (m_stringBundle)
+ {
+ nsAutoCString hostName;
+ nsresult rv = GetHostName(hostName);
+ if (NS_SUCCEEDED(rv))
+ {
+ const NS_ConvertUTF8toUTF16 hostName16(hostName);
+ const char16_t *params[] = { hostName16.get() };
+ rv = m_stringBundle->FormatStringFromName(
+ NS_ConvertASCIItoUTF16(aMsgName).get(),
+ params, 1,getter_Copies(message));
+ if (NS_SUCCEEDED(rv))
+ return AlertUser(message, aUrl);
+ }
+ }
+
+ // Error condition
+ message.AssignLiteral("String Name ");
+ message.AppendASCII(aMsgName);
+ FEAlert(message, aUrl);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsImapIncomingServer::FEAlertFromServer(const nsACString& aServerString,
+ nsIMsgMailNewsUrl *aUrl)
+{
+ NS_ENSURE_TRUE(!aServerString.IsEmpty(), NS_OK);
+
+ nsCString message(aServerString);
+ message.Trim(" \t\b\r\n");
+ if (message.Last() != '.')
+ message.Append('.');
+
+ // Skip over the first two words (the command tag and "NO").
+ // Find the first word break.
+ int32_t pos = message.FindChar(' ');
+
+ // Find the second word break.
+ if (pos != -1)
+ pos = message.FindChar(' ', pos + 1);
+
+ // Adjust the message.
+ if (pos != -1)
+ message = Substring(message, pos + 1);
+
+ nsString hostName;
+ GetPrettyName(hostName);
+
+ const char16_t *formatStrings[] =
+ {
+ hostName.get(),
+ nullptr,
+ nullptr
+ };
+
+ nsString msgName;
+ int32_t numStrings;
+ nsString fullMessage;
+ nsCOMPtr<nsIImapUrl> imapUrl = do_QueryInterface(aUrl);
+ NS_ENSURE_TRUE(imapUrl, NS_ERROR_INVALID_ARG);
+
+ nsImapState imapState;
+ nsImapAction imapAction;
+
+ imapUrl->GetRequiredImapState(&imapState);
+ imapUrl->GetImapAction(&imapAction);
+ nsString folderName;
+
+ NS_ConvertUTF8toUTF16 unicodeMsg(message);
+
+ nsCOMPtr<nsIMsgFolder> folder;
+ if (imapState == nsIImapUrl::nsImapSelectedState ||
+ imapAction == nsIImapUrl::nsImapFolderStatus)
+ {
+ aUrl->GetFolder(getter_AddRefs(folder));
+ if (folder)
+ folder->GetPrettyName(folderName);
+ numStrings = 3;
+ msgName.AssignLiteral("imapFolderCommandFailed");
+ formatStrings[1] = folderName.get();
+ }
+ else
+ {
+ msgName.AssignLiteral("imapServerCommandFailed");
+ numStrings = 2;
+ }
+
+ formatStrings[numStrings -1] = unicodeMsg.get();
+
+ nsresult rv = GetStringBundle();
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (m_stringBundle)
+ {
+ rv = m_stringBundle->FormatStringFromName(msgName.get(),
+ formatStrings, numStrings, getter_Copies(fullMessage));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return AlertUser(fullMessage, aUrl);
+}
+
+#define IMAP_MSGS_URL "chrome://messenger/locale/imapMsgs.properties"
+
+nsresult nsImapIncomingServer::GetStringBundle()
+{
+ if (m_stringBundle)
+ return NS_OK;
+
+ nsCOMPtr<nsIStringBundleService> sBundleService =
+ mozilla::services::GetStringBundleService();
+ NS_ENSURE_TRUE(sBundleService, NS_ERROR_UNEXPECTED);
+ return sBundleService->CreateBundle(IMAP_MSGS_URL, getter_AddRefs(m_stringBundle));
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::GetImapStringByName(const char* msgName, nsAString& aString)
+{
+ nsresult rv = NS_OK;
+ GetStringBundle();
+ if (m_stringBundle)
+ {
+ nsString res_str;
+ rv = m_stringBundle->GetStringFromName(
+ NS_ConvertASCIItoUTF16(msgName).get(),
+ getter_Copies(res_str));
+ aString.Assign(res_str);
+ if (NS_SUCCEEDED(rv))
+ return rv;
+ }
+ aString.AssignLiteral("String Name ");
+ // mscott: FIX ME
+ aString.AppendASCII(msgName);
+ return NS_OK;
+}
+
+nsresult nsImapIncomingServer::ResetFoldersToUnverified(nsIMsgFolder *parentFolder)
+{
+ nsresult rv = NS_OK;
+ if (!parentFolder)
+ {
+ nsCOMPtr<nsIMsgFolder> rootFolder;
+ rv = GetRootFolder(getter_AddRefs(rootFolder));
+ NS_ENSURE_SUCCESS(rv, rv);
+ return ResetFoldersToUnverified(rootFolder);
+ }
+ else
+ {
+ nsCOMPtr<nsISimpleEnumerator> subFolders;
+ nsCOMPtr<nsIMsgImapMailFolder> imapFolder = do_QueryInterface(parentFolder, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = imapFolder->SetVerifiedAsOnlineFolder(false);
+ rv = parentFolder->GetSubFolders(getter_AddRefs(subFolders));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool moreFolders = false;
+ while (NS_SUCCEEDED(subFolders->HasMoreElements(&moreFolders)) && moreFolders)
+ {
+ nsCOMPtr<nsISupports> child;
+ rv = subFolders->GetNext(getter_AddRefs(child));
+ if (NS_SUCCEEDED(rv) && child)
+ {
+ nsCOMPtr<nsIMsgFolder> childFolder = do_QueryInterface(child, &rv);
+ if (NS_SUCCEEDED(rv) && childFolder)
+ {
+ rv = ResetFoldersToUnverified(childFolder);
+ if (NS_FAILED(rv))
+ break;
+ }
+ }
+ }
+ }
+ return rv;
+}
+
+void
+nsImapIncomingServer::GetUnverifiedFolders(nsCOMArray<nsIMsgImapMailFolder> &aFoldersArray)
+{
+ nsCOMPtr<nsIMsgFolder> rootFolder;
+ if (NS_FAILED(GetRootFolder(getter_AddRefs(rootFolder))) || !rootFolder)
+ return;
+
+ nsCOMPtr<nsIMsgImapMailFolder> imapRoot(do_QueryInterface(rootFolder));
+ // don't need to verify the root.
+ if (imapRoot)
+ imapRoot->SetVerifiedAsOnlineFolder(true);
+
+ GetUnverifiedSubFolders(rootFolder, aFoldersArray);
+}
+
+void
+nsImapIncomingServer::GetUnverifiedSubFolders(nsIMsgFolder *parentFolder,
+ nsCOMArray<nsIMsgImapMailFolder> &aFoldersArray)
+{
+ nsCOMPtr<nsIMsgImapMailFolder> imapFolder(do_QueryInterface(parentFolder));
+
+ bool verified = false, explicitlyVerify = false;
+ if (imapFolder)
+ {
+ nsresult rv = imapFolder->GetVerifiedAsOnlineFolder(&verified);
+ if (NS_SUCCEEDED(rv))
+ rv = imapFolder->GetExplicitlyVerify(&explicitlyVerify);
+
+ if (NS_SUCCEEDED(rv) && (!verified || explicitlyVerify))
+ aFoldersArray.AppendObject(imapFolder);
+ }
+
+ nsCOMPtr<nsISimpleEnumerator> subFolders;
+ if (NS_SUCCEEDED(parentFolder->GetSubFolders(getter_AddRefs(subFolders))))
+ {
+ bool moreFolders;
+
+ while (NS_SUCCEEDED(subFolders->HasMoreElements(&moreFolders)) && moreFolders)
+ {
+ nsCOMPtr<nsISupports> child;
+ subFolders->GetNext(getter_AddRefs(child));
+ if (child)
+ {
+ nsCOMPtr<nsIMsgFolder> childFolder(do_QueryInterface(child));
+ if (childFolder)
+ GetUnverifiedSubFolders(childFolder, aFoldersArray);
+ }
+ }
+ }
+}
+
+NS_IMETHODIMP nsImapIncomingServer::ForgetSessionPassword()
+{
+ nsresult rv = nsMsgIncomingServer::ForgetSessionPassword();
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ // fix for bugscape bug #15485
+ // if we use turbo, and we logout, we need to make sure
+ // the server doesn't think it's authenticated.
+ // the biff timer continues to fire when you use turbo
+ // (see #143848). if we exited, we've set the password to null
+ // but if we're authenticated, and the biff timer goes off
+ // we'll still perform biff, because we use m_userAuthenticated
+ // to determine if we require a password for biff.
+ // (if authenticated, we don't require a password
+ // see nsMsgBiffManager::PerformBiff())
+ // performing biff without a password will pop up the prompt dialog
+ // which is pretty wacky, when it happens after you quit the application
+ m_userAuthenticated = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsImapIncomingServer::GetServerRequiresPasswordForBiff(bool *aServerRequiresPasswordForBiff)
+{
+ NS_ENSURE_ARG_POINTER(aServerRequiresPasswordForBiff);
+ // if the user has already been authenticated, we've got the password
+ *aServerRequiresPasswordForBiff = !m_userAuthenticated;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsImapIncomingServer::ForgetPassword()
+{
+ return nsMsgIncomingServer::ForgetPassword();
+}
+
+
+NS_IMETHODIMP
+nsImapIncomingServer::AsyncGetPassword(nsIImapProtocol *aProtocol,
+ bool aNewPasswordRequested,
+ nsACString &aPassword)
+{
+ if (m_password.IsEmpty())
+ {
+ // We're now going to need to do something that will end up with us either
+ // poking login manager or prompting the user. We need to ensure we only
+ // do one prompt at a time (and login manager could cause a master password
+ // prompt), so we need to use the async prompter.
+ nsresult rv;
+ nsCOMPtr<nsIMsgAsyncPrompter> asyncPrompter =
+ do_GetService(NS_MSGASYNCPROMPTER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIMsgAsyncPromptListener> promptListener(do_QueryInterface(aProtocol));
+ rv = asyncPrompter->QueueAsyncAuthPrompt(m_serverKey, aNewPasswordRequested,
+ promptListener);
+ // Explict NS_ENSURE_SUCCESS for debug purposes as errors tend to get
+ // hidden.
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ if (!m_password.IsEmpty())
+ aPassword = m_password;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::PromptPassword(nsIMsgWindow *aMsgWindow,
+ nsACString &aPassword)
+{
+ nsString passwordTitle;
+ GetImapStringByName("imapEnterPasswordPromptTitle", passwordTitle);
+ NS_ENSURE_STATE(m_stringBundle);
+
+ nsAutoCString userName;
+ GetRealUsername(userName);
+
+ nsAutoCString hostName;
+ GetRealHostName(hostName);
+
+ nsresult rv = GetStringBundle();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ConvertASCIItoUTF16 finalUserName(userName);
+ NS_ConvertASCIItoUTF16 finalHostName(hostName);
+
+ const char16_t *formatStrings[] = { finalUserName.get(), finalHostName.get() };
+
+ nsString passwordText;
+ rv = m_stringBundle->FormatStringFromName(
+ u"imapEnterServerPasswordPrompt",
+ formatStrings, 2, getter_Copies(passwordText));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = GetPasswordWithUI(passwordText, passwordTitle, aMsgWindow, aPassword);
+ if (NS_SUCCEEDED(rv))
+ m_password = aPassword;
+ return rv;
+}
+
+// for the nsIImapServerSink interface
+NS_IMETHODIMP nsImapIncomingServer::SetCapability(eIMAPCapabilityFlags capability)
+{
+ m_capability = capability;
+ SetIsGMailServer((capability & kGmailImapCapability) != 0);
+ SetCapabilityACL(capability & kACLCapability);
+ SetCapabilityQuota(capability & kQuotaCapability);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsImapIncomingServer::SetServerID(const nsACString &aServerID)
+{
+ return SetServerIDPref(aServerID);
+}
+
+NS_IMETHODIMP nsImapIncomingServer::CommitNamespaces()
+{
+ nsresult rv;
+ nsCOMPtr<nsIImapHostSessionList> hostSession = do_GetService(kCImapHostSessionListCID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return hostSession->CommitNamespacesForHost(this);
+}
+
+NS_IMETHODIMP nsImapIncomingServer::PseudoInterruptMsgLoad(nsIMsgFolder *aImapFolder, nsIMsgWindow *aMsgWindow,
+ bool *interrupted)
+{
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIImapProtocol> connection;
+ PR_CEnterMonitor(this);
+ // iterate through the connection cache for a connection that is loading
+ // a message in this folder and should be pseudo-interrupted.
+ int32_t cnt = m_connectionCache.Count();
+
+ for (int32_t i = 0; i < cnt; ++i)
+ {
+ connection = m_connectionCache[i];
+ if (connection)
+ rv = connection->PseudoInterruptMsgLoad(aImapFolder, aMsgWindow, interrupted);
+ }
+
+ PR_CExitMonitor(this);
+ return rv;
+}
+
+NS_IMETHODIMP nsImapIncomingServer::ResetNamespaceReferences()
+{
+ nsCOMPtr <nsIMsgFolder> rootFolder;
+ nsresult rv = GetRootFolder(getter_AddRefs(rootFolder));
+ if (NS_SUCCEEDED(rv) && rootFolder)
+ {
+ nsCOMPtr <nsIMsgImapMailFolder> imapFolder = do_QueryInterface(rootFolder);
+ if (imapFolder)
+ rv = imapFolder->ResetNamespaceReferences();
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsImapIncomingServer::SetUserAuthenticated(bool aUserAuthenticated)
+{
+ m_userAuthenticated = aUserAuthenticated;
+ if (aUserAuthenticated)
+ {
+ nsresult rv;
+ nsCOMPtr<nsIMsgAccountManager> accountManager =
+ do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ accountManager->SetUserNeedsToAuthenticate(false);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsImapIncomingServer::GetUserAuthenticated(bool *aUserAuthenticated)
+{
+ NS_ENSURE_ARG_POINTER(aUserAuthenticated);
+ *aUserAuthenticated = m_userAuthenticated;
+ return NS_OK;
+}
+
+/* void SetMailServerUrls (in string manageMailAccount, in string manageLists, in string manageFilters); */
+NS_IMETHODIMP nsImapIncomingServer::SetMailServerUrls(const nsACString& manageMailAccount, const nsACString& manageLists,
+ const nsACString& manageFilters)
+{
+ return SetManageMailAccountUrl(manageMailAccount);
+}
+
+NS_IMETHODIMP nsImapIncomingServer::SetManageMailAccountUrl(const nsACString& manageMailAccountUrl)
+{
+ m_manageMailAccountUrl = manageMailAccountUrl;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsImapIncomingServer::GetManageMailAccountUrl(nsACString& manageMailAccountUrl)
+{
+ manageMailAccountUrl = m_manageMailAccountUrl;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::StartPopulatingWithUri(nsIMsgWindow *aMsgWindow, bool aForceToServer /*ignored*/, const char *uri)
+{
+ NS_ENSURE_ARG_POINTER (uri);
+
+ nsresult rv;
+ mDoingSubscribeDialog = true;
+
+ rv = EnsureInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+ rv = mInner->StartPopulatingWithUri(aMsgWindow, aForceToServer, uri);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ // imap always uses the canonical delimiter form of paths for subscribe ui.
+ rv = SetDelimiter('/');
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = SetShowFullName(false);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCString serverUri;
+ rv = GetServerURI(serverUri);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+/*
+ if uri = imap://user@host/foo/bar, the serverUri is imap://user@host
+ to get path from uri, skip over imap://user@host + 1 (for the /)
+*/
+ const char *path = uri + serverUri.Length() + 1;
+ return imapService->GetListOfFoldersWithPath(this, aMsgWindow, nsDependentCString(path));
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::StartPopulating(nsIMsgWindow *aMsgWindow, bool aForceToServer /*ignored*/, bool aGetOnlyNew)
+{
+ nsresult rv;
+ mDoingSubscribeDialog = true;
+
+ rv = EnsureInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+ rv = mInner->StartPopulating(aMsgWindow, aForceToServer, aGetOnlyNew);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ // imap always uses the canonical delimiter form of paths for subscribe ui.
+ rv = SetDelimiter('/');
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = SetShowFullName(false);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+ return imapService->GetListOfFoldersOnServer(this, aMsgWindow);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::OnStartRunningUrl(nsIURI *url)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::OnStopRunningUrl(nsIURI *url, nsresult exitCode)
+{
+ nsresult rv = exitCode;
+
+ // xxx todo get msgWindow from url
+ nsCOMPtr<nsIMsgWindow> msgWindow;
+ nsCOMPtr<nsIImapUrl> imapUrl = do_QueryInterface(url);
+ if (imapUrl) {
+ nsImapAction imapAction = nsIImapUrl::nsImapTest;
+ imapUrl->GetImapAction(&imapAction);
+ switch (imapAction) {
+ case nsIImapUrl::nsImapDiscoverAllAndSubscribedBoxesUrl:
+ case nsIImapUrl::nsImapDiscoverChildrenUrl:
+ rv = UpdateSubscribed();
+ NS_ENSURE_SUCCESS(rv, rv);
+ mDoingSubscribeDialog = false;
+ rv = StopPopulating(msgWindow);
+ NS_ENSURE_SUCCESS(rv, rv);
+ break;
+ case nsIImapUrl::nsImapDiscoverAllBoxesUrl:
+ if (NS_SUCCEEDED(exitCode))
+ DiscoveryDone();
+ break;
+ case nsIImapUrl::nsImapFolderStatus:
+ {
+ nsCOMPtr<nsIMsgFolder> msgFolder;
+ nsCOMPtr<nsIMsgMailNewsUrl> mailUrl = do_QueryInterface(imapUrl);
+ mailUrl->GetFolder(getter_AddRefs(msgFolder));
+ if (msgFolder)
+ {
+ nsresult rv;
+ nsCOMPtr<nsIMsgMailSession> session =
+ do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ bool folderOpen;
+ rv = session->IsFolderOpenInWindow(msgFolder, &folderOpen);
+ if (NS_SUCCEEDED(rv) && !folderOpen && msgFolder)
+ msgFolder->SetMsgDatabase(nullptr);
+ nsCOMPtr<nsIMsgImapMailFolder> imapFolder = do_QueryInterface(msgFolder);
+ m_foldersToStat.RemoveObject(imapFolder);
+ }
+ // if we get an error running the url, it's better
+ // not to chain the next url.
+ if (NS_FAILED(exitCode) && exitCode != NS_MSG_ERROR_IMAP_COMMAND_FAILED)
+ m_foldersToStat.Clear();
+ if (m_foldersToStat.Count() > 0)
+ m_foldersToStat[0]->UpdateStatus(this, nullptr);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::SetIncomingServer(nsIMsgIncomingServer *aServer)
+{
+ nsresult rv = EnsureInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+ return mInner->SetIncomingServer(aServer);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::SetShowFullName(bool showFullName)
+{
+ nsresult rv = EnsureInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+ return mInner->SetShowFullName(showFullName);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::GetDelimiter(char *aDelimiter)
+{
+ nsresult rv = EnsureInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+ return mInner->GetDelimiter(aDelimiter);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::SetDelimiter(char aDelimiter)
+{
+ nsresult rv = EnsureInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+ return mInner->SetDelimiter(aDelimiter);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::SetAsSubscribed(const nsACString &path)
+{
+ nsresult rv = EnsureInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+ return mInner->SetAsSubscribed(path);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::UpdateSubscribed()
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::AddTo(const nsACString &aName, bool addAsSubscribed,
+ bool aSubscribable, bool changeIfExists)
+{
+ nsresult rv = EnsureInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ // RFC 3501 allows UTF-8 in addition to modified UTF-7
+ // If it's not UTF-8, it cannot be MUTF7, either. We just ignore it.
+ // (otherwise we'll crash. see #63186)
+ if (!MsgIsUTF8(aName))
+ return NS_OK;
+
+ if (!NS_IsAscii(aName.BeginReading(), aName.Length())) {
+ nsAutoCString name;
+ CopyUTF16toMUTF7(NS_ConvertUTF8toUTF16(aName), name);
+ return mInner->AddTo(name, addAsSubscribed, aSubscribable, changeIfExists);
+ }
+ return mInner->AddTo(aName, addAsSubscribed, aSubscribable, changeIfExists);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::StopPopulating(nsIMsgWindow *aMsgWindow)
+{
+ nsCOMPtr<nsISubscribeListener> listener;
+ (void) GetSubscribeListener(getter_AddRefs(listener));
+
+ if (listener)
+ listener->OnDonePopulating();
+
+ nsresult rv = EnsureInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+ return mInner->StopPopulating(aMsgWindow);
+}
+
+
+NS_IMETHODIMP
+nsImapIncomingServer::SubscribeCleanup()
+{
+ m_subscribeFolders.Clear();
+ return ClearInner();
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::SetSubscribeListener(nsISubscribeListener *aListener)
+{
+ nsresult rv = EnsureInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+ return mInner->SetSubscribeListener(aListener);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::GetSubscribeListener(nsISubscribeListener **aListener)
+{
+ nsresult rv = EnsureInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+ return mInner->GetSubscribeListener(aListener);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::Subscribe(const char16_t *aName)
+{
+ NS_ENSURE_ARG_POINTER(aName);
+
+ return SubscribeToFolder(nsDependentString(aName), true, nullptr);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::Unsubscribe(const char16_t *aName)
+{
+ NS_ENSURE_ARG_POINTER(aName);
+
+ return SubscribeToFolder(nsDependentString(aName), false, nullptr);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::SubscribeToFolder(const nsAString& aName, bool subscribe, nsIURI **aUri)
+{
+ nsresult rv;
+ nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgFolder> rootMsgFolder;
+ rv = GetRootFolder(getter_AddRefs(rootMsgFolder));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // 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.
+
+ // aName is not a genuine UTF-16 but just a zero-padded modified UTF-7
+ NS_LossyConvertUTF16toASCII folderCName(aName);
+ nsCOMPtr<nsIMsgFolder> msgFolder;
+ if (rootMsgFolder && !aName.IsEmpty())
+ rv = rootMsgFolder->FindSubFolder(folderCName, getter_AddRefs(msgFolder));
+
+ nsCOMPtr<nsIThread> thread(do_GetCurrentThread());
+
+ nsAutoString unicodeName;
+ rv = CopyMUTF7toUTF16(folderCName, unicodeName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (subscribe)
+ rv = imapService->SubscribeFolder(msgFolder, unicodeName, nullptr, aUri);
+ else
+ rv = imapService->UnsubscribeFolder(msgFolder, unicodeName, nullptr, nullptr);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::SetDoingLsub(bool doingLsub)
+{
+ mDoingLsub = doingLsub;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::GetDoingLsub(bool *doingLsub)
+{
+ NS_ENSURE_ARG_POINTER(doingLsub);
+ *doingLsub = mDoingLsub;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::ReDiscoverAllFolders()
+{
+ return PerformExpand(nullptr);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::SetState(const nsACString &path, bool state,
+ bool *stateChanged)
+{
+ nsresult rv = EnsureInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+ return mInner->SetState(path, state, stateChanged);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::HasChildren(const nsACString &path, bool *aHasChildren)
+{
+ nsresult rv = EnsureInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+ return mInner->HasChildren(path, aHasChildren);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::IsSubscribed(const nsACString &path,
+ bool *aIsSubscribed)
+{
+ nsresult rv = EnsureInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+ return mInner->IsSubscribed(path, aIsSubscribed);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::IsSubscribable(const nsACString &path, bool *aIsSubscribable)
+{
+ nsresult rv = EnsureInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+ return mInner->IsSubscribable(path, aIsSubscribable);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::GetLeafName(const nsACString &path, nsAString &aLeafName)
+{
+ nsresult rv = EnsureInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+ return mInner->GetLeafName(path, aLeafName);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::GetFirstChildURI(const nsACString &path, nsACString &aResult)
+{
+ nsresult rv = EnsureInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+ return mInner->GetFirstChildURI(path, aResult);
+}
+
+
+NS_IMETHODIMP
+nsImapIncomingServer::GetChildren(const nsACString &aPath,
+ nsISimpleEnumerator **aResult)
+{
+ nsresult rv = EnsureInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+ return mInner->GetChildren(aPath, aResult);
+}
+
+nsresult
+nsImapIncomingServer::EnsureInner()
+{
+ nsresult rv = NS_OK;
+
+ if (mInner)
+ return NS_OK;
+
+ mInner = do_CreateInstance(kSubscribableServerCID,&rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+ return SetIncomingServer(this);
+}
+
+nsresult
+nsImapIncomingServer::ClearInner()
+{
+ nsresult rv = NS_OK;
+ if (mInner)
+ {
+ rv = mInner->SetSubscribeListener(nullptr);
+ NS_ENSURE_SUCCESS(rv,rv);
+ rv = mInner->SetIncomingServer(nullptr);
+ NS_ENSURE_SUCCESS(rv,rv);
+ mInner = nullptr;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::CommitSubscribeChanges()
+{
+ return ReDiscoverAllFolders();
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::GetCanBeDefaultServer(bool *canBeDefaultServer)
+{
+ NS_ENSURE_ARG_POINTER(canBeDefaultServer);
+ *canBeDefaultServer = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::GetCanCompactFoldersOnServer(bool *canCompactFoldersOnServer)
+{
+ NS_ENSURE_ARG_POINTER(canCompactFoldersOnServer);
+ // Initialize canCompactFoldersOnServer true, a default value for IMAP
+ *canCompactFoldersOnServer = true;
+ GetPrefForServerAttribute("canCompactFoldersOnServer", canCompactFoldersOnServer);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::GetCanUndoDeleteOnServer(bool *canUndoDeleteOnServer)
+{
+ NS_ENSURE_ARG_POINTER(canUndoDeleteOnServer);
+ // Initialize canUndoDeleteOnServer true, a default value for IMAP
+ *canUndoDeleteOnServer = true;
+ GetPrefForServerAttribute("canUndoDeleteOnServer", canUndoDeleteOnServer);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::GetCanSearchMessages(bool *canSearchMessages)
+{
+ NS_ENSURE_ARG_POINTER(canSearchMessages);
+ // Initialize canSearchMessages true, a default value for IMAP
+ *canSearchMessages = true;
+ GetPrefForServerAttribute("canSearchMessages", canSearchMessages);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::GetCanEmptyTrashOnExit(bool *canEmptyTrashOnExit)
+{
+ NS_ENSURE_ARG_POINTER(canEmptyTrashOnExit);
+ // Initialize canEmptyTrashOnExit true, a default value for IMAP
+ *canEmptyTrashOnExit = true;
+ GetPrefForServerAttribute("canEmptyTrashOnExit", canEmptyTrashOnExit);
+ return NS_OK;
+}
+
+nsresult
+nsImapIncomingServer::CreateHostSpecificPrefName(const char *prefPrefix, nsAutoCString &prefName)
+{
+ NS_ENSURE_ARG_POINTER(prefPrefix);
+
+ nsCString hostName;
+ nsresult rv = GetHostName(hostName);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ prefName = prefPrefix;
+ prefName.Append('.');
+ prefName.Append(hostName);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::GetSupportsDiskSpace(bool *aSupportsDiskSpace)
+{
+ NS_ENSURE_ARG_POINTER(aSupportsDiskSpace);
+ nsAutoCString prefName;
+ nsresult rv = CreateHostSpecificPrefName("default_supports_diskspace", prefName);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv))
+ rv = prefBranch->GetBoolPref(prefName.get(), aSupportsDiskSpace);
+
+ // Couldn't get the default value with the hostname.
+ // Fall back on IMAP default value
+ if (NS_FAILED(rv)) // set default value
+ *aSupportsDiskSpace = true;
+ return NS_OK;
+}
+
+// count number of non-busy connections in cache
+NS_IMETHODIMP
+nsImapIncomingServer::GetNumIdleConnections(int32_t *aNumIdleConnections)
+{
+ NS_ENSURE_ARG_POINTER(aNumIdleConnections);
+ *aNumIdleConnections = 0;
+
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIImapProtocol> connection;
+ bool isBusy = false;
+ bool isInboxConnection;
+ PR_CEnterMonitor(this);
+
+ int32_t cnt = m_connectionCache.Count();
+
+ // loop counting idle connections
+ for (int32_t i = 0; i < cnt; ++i)
+ {
+ connection = m_connectionCache[i];
+ if (connection)
+ {
+ rv = connection->IsBusy(&isBusy, &isInboxConnection);
+ if (NS_FAILED(rv))
+ continue;
+ if (!isBusy)
+ (*aNumIdleConnections)++;
+ }
+ }
+ PR_CExitMonitor(this);
+ return rv;
+}
+
+
+/**
+ * Get the preference that tells us whether the imap server in question allows
+ * us to create subfolders. Some ISPs might not want users to create any folders
+ * besides the existing ones.
+ * We do want to identify all those servers that don't allow creation of subfolders
+ * and take them out of the account picker in the Copies and Folder panel.
+ */
+NS_IMETHODIMP
+nsImapIncomingServer::GetCanCreateFoldersOnServer(bool *aCanCreateFoldersOnServer)
+{
+ NS_ENSURE_ARG_POINTER(aCanCreateFoldersOnServer);
+ // Initialize aCanCreateFoldersOnServer true, a default value for IMAP
+ *aCanCreateFoldersOnServer = true;
+ GetPrefForServerAttribute("canCreateFolders", aCanCreateFoldersOnServer);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::GetOfflineSupportLevel(int32_t *aSupportLevel)
+{
+ NS_ENSURE_ARG_POINTER(aSupportLevel);
+ nsresult rv = NS_OK;
+
+ rv = GetIntValue("offline_support_level", aSupportLevel);
+ if (*aSupportLevel != OFFLINE_SUPPORT_LEVEL_UNDEFINED)
+ return rv;
+
+ nsAutoCString prefName;
+ rv = CreateHostSpecificPrefName("default_offline_support_level", prefName);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv))
+ rv = prefBranch->GetIntPref(prefName.get(), aSupportLevel);
+
+ // Couldn't get the pref value with the hostname.
+ // Fall back on IMAP default value
+ if (NS_FAILED(rv)) // set default value
+ *aSupportLevel = OFFLINE_SUPPORT_LEVEL_REGULAR;
+ return NS_OK;
+}
+
+// Called only during the migration process. This routine enables the generation of
+// unique account name based on the username, hostname and the port. If the port
+// is valid and not a default one, it will be appended to the account name.
+NS_IMETHODIMP
+nsImapIncomingServer::GeneratePrettyNameForMigration(nsAString& aPrettyName)
+{
+ nsCString userName;
+ nsCString hostName;
+
+/**
+ * Pretty name for migrated account is of format username@hostname:<port>,
+ * provided the port is valid and not the default
+*/
+ // Get user name to construct pretty name
+ nsresult rv = GetUsername(userName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Get host name to construct pretty name
+ rv = GetHostName(hostName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ int32_t defaultServerPort;
+ int32_t defaultSecureServerPort;
+
+ // Here, the final contract ID is already known, so use it directly for efficiency.
+ nsCOMPtr <nsIMsgProtocolInfo> protocolInfo = do_GetService(NS_IMAPPROTOCOLINFO_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ // Get the default port
+ rv = protocolInfo->GetDefaultServerPort(false, &defaultServerPort);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ // Get the default secure port
+ rv = protocolInfo->GetDefaultServerPort(true, &defaultSecureServerPort);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ // Get the current server port
+ int32_t serverPort = PORT_NOT_SET;
+ rv = GetPort(&serverPort);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ // Is the server secure ?
+ int32_t socketType;
+ rv = GetSocketType(&socketType);
+ NS_ENSURE_SUCCESS(rv,rv);
+ bool isSecure = (socketType == nsMsgSocketType::SSL);
+
+ // Is server port a default port ?
+ bool isItDefaultPort = false;
+ if (((serverPort == defaultServerPort) && !isSecure)||
+ ((serverPort == defaultSecureServerPort) && isSecure))
+ isItDefaultPort = true;
+
+ // Construct pretty name from username and hostname
+ nsAutoString constructedPrettyName;
+ CopyASCIItoUTF16(userName,constructedPrettyName);
+ constructedPrettyName.Append('@');
+ constructedPrettyName.Append(NS_ConvertASCIItoUTF16(hostName));
+
+ // If the port is valid and not default, add port value to the pretty name
+ if ((serverPort > 0) && (!isItDefaultPort)) {
+ constructedPrettyName.Append(':');
+ constructedPrettyName.AppendInt(serverPort);
+ }
+
+ // Format the pretty name
+ return GetFormattedStringFromName(constructedPrettyName,
+ "imapDefaultAccountName",
+ aPrettyName);
+}
+
+nsresult
+nsImapIncomingServer::GetFormattedStringFromName(const nsAString& aValue,
+ const char* aName,
+ nsAString& aResult)
+{
+ nsresult rv = GetStringBundle();
+ if (m_stringBundle)
+ {
+ nsString tmpVal (aValue);
+ const char16_t *formatStrings[] = { tmpVal.get() };
+
+ nsString result;
+ rv = m_stringBundle->FormatStringFromName(
+ NS_ConvertASCIItoUTF16(aName).get(),
+ formatStrings, 1, getter_Copies(result));
+ aResult.Assign(result);
+ }
+ return rv;
+}
+
+nsresult
+nsImapIncomingServer::GetPrefForServerAttribute(const char *prefSuffix, bool *prefValue)
+{
+ // Any caller of this function must initialize prefValue with a default value
+ // as this code will not set prefValue when the pref does not exist and return
+ // NS_OK anyway
+
+ if (!mPrefBranch)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ NS_ENSURE_ARG_POINTER(prefValue);
+
+ if (NS_FAILED(mPrefBranch->GetBoolPref(prefSuffix, prefValue)))
+ mDefPrefBranch->GetBoolPref(prefSuffix, prefValue);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::GetCanFileMessagesOnServer(bool *aCanFileMessagesOnServer)
+{
+ NS_ENSURE_ARG_POINTER(aCanFileMessagesOnServer);
+ // Initialize aCanFileMessagesOnServer true, a default value for IMAP
+ *aCanFileMessagesOnServer = true;
+ GetPrefForServerAttribute("canFileMessages", aCanFileMessagesOnServer);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::SetSearchValue(const nsAString &searchValue)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::GetSupportsSubscribeSearch(bool *retVal)
+{
+ NS_ENSURE_ARG_POINTER(retVal);
+ *retVal = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::GetFilterScope(nsMsgSearchScopeValue *filterScope)
+{
+ NS_ENSURE_ARG_POINTER(filterScope);
+ // If the inbox is enabled for offline use, then use the offline filter
+ // scope, else use the online filter scope.
+ //
+ // XXX We use the same scope for all folders with the same incoming server,
+ // yet it is possible to set the offline flag separately for each folder.
+ // Manual filters could perhaps check the offline status of each folder,
+ // though it's hard to see how to make that work since we only store filters
+ // per server.
+ //
+ nsCOMPtr<nsIMsgFolder> rootMsgFolder;
+ nsresult rv = GetRootMsgFolder(getter_AddRefs(rootMsgFolder));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIMsgFolder> offlineInboxMsgFolder;
+ rv = rootMsgFolder->GetFolderWithFlags(nsMsgFolderFlags::Inbox |
+ nsMsgFolderFlags::Offline,
+ getter_AddRefs(offlineInboxMsgFolder));
+
+ *filterScope = offlineInboxMsgFolder ? nsMsgSearchScope::offlineMailFilter
+ : nsMsgSearchScope::onlineMailFilter;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::GetSearchScope(nsMsgSearchScopeValue *searchScope)
+{
+ NS_ENSURE_ARG_POINTER(searchScope);
+ *searchScope = WeAreOffline() ? nsMsgSearchScope::offlineMail : nsMsgSearchScope::onlineMail;
+ return NS_OK;
+}
+
+// This is a recursive function. It gets new messages for current folder
+// first if it is marked, then calls itself recursively for each subfolder.
+NS_IMETHODIMP
+nsImapIncomingServer::GetNewMessagesForNonInboxFolders(nsIMsgFolder *aFolder,
+ nsIMsgWindow *aWindow,
+ bool forceAllFolders,
+ bool performingBiff)
+{
+ NS_ENSURE_ARG_POINTER(aFolder);
+ static bool gGotStatusPref = false;
+ static bool gUseStatus = false;
+
+ bool isServer;
+ (void) aFolder->GetIsServer(&isServer);
+ // Check this folder for new messages if it is marked to be checked
+ // or if we are forced to check all folders
+ uint32_t flags = 0;
+ aFolder->GetFlags(&flags);
+ nsresult rv;
+ nsCOMPtr<nsIMsgImapMailFolder> imapFolder = do_QueryInterface(aFolder, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ bool canOpen;
+ imapFolder->GetCanOpenFolder(&canOpen);
+ if (canOpen && ((forceAllFolders &&
+ !(flags & (nsMsgFolderFlags::Inbox | nsMsgFolderFlags::Trash |
+ nsMsgFolderFlags::Junk | nsMsgFolderFlags::Virtual))) ||
+ flags & nsMsgFolderFlags::CheckNew))
+ {
+ // Get new messages for this folder.
+ aFolder->SetGettingNewMessages(true);
+ if (performingBiff)
+ imapFolder->SetPerformingBiff(true);
+ bool isOpen = false;
+ nsCOMPtr <nsIMsgMailSession> mailSession = do_GetService(NS_MSGMAILSESSION_CONTRACTID);
+ if (mailSession && aFolder)
+ mailSession->IsFolderOpenInWindow(aFolder, &isOpen);
+ // eventually, the gGotStatusPref should go away, once we work out the kinks
+ // from using STATUS.
+ if (!gGotStatusPref)
+ {
+ nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID);
+ if(prefBranch)
+ prefBranch->GetBoolPref("mail.imap.use_status_for_biff", &gUseStatus);
+ gGotStatusPref = true;
+ }
+ if (gUseStatus && !isOpen)
+ {
+ if (!isServer && m_foldersToStat.IndexOf(imapFolder) == -1)
+ m_foldersToStat.AppendObject(imapFolder);
+ }
+ else
+ aFolder->UpdateFolder(aWindow);
+ }
+
+ // Loop through all subfolders to get new messages for them.
+ nsCOMPtr<nsISimpleEnumerator> enumerator;
+ rv = aFolder->GetSubFolders(getter_AddRefs(enumerator));
+ if (NS_FAILED(rv))
+ return rv;
+
+ bool hasMore;
+ while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore)
+ {
+ nsCOMPtr<nsISupports> item;
+ enumerator->GetNext(getter_AddRefs(item));
+
+ nsCOMPtr<nsIMsgFolder> msgFolder(do_QueryInterface(item));
+ if (!msgFolder)
+ {
+ NS_WARNING("Not an nsIMsgFolder");
+ continue;
+ }
+ GetNewMessagesForNonInboxFolders(msgFolder, aWindow, forceAllFolders,
+ performingBiff);
+ }
+ if (isServer && m_foldersToStat.Count() > 0)
+ m_foldersToStat[0]->UpdateStatus(this, nullptr);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::GetArbitraryHeaders(nsACString &aResult)
+{
+ nsCOMPtr <nsIMsgFilterList> filterList;
+ nsresult rv = GetFilterList(nullptr, getter_AddRefs(filterList));
+ NS_ENSURE_SUCCESS(rv,rv);
+ return filterList->GetArbitraryHeaders(aResult);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::GetShowAttachmentsInline(bool *aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = true; // true per default
+ nsresult rv;
+ nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ prefBranch->GetBoolPref("mail.inline_attachments", aResult);
+ return NS_OK; // In case this pref is not set we need to return NS_OK.
+}
+
+NS_IMETHODIMP nsImapIncomingServer::SetSocketType(int32_t aSocketType)
+{
+ int32_t oldSocketType;
+ nsresult rv = GetSocketType(&oldSocketType);
+ if (NS_SUCCEEDED(rv) && oldSocketType != aSocketType)
+ CloseCachedConnections();
+ return nsMsgIncomingServer::SetSocketType(aSocketType);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::OnUserOrHostNameChanged(const nsACString& oldName,
+ const nsACString& newName,
+ bool hostnameChanged)
+{
+ nsresult rv;
+ // 1. Do common things in the base class.
+ rv = nsMsgIncomingServer::OnUserOrHostNameChanged(oldName, newName, hostnameChanged);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ // 2. Reset 'HaveWeEverDiscoveredFolders' flag so the new folder list can be
+ // reloaded (ie, DiscoverMailboxList() will be invoked in nsImapProtocol).
+ nsCOMPtr<nsIImapHostSessionList> hostSessionList = do_GetService(kCImapHostSessionListCID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsAutoCString serverKey;
+ rv = GetKey(serverKey);
+ NS_ENSURE_SUCCESS(rv, rv);
+ hostSessionList->SetHaveWeEverDiscoveredFoldersForHost(serverKey.get(), false);
+ // 3. Make all the existing folders 'unverified' so that they can be
+ // removed from the folder pane after users log into the new server.
+ ResetFoldersToUnverified(nullptr);
+ return NS_OK;
+}
+
+// use canonical format in originalUri & convertedUri
+NS_IMETHODIMP
+nsImapIncomingServer::GetUriWithNamespacePrefixIfNecessary(int32_t namespaceType,
+ const nsACString& originalUri,
+ nsACString& convertedUri)
+{
+ nsresult rv = NS_OK;
+ nsAutoCString serverKey;
+ rv = GetKey(serverKey);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIImapHostSessionList> hostSessionList = do_GetService(kCImapHostSessionListCID, &rv);
+ nsIMAPNamespace *ns = nullptr;
+ rv = hostSessionList->GetDefaultNamespaceOfTypeForHost(serverKey.get(), (EIMAPNamespaceType)namespaceType, ns);
+ if (ns)
+ {
+ nsAutoCString namespacePrefix(ns->GetPrefix());
+ if (!namespacePrefix.IsEmpty())
+ {
+ // check if namespacePrefix is the same as the online directory; if so, ignore it.
+ nsAutoCString onlineDir;
+ rv = GetServerDirectory(onlineDir);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!onlineDir.IsEmpty())
+ {
+ char delimiter = ns->GetDelimiter();
+ if ( onlineDir.Last() != delimiter )
+ onlineDir += delimiter;
+ if (onlineDir.Equals(namespacePrefix))
+ return NS_OK;
+ }
+
+ MsgReplaceChar(namespacePrefix, ns->GetDelimiter(), '/'); // use canonical format
+ nsCString uri(originalUri);
+ int32_t index = uri.Find("//"); // find scheme
+ index = uri.FindChar('/', index + 2); // find '/' after scheme
+ // it may be the case that this is the INBOX uri, in which case
+ // we don't want to prepend the namespace. In that case, the uri ends with "INBOX",
+ // but the namespace is "INBOX/", so they don't match.
+ if (MsgFind(uri, namespacePrefix, false, index + 1) != index + 1 &&
+ !MsgLowerCaseEqualsLiteral(Substring(uri, index + 1), "inbox"))
+ uri.Insert(namespacePrefix, index + 1); // insert namespace prefix
+ convertedUri = uri;
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsImapIncomingServer::GetTrashFolderName(nsAString& retval)
+{
+ nsresult rv = GetUnicharValue(PREF_TRASH_FOLDER_NAME, retval);
+ if (NS_FAILED(rv))
+ return rv;
+ if (retval.IsEmpty())
+ retval = NS_LITERAL_STRING(DEFAULT_TRASH_FOLDER_NAME);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsImapIncomingServer::SetTrashFolderName(const nsAString& chvalue)
+{
+ // clear trash flag from the old pref
+ nsAutoString oldTrashName;
+ nsresult rv = GetTrashFolderName(oldTrashName);
+ if (NS_SUCCEEDED(rv))
+ {
+ nsAutoCString oldTrashNameUtf7;
+ rv = CopyUTF16toMUTF7(oldTrashName, oldTrashNameUtf7);
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsIMsgFolder> oldFolder;
+ rv = GetFolder(oldTrashNameUtf7, getter_AddRefs(oldFolder));
+ if (NS_SUCCEEDED(rv) && oldFolder)
+ oldFolder->ClearFlag(nsMsgFolderFlags::Trash);
+ }
+ }
+ return SetUnicharValue(PREF_TRASH_FOLDER_NAME, chvalue);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::GetMsgFolderFromURI(nsIMsgFolder *aFolderResource,
+ const nsACString& aURI,
+ nsIMsgFolder **aFolder)
+{
+ nsCOMPtr<nsIMsgFolder> msgFolder;
+ bool namespacePrefixAdded = false;
+ nsCString folderUriWithNamespace;
+
+ // Check if the folder exists as is...
+ nsresult rv = GetExistingMsgFolder(aURI, folderUriWithNamespace,
+ namespacePrefixAdded, false,
+ getter_AddRefs(msgFolder));
+
+ // Or try again with a case-insensitive lookup
+ if (NS_FAILED(rv) || !msgFolder)
+ rv = GetExistingMsgFolder(aURI, folderUriWithNamespace,
+ namespacePrefixAdded, true,
+ getter_AddRefs(msgFolder));
+
+ if (NS_FAILED(rv) || !msgFolder) {
+ // we didn't find the folder so we will have to create a new one.
+ if (namespacePrefixAdded)
+ {
+ nsCOMPtr <nsIRDFService> rdf = do_GetService("@mozilla.org/rdf/rdf-service;1", &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr<nsIRDFResource> resource;
+ rv = rdf->GetResource(folderUriWithNamespace, getter_AddRefs(resource));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr <nsIMsgFolder> folderResource;
+ folderResource = do_QueryInterface(resource, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+ msgFolder = folderResource;
+ }
+ else
+ msgFolder = aFolderResource;
+ }
+
+ msgFolder.swap(*aFolder);
+ return NS_OK;
+}
+
+nsresult
+nsImapIncomingServer::GetExistingMsgFolder(const nsACString& aURI,
+ nsACString& aFolderUriWithNamespace,
+ bool& aNamespacePrefixAdded,
+ bool aCaseInsensitive,
+ nsIMsgFolder **aFolder)
+{
+ nsCOMPtr<nsIMsgFolder> rootMsgFolder;
+ nsresult rv = GetRootMsgFolder(getter_AddRefs(rootMsgFolder));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aNamespacePrefixAdded = false;
+ // Check if the folder exists as is...Even if we have a personal namespace,
+ // it might be in another namespace (e.g., shared) and this will catch that.
+ rv = rootMsgFolder->GetChildWithURI(aURI, true, aCaseInsensitive, aFolder);
+
+ // If we couldn't find the folder as is, check if we need to prepend the
+ // personal namespace
+ if (!*aFolder)
+ {
+ GetUriWithNamespacePrefixIfNecessary(kPersonalNamespace, aURI,
+ aFolderUriWithNamespace);
+ if (!aFolderUriWithNamespace.IsEmpty())
+ {
+ aNamespacePrefixAdded = true;
+ rv = rootMsgFolder->GetChildWithURI(aFolderUriWithNamespace, true,
+ aCaseInsensitive, aFolder);
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::CramMD5Hash(const char *decodedChallenge, const char *key, char **result)
+{
+ NS_ENSURE_ARG_POINTER(decodedChallenge);
+ NS_ENSURE_ARG_POINTER(key);
+
+ unsigned char resultDigest[DIGEST_LENGTH];
+ nsresult rv = MSGCramMD5(decodedChallenge, strlen(decodedChallenge), key, strlen(key), resultDigest);
+ NS_ENSURE_SUCCESS(rv, rv);
+ *result = (char *) malloc(DIGEST_LENGTH);
+ if (*result)
+ memcpy(*result, resultDigest, DIGEST_LENGTH);
+ return (*result) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::GetLoginUsername(nsACString &aLoginUsername)
+{
+ return GetRealUsername(aLoginUsername);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::GetOriginalUsername(nsACString &aUsername)
+{
+ return GetUsername(aUsername);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::GetServerKey(nsACString &aServerKey)
+{
+ return GetKey(aServerKey);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::GetServerPassword(nsACString &aPassword)
+{
+ return GetPassword(aPassword);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::RemoveServerConnection(nsIImapProtocol* aProtocol)
+{
+ return RemoveConnection(aProtocol);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::GetServerShuttingDown(bool* aShuttingDown)
+{
+ return GetShuttingDown(aShuttingDown);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::ResetServerConnection(const nsACString& aFolderName)
+{
+ return ResetConnection(aFolderName);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::SetServerDoingLsub(bool aDoingLsub)
+{
+ return SetDoingLsub(aDoingLsub);
+}
+
+NS_IMETHODIMP
+nsImapIncomingServer::SetServerForceSelect(const nsACString &aForceSelect)
+{
+ return SetForceSelect(aForceSelect);
+}