/* -*- 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 "netCore.h" #include "../public/nsIImapHostSessionList.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 "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 "nsMsgUtils.h" #include "nsServiceManagerUtils.h" #include "nsComponentManagerUtils.h" #include "nsCRTGlue.h" #include "mozilla/Components.h" #include "nsNetUtil.h" #include "mozilla/Utf8.h" #include "mozilla/LoadInfo.h" using namespace mozilla; // Despite its name, this contains a folder path, for example INBOX/Trash. #define PREF_TRASH_FOLDER_PATH "trash_folder_name" #define DEFAULT_TRASH_FOLDER_PATH "Trash" // XXX Is this a useful default? #define NS_SUBSCRIBABLESERVER_CID \ { \ 0x8510876a, 0x1dd2, 0x11b2, { \ 0x82, 0x53, 0x91, 0xf7, 0x1b, 0x34, 0x8a, 0x25 \ } \ } static NS_DEFINE_CID(kSubscribableServerCID, NS_SUBSCRIBABLESERVER_CID); #define NS_IIMAPHOSTSESSIONLIST_CID \ { \ 0x479ce8fc, 0xe725, 0x11d2, { \ 0xa5, 0x05, 0x00, 0x60, 0xb0, 0xfc, 0x04, 0xb7 \ } \ } 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; mUtf8AcceptEnabled = false; } nsImapIncomingServer::~nsImapIncomingServer() { mozilla::DebugOnly 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 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 accountManager = do_GetService("@mozilla.org/messenger/account-manager;1", &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr 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 = GetUsername(username); NS_ENSURE_SUCCESS(rv, rv); rv = GetHostName(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 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 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 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_BOOL(nsImapIncomingServer, ForceSelect, "force_select_imap") 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_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_IMPL_SERVERPREF_BOOL(nsImapIncomingServer, AllowUTF8Accept, "allow_utf8_accept") 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 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); // Despite its name, this returns the trash folder path, for example // INBOX/Trash. nsAutoString trashFolderName; nsresult rv = GetTrashFolderName(trashFolderName); if (NS_SUCCEEDED(rv)) { nsAutoCString trashFolderNameUtf7or8; bool useUTF8 = false; GetUtf8AcceptEnabled(&useUTF8); if (useUTF8) { CopyUTF16toUTF8(trashFolderName, trashFolderNameUtf7or8); } else { CopyUTF16toMUTF7(trashFolderName, trashFolderNameUtf7or8); } nsCOMPtr trashFolder; // 'trashFolderName' being a path here works well since this is appended // to the server's root folder in GetFolder(). rv = GetFolder(trashFolderNameUtf7or8, getter_AddRefs(trashFolder)); NS_ENSURE_SUCCESS(rv, rv); nsCString trashURI; trashFolder->GetURI(trashURI); rv = 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, SendID, "send_client_info") 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 aProtocol; nsresult rv = GetImapConnection(aImapUrl, getter_AddRefs(aProtocol)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr 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 unforeseen problems let's give it a second chance // to run the url if (NS_FAILED(rv) && rv != NS_ERROR_ILLEGAL_VALUE) { 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 protocolInstance; nsImapProtocol::LogImapUrl("creating protocol instance to retry queued url", aImapUrl); nsCOMPtr thread(do_GetCurrentThread()); rv = GetImapConnection(aImapUrl, getter_AddRefs(protocolInstance)); if (NS_SUCCEEDED(rv) && protocolInstance) { nsCOMPtr 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. } } 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 protocolInstance; MutexAutoLock mon(mLock); int32_t cnt = m_urlQueue.Count(); while (cnt > 0 && !urlRun && keepGoing) { nsCOMPtr aImapUrl(m_urlQueue[0]); 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 url = do_QueryInterface(aImapUrl, &rv); if (NS_SUCCEEDED(rv) && url) { nsImapProtocol::LogImapUrl("playing queued url", aImapUrl); rv = protocolInstance->LoadImapUrl(url, aConsumer); if (NS_SUCCEEDED(rv)) { bool isInbox; protocolInstance->IsBusy(&urlRun, &isInbox); if (!urlRun) nsImapProtocol::LogImapUrl("didn't need to run", aImapUrl); removeUrlFromQueue = true; } else { nsImapProtocol::LogImapUrl("playing queued url failed", aImapUrl); } } } 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 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 aMailNewsUrl(do_QueryInterface(aImapUrl, &rv)); if (aMailNewsUrl && aImapUrl) { nsCOMPtr mockChannel; if (NS_SUCCEEDED(aImapUrl->GetMockChannel(getter_AddRefs(mockChannel))) && mockChannel) { nsresult requestStatus; mockChannel->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 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) { RemoveConnection(aConnection); aConnection->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 connection; nsCOMPtr 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. // 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 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) { connection.forget(aImapConnection); } else if (canRunButBusy) { // do nothing; return NS_OK; for queuing } // 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) { freeConnection.forget(aImapConnection); } else { 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 dummyUsedToEnsureNSSIsInitialized = do_GetService("@mozilla.org/psm;1", &rv); NS_ENSURE_SUCCESS(rv, rv); } break; default: break; } nsCOMPtr hostSession = do_GetService(kCImapHostSessionListCID, &rv); NS_ENSURE_SUCCESS(rv, rv); RefPtr protocolInstance(new nsImapProtocol()); rv = protocolInstance->Initialize(hostSession, this); NS_ENSURE_SUCCESS(rv, rv); // It implements nsIChannel, and all channels require loadInfo. protocolInstance->SetLoadInfo(new mozilla::net::LoadInfo( nsContentUtils::GetSystemPrincipal(), nullptr, nullptr, nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, nsIContentPolicy::TYPE_OTHER)); // take the protocol instance and add it to the connectionCache m_connectionCache.AppendObject(protocolInstance); protocolInstance.forget(aImapConnection); return rv; } NS_IMETHODIMP nsImapIncomingServer::CloseConnectionForFolder( nsIMsgFolder* aMsgFolder) { nsresult rv = NS_OK; nsCOMPtr connection; bool isBusy = false, isInbox = false; nsCString inFolderName; nsCString connectionFolderName; nsCOMPtr 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 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) { nsString password; nsresult rv; rv = GetPassword(password); NS_ENSURE_SUCCESS(rv, rv); if (password.IsEmpty()) { // Check if this is due to oauth2 showing empty password. If so, keep going. int32_t authMethod = 0; GetAuthMethod(&authMethod); if (authMethod != nsMsgAuthMethod::OAuth2) return NS_OK; } rv = ResetFoldersToUnverified(nullptr); nsCOMPtr rootMsgFolder; rv = GetRootFolder(getter_AddRefs(rootMsgFolder)); NS_ENSURE_SUCCESS(rv, rv); if (!rootMsgFolder) return NS_ERROR_FAILURE; nsCOMPtr imapService = do_GetService("@mozilla.org/messenger/imapservice;1", &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr thread(do_GetCurrentThread()); rv = imapService->DiscoverAllFolders(rootMsgFolder, this, aMsgWindow); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr hostSessionList = do_GetService(kCImapHostSessionListCID, &rv); if (NS_SUCCEEDED(rv)) { nsAutoCString serverKey; rv = GetKey(serverKey); if (!serverKey.IsEmpty()) hostSessionList->SetDiscoveryForHostInProgress(serverKey.get(), true); } return rv; } NS_IMETHODIMP nsImapIncomingServer::VerifyLogon(nsIUrlListener* aUrlListener, nsIMsgWindow* aMsgWindow, nsIURI** aURL) { nsresult rv; nsCOMPtr imapService = do_GetService("@mozilla.org/messenger/imapservice;1", &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr 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 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 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 nsACString& serverUri, nsIMsgFolder** rootFolder) { nsImapMailFolder* newRootFolder = new nsImapMailFolder; newRootFolder->Init(serverUri); 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 hostFolder; nsCOMPtr aFolder; bool explicitlyVerify = false; *aNewFolder = false; nsCOMPtr rootFolder; rv = GetRootFolder(getter_AddRefs(rootFolder)); 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 (rootFolder && !dupFolderPath.IsEmpty()) { nsCOMPtr msgFolder; bool isNamespace = false; bool noSelect = false; rv = rootFolder->FindSubFolder(dupFolderPath, getter_AddRefs(msgFolder)); NS_ENSURE_SUCCESS(rv, rv); m_subscribeFolders.AppendObject(msgFolder); noSelect = (boxFlags & kNoselect) != 0; nsCOMPtr 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(rootFolder, &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.AppendLiteral("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 (folderPath.LowerCaseEqualsLiteral("inbox") && hierarchyDelimiter == kOnlineHierarchySeparatorNil) { hierarchyDelimiter = '/'; // set to default in this case (as in 4.x) hostFolder->SetHierarchyDelimiter(hierarchyDelimiter); } nsCOMPtr child; // nsCString possibleName(aSpec->allocatedPathName); uri.Append('/'); uri.Append(dupFolderPath); bool caseInsensitive = dupFolderPath.LowerCaseEqualsLiteral("inbox"); rootFolder->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 parent; bool parentIsNew; caseInsensitive = parentName.LowerCaseEqualsLiteral("inbox"); rootFolder->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 = dupFolderPath.LowerCaseEqualsLiteral("inbox"); rootFolder->GetChildWithURI(uri, true, caseInsensitive, getter_AddRefs(child)); } if (child) { nsCOMPtr 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. dupFolderPath.ReplaceChar('/', hierarchyDelimiter); if (hierarchyDelimiter != '/') { nsImapUrl::UnescapeSlashes(dupFolderPath); } // GMail gives us a localized name for the inbox but doesn't let // us select that localized name. if (boxFlags & kImapInbox) imapFolder->SetOnlineName("INBOX"_ns); else if (onlineName.IsEmpty() || !onlineName.Equals(dupFolderPath)) imapFolder->SetOnlineName(dupFolderPath); if (hierarchyDelimiter != '/') { nsImapUrl::UnescapeSlashes(folderName); } if (NS_SUCCEEDED(CopyFolderNameToUTF16(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 rootFolder; nsresult rv = GetRootFolder(getter_AddRefs(rootFolder)); if (NS_SUCCEEDED(rv) && rootFolder) { nsCOMPtr imapRoot = do_QueryInterface(rootFolder); if (imapRoot) { nsCOMPtr 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 rootFolder; nsresult rv = GetRootFolder(getter_AddRefs(rootFolder)); if (NS_SUCCEEDED(rv) && rootFolder) { nsCOMPtr imapRoot = do_QueryInterface(rootFolder); if (imapRoot) { nsCOMPtr foundFolder; rv = imapRoot->FindOnlineSubFolder(folderPath, getter_AddRefs(foundFolder)); if (NS_SUCCEEDED(rv) && foundFolder) { nsCOMPtr 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 rootFolder; nsresult rv = GetRootFolder(getter_AddRefs(rootFolder)); if (NS_SUCCEEDED(rv) && rootFolder) { nsCOMPtr imapRoot = do_QueryInterface(rootFolder); if (imapRoot) { nsCOMPtr 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 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); rv = GetOrCreateFolder(uriString, 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 me; rv = GetFolder(oldName, getter_AddRefs(me)); if (NS_FAILED(rv)) return rv; nsCOMPtr 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 folder; folder = do_QueryInterface(me, &rv); if (NS_SUCCEEDED(rv)) { folder->RenameLocal(tmpNewName, parent); nsCOMPtr parentImapFolder = do_QueryInterface(parent); if (parentImapFolder) parentImapFolder->RenameClient(msgWindow, me, oldName, tmpNewName); nsCOMPtr newFolder; nsString unicodeNewName; // `tmpNewName` is in MUTF-7 or UTF-8. It needs to be convert to UTF-8. CopyFolderNameToUTF16(tmpNewName, unicodeNewName); CopyUTF16toUTF8(unicodeNewName, tmpNewName); rv = GetFolder(tmpNewName, getter_AddRefs(newFolder)); if (NS_SUCCEEDED(rv)) { newFolder->NotifyFolderEvent(kRenameCompleted); } } } } return rv; } NS_IMETHODIMP nsImapIncomingServer::FolderIsNoSelect( const nsACString& aFolderName, bool* result) { NS_ENSURE_ARG_POINTER(result); nsCOMPtr 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 rootFolder; nsresult rv = GetRootFolder(getter_AddRefs(rootFolder)); if (NS_SUCCEEDED(rv) && rootFolder) { nsCOMPtr imapRoot = do_QueryInterface(rootFolder); if (imapRoot) { nsCOMPtr 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 rootFolder; nsresult rv = GetRootFolder(getter_AddRefs(rootFolder)); if (NS_SUCCEEDED(rv) && rootFolder) { nsCOMPtr folder; rv = rootFolder->FindSubFolder(folderName, getter_AddRefs(folder)); if (NS_SUCCEEDED(rv) && folder) { nsCOMPtr imapFolder = do_QueryInterface(folder); if (imapFolder) imapFolder->GetVerifiedAsOnlineFolder(aResult); } } return rv; } NS_IMETHODIMP nsImapIncomingServer::DiscoveryDone() { if (mDoingSubscribeDialog) return NS_OK; nsCOMPtr 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 resource 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 accountMgr = do_GetService("@mozilla.org/messenger/account-manager;1", &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr identity; rv = accountMgr->GetFirstIdentityForServer(this, getter_AddRefs(identity)); if (NS_SUCCEEDED(rv) && identity) { nsCString folderUri; identity->GetFccFolder(folderUri); nsCString existingUri; if (CheckSpecialFolder(folderUri, nsMsgFolderFlags::SentMail, existingUri)) { identity->SetFccFolder(existingUri); identity->SetFccFolderPickerMode("1"_ns); } identity->GetDraftFolder(folderUri); if (CheckSpecialFolder(folderUri, nsMsgFolderFlags::Drafts, existingUri)) { identity->SetDraftFolder(existingUri); identity->SetDraftsFolderPickerMode("1"_ns); } bool archiveEnabled; identity->GetArchiveEnabled(&archiveEnabled); if (archiveEnabled) { identity->GetArchiveFolder(folderUri); if (CheckSpecialFolder(folderUri, nsMsgFolderFlags::Archive, existingUri)) { identity->SetArchiveFolder(existingUri); identity->SetArchivesFolderPickerMode("1"_ns); } } identity->GetStationeryFolder(folderUri); if (!folderUri.IsEmpty()) { nsCOMPtr folder; rv = GetOrCreateFolder(folderUri, getter_AddRefs(folder)); if (NS_SUCCEEDED(rv)) rv = folder->SetFlag(nsMsgFolderFlags::Templates); } } nsCOMPtr spamSettings; rv = GetSpamSettings(getter_AddRefs(spamSettings)); if (NS_SUCCEEDED(rv) && spamSettings) { nsCString spamFolderUri, existingUri; spamSettings->GetSpamFolderURI(spamFolderUri); if (CheckSpecialFolder(spamFolderUri, nsMsgFolderFlags::Junk, existingUri)) { // This only sets the cached values in the spam settings object. spamSettings->SetActionTargetFolder(existingUri); spamSettings->SetMoveTargetMode( nsISpamSettings::MOVE_TARGET_MODE_FOLDER); // Set the preferences too so that the values persist. SetUnicharValue("spamActionTargetFolder", NS_ConvertUTF8toUTF16(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. nsTArray> trashFolders; rv = rootMsgFolder->GetFoldersWithFlags(nsMsgFolderFlags::Trash, trashFolders); if (NS_SUCCEEDED(rv)) { nsAutoString trashName; if (NS_SUCCEEDED(GetTrashFolderName(trashName))) { for (auto trashFolder : trashFolders) { // 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. nsAutoString retval; rv = GetUnicharValue(PREF_TRASH_FOLDER_PATH, retval); if (isGMailServer && (NS_FAILED(rv) || retval.IsEmpty())) { nsCOMPtr imapFolder( do_QueryInterface(trashFolder)); int32_t boxFlags; imapFolder->GetBoxFlags(&boxFlags); if (boxFlags & kImapXListTrash) { continue; } } else { // Store the trash folder path. We maintain the full path in the // trash_folder_name preference since the full path is stored // there when selecting a trash folder in the Account Manager. nsAutoCString trashURL; rv = trashFolder->GetFolderURL(trashURL); if (NS_FAILED(rv)) { continue; } nsCOMPtr uri; rv = NS_NewURI(getter_AddRefs(uri), trashURL); if (NS_FAILED(rv)) { continue; } nsAutoCString trashPath; uri->GetPathQueryRef(trashPath); nsAutoCString unescapedName; MsgUnescapeString(Substring(trashPath, 1), // Skip leading slash. nsINetUtil::ESCAPE_URL_PATH, unescapedName); nsAutoString nameUnicode; if (NS_FAILED(CopyFolderNameToUTF16(unescapedName, nameUnicode)) || trashName.Equals(nameUnicode)) { continue; } if (trashFolders.Length() == 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_PATH, nameUnicode); continue; } } // We clear the trash folder flag if the trash folder path doesn't // match mail.server.serverX.trash_folder_name. trashFolder->ClearFlag(nsMsgFolderFlags::Trash); } } } } bool usingSubscription = true; GetUsingSubscription(&usingSubscription); nsCOMArray 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 currentImapFolder(unverifiedFolders[k]); nsCOMPtr 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 { nsCOMPtr parent; currentFolder->GetParent(getter_AddRefs(parent)); if (parent) { currentImapFolder->RemoveLocalSelf(); } } } 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(nsCString& folderUri, uint32_t folderFlag, nsCString& existingUri) { nsCOMPtr folder; nsCOMPtr rootMsgFolder; nsresult rv = GetRootFolder(getter_AddRefs(rootMsgFolder)); NS_ENSURE_SUCCESS(rv, false); nsCOMPtr existingFolder; rootMsgFolder->GetFolderWithFlags(folderFlag, getter_AddRefs(existingFolder)); if (!folderUri.IsEmpty() && NS_SUCCEEDED(GetOrCreateFolder(folderUri, getter_AddRefs(folder)))) { nsCOMPtr 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; } bool nsImapIncomingServer::NoDescendentsAreVerified( nsIMsgFolder* parentFolder) { nsTArray> subFolders; nsresult rv = parentFolder->GetSubFolders(subFolders); if (NS_SUCCEEDED(rv)) { for (nsIMsgFolder* child : subFolders) { nsCOMPtr childImapFolder = do_QueryInterface(child, &rv); if (NS_SUCCEEDED(rv) && childImapFolder) { bool childVerified = false; rv = childImapFolder->GetVerifiedAsOnlineFolder(&childVerified); if (NS_SUCCEEDED(rv) && childVerified) { return false; } if (!NoDescendentsAreVerified(child)) { return false; } } } } // If we get this far we didn't find any verified. return true; } bool nsImapIncomingServer::AllDescendentsAreNoSelect( nsIMsgFolder* parentFolder) { nsTArray> subFolders; nsresult rv = parentFolder->GetSubFolders(subFolders); if (NS_SUCCEEDED(rv)) { for (nsIMsgFolder* child : subFolders) { nsCOMPtr childImapFolder = do_QueryInterface(child, &rv); if (NS_SUCCEEDED(rv) && childImapFolder) { uint32_t flags; rv = child->GetFlags(&flags); bool isNoSelect = NS_SUCCEEDED(rv) && (flags & nsMsgFolderFlags::ImapNoselect); if (!isNoSelect) { return false; } if (!AllDescendentsAreNoSelect(child)) { return false; } } } } // If we get this far we found none without the Noselect flag. return true; } NS_IMETHODIMP nsImapIncomingServer::PromptLoginFailed(nsIMsgWindow* aMsgWindow, int32_t* aResult) { nsAutoCString hostName; GetHostName(hostName); nsAutoCString userName; GetUsername(userName); nsAutoString accountName; GetPrettyName(accountName); return MsgPromptLoginFailed(aMsgWindow, hostName, userName, accountName, 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); AutoTArray params = {hostName, tempString}; rv = m_stringBundle->FormatStringFromName("imapServerAlert", params, message); if (NS_SUCCEEDED(rv)) { aUrl->SetErrorCode("imap-server-alert"_ns); aUrl->SetErrorMessage(message); return AlertUser(message, aUrl); } } } return AlertUser(aAlertString, aUrl); } nsresult nsImapIncomingServer::AlertUser(const nsAString& aString, nsIMsgMailNewsUrl* aUrl) { nsresult rv; nsCOMPtr mailSession = do_GetService("@mozilla.org/messenger/services/session;1", &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)) { AutoTArray params; CopyUTF8toUTF16(hostName, *params.AppendElement()); rv = m_stringBundle->FormatStringFromName(aMsgName, params, message); if (NS_SUCCEEDED(rv)) { aUrl->SetErrorCode(nsDependentCString(aMsgName)); aUrl->SetErrorMessage(message); 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"); NS_ENSURE_TRUE(!message.IsEmpty(), NS_OK); 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); AutoTArray formatStrings = {hostName}; const char* msgName; nsString fullMessage; nsCOMPtr 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); aUrl->SetErrorCode("imap-server-error"_ns); aUrl->SetErrorMessage(unicodeMsg); nsCOMPtr folder; if (imapState == nsIImapUrl::nsImapSelectedState || imapAction == nsIImapUrl::nsImapFolderStatus) { aUrl->GetFolder(getter_AddRefs(folder)); if (folder) folder->GetPrettyName(folderName); msgName = "imapFolderCommandFailed"; formatStrings.AppendElement(folderName); } else { msgName = "imapServerCommandFailed"; } formatStrings.AppendElement(unicodeMsg); nsresult rv = GetStringBundle(); NS_ENSURE_SUCCESS(rv, rv); if (m_stringBundle) { rv = m_stringBundle->FormatStringFromName(msgName, formatStrings, 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 sBundleService = mozilla::components::StringBundle::Service(); 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(msgName, 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 rootFolder; rv = GetRootFolder(getter_AddRefs(rootFolder)); NS_ENSURE_SUCCESS(rv, rv); return ResetFoldersToUnverified(rootFolder); } nsCOMPtr imapFolder = do_QueryInterface(parentFolder, &rv); NS_ENSURE_SUCCESS(rv, rv); rv = imapFolder->SetVerifiedAsOnlineFolder(false); nsTArray> subFolders; rv = parentFolder->GetSubFolders(subFolders); NS_ENSURE_SUCCESS(rv, rv); for (nsIMsgFolder* child : subFolders) { rv = ResetFoldersToUnverified(child); NS_ENSURE_SUCCESS(rv, rv); } return rv; } void nsImapIncomingServer::GetUnverifiedFolders( nsCOMArray& aFoldersArray) { nsCOMPtr rootFolder; if (NS_FAILED(GetRootFolder(getter_AddRefs(rootFolder))) || !rootFolder) return; nsCOMPtr 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& aFoldersArray) { nsCOMPtr 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); } nsTArray> subFolders; if (NS_SUCCEEDED(parentFolder->GetSubFolders(subFolders))) { for (nsIMsgFolder* child : subFolders) { GetUnverifiedSubFolders(child, aFoldersArray); } } } NS_IMETHODIMP nsImapIncomingServer::ForgetSessionPassword(bool modifyLogin) { bool usingOauth2 = false; if (modifyLogin) { // Only need to check for oauth2 if modifyLogin is true. int32_t authMethod = 0; GetAuthMethod(&authMethod); usingOauth2 = (authMethod == nsMsgAuthMethod::OAuth2); } // Clear the cached password if not using Oauth2 or if modifyLogin is false. if (!usingOauth2 || !modifyLogin) { nsresult rv = nsMsgIncomingServer::ForgetSessionPassword(modifyLogin); 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, nsAString& 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 asyncPrompter = do_GetService("@mozilla.org/messenger/msgAsyncPrompter;1", &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr promptListener( do_QueryInterface(aProtocol)); rv = asyncPrompter->QueueAsyncAuthPrompt(m_serverKey, aNewPasswordRequested, promptListener); // Explicit 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; } // Get password already stored in login manager. This won't trigger a prompt // if no password string is present. NS_IMETHODIMP nsImapIncomingServer::SyncGetPassword(nsAString& aPassword) { nsresult rv = NS_OK; if (NS_SUCCEEDED(GetPasswordWithoutUI()) && !m_password.IsEmpty()) aPassword = m_password; else rv = NS_ERROR_NOT_AVAILABLE; return rv; } NS_IMETHODIMP nsImapIncomingServer::PromptPassword(nsIMsgWindow* aMsgWindow, nsAString& aPassword) { nsAutoCString userName; GetUsername(userName); nsAutoCString hostName; GetHostName(hostName); nsresult rv = GetStringBundle(); NS_ENSURE_SUCCESS(rv, rv); AutoTArray formatStrings; CopyUTF8toUTF16(userName, *formatStrings.AppendElement()); nsString passwordTitle; rv = m_stringBundle->FormatStringFromName( "imapEnterPasswordPromptTitleWithUsername", formatStrings, passwordTitle); NS_ENSURE_SUCCESS(rv, rv); AutoTArray formatStrings2; CopyUTF8toUTF16(userName, *formatStrings2.AppendElement()); CopyUTF8toUTF16(hostName, *formatStrings2.AppendElement()); nsString passwordText; rv = m_stringBundle->FormatStringFromName("imapEnterServerPasswordPrompt", formatStrings2, passwordText); NS_ENSURE_SUCCESS(rv, rv); rv = GetPasswordWithUI(passwordText, passwordTitle, 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); return NS_OK; } NS_IMETHODIMP nsImapIncomingServer::GetCapability(eIMAPCapabilityFlags* capability) { NS_ENSURE_ARG_POINTER(capability); *capability = m_capability; return NS_OK; } NS_IMETHODIMP nsImapIncomingServer::SetServerID(const nsACString& aServerID) { return SetServerIDPref(aServerID); } NS_IMETHODIMP nsImapIncomingServer::CommitNamespaces() { nsresult rv; nsCOMPtr 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 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 rootFolder; nsresult rv = GetRootFolder(getter_AddRefs(rootFolder)); if (NS_SUCCEEDED(rv) && rootFolder) { nsCOMPtr 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 accountManager = do_GetService("@mozilla.org/messenger/account-manager;1", &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 nsACString& 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 imapService = do_GetService("@mozilla.org/messenger/imapservice;1", &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 /) */ return imapService->GetListOfFoldersWithPath( this, aMsgWindow, Substring(uri, serverUri.Length() + 1)); } 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 imapService = do_GetService("@mozilla.org/messenger/imapservice;1", &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 msgWindow; nsCOMPtr 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 msgFolder; nsCOMPtr mailUrl = do_QueryInterface(imapUrl); mailUrl->GetFolder(getter_AddRefs(msgFolder)); if (msgFolder) { nsresult rv; nsCOMPtr session = do_GetService("@mozilla.org/messenger/services/session;1", &rv); NS_ENSURE_SUCCESS(rv, rv); bool folderOpen; rv = session->IsFolderOpenInWindow(msgFolder, &folderOpen); if (NS_SUCCEEDED(rv) && !folderOpen && msgFolder) msgFolder->SetMsgDatabase(nullptr); nsCOMPtr 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 MUTF-7. // If it's not UTF-8, it's not 7bit-ASCII and cannot be MUTF-7 either. // We just ignore it. if (!mozilla::IsUtf8(aName)) return NS_OK; // Now handle subscription folder names as UTF-8 so don't convert to MUTF-7. return mInner->AddTo(aName, addAsSubscribed, aSubscribable, changeIfExists); } NS_IMETHODIMP nsImapIncomingServer::StopPopulating(nsIMsgWindow* aMsgWindow) { 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 imapService = do_GetService("@mozilla.org/messenger/imapservice;1", &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr 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 MUTF-7. NS_ConvertUTF16toUTF8 folderCName(aName); nsCOMPtr msgFolder; if (rootMsgFolder && !aName.IsEmpty()) rv = rootMsgFolder->FindSubFolder(folderCName, getter_AddRefs(msgFolder)); nsCOMPtr thread(do_GetCurrentThread()); if (subscribe) rv = imapService->SubscribeFolder(msgFolder, aName, nullptr, aUri); else rv = imapService->UnsubscribeFolder(msgFolder, aName, 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::SetUtf8AcceptEnabled(bool enabled) { mUtf8AcceptEnabled = enabled; return NS_OK; } NS_IMETHODIMP nsImapIncomingServer::GetUtf8AcceptEnabled(bool* enabled) { NS_ENSURE_ARG_POINTER(enabled); *enabled = mUtf8AcceptEnabled; 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::GetChildURIs(const nsACString& aPath, nsTArray& aResult) { nsresult rv = EnsureInner(); NS_ENSURE_SUCCESS(rv, rv); return mInner->GetChildURIs(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; } 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 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 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 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:, * 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 protocolInfo = do_GetService("@mozilla.org/messenger/protocol/info;1?type=imap", &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); AutoTArray formatStrings = {tmpVal}; nsString result; rv = m_stringBundle->FormatStringFromName(aName, formatStrings, 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::GetFolderView(nsITreeView** aView) { nsresult rv = EnsureInner(); NS_ENSURE_SUCCESS(rv, rv); return mInner->GetFolderView(aView); } 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 rootMsgFolder; nsresult rv = GetRootMsgFolder(getter_AddRefs(rootMsgFolder)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr 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 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 mailSession = do_GetService("@mozilla.org/messenger/services/session;1"); 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 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. nsTArray> subFolders; rv = aFolder->GetSubFolders(subFolders); NS_ENSURE_SUCCESS(rv, rv); for (nsIMsgFolder* msgFolder : subFolders) { 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 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 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); } // 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 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; } namespacePrefix.ReplaceChar(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 (uri.Find(namespacePrefix, index + 1) != index + 1 && !Substring(uri, index + 1).LowerCaseEqualsLiteral("inbox")) uri.Insert(namespacePrefix, index + 1); // insert namespace prefix convertedUri = uri; } } return rv; } NS_IMETHODIMP nsImapIncomingServer::GetTrashFolderName(nsAString& retval) { // Despite its name, this returns a path, for example INBOX/Trash. nsresult rv = GetUnicharValue(PREF_TRASH_FOLDER_PATH, retval); if (NS_FAILED(rv)) return rv; if (retval.IsEmpty()) retval = NS_LITERAL_STRING_FROM_CSTRING(DEFAULT_TRASH_FOLDER_PATH); return NS_OK; } NS_IMETHODIMP nsImapIncomingServer::SetTrashFolderName( const nsAString& chvalue) { // Clear trash flag from the old pref. // Despite its name, this returns the trash folder path, for example // INBOX/Trash. bool useUTF8 = false; GetUtf8AcceptEnabled(&useUTF8); nsAutoString oldTrashName; nsresult rv = GetTrashFolderName(oldTrashName); if (NS_SUCCEEDED(rv)) { nsAutoCString oldTrashNameUtf7or8; nsCOMPtr oldFolder; // 'trashFolderName' being a path here works well since this is appended // to the server's root folder in GetFolder(). if (useUTF8) { CopyUTF16toUTF8(oldTrashName, oldTrashNameUtf7or8); } else { CopyUTF16toMUTF7(oldTrashName, oldTrashNameUtf7or8); } rv = GetFolder(oldTrashNameUtf7or8, getter_AddRefs(oldFolder)); if (NS_SUCCEEDED(rv) && oldFolder) oldFolder->ClearFlag(nsMsgFolderFlags::Trash); } // If the user configured delete mode (model) is currently "move to trash", // mark the newly designated trash folder name as the active trash // destination folder. int32_t deleteModel; rv = GetDeleteModel(&deleteModel); if (NS_SUCCEEDED(rv) && (deleteModel == nsMsgImapDeleteModels::MoveToTrash)) { nsAutoCString newTrashNameUtf7or8; if (useUTF8) { CopyUTF16toUTF8(PromiseFlatString(chvalue), newTrashNameUtf7or8); } else { CopyUTF16toMUTF7(PromiseFlatString(chvalue), newTrashNameUtf7or8); } nsCOMPtr newTrashFolder; rv = GetFolder(newTrashNameUtf7or8, getter_AddRefs(newTrashFolder)); if (NS_SUCCEEDED(rv) && newTrashFolder) newTrashFolder->SetFlag(nsMsgFolderFlags::Trash); } return SetUnicharValue(PREF_TRASH_FOLDER_PATH, chvalue); } NS_IMETHODIMP nsImapIncomingServer::GetMsgFolderFromURI(nsIMsgFolder* aFolderResource, const nsACString& aURI, nsIMsgFolder** aFolder) { nsCOMPtr msgFolder; bool namespacePrefixAdded = false; nsCString folderUriWithNamespace; // clang-format off // 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)); // clang-format on if (NS_FAILED(rv) || !msgFolder) { // we didn't find the folder so we will have to create a new one. if (namespacePrefixAdded) { nsCOMPtr folder; rv = GetOrCreateFolder(folderUriWithNamespace, getter_AddRefs(folder)); NS_ENSURE_SUCCESS(rv, rv); msgFolder = folder; } else msgFolder = aFolderResource; } msgFolder.forget(aFolder); return (aFolder ? NS_OK : NS_ERROR_FAILURE); } nsresult nsImapIncomingServer::GetExistingMsgFolder( const nsACString& aURI, nsACString& aFolderUriWithNamespace, bool& aNamespacePrefixAdded, bool aCaseInsensitive, nsIMsgFolder** aFolder) { nsCOMPtr 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 GetUsername(aLoginUsername); } NS_IMETHODIMP nsImapIncomingServer::GetOriginalUsername(nsACString& aUsername) { return GetUsername(aUsername); } NS_IMETHODIMP nsImapIncomingServer::GetServerKey(nsACString& aServerKey) { return GetKey(aServerKey); } NS_IMETHODIMP nsImapIncomingServer::GetServerPassword(nsAString& 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::SetServerUtf8AcceptEnabled(bool enabled) { return SetUtf8AcceptEnabled(enabled); }