/* -*- 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 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(NS_MSGACCOUNTMANAGER_CONTRACTID, &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 = 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 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_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 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 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 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 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 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. 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 protocolInstance ; MutexAutoLock mon(mLock); int32_t cnt = m_urlQueue.Count(); while (cnt > 0 && !urlRun && keepGoing) { nsCOMPtr aImapUrl(m_urlQueue[0]); nsCOMPtr 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 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 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; nsCOMPtr 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 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 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 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. 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 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 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 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 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) { nsCString password; nsresult rv; rv = GetPassword(password); NS_ENSURE_SUCCESS(rv, rv); if (password.IsEmpty()) 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(NS_IMAPSERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr 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 imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &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 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 hostFolder; nsCOMPtr aFolder; bool explicitlyVerify = false; *aNewFolder = false; nsCOMPtr 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 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 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 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 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 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 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); nsCOMPtr rdf(do_GetService(kRDFServiceCID, &rv)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr res; rv = rdf->GetResource(uriString, getter_AddRefs(res)); if (NS_SUCCEEDED(rv)) { nsCOMPtr 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 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 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 folderRenameAtom; folderRenameAtom = MsgGetAtom("RenameCompleted"); newFolder->NotifyFolderEvent(folderRenameAtom); } } } } 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 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 rdf(do_GetService("@mozilla.org/rdf/rdf-service;1", &rv)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr accountMgr = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &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(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 res; if (!folderUri.IsEmpty() && NS_SUCCEEDED(rdf->GetResource(folderUri, getter_AddRefs(res)))) { nsCOMPtr folder(do_QueryInterface(res, &rv)); 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(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 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 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 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 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 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 res; 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(rdf->GetResource(folderUri, getter_AddRefs(res)))) { folder = do_QueryInterface(res, &rv); if (NS_SUCCEEDED(rv)) { 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; } nsresult nsImapIncomingServer::DeleteNonVerifiedFolders(nsIMsgFolder *curFolder) { bool autoUnsubscribeFromNoSelectFolders = true; nsresult rv; nsCOMPtr prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); if (NS_SUCCEEDED(rv)) prefBranch->GetBoolPref("mail.imap.auto_unsubscribe_from_noselect_folders", &autoUnsubscribeFromNoSelectFolders); nsCOMPtr subFolders; rv = curFolder->GetSubFolders(getter_AddRefs(subFolders)); if(NS_SUCCEEDED(rv)) { bool moreFolders; while (NS_SUCCEEDED(subFolders->HasMoreElements(&moreFolders)) && moreFolders) { nsCOMPtr child; rv = subFolders->GetNext(getter_AddRefs(child)); if (NS_SUCCEEDED(rv) && child) { bool childVerified = false; nsCOMPtr childImapFolder = do_QueryInterface(child, &rv); if (NS_SUCCEEDED(rv) && childImapFolder) { uint32_t flags; nsCOMPtr 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 parent; rv = curFolder->GetParent(getter_AddRefs(parent)); if (NS_SUCCEEDED(rv) && parent) { nsCOMPtr imapParent = do_QueryInterface(parent); if (imapParent) imapParent->RemoveSubFolder(curFolder); } return rv; } bool nsImapIncomingServer::NoDescendentsAreVerified(nsIMsgFolder *parentFolder) { bool nobodyIsVerified = true; nsCOMPtr subFolders; nsresult rv = parentFolder->GetSubFolders(getter_AddRefs(subFolders)); if(NS_SUCCEEDED(rv)) { bool moreFolders; while (NS_SUCCEEDED(subFolders->HasMoreElements(&moreFolders)) && moreFolders && nobodyIsVerified) { nsCOMPtr child; rv = subFolders->GetNext(getter_AddRefs(child)); if (NS_SUCCEEDED(rv) && child) { bool childVerified = false; nsCOMPtr childImapFolder = do_QueryInterface(child, &rv); if (NS_SUCCEEDED(rv) && childImapFolder) { nsCOMPtr childFolder = do_QueryInterface(child, &rv); rv = childImapFolder->GetVerifiedAsOnlineFolder(&childVerified); nobodyIsVerified = !childVerified && NoDescendentsAreVerified(childFolder); } } } } return nobodyIsVerified; } bool nsImapIncomingServer::AllDescendentsAreNoSelect(nsIMsgFolder *parentFolder) { bool allDescendentsAreNoSelect = true; nsCOMPtr subFolders; nsresult rv = parentFolder->GetSubFolders(getter_AddRefs(subFolders)); if(NS_SUCCEEDED(rv)) { bool moreFolders; while (NS_SUCCEEDED(subFolders->HasMoreElements(&moreFolders)) && moreFolders && allDescendentsAreNoSelect) { nsCOMPtr child; rv = subFolders->GetNext(getter_AddRefs(child)); if (NS_SUCCEEDED(rv) && child) { bool childIsNoSelect = false; nsCOMPtr childImapFolder = do_QueryInterface(child, &rv); if (NS_SUCCEEDED(rv) && childImapFolder) { uint32_t flags; nsCOMPtr 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 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 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 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 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 rootFolder; rv = GetRootFolder(getter_AddRefs(rootFolder)); NS_ENSURE_SUCCESS(rv, rv); return ResetFoldersToUnverified(rootFolder); } else { nsCOMPtr subFolders; nsCOMPtr 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 child; rv = subFolders->GetNext(getter_AddRefs(child)); if (NS_SUCCEEDED(rv) && child) { nsCOMPtr childFolder = do_QueryInterface(child, &rv); if (NS_SUCCEEDED(rv) && childFolder) { rv = ResetFoldersToUnverified(childFolder); if (NS_FAILED(rv)) break; } } } } 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); } nsCOMPtr subFolders; if (NS_SUCCEEDED(parentFolder->GetSubFolders(getter_AddRefs(subFolders)))) { bool moreFolders; while (NS_SUCCEEDED(subFolders->HasMoreElements(&moreFolders)) && moreFolders) { nsCOMPtr child; subFolders->GetNext(getter_AddRefs(child)); if (child) { nsCOMPtr 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 asyncPrompter = do_GetService(NS_MSGASYNCPROMPTER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr 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 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(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 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 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 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(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 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 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 imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &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 modified UTF-7 NS_LossyConvertUTF16toASCII folderCName(aName); nsCOMPtr msgFolder; if (rootMsgFolder && !aName.IsEmpty()) rv = rootMsgFolder->FindSubFolder(folderCName, getter_AddRefs(msgFolder)); nsCOMPtr 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 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(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 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(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 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 enumerator; rv = aFolder->GetSubFolders(getter_AddRefs(enumerator)); if (NS_FAILED(rv)) return rv; bool hasMore; while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore) { nsCOMPtr item; enumerator->GetNext(getter_AddRefs(item)); nsCOMPtr 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 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); } 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 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 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 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 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 rdf = do_GetService("@mozilla.org/rdf/rdf-service;1", &rv); NS_ENSURE_SUCCESS(rv,rv); nsCOMPtr resource; rv = rdf->GetResource(folderUriWithNamespace, getter_AddRefs(resource)); NS_ENSURE_SUCCESS(rv,rv); nsCOMPtr 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 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); }