/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "msgCore.h"
#include "nsMsgImapCID.h"

#include "netCore.h"
#include "nsIMAPHostSessionList.h"
#include "nsImapIncomingServer.h"
#include "nsIMsgAccountManager.h"
#include "nsIMsgIdentity.h"
#include "nsIImapUrl.h"
#include "nsIUrlListener.h"
#include "nsThreadUtils.h"
#include "nsImapProtocol.h"
#include "nsCOMPtr.h"
#include "nsIPrefBranch.h"
#include "nsIPrefService.h"
#include "nsMsgFolderFlags.h"
#include "prmem.h"
#include "plstr.h"
#include "nsIMsgFolder.h"
#include "nsIMsgWindow.h"
#include "nsImapMailFolder.h"
#include "nsImapUtils.h"
#include "nsIRDFService.h"
#include "nsRDFCID.h"
#include "nsIMsgMailNewsUrl.h"
#include "nsIImapService.h"
#include "nsMsgI18N.h"
#include "nsIImapMockChannel.h"
// for the memory cache...
#include "nsICacheEntry.h"
#include "nsImapUrl.h"
#include "nsIMsgProtocolInfo.h"
#include "nsIMsgMailSession.h"
#include "nsIMAPNamespace.h"
#include "nsArrayUtils.h"
#include "nsITimer.h"
#include "nsMsgUtils.h"
#include "nsServiceManagerUtils.h"
#include "nsComponentManagerUtils.h"
#include "nsCRTGlue.h"
#include "mozilla/Services.h"

using namespace mozilla;

#define PREF_TRASH_FOLDER_NAME "trash_folder_name"
#define DEFAULT_TRASH_FOLDER_NAME "Trash"

static NS_DEFINE_CID(kImapProtocolCID, NS_IMAPPROTOCOL_CID);
static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
static NS_DEFINE_CID(kSubscribableServerCID, NS_SUBSCRIBABLESERVER_CID);
static NS_DEFINE_CID(kCImapHostSessionListCID, NS_IIMAPHOSTSESSIONLIST_CID);

NS_IMPL_ADDREF_INHERITED(nsImapIncomingServer, nsMsgIncomingServer)
NS_IMPL_RELEASE_INHERITED(nsImapIncomingServer, nsMsgIncomingServer)

NS_INTERFACE_MAP_BEGIN(nsImapIncomingServer)
  NS_INTERFACE_MAP_ENTRY(nsIImapServerSink)
  NS_INTERFACE_MAP_ENTRY(nsIImapIncomingServer)
  NS_INTERFACE_MAP_ENTRY(nsISubscribableServer)
  NS_INTERFACE_MAP_ENTRY(nsIUrlListener)
NS_INTERFACE_MAP_END_INHERITING(nsMsgIncomingServer)

nsImapIncomingServer::nsImapIncomingServer()
  : mLock("nsImapIncomingServer.mLock")
{
  m_capability = kCapabilityUndefined;
  mDoingSubscribeDialog = false;
  mDoingLsub = false;
  m_canHaveFilters = true;
  m_userAuthenticated = false;
  m_shuttingDown = false;
}

nsImapIncomingServer::~nsImapIncomingServer()
{
  mozilla::DebugOnly<nsresult> rv = ClearInner();
  NS_ASSERTION(NS_SUCCEEDED(rv), "ClearInner failed");
  CloseCachedConnections();
}

NS_IMETHODIMP nsImapIncomingServer::SetKey(const nsACString& aKey)  // override nsMsgIncomingServer's implementation...
{
  nsMsgIncomingServer::SetKey(aKey);

  // okay now that the key has been set, we need to add ourselves to the
  // host session list...

  // every time we create an imap incoming server, we need to add it to the
  // host session list!!

  nsresult rv;
  nsCOMPtr<nsIImapHostSessionList> hostSession = do_GetService(kCImapHostSessionListCID, &rv);
  NS_ENSURE_SUCCESS(rv, rv);

  nsCString key(aKey);
  hostSession->AddHostToList(key.get(), this);
  nsMsgImapDeleteModel deleteModel = nsMsgImapDeleteModels::MoveToTrash; // default to trash
  GetDeleteModel(&deleteModel);
  hostSession->SetDeleteIsMoveToTrashForHost(key.get(), deleteModel == nsMsgImapDeleteModels::MoveToTrash);
  hostSession->SetShowDeletedMessagesForHost(key.get(), deleteModel == nsMsgImapDeleteModels::IMAPDelete);

  nsAutoCString onlineDir;
  rv = GetServerDirectory(onlineDir);
  NS_ENSURE_SUCCESS(rv, rv);
  if (!onlineDir.IsEmpty())
    hostSession->SetOnlineDirForHost(key.get(), onlineDir.get());

  nsCString personalNamespace;
  nsCString publicNamespace;
  nsCString otherUsersNamespace;

  rv = GetPersonalNamespace(personalNamespace);
  NS_ENSURE_SUCCESS(rv, rv);
  rv = GetPublicNamespace(publicNamespace);
  NS_ENSURE_SUCCESS(rv, rv);
  rv = GetOtherUsersNamespace(otherUsersNamespace);
  NS_ENSURE_SUCCESS(rv, rv);

  if (personalNamespace.IsEmpty() && publicNamespace.IsEmpty() && otherUsersNamespace.IsEmpty())
      personalNamespace.AssignLiteral("\"\"");

  hostSession->SetNamespaceFromPrefForHost(key.get(), personalNamespace.get(),
                                           kPersonalNamespace);

  if (!publicNamespace.IsEmpty())
      hostSession->SetNamespaceFromPrefForHost(key.get(), publicNamespace.get(),
                                               kPublicNamespace);

  if (!otherUsersNamespace.IsEmpty())
      hostSession->SetNamespaceFromPrefForHost(key.get(), otherUsersNamespace.get(),
                                               kOtherUsersNamespace);
  return rv;
}

// construct the pretty name to show to the user if they haven't
// specified one. This should be overridden for news and mail.
NS_IMETHODIMP
nsImapIncomingServer::GetConstructedPrettyName(nsAString& retval)
{
  nsAutoCString username;
  nsAutoCString hostName;
  nsresult rv;

  nsCOMPtr<nsIMsgAccountManager> accountManager =
           do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
  NS_ENSURE_SUCCESS(rv,rv);

  nsCOMPtr<nsIMsgIdentity> identity;
  rv = accountManager->GetFirstIdentityForServer(this, getter_AddRefs(identity));
  NS_ENSURE_SUCCESS(rv,rv);

  nsAutoString emailAddress;

  if (NS_SUCCEEDED(rv) && identity)
  {
    nsCString identityEmailAddress;
    identity->GetEmail(identityEmailAddress);
    CopyASCIItoUTF16(identityEmailAddress, emailAddress);
  }
  else
  {
    rv = GetRealUsername(username);
    NS_ENSURE_SUCCESS(rv,rv);
    rv = GetRealHostName(hostName);
    NS_ENSURE_SUCCESS(rv,rv);
    if (!username.IsEmpty() && !hostName.IsEmpty()) {
      CopyASCIItoUTF16(username, emailAddress);
      emailAddress.Append('@');
      emailAddress.Append(NS_ConvertASCIItoUTF16(hostName));
    }
  }

  return GetFormattedStringFromName(emailAddress, "imapDefaultAccountName", retval);
}


NS_IMETHODIMP nsImapIncomingServer::GetLocalStoreType(nsACString& type)
{
  type.AssignLiteral("imap");
  return NS_OK;
}

NS_IMETHODIMP nsImapIncomingServer::GetLocalDatabaseType(nsACString& type)
{
  type.AssignLiteral("imap");
  return NS_OK;
}

NS_IMETHODIMP
nsImapIncomingServer::GetServerDirectory(nsACString& serverDirectory)
{
  return GetCharValue("server_sub_directory", serverDirectory);
}

NS_IMETHODIMP
nsImapIncomingServer::SetServerDirectory(const nsACString& serverDirectory)
{
  nsCString serverKey;
  nsresult rv = GetKey(serverKey);
  if (NS_SUCCEEDED(rv))
  {
    nsCOMPtr<nsIImapHostSessionList> hostSession = do_GetService(kCImapHostSessionListCID, &rv);
    if (NS_SUCCEEDED(rv))
      hostSession->SetOnlineDirForHost(serverKey.get(), PromiseFlatCString(serverDirectory).get());
  }
  return SetCharValue("server_sub_directory", serverDirectory);
}

NS_IMETHODIMP
nsImapIncomingServer::GetOverrideNamespaces(bool *bVal)
{
  return GetBoolValue("override_namespaces", bVal);
}

NS_IMETHODIMP
nsImapIncomingServer::SetOverrideNamespaces(bool bVal)
{
  nsCString serverKey;
  GetKey(serverKey);
  if (!serverKey.IsEmpty())
  {
    nsresult rv;
    nsCOMPtr<nsIImapHostSessionList> hostSession = do_GetService(kCImapHostSessionListCID, &rv);
    if (NS_SUCCEEDED(rv))
      hostSession->SetNamespacesOverridableForHost(serverKey.get(), bVal);
  }
  return SetBoolValue("override_namespaces", bVal);
}

NS_IMETHODIMP
nsImapIncomingServer::GetUsingSubscription(bool *bVal)
{
  return GetBoolValue("using_subscription", bVal);
}

NS_IMETHODIMP
nsImapIncomingServer::SetUsingSubscription(bool bVal)
{
  nsCString serverKey;
  GetKey(serverKey);
  if (!serverKey.IsEmpty())
  {
    nsresult rv;
    nsCOMPtr<nsIImapHostSessionList> hostSession = do_GetService(kCImapHostSessionListCID, &rv);
    if (NS_SUCCEEDED(rv))
      hostSession->SetHostIsUsingSubscription(serverKey.get(), bVal);
  }
  return SetBoolValue("using_subscription", bVal);
}

NS_IMETHODIMP
nsImapIncomingServer::GetMaximumConnectionsNumber(int32_t *aMaxConnections)
{
  NS_ENSURE_ARG_POINTER(aMaxConnections);

  nsresult rv = GetIntValue("max_cached_connections", aMaxConnections);
  // Get our maximum connection count. We need at least 1. If the value is 0,
  // we use the default of 5. If it's negative, we treat that as 1.
  if (NS_SUCCEEDED(rv) && *aMaxConnections > 0)
    return NS_OK;

  *aMaxConnections = (NS_FAILED(rv) || (*aMaxConnections == 0)) ? 5 : 1;
  (void)SetMaximumConnectionsNumber(*aMaxConnections);

  return NS_OK;
}

NS_IMETHODIMP
nsImapIncomingServer::SetMaximumConnectionsNumber(int32_t aMaxConnections)
{
  return SetIntValue("max_cached_connections", aMaxConnections);
}

NS_IMPL_SERVERPREF_BOOL(nsImapIncomingServer, DualUseFolders,
                        "dual_use_folders")

NS_IMPL_SERVERPREF_STR(nsImapIncomingServer, AdminUrl,
                       "admin_url")

NS_IMPL_SERVERPREF_BOOL(nsImapIncomingServer, CleanupInboxOnExit,
                        "cleanup_inbox_on_exit")

NS_IMPL_SERVERPREF_BOOL(nsImapIncomingServer, OfflineDownload,
                        "offline_download")

NS_IMPL_SERVERPREF_INT(nsImapIncomingServer, EmptyTrashThreshhold,
                       "empty_trash_threshhold")

NS_IMPL_SERVERPREF_BOOL(nsImapIncomingServer, DownloadBodiesOnGetNewMail,
                        "download_bodies_on_get_new_mail")

NS_IMPL_SERVERPREF_BOOL(nsImapIncomingServer, AutoSyncOfflineStores,
                        "autosync_offline_stores")

NS_IMPL_SERVERPREF_BOOL(nsImapIncomingServer, UseIdle,
                        "use_idle")

NS_IMPL_SERVERPREF_BOOL(nsImapIncomingServer, CheckAllFoldersForNew,
                        "check_all_folders_for_new")

NS_IMPL_SERVERPREF_BOOL(nsImapIncomingServer, UseCondStore,
                        "use_condstore")

NS_IMPL_SERVERPREF_BOOL(nsImapIncomingServer, IsGMailServer,
                        "is_gmail")

NS_IMPL_SERVERPREF_BOOL(nsImapIncomingServer, UseCompressDeflate,
                        "use_compress_deflate")

NS_IMPL_SERVERPREF_INT(nsImapIncomingServer, AutoSyncMaxAgeDays,
                        "autosync_max_age_days")

NS_IMETHODIMP
nsImapIncomingServer::GetShuttingDown(bool *retval)
{
  NS_ENSURE_ARG_POINTER(retval);
  *retval = m_shuttingDown;
  return NS_OK;
}

NS_IMETHODIMP
nsImapIncomingServer::SetShuttingDown(bool val)
{
  m_shuttingDown = val;
  return NS_OK;
}

NS_IMETHODIMP
nsImapIncomingServer::GetDeleteModel(int32_t *retval)
{
  NS_ENSURE_ARG(retval);
  return GetIntValue("delete_model", retval);
}

NS_IMETHODIMP
nsImapIncomingServer::SetDeleteModel(int32_t ivalue)
{
  nsresult rv = SetIntValue("delete_model", ivalue);
  if (NS_SUCCEEDED(rv))
  {
    nsCOMPtr<nsIImapHostSessionList> hostSession =
        do_GetService(kCImapHostSessionListCID, &rv);
    NS_ENSURE_SUCCESS(rv,rv);
    hostSession->SetDeleteIsMoveToTrashForHost(m_serverKey.get(), ivalue == nsMsgImapDeleteModels::MoveToTrash);
    hostSession->SetShowDeletedMessagesForHost(m_serverKey.get(), ivalue == nsMsgImapDeleteModels::IMAPDelete);

    nsAutoString trashFolderName;
    nsresult rv = GetTrashFolderName(trashFolderName);
    if (NS_SUCCEEDED(rv))
    {
      nsAutoCString trashFolderNameUtf7;
      rv = CopyUTF16toMUTF7(trashFolderName, trashFolderNameUtf7);
      if (NS_SUCCEEDED(rv))
      {
        nsCOMPtr<nsIMsgFolder> trashFolder;
        // XXX GetFolder only returns folders one level below root.
        //     trashFolderName is a leaf name. So this will not find INBOX.Trash
        rv = GetFolder(trashFolderNameUtf7, getter_AddRefs(trashFolder));
        NS_ENSURE_SUCCESS(rv, rv);
        nsCString trashURI;
        trashFolder->GetURI(trashURI);
        GetMsgFolderFromURI(trashFolder, trashURI, getter_AddRefs(trashFolder));
        if (NS_SUCCEEDED(rv) && trashFolder)
        {
           // If the trash folder is used, set the flag, otherwise clear it.
          if (ivalue == nsMsgImapDeleteModels::MoveToTrash)
            trashFolder->SetFlag(nsMsgFolderFlags::Trash);
          else
            trashFolder->ClearFlag(nsMsgFolderFlags::Trash);
        }
      }
    }
  }
  return rv;
}

NS_IMPL_SERVERPREF_INT(nsImapIncomingServer, TimeOutLimits,
                       "timeout")

NS_IMPL_SERVERPREF_STR(nsImapIncomingServer, ServerIDPref,
                       "serverIDResponse")

NS_IMPL_SERVERPREF_STR(nsImapIncomingServer, PersonalNamespace,
                       "namespace.personal")

NS_IMPL_SERVERPREF_STR(nsImapIncomingServer, PublicNamespace,
                       "namespace.public")

NS_IMPL_SERVERPREF_STR(nsImapIncomingServer, OtherUsersNamespace,
                       "namespace.other_users")

NS_IMPL_SERVERPREF_BOOL(nsImapIncomingServer, FetchByChunks,
                       "fetch_by_chunks")

NS_IMPL_SERVERPREF_BOOL(nsImapIncomingServer, MimePartsOnDemand,
                       "mime_parts_on_demand")

NS_IMPL_SERVERPREF_BOOL(nsImapIncomingServer, SendID,
                       "send_client_info")

NS_IMPL_SERVERPREF_BOOL(nsImapIncomingServer, CapabilityACL,
                       "cacheCapa.acl")
NS_IMPL_SERVERPREF_BOOL(nsImapIncomingServer, CapabilityQuota,
                       "cacheCapa.quota")

NS_IMETHODIMP
nsImapIncomingServer::GetIsAOLServer(bool *aBool)
{
  NS_ENSURE_ARG_POINTER(aBool);
  *aBool = ((m_capability & kAOLImapCapability) != 0);
  return NS_OK;
}

NS_IMETHODIMP
nsImapIncomingServer::SetIsAOLServer(bool aBool)
{
  if (aBool)
    m_capability |= kAOLImapCapability;
  else
    m_capability &= ~kAOLImapCapability;
  return NS_OK;
}

NS_IMETHODIMP
nsImapIncomingServer::UpdateTrySTARTTLSPref(bool aStartTLSSucceeded)
{
  SetSocketType(aStartTLSSucceeded ? nsMsgSocketType::alwaysSTARTTLS :
                                     nsMsgSocketType::plain);
  return NS_OK;
}

NS_IMETHODIMP
nsImapIncomingServer::GetImapConnectionAndLoadUrl(nsIImapUrl* aImapUrl,
                                                  nsISupports* aConsumer)
{
  nsCOMPtr<nsIImapProtocol> aProtocol;

  nsresult rv = GetImapConnection(aImapUrl, getter_AddRefs(aProtocol));
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(aImapUrl, &rv);
  if (aProtocol)
  {
    rv = aProtocol->LoadImapUrl(mailnewsurl, aConsumer);
    // *** jt - in case of the time out situation or the connection gets
    // terminated by some unforseen problems let's give it a second chance
    // to run the url
    if (NS_FAILED(rv))
    {
      NS_ASSERTION(false, "shouldn't get an error loading url");
      rv = aProtocol->LoadImapUrl(mailnewsurl, aConsumer);
    }
  }
  else
  {   // unable to get an imap connection to run the url; add to the url
     // queue
    nsImapProtocol::LogImapUrl("queuing url", aImapUrl);
    PR_CEnterMonitor(this);
    m_urlQueue.AppendObject(aImapUrl);
    m_urlConsumers.AppendElement(aConsumer);
    NS_IF_ADDREF(aConsumer);
    PR_CExitMonitor(this);
    // let's try running it now - maybe the connection is free now.
    bool urlRun;
    rv = LoadNextQueuedUrl(nullptr, &urlRun);
  }

  return rv;
}


NS_IMETHODIMP
nsImapIncomingServer::PrepareToRetryUrl(nsIImapUrl *aImapUrl, nsIImapMockChannel **aChannel)
{
  NS_ENSURE_ARG_POINTER(aChannel);
  NS_ENSURE_ARG_POINTER(aImapUrl);
  // maybe there's more we could do here, but this is all we need now.
  return aImapUrl->GetMockChannel(aChannel);
}

NS_IMETHODIMP
nsImapIncomingServer::SuspendUrl(nsIImapUrl *aImapUrl)
{
  NS_ENSURE_ARG_POINTER(aImapUrl);
  nsImapProtocol::LogImapUrl("suspending url", aImapUrl);
  PR_CEnterMonitor(this);
  m_urlQueue.AppendObject(aImapUrl);
  m_urlConsumers.AppendElement(nullptr);
  PR_CExitMonitor(this);
  return NS_OK;
}

NS_IMETHODIMP
nsImapIncomingServer::RetryUrl(nsIImapUrl *aImapUrl, nsIImapMockChannel *aChannel)
{
  nsresult rv;
  // Get current thread envent queue
  aImapUrl->SetMockChannel(aChannel);
  nsCOMPtr <nsIImapProtocol> protocolInstance;
  nsImapProtocol::LogImapUrl("creating protocol instance to retry queued url", aImapUrl);
  nsCOMPtr<nsIThread> thread(do_GetCurrentThread());
  rv = GetImapConnection(aImapUrl, getter_AddRefs(protocolInstance));
  if (NS_SUCCEEDED(rv) && protocolInstance)
  {
    nsCOMPtr<nsIURI> url = do_QueryInterface(aImapUrl, &rv);
    if (NS_SUCCEEDED(rv) && url)
    {
      nsImapProtocol::LogImapUrl("retrying  url", aImapUrl);
      rv = protocolInstance->LoadImapUrl(url, nullptr); // ### need to save the display consumer.
      NS_ASSERTION(NS_SUCCEEDED(rv), "failed running queued url");
    }
  }
  return rv;
}

// checks to see if there are any queued urls on this incoming server,
// and if so, tries to run the oldest one. Returns true if the url is run
// on the passed in protocol connection.
NS_IMETHODIMP
nsImapIncomingServer::LoadNextQueuedUrl(nsIImapProtocol *aProtocol, bool *aResult)
{
  if (WeAreOffline())
    return NS_MSG_ERROR_OFFLINE;

  nsresult rv = NS_OK;
  bool urlRun = false;
  bool keepGoing = true;
  nsCOMPtr <nsIImapProtocol>  protocolInstance ;

  MutexAutoLock mon(mLock);
  int32_t cnt = m_urlQueue.Count();

  while (cnt > 0 && !urlRun && keepGoing)
  {
    nsCOMPtr<nsIImapUrl> aImapUrl(m_urlQueue[0]);
    nsCOMPtr<nsIMsgMailNewsUrl> aMailNewsUrl(do_QueryInterface(aImapUrl, &rv));

    bool removeUrlFromQueue = false;
    if (aImapUrl)
    {
      nsImapProtocol::LogImapUrl("considering playing queued url", aImapUrl);
      rv = DoomUrlIfChannelHasError(aImapUrl, &removeUrlFromQueue);
      NS_ENSURE_SUCCESS(rv, rv);
      // if we didn't doom the url, lets run it.
      if (!removeUrlFromQueue)
      {
        nsISupports *aConsumer = m_urlConsumers.ElementAt(0);
        NS_IF_ADDREF(aConsumer);

        nsImapProtocol::LogImapUrl("creating protocol instance to play queued url", aImapUrl);
        rv = GetImapConnection(aImapUrl, getter_AddRefs(protocolInstance));
        if (NS_SUCCEEDED(rv) && protocolInstance)
        {
          nsCOMPtr<nsIURI> url = do_QueryInterface(aImapUrl, &rv);
          if (NS_SUCCEEDED(rv) && url)
          {
            nsImapProtocol::LogImapUrl("playing queued url", aImapUrl);
            rv = protocolInstance->LoadImapUrl(url, aConsumer);
            NS_ASSERTION(NS_SUCCEEDED(rv), "failed running queued url");
            bool isInbox;
            protocolInstance->IsBusy(&urlRun, &isInbox);
            if (!urlRun)
              nsImapProtocol::LogImapUrl("didn't need to run", aImapUrl);
            removeUrlFromQueue = true;
          }
        }
        else
        {
          nsImapProtocol::LogImapUrl("failed creating protocol instance to play queued url", aImapUrl);
          keepGoing = false;
        }
        NS_IF_RELEASE(aConsumer);
      }
      if (removeUrlFromQueue)
      {
        m_urlQueue.RemoveObjectAt(0);
        m_urlConsumers.RemoveElementAt(0);
      }
    }
    cnt = m_urlQueue.Count();
  }
  if (aResult)
    *aResult = urlRun && aProtocol && aProtocol == protocolInstance;

  return rv;
}

NS_IMETHODIMP
nsImapIncomingServer::AbortQueuedUrls()
{
  nsresult rv = NS_OK;

  MutexAutoLock mon(mLock);
  int32_t cnt = m_urlQueue.Count();

  while (cnt > 0)
  {
    nsCOMPtr<nsIImapUrl> aImapUrl(m_urlQueue[cnt - 1]);
    bool removeUrlFromQueue = false;

    if (aImapUrl)
    {
      rv = DoomUrlIfChannelHasError(aImapUrl, &removeUrlFromQueue);
      NS_ENSURE_SUCCESS(rv, rv);
      if (removeUrlFromQueue)
      {
        m_urlQueue.RemoveObjectAt(cnt - 1);
        m_urlConsumers.RemoveElementAt(cnt - 1);
      }
    }
    cnt--;
  }

  return rv;
}

// if this url has a channel with an error, doom it and its mem cache entries,
// and notify url listeners.
nsresult nsImapIncomingServer::DoomUrlIfChannelHasError(nsIImapUrl *aImapUrl, bool *urlDoomed)
{
  nsresult rv = NS_OK;

  nsCOMPtr<nsIMsgMailNewsUrl> aMailNewsUrl(do_QueryInterface(aImapUrl, &rv));

  if (aMailNewsUrl && aImapUrl)
  {
    nsCOMPtr <nsIImapMockChannel> mockChannel;

    if (NS_SUCCEEDED(aImapUrl->GetMockChannel(getter_AddRefs(mockChannel))) && mockChannel)
    {
      nsresult requestStatus;
      nsCOMPtr<nsIRequest> request = do_QueryInterface(mockChannel);
      if (!request)
        return NS_ERROR_FAILURE;
      request->GetStatus(&requestStatus);
      if (NS_FAILED(requestStatus))
      {
        nsresult res;
        *urlDoomed = true;
        nsImapProtocol::LogImapUrl("dooming url", aImapUrl);

        mockChannel->Close(); // try closing it to get channel listener nulled out.

        if (aMailNewsUrl)
        {
          nsCOMPtr<nsICacheEntry> cacheEntry;
          res = aMailNewsUrl->GetMemCacheEntry(getter_AddRefs(cacheEntry));
          if (NS_SUCCEEDED(res) && cacheEntry)
            cacheEntry->AsyncDoom(nullptr);
          // we're aborting this url - tell listeners
          aMailNewsUrl->SetUrlState(false, NS_MSG_ERROR_URL_ABORTED);
        }
      }
    }
  }
  return rv;
}

NS_IMETHODIMP
nsImapIncomingServer::RemoveConnection(nsIImapProtocol* aImapConnection)
{
  PR_CEnterMonitor(this);
  if (aImapConnection)
    m_connectionCache.RemoveObject(aImapConnection);

  PR_CExitMonitor(this);
  return NS_OK;
}

bool
nsImapIncomingServer::ConnectionTimeOut(nsIImapProtocol* aConnection)
{
  bool retVal = false;
  if (!aConnection) return retVal;
  nsresult rv;

  int32_t timeoutInMinutes = 0;
  rv = GetTimeOutLimits(&timeoutInMinutes);
  if (NS_FAILED(rv) || timeoutInMinutes <= 0 || timeoutInMinutes > 29)
  {
    timeoutInMinutes = 29;
    SetTimeOutLimits(timeoutInMinutes);
  }

  PRTime cacheTimeoutLimits = timeoutInMinutes * 60 * PR_USEC_PER_SEC;
  PRTime lastActiveTimeStamp;
  rv = aConnection->GetLastActiveTimeStamp(&lastActiveTimeStamp);

  if (PR_Now() - lastActiveTimeStamp >= cacheTimeoutLimits)
  {
      nsCOMPtr<nsIImapProtocol> aProtocol(do_QueryInterface(aConnection,
                                                            &rv));
      if (NS_SUCCEEDED(rv) && aProtocol)
      {
        RemoveConnection(aConnection);
        aProtocol->TellThreadToDie(false);
        retVal = true;
      }
  }
  return retVal;
}

nsresult
nsImapIncomingServer::GetImapConnection(nsIImapUrl * aImapUrl,
                                        nsIImapProtocol ** aImapConnection)
{
  NS_ENSURE_ARG_POINTER(aImapUrl);

  nsresult rv = NS_OK;
  bool canRunUrlImmediately = false;
  bool canRunButBusy = false;
  nsCOMPtr<nsIImapProtocol> connection;
  nsCOMPtr<nsIImapProtocol> freeConnection;
  bool isBusy = false;
  bool isInboxConnection = false;

  PR_CEnterMonitor(this);

  int32_t maxConnections;
  (void)GetMaximumConnectionsNumber(&maxConnections);

  int32_t cnt = m_connectionCache.Count();

  *aImapConnection = nullptr;
  // iterate through the connection cache for a connection that can handle this url.
  bool userCancelled = false;

  // loop until we find a connection that can run the url, or doesn't have to wait?
  for (int32_t i = cnt - 1; i >= 0 && !canRunUrlImmediately && !canRunButBusy; i--)
  {
    connection = m_connectionCache[i];
    if (connection)
    {
      bool badConnection = ConnectionTimeOut(connection);
      if (!badConnection)
      {
        badConnection = NS_FAILED(connection->CanHandleUrl(aImapUrl,
                                                           &canRunUrlImmediately,
                                                           &canRunButBusy));
#ifdef DEBUG_bienvenu
        nsAutoCString curSelectedFolderName;
        if (connection)
          connection->GetSelectedMailboxName(getter_Copies(curSelectedFolderName));
        // check that no other connection is in the same selected state.
        if (!curSelectedFolderName.IsEmpty())
        {
          for (uint32_t j = 0; j < cnt; j++)
          {
            if (j != i)
            {
              nsCOMPtr<nsIImapProtocol> otherConnection = do_QueryElementAt(m_connectionCache, j);
              if (otherConnection)
              {
                nsAutoCString otherSelectedFolderName;
                otherConnection->GetSelectedMailboxName(getter_Copies(otherSelectedFolderName));
                NS_ASSERTION(!curSelectedFolderName.Equals(otherSelectedFolderName), "two connections selected on same folder");
              }

            }
          }
        }
#endif // DEBUG_bienvenu
      }
      if (badConnection)
      {
        connection = nullptr;
        continue;
      }
    }

    // if this connection is wrong, but it's not busy, check if we should designate
    // it as the free connection.
    if (!canRunUrlImmediately && !canRunButBusy && connection)
    {
        rv = connection->IsBusy(&isBusy, &isInboxConnection);
        if (NS_FAILED(rv))
          continue;
        // if max connections is <= 1, we have to re-use the inbox connection.
        if (!isBusy && (!isInboxConnection || maxConnections <= 1))
        {
          if (!freeConnection)
            freeConnection = connection;
          else  // check which is the better free connection to use.
          {     // We prefer one not in the selected state.
            nsAutoCString selectedFolderName;
            connection->GetSelectedMailboxName(getter_Copies(selectedFolderName));
            if (selectedFolderName.IsEmpty())
              freeConnection = connection;
          }
        }
    }
    // don't leave this loop with connection set if we can't use it!
    if (!canRunButBusy && !canRunUrlImmediately)
      connection = nullptr;
  }

  nsImapState requiredState;
  aImapUrl->GetRequiredImapState(&requiredState);
  // refresh cnt in case we killed one or more dead connections. This
  // will prevent us from not spinning up a new connection when all
  // connections were dead.
  cnt = m_connectionCache.Count();
  // if we got here and we have a connection, then we should return it!
  if (canRunUrlImmediately && connection)
  {
    *aImapConnection = connection;
    NS_IF_ADDREF(*aImapConnection);
  }
  else if (canRunButBusy)
  {
    // do nothing; return NS_OK; for queuing
  }
  else if (userCancelled)
  {
    rv = NS_BINDING_ABORTED;  // user cancelled
  }
  // CanHandleUrl will pretend that some types of urls require a selected state url
  // (e.g., a folder delete or msg append) but we shouldn't create new connections
  // for these types of urls if we have a free connection. So we check the actual
  // required state here.
  else if (cnt < maxConnections
      && (!freeConnection || requiredState == nsIImapUrl::nsImapSelectedState))
    rv = CreateProtocolInstance(aImapConnection);
  else if (freeConnection)
  {
    *aImapConnection = freeConnection;
    NS_IF_ADDREF(*aImapConnection);
  }
  else // cannot get anyone to handle the url queue it
  {
    if (cnt >= maxConnections)
      nsImapProtocol::LogImapUrl("exceeded connection cache limit", aImapUrl);
      // caller will queue the url
  }

  PR_CExitMonitor(this);
  return rv;
}

nsresult
nsImapIncomingServer::CreateProtocolInstance(nsIImapProtocol ** aImapConnection)
{
  // create a new connection and add it to the connection cache
  // we may need to flag the protocol connection as busy so we don't get
  // a race condition where someone else goes through this code

  int32_t authMethod;
  GetAuthMethod(&authMethod);
  nsresult rv;
  // pre-flight that we have nss - on the ui thread - for MD5 etc.
  switch (authMethod)
  {
    case nsMsgAuthMethod::passwordEncrypted:
    case nsMsgAuthMethod::secure:
    case nsMsgAuthMethod::anything:
      {
        nsCOMPtr<nsISupports> dummyUsedToEnsureNSSIsInitialized =
          do_GetService("@mozilla.org/psm;1", &rv);
        NS_ENSURE_SUCCESS(rv, rv);
      }
      break;
    default:
      break;
  }
  nsIImapProtocol * protocolInstance;
  rv = CallCreateInstance(kImapProtocolCID, &protocolInstance);
  if (NS_SUCCEEDED(rv) && protocolInstance)
  {
    nsCOMPtr<nsIImapHostSessionList> hostSession =
      do_GetService(kCImapHostSessionListCID, &rv);
    if (NS_SUCCEEDED(rv))
      rv = protocolInstance->Initialize(hostSession, this);
  }

  // take the protocol instance and add it to the connectionCache
  if (protocolInstance)
    m_connectionCache.AppendObject(protocolInstance);
  *aImapConnection = protocolInstance; // this is already ref counted.
  return rv;
}

NS_IMETHODIMP nsImapIncomingServer::CloseConnectionForFolder(nsIMsgFolder *aMsgFolder)
{
  nsresult rv = NS_OK;
  nsCOMPtr<nsIImapProtocol> connection;
  bool isBusy = false, isInbox = false;
  nsCString inFolderName;
  nsCString connectionFolderName;
  nsCOMPtr <nsIMsgImapMailFolder> imapFolder = do_QueryInterface(aMsgFolder);

  if (!imapFolder)
    return NS_ERROR_NULL_POINTER;

  int32_t cnt = m_connectionCache.Count();
  NS_ENSURE_SUCCESS(rv, rv);

  imapFolder->GetOnlineName(inFolderName);
  PR_CEnterMonitor(this);

  for (int32_t i = 0; i < cnt; ++i)
  {
    connection = m_connectionCache[i];
    if (connection)
    {
      rv = connection->GetSelectedMailboxName(getter_Copies(connectionFolderName));
      if (connectionFolderName.Equals(inFolderName))
      {
        rv = connection->IsBusy(&isBusy, &isInbox);
        if (!isBusy)
          rv = connection->TellThreadToDie(true);
        break; // found it, end of the loop
      }
    }
  }

  PR_CExitMonitor(this);
  return rv;
}

NS_IMETHODIMP nsImapIncomingServer::ResetConnection(const nsACString& folderName)
{
  nsresult rv = NS_OK;
  nsCOMPtr<nsIImapProtocol> connection;
  bool isBusy = false, isInbox = false;
  nsCString curFolderName;

  int32_t cnt = m_connectionCache.Count();

  PR_CEnterMonitor(this);

  for (int32_t i = 0; i < cnt; ++i)
  {
    connection = m_connectionCache[i];
    if (connection)
    {
      rv = connection->GetSelectedMailboxName(getter_Copies(curFolderName));
      if (curFolderName.Equals(folderName))
      {
        rv = connection->IsBusy(&isBusy, &isInbox);
        if (!isBusy)
            rv = connection->ResetToAuthenticatedState();
        break; // found it, end of the loop
      }
    }
  }

  PR_CExitMonitor(this);
  return rv;
}

NS_IMETHODIMP
nsImapIncomingServer::PerformExpand(nsIMsgWindow *aMsgWindow)
{
  nsCString password;
  nsresult rv;
  rv = GetPassword(password);
  NS_ENSURE_SUCCESS(rv, rv);

  if (password.IsEmpty())
    return NS_OK;

  rv = ResetFoldersToUnverified(nullptr);

  nsCOMPtr<nsIMsgFolder> rootMsgFolder;
  rv = GetRootFolder(getter_AddRefs(rootMsgFolder));
  NS_ENSURE_SUCCESS(rv, rv);

  if (!rootMsgFolder) return NS_ERROR_FAILURE;

  nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
  NS_ENSURE_SUCCESS(rv, rv);
  nsCOMPtr<nsIThread> thread(do_GetCurrentThread());
  rv = imapService->DiscoverAllFolders(rootMsgFolder,
                                       this, aMsgWindow, nullptr);
  return rv;
}

NS_IMETHODIMP
nsImapIncomingServer::VerifyLogon(nsIUrlListener *aUrlListener,
                                  nsIMsgWindow *aMsgWindow, nsIURI **aURL)
{
  nsresult rv;

  nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
  NS_ENSURE_SUCCESS(rv, rv);
  nsCOMPtr<nsIMsgFolder> rootFolder;
  // this will create the resource if it doesn't exist, but it shouldn't
  // do anything on disk.
  rv = GetRootFolder(getter_AddRefs(rootFolder));
  NS_ENSURE_SUCCESS(rv, rv);
  return imapService->VerifyLogon(rootFolder, aUrlListener, aMsgWindow, aURL);
}

NS_IMETHODIMP nsImapIncomingServer::PerformBiff(nsIMsgWindow* aMsgWindow)
{
  nsCOMPtr<nsIMsgFolder> rootMsgFolder;
  nsresult rv = GetRootMsgFolder(getter_AddRefs(rootMsgFolder));
  if(NS_SUCCEEDED(rv))
  {
    SetPerformingBiff(true);
    rv = rootMsgFolder->GetNewMessages(aMsgWindow, nullptr);
  }
  return rv;
}


NS_IMETHODIMP
nsImapIncomingServer::CloseCachedConnections()
{
  nsCOMPtr<nsIImapProtocol> connection;
  PR_CEnterMonitor(this);

  // iterate through the connection cache closing open connections.
  int32_t cnt = m_connectionCache.Count();

  for (int32_t i = cnt; i > 0; --i)
  {
    connection = m_connectionCache[i - 1];
    if (connection)
      connection->TellThreadToDie(true);
  }

  PR_CExitMonitor(this);
  return NS_OK;
}

nsresult
nsImapIncomingServer::CreateRootFolderFromUri(const nsCString &serverUri,
                                              nsIMsgFolder **rootFolder)
{
  nsImapMailFolder *newRootFolder = new nsImapMailFolder;
  if (!newRootFolder)
    return NS_ERROR_OUT_OF_MEMORY;
  newRootFolder->Init(serverUri.get());
  NS_ADDREF(*rootFolder = newRootFolder);
  return NS_OK;
}

// nsIImapServerSink impl
// aNewFolder will not be set if we're listing for the subscribe UI, since that's the way 4.x worked.
NS_IMETHODIMP nsImapIncomingServer::PossibleImapMailbox(const nsACString& folderPath,
                                                        char hierarchyDelimiter,
                                                        int32_t boxFlags, bool *aNewFolder)
{
  NS_ENSURE_ARG_POINTER(aNewFolder);
  NS_ENSURE_TRUE(!folderPath.IsEmpty(), NS_ERROR_FAILURE);

  // folderPath is in canonical format, i.e., hierarchy separator has been replaced with '/'
  nsresult rv;
  bool found = false;
  bool haveParent = false;
  nsCOMPtr<nsIMsgImapMailFolder> hostFolder;
  nsCOMPtr<nsIMsgFolder> aFolder;
  bool explicitlyVerify = false;

  *aNewFolder = false;
  nsCOMPtr<nsIMsgFolder> a_nsIFolder;
  rv = GetRootFolder(getter_AddRefs(a_nsIFolder));

  if(NS_FAILED(rv))
    return rv;

  nsAutoCString dupFolderPath(folderPath);
  if (dupFolderPath.Last() == '/')
  {
    dupFolderPath.SetLength(dupFolderPath.Length()-1);
    if (dupFolderPath.IsEmpty())
      return NS_ERROR_FAILURE;
    // *** this is what we did in 4.x in order to list uw folder only
    // mailbox in order to get the \NoSelect flag
    explicitlyVerify = !(boxFlags & kNameSpace);
  }
  if (mDoingSubscribeDialog)
  {
    // Make sure the imapmailfolder object has the right delimiter because the unsubscribed
    // folders (those not in the 'lsub' list) have the delimiter set to the default ('^').
    if (a_nsIFolder && !dupFolderPath.IsEmpty())
    {
      nsCOMPtr<nsIMsgFolder> msgFolder;
      bool isNamespace = false;
      bool noSelect = false;

      rv = a_nsIFolder->FindSubFolder(dupFolderPath, getter_AddRefs(msgFolder));
      NS_ENSURE_SUCCESS(rv,rv);
      m_subscribeFolders.AppendObject(msgFolder);
      noSelect = (boxFlags & kNoselect) != 0;
      nsCOMPtr<nsIMsgImapMailFolder> imapFolder = do_QueryInterface(msgFolder, &rv);
      NS_ENSURE_SUCCESS(rv,rv);
      imapFolder->SetHierarchyDelimiter(hierarchyDelimiter);
      isNamespace = (boxFlags & kNameSpace) != 0;
      if (!isNamespace)
        rv = AddTo(dupFolderPath, mDoingLsub && !noSelect/* add as subscribed */,
                   !noSelect, mDoingLsub /* change if exists */);
      NS_ENSURE_SUCCESS(rv,rv);
      return rv;
    }
  }

  hostFolder = do_QueryInterface(a_nsIFolder, &rv);
  if (NS_FAILED(rv))
    return rv;

  nsAutoCString tempFolderName(dupFolderPath);
  nsAutoCString tokenStr, remStr, changedStr;
  int32_t slashPos = tempFolderName.FindChar('/');
  if (slashPos > 0)
  {
    tokenStr = StringHead(tempFolderName, slashPos);
    remStr = Substring(tempFolderName, slashPos);
  }
  else
    tokenStr.Assign(tempFolderName);

  if ((int32_t(PL_strcasecmp(tokenStr.get(), "INBOX"))==0) && (strcmp(tokenStr.get(), "INBOX") != 0))
    changedStr.Append("INBOX");
  else
    changedStr.Append(tokenStr);

  if (slashPos > 0 )
    changedStr.Append(remStr);

  dupFolderPath.Assign(changedStr);
  nsAutoCString folderName(dupFolderPath);

  nsAutoCString uri;
  nsCString serverUri;
  GetServerURI(serverUri);
  uri.Assign(serverUri);
  int32_t leafPos = folderName.RFindChar('/');
  nsAutoCString parentName(folderName);
  nsAutoCString parentUri(uri);

  if (leafPos > 0)
  {
    // If there is a hierarchy, there is a parent.
    // Don't strip off slash if it's the first character
    parentName.SetLength(leafPos);
    folderName.Cut(0, leafPos + 1);	// get rid of the parent name
    haveParent = true;
    parentUri.Append('/');
    parentUri.Append(parentName);
  }
  if (MsgLowerCaseEqualsLiteral(folderPath, "inbox") &&
    hierarchyDelimiter == kOnlineHierarchySeparatorNil)
  {
    hierarchyDelimiter = '/'; // set to default in this case (as in 4.x)
    hostFolder->SetHierarchyDelimiter(hierarchyDelimiter);
  }

  nsCOMPtr <nsIMsgFolder> child;

  // nsCString possibleName(aSpec->allocatedPathName);
  uri.Append('/');
  uri.Append(dupFolderPath);
  bool caseInsensitive = MsgLowerCaseEqualsLiteral(dupFolderPath, "inbox");
  a_nsIFolder->GetChildWithURI(uri, true, caseInsensitive, getter_AddRefs(child));
  // if we couldn't find this folder by URI, tell the imap code it's a new folder to us
  *aNewFolder = !child;
  if (child)
    found = true;
  if (!found)
  {
    // trying to find/discover the parent
    if (haveParent)
    {
      nsCOMPtr <nsIMsgFolder> parent;
      bool parentIsNew;
      caseInsensitive = MsgLowerCaseEqualsLiteral(parentName, "inbox");
      a_nsIFolder->GetChildWithURI(parentUri, true, caseInsensitive, getter_AddRefs(parent));
      if (!parent /* || parentFolder->GetFolderNeedsAdded()*/)
      {
        PossibleImapMailbox(parentName, hierarchyDelimiter, kNoselect | // be defensive
          ((boxFlags  & //only inherit certain flags from the child
          (kPublicMailbox | kOtherUsersMailbox | kPersonalMailbox))), &parentIsNew);
      }
    }
    rv = hostFolder->CreateClientSubfolderInfo(dupFolderPath, hierarchyDelimiter,boxFlags, false);
    NS_ENSURE_SUCCESS(rv, rv);
    caseInsensitive = MsgLowerCaseEqualsLiteral(dupFolderPath, "inbox");
    a_nsIFolder->GetChildWithURI(uri, true, caseInsensitive, getter_AddRefs(child));
  }
  if (child)
  {
    nsCOMPtr <nsIMsgImapMailFolder> imapFolder = do_QueryInterface(child);
    if (imapFolder)
    {
      nsAutoCString onlineName;
      nsAutoString unicodeName;
      imapFolder->SetVerifiedAsOnlineFolder(true);
      imapFolder->SetHierarchyDelimiter(hierarchyDelimiter);
      if (boxFlags & kImapTrash)
      {
        int32_t deleteModel;
        GetDeleteModel(&deleteModel);
        if (deleteModel == nsMsgImapDeleteModels::MoveToTrash)
          child->SetFlag(nsMsgFolderFlags::Trash);
      }

      imapFolder->SetBoxFlags(boxFlags);
      imapFolder->SetExplicitlyVerify(explicitlyVerify);
      imapFolder->GetOnlineName(onlineName);

      // online name needs to use the correct hierarchy delimiter (I think...)
      // or the canonical path - one or the other, but be consistent.
      MsgReplaceChar(dupFolderPath, '/', hierarchyDelimiter);
      if (hierarchyDelimiter != '/')
        nsImapUrl::UnescapeSlashes(dupFolderPath.BeginWriting());

      // GMail gives us a localized name for the inbox but doesn't let
      // us select that localized name.
      if (boxFlags & kImapInbox)
        imapFolder->SetOnlineName(NS_LITERAL_CSTRING("INBOX"));
      else if (onlineName.IsEmpty() || !onlineName.Equals(dupFolderPath))
        imapFolder->SetOnlineName(dupFolderPath);

      if (hierarchyDelimiter != '/')
        nsImapUrl::UnescapeSlashes(folderName.BeginWriting());
      if (NS_SUCCEEDED(CopyMUTF7toUTF16(folderName, unicodeName)))
        child->SetPrettyName(unicodeName);
    }
  }
  if (!found && child)
    child->SetMsgDatabase(nullptr); // close the db, so we don't hold open all the .msf files for new folders
  return NS_OK;
}

NS_IMETHODIMP nsImapIncomingServer::AddFolderRights(const nsACString& mailboxName, const nsACString& userName,
                                                    const nsACString& rights)
{
  nsCOMPtr <nsIMsgFolder> rootFolder;
  nsresult rv = GetRootFolder(getter_AddRefs(rootFolder));
  if(NS_SUCCEEDED(rv) && rootFolder)
  {
    nsCOMPtr <nsIMsgImapMailFolder> imapRoot = do_QueryInterface(rootFolder);
    if (imapRoot)
    {
      nsCOMPtr <nsIMsgImapMailFolder> foundFolder;
      rv = imapRoot->FindOnlineSubFolder(mailboxName, getter_AddRefs(foundFolder));
      if (NS_SUCCEEDED(rv) && foundFolder)
        return foundFolder->AddFolderRights(userName, rights);
    }
  }
  return rv;
}

NS_IMETHODIMP nsImapIncomingServer::FolderNeedsACLInitialized(const nsACString& folderPath,
                                                              bool *aNeedsACLInitialized)
{
  NS_ENSURE_ARG_POINTER(aNeedsACLInitialized);
  nsCOMPtr <nsIMsgFolder> rootFolder;
  nsresult rv = GetRootFolder(getter_AddRefs(rootFolder));
  if(NS_SUCCEEDED(rv) && rootFolder)
  {
    nsCOMPtr <nsIMsgImapMailFolder> imapRoot = do_QueryInterface(rootFolder);
    if (imapRoot)
    {
      nsCOMPtr <nsIMsgImapMailFolder> foundFolder;
      rv = imapRoot->FindOnlineSubFolder(folderPath, getter_AddRefs(foundFolder));
      if (NS_SUCCEEDED(rv) && foundFolder)
      {
        nsCOMPtr <nsIImapMailFolderSink> folderSink = do_QueryInterface(foundFolder);
        if (folderSink)
          return folderSink->GetFolderNeedsACLListed(aNeedsACLInitialized);
      }
    }
  }
  *aNeedsACLInitialized = false; // maybe we want to say TRUE here...
  return NS_OK;
}

NS_IMETHODIMP nsImapIncomingServer::RefreshFolderRights(const nsACString& folderPath)
{
  nsCOMPtr <nsIMsgFolder> rootFolder;
  nsresult rv = GetRootFolder(getter_AddRefs(rootFolder));
  if(NS_SUCCEEDED(rv) && rootFolder)
  {
    nsCOMPtr <nsIMsgImapMailFolder> imapRoot = do_QueryInterface(rootFolder);
    if (imapRoot)
    {
      nsCOMPtr <nsIMsgImapMailFolder> foundFolder;
      rv = imapRoot->FindOnlineSubFolder(folderPath, getter_AddRefs(foundFolder));
      if (NS_SUCCEEDED(rv) && foundFolder)
        return foundFolder->RefreshFolderRights();
    }
  }
  return rv;
}

nsresult nsImapIncomingServer::GetFolder(const nsACString& name, nsIMsgFolder** pFolder)
{
  NS_ENSURE_ARG_POINTER(pFolder);
  NS_ENSURE_TRUE(!name.IsEmpty(), NS_ERROR_FAILURE);
  nsresult rv;
  *pFolder = nullptr;

  nsCOMPtr<nsIMsgFolder> rootFolder;
  rv = GetRootFolder(getter_AddRefs(rootFolder));
  if (NS_SUCCEEDED(rv) && rootFolder)
  {
    nsCString uri;
    rv = rootFolder->GetURI(uri);
    if (NS_SUCCEEDED(rv) && !uri.IsEmpty())
    {
      nsAutoCString uriString(uri);
      uriString.Append('/');
      uriString.Append(name);
      nsCOMPtr<nsIRDFService> rdf(do_GetService(kRDFServiceCID, &rv));
      NS_ENSURE_SUCCESS(rv, rv);
      nsCOMPtr<nsIRDFResource> res;
      rv = rdf->GetResource(uriString, getter_AddRefs(res));
      if (NS_SUCCEEDED(rv))
      {
        nsCOMPtr<nsIMsgFolder> folder(do_QueryInterface(res, &rv));
        if (NS_SUCCEEDED(rv) && folder)
          folder.swap(*pFolder);
      }
    }
  }
  return rv;
}

NS_IMETHODIMP  nsImapIncomingServer::OnlineFolderDelete(const nsACString& aFolderName)
{
  return NS_OK;
}

NS_IMETHODIMP  nsImapIncomingServer::OnlineFolderCreateFailed(const nsACString& aFolderName)
{
  return NS_OK;
}

NS_IMETHODIMP nsImapIncomingServer::OnlineFolderRename(nsIMsgWindow *msgWindow, const nsACString& oldName,
                                                       const nsACString& newName)
{
  nsresult rv = NS_ERROR_FAILURE;
  if (!newName.IsEmpty())
  {
    nsCOMPtr<nsIMsgFolder> me;
    rv = GetFolder(oldName, getter_AddRefs(me));
    if (NS_FAILED(rv))
      return rv;

    nsCOMPtr<nsIMsgFolder> parent;
    nsCString tmpNewName (newName);
    int32_t folderStart = tmpNewName.RFindChar('/');
    if (folderStart > 0)
    {
      rv = GetFolder(StringHead(tmpNewName, folderStart), getter_AddRefs(parent));
    }
    else  // root is the parent
      rv = GetRootFolder(getter_AddRefs(parent));
    if (NS_SUCCEEDED(rv) && parent)
    {
      nsCOMPtr<nsIMsgImapMailFolder> folder;
      folder = do_QueryInterface(me, &rv);
      if (NS_SUCCEEDED(rv))
      {
        folder->RenameLocal(tmpNewName, parent);
        nsCOMPtr<nsIMsgImapMailFolder> parentImapFolder = do_QueryInterface(parent);

        if (parentImapFolder)
          parentImapFolder->RenameClient(msgWindow, me, oldName, tmpNewName);

        nsCOMPtr <nsIMsgFolder> newFolder;
        nsString unicodeNewName;
        // tmpNewName is imap mod utf7. It needs to be convert to utf8.
        CopyMUTF7toUTF16(tmpNewName, unicodeNewName);
        CopyUTF16toUTF8(unicodeNewName, tmpNewName);
        rv = GetFolder(tmpNewName, getter_AddRefs(newFolder));
        if (NS_SUCCEEDED(rv))
        {
          nsCOMPtr <nsIAtom> folderRenameAtom;
          folderRenameAtom = MsgGetAtom("RenameCompleted");
          newFolder->NotifyFolderEvent(folderRenameAtom);
        }
      }
    }
  }
  return rv;
}

NS_IMETHODIMP  nsImapIncomingServer::FolderIsNoSelect(const nsACString& aFolderName, bool *result)
{
  NS_ENSURE_ARG_POINTER(result);
  nsCOMPtr<nsIMsgFolder> msgFolder;
  nsresult rv = GetFolder(aFolderName, getter_AddRefs(msgFolder));
  if (NS_SUCCEEDED(rv) && msgFolder)
  {
    uint32_t flags;
    msgFolder->GetFlags(&flags);
    *result = ((flags & nsMsgFolderFlags::ImapNoselect) != 0);
  }
  else
   *result = false;
  return NS_OK;
}

NS_IMETHODIMP nsImapIncomingServer::SetFolderAdminURL(const nsACString& aFolderName, const nsACString& aFolderAdminUrl)
{
  nsCOMPtr <nsIMsgFolder> rootFolder;
  nsresult rv = GetRootFolder(getter_AddRefs(rootFolder));
  if(NS_SUCCEEDED(rv) && rootFolder)
  {
    nsCOMPtr <nsIMsgImapMailFolder> imapRoot = do_QueryInterface(rootFolder);
    if (imapRoot)
    {
      nsCOMPtr <nsIMsgImapMailFolder> foundFolder;
      rv = imapRoot->FindOnlineSubFolder(aFolderName, getter_AddRefs(foundFolder));
      if (NS_SUCCEEDED(rv) && foundFolder)
        return foundFolder->SetAdminUrl(aFolderAdminUrl);
    }
  }
  return rv;
}

NS_IMETHODIMP  nsImapIncomingServer::FolderVerifiedOnline(const nsACString& folderName, bool *aResult)
{
  NS_ENSURE_ARG_POINTER(aResult);
  *aResult = false;
  nsCOMPtr<nsIMsgFolder> rootFolder;
  nsresult rv = GetRootFolder(getter_AddRefs(rootFolder));
  if (NS_SUCCEEDED(rv) && rootFolder)
  {
    nsCOMPtr<nsIMsgFolder> folder;
    rv = rootFolder->FindSubFolder(folderName, getter_AddRefs(folder));
    if (NS_SUCCEEDED(rv) && folder)
    {
      nsCOMPtr<nsIMsgImapMailFolder> imapFolder = do_QueryInterface(folder);
      if (imapFolder)
        imapFolder->GetVerifiedAsOnlineFolder(aResult);
    }
  }
  return rv;
}

NS_IMETHODIMP nsImapIncomingServer::DiscoveryDone()
{
  if (mDoingSubscribeDialog)
    return NS_OK;

  nsCOMPtr<nsIMsgFolder> rootMsgFolder;
  nsresult rv = GetRootFolder(getter_AddRefs(rootMsgFolder));
  if (NS_SUCCEEDED(rv) && rootMsgFolder)
  {
    // GetResource() may return a node which is not in the folder
    // tree hierarchy but in the rdf cache in case of the non-existing default
    // Sent, Drafts, and Templates folders. The resouce will be eventually
    // released when the rdf service shuts down. When we create the default
    // folders later on in the imap server, the subsequent GetResource() of the
    // same uri will get us the cached rdf resource which should have the folder
    // flag set appropriately.
    nsCOMPtr<nsIRDFService> rdf(do_GetService("@mozilla.org/rdf/rdf-service;1",
                                              &rv));
    NS_ENSURE_SUCCESS(rv, rv);

    nsCOMPtr<nsIMsgAccountManager> accountMgr = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
    NS_ENSURE_SUCCESS(rv, rv);

    nsCOMPtr<nsIMsgIdentity> identity;
    rv = accountMgr->GetFirstIdentityForServer(this, getter_AddRefs(identity));
    if (NS_SUCCEEDED(rv) && identity)
    {
      nsCString folderUri;
      identity->GetFccFolder(folderUri);
      nsCString existingUri;

      if (CheckSpecialFolder(rdf, folderUri, nsMsgFolderFlags::SentMail,
                             existingUri))
      {
        identity->SetFccFolder(existingUri);
        identity->SetFccFolderPickerMode(NS_LITERAL_CSTRING("1"));
      }
      identity->GetDraftFolder(folderUri);
      if (CheckSpecialFolder(rdf, folderUri, nsMsgFolderFlags::Drafts,
                             existingUri))
      {
        identity->SetDraftFolder(existingUri);
        identity->SetDraftsFolderPickerMode(NS_LITERAL_CSTRING("1"));
      }
      bool archiveEnabled;
      identity->GetArchiveEnabled(&archiveEnabled);
      if (archiveEnabled)
      {
        identity->GetArchiveFolder(folderUri);
        if (CheckSpecialFolder(rdf, folderUri, nsMsgFolderFlags::Archive,
                               existingUri))
        {
          identity->SetArchiveFolder(existingUri);
          identity->SetArchivesFolderPickerMode(NS_LITERAL_CSTRING("1"));
        }
      }
      identity->GetStationeryFolder(folderUri);
      nsCOMPtr<nsIRDFResource> res;
      if (!folderUri.IsEmpty() && NS_SUCCEEDED(rdf->GetResource(folderUri, getter_AddRefs(res))))
      {
        nsCOMPtr<nsIMsgFolder> folder(do_QueryInterface(res, &rv));
        if (NS_SUCCEEDED(rv))
          rv = folder->SetFlag(nsMsgFolderFlags::Templates);
      }
    }

    nsCOMPtr<nsISpamSettings> spamSettings;
    rv = GetSpamSettings(getter_AddRefs(spamSettings));
    if (NS_SUCCEEDED(rv) && spamSettings)
    {
      nsCString spamFolderUri, existingUri;
      spamSettings->GetSpamFolderURI(getter_Copies(spamFolderUri));
      if (CheckSpecialFolder(rdf, spamFolderUri, nsMsgFolderFlags::Junk,
                             existingUri))
      {
        // This only sets the cached values in the spam settings object.
        spamSettings->SetActionTargetFolder(existingUri.get());
        spamSettings->SetMoveTargetMode(nsISpamSettings::MOVE_TARGET_MODE_FOLDER);
        // Set the preferences too so that the values persist.
        SetCharValue("spamActionTargetFolder", existingUri);
        SetIntValue("moveTargetMode", nsISpamSettings::MOVE_TARGET_MODE_FOLDER);
      }
    }

    bool isGMailServer;
    GetIsGMailServer(&isGMailServer);

    // Verify there is only one trash folder. Another might be present if
    // the trash name has been changed. Or we might be a gmail server and
    // want to switch to gmail's trash folder.
    nsCOMPtr<nsIArray> trashFolders;
    rv = rootMsgFolder->GetFoldersWithFlags(nsMsgFolderFlags::Trash,
                                            getter_AddRefs(trashFolders));

    if (NS_SUCCEEDED(rv) && trashFolders)
    {
      uint32_t numFolders;
      trashFolders->GetLength(&numFolders);
      nsAutoString trashName;
      if (NS_SUCCEEDED(GetTrashFolderName(trashName)))
      {
        for (uint32_t i = 0; i < numFolders; i++)
        {
          nsCOMPtr<nsIMsgFolder> trashFolder(do_QueryElementAt(trashFolders, i));
          if (trashFolder)
          {
            // If we're a gmail server, we clear the trash flags from folder(s)
            // without the kImapXListTrash flag. For normal servers, we clear
            // the trash folder flag if the folder name doesn't match the
            // pref trash folder name.
            if (isGMailServer)
            {
              nsCOMPtr<nsIMsgImapMailFolder> imapFolder(do_QueryInterface(trashFolder));
              int32_t boxFlags;
              imapFolder->GetBoxFlags(&boxFlags);
              if (boxFlags & kImapXListTrash)
              {
                continue;
              }
            }
            else
            {
              // trashName is the leaf name on the folder URI, which will be
              // different from the folder GetName if the trash name is
              // localized.
              nsAutoCString trashURL;
              trashFolder->GetFolderURL(trashURL);
              int32_t leafPos = trashURL.RFindChar('/');
              nsAutoCString unescapedName;
              MsgUnescapeString(Substring(trashURL, leafPos + 1),
                                nsINetUtil::ESCAPE_URL_PATH, unescapedName);
              nsAutoString nameUnicode;
              if (NS_FAILED(CopyMUTF7toUTF16(unescapedName, nameUnicode)) ||
                  trashName.Equals(nameUnicode))
              {
                continue;
              }
              if (numFolders == 1)
              {
                // We got here because the preferred trash folder does not
                // exist, but a folder got discovered to be the trash folder.
                SetUnicharValue(PREF_TRASH_FOLDER_NAME, nameUnicode);
                continue;
              }
            }
            trashFolder->ClearFlag(nsMsgFolderFlags::Trash);
          }
        }
      }
    }
  }

  bool usingSubscription = true;
  GetUsingSubscription(&usingSubscription);

  nsCOMArray<nsIMsgImapMailFolder> unverifiedFolders;
  GetUnverifiedFolders(unverifiedFolders);

  int32_t count = unverifiedFolders.Count();
  for (int32_t k = 0; k < count; ++k)
  {
    bool explicitlyVerify = false;
    bool hasSubFolders = false;
    uint32_t folderFlags;
    nsCOMPtr<nsIMsgImapMailFolder> currentImapFolder(unverifiedFolders[k]);
    nsCOMPtr<nsIMsgFolder> currentFolder(do_QueryInterface(currentImapFolder, &rv));
    if (NS_FAILED(rv))
      continue;

    currentFolder->GetFlags(&folderFlags);
    if (folderFlags & nsMsgFolderFlags::Virtual) // don't remove virtual folders
      continue;

    if ((!usingSubscription ||
         (NS_SUCCEEDED(currentImapFolder->GetExplicitlyVerify(&explicitlyVerify)) &&
          explicitlyVerify)) ||
        ((NS_SUCCEEDED(currentFolder->GetHasSubFolders(&hasSubFolders)) &&
          hasSubFolders) &&
         !NoDescendentsAreVerified(currentFolder)))
    {
      bool isNamespace;
      currentImapFolder->GetIsNamespace(&isNamespace);
      if (!isNamespace) // don't list namespaces explicitly
      {
        // If there are no subfolders and this is unverified, we don't want to
        // run this url. That is, we want to undiscover the folder.
        // If there are subfolders and no descendants are verified, we want to
        // undiscover all of the folders.
        // Only if there are subfolders and at least one of them is verified
        // do we want to refresh that folder's flags, because it won't be going
        // away.
        currentImapFolder->SetExplicitlyVerify(false);
        currentImapFolder->List();
      }
    }
    else
      DeleteNonVerifiedFolders(currentFolder);
  }

  return rv;
}

// Check if the special folder corresponding to the uri exists. If not, check
// if there already exists a folder with the special folder flag (the server may
// have told us about a folder to use through XLIST). If so, return the uri of
// the existing special folder. If not, set the special flag on the folder so
// it will be there if and when the folder is created.
// Return true if we found an existing special folder different than
// the one specified in prefs, and the one specified by prefs doesn't exist.
bool nsImapIncomingServer::CheckSpecialFolder(nsIRDFService *rdf,
                                                nsCString &folderUri,
                                                uint32_t folderFlag,
                                                nsCString &existingUri)
{
  nsCOMPtr<nsIRDFResource> res;
  nsCOMPtr<nsIMsgFolder> folder;
  nsCOMPtr<nsIMsgFolder> rootMsgFolder;
  nsresult rv = GetRootFolder(getter_AddRefs(rootMsgFolder));
  NS_ENSURE_SUCCESS(rv, false);
  nsCOMPtr<nsIMsgFolder> existingFolder;
  rootMsgFolder->GetFolderWithFlags(folderFlag, getter_AddRefs(existingFolder));

  if (!folderUri.IsEmpty() && NS_SUCCEEDED(rdf->GetResource(folderUri, getter_AddRefs(res))))
  {
    folder = do_QueryInterface(res, &rv);
    if (NS_SUCCEEDED(rv))
    {
      nsCOMPtr<nsIMsgFolder> parent;
      folder->GetParent(getter_AddRefs(parent));
      if (parent)
      {
        existingFolder = nullptr;
      }
      if (!existingFolder)
      {
        folder->SetFlag(folderFlag);
      }

      nsString folderName;
      folder->GetPrettyName(folderName);
      // this will set the localized name based on the folder flag.
      folder->SetPrettyName(folderName);
    }
  }

  if (existingFolder)
  {
    existingFolder->GetURI(existingUri);
    return true;
  }

  return false;
}

nsresult nsImapIncomingServer::DeleteNonVerifiedFolders(nsIMsgFolder *curFolder)
{
  bool autoUnsubscribeFromNoSelectFolders = true;
  nsresult rv;
  nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
  if (NS_SUCCEEDED(rv))
    prefBranch->GetBoolPref("mail.imap.auto_unsubscribe_from_noselect_folders", &autoUnsubscribeFromNoSelectFolders);

  nsCOMPtr<nsISimpleEnumerator> subFolders;

  rv = curFolder->GetSubFolders(getter_AddRefs(subFolders));
  if(NS_SUCCEEDED(rv))
  {
    bool moreFolders;

    while (NS_SUCCEEDED(subFolders->HasMoreElements(&moreFolders)) && moreFolders)
    {
      nsCOMPtr<nsISupports> child;
      rv = subFolders->GetNext(getter_AddRefs(child));
      if (NS_SUCCEEDED(rv) && child)
      {
        bool childVerified = false;
        nsCOMPtr <nsIMsgImapMailFolder> childImapFolder = do_QueryInterface(child, &rv);
        if (NS_SUCCEEDED(rv) && childImapFolder)
        {
          uint32_t flags;

          nsCOMPtr <nsIMsgFolder> childFolder = do_QueryInterface(child, &rv);
          rv = childImapFolder->GetVerifiedAsOnlineFolder(&childVerified);

          rv = childFolder->GetFlags(&flags);
          bool folderIsNoSelectFolder = NS_SUCCEEDED(rv) && ((flags & nsMsgFolderFlags::ImapNoselect) != 0);

          bool usingSubscription = true;
          GetUsingSubscription(&usingSubscription);
          if (usingSubscription)
          {
            bool folderIsNameSpace = false;
            bool noDescendentsAreVerified = NoDescendentsAreVerified(childFolder);
            bool shouldDieBecauseNoSelect = (folderIsNoSelectFolder ?
              ((noDescendentsAreVerified || AllDescendentsAreNoSelect(childFolder)) && !folderIsNameSpace)
              : false);
            if (!childVerified && (noDescendentsAreVerified || shouldDieBecauseNoSelect))
            {
            }
          }
          else
          {
          }
        }
      }
    }
  }

  nsCOMPtr<nsIMsgFolder> parent;
  rv = curFolder->GetParent(getter_AddRefs(parent));

  if (NS_SUCCEEDED(rv) && parent)
  {
    nsCOMPtr<nsIMsgImapMailFolder> imapParent = do_QueryInterface(parent);
    if (imapParent)
      imapParent->RemoveSubFolder(curFolder);
  }

  return rv;
}

bool nsImapIncomingServer::NoDescendentsAreVerified(nsIMsgFolder *parentFolder)
{
  bool nobodyIsVerified = true;
  nsCOMPtr<nsISimpleEnumerator> subFolders;
  nsresult rv = parentFolder->GetSubFolders(getter_AddRefs(subFolders));
  if(NS_SUCCEEDED(rv))
  {
    bool moreFolders;
    while (NS_SUCCEEDED(subFolders->HasMoreElements(&moreFolders)) &&
           moreFolders && nobodyIsVerified)
    {
      nsCOMPtr<nsISupports> child;
      rv = subFolders->GetNext(getter_AddRefs(child));
      if (NS_SUCCEEDED(rv) && child)
      {
        bool childVerified = false;
        nsCOMPtr <nsIMsgImapMailFolder> childImapFolder = do_QueryInterface(child, &rv);
        if (NS_SUCCEEDED(rv) && childImapFolder)
        {
          nsCOMPtr <nsIMsgFolder> childFolder = do_QueryInterface(child, &rv);
          rv = childImapFolder->GetVerifiedAsOnlineFolder(&childVerified);
          nobodyIsVerified = !childVerified && NoDescendentsAreVerified(childFolder);
        }
      }
    }
  }
  return nobodyIsVerified;
}


bool nsImapIncomingServer::AllDescendentsAreNoSelect(nsIMsgFolder *parentFolder)
{
  bool allDescendentsAreNoSelect = true;
  nsCOMPtr<nsISimpleEnumerator> subFolders;
  nsresult rv = parentFolder->GetSubFolders(getter_AddRefs(subFolders));
  if(NS_SUCCEEDED(rv))
  {
    bool moreFolders;
    while (NS_SUCCEEDED(subFolders->HasMoreElements(&moreFolders)) &&
           moreFolders && allDescendentsAreNoSelect)
    {
      nsCOMPtr<nsISupports> child;
      rv = subFolders->GetNext(getter_AddRefs(child));
      if (NS_SUCCEEDED(rv) && child)
      {
        bool childIsNoSelect = false;
        nsCOMPtr <nsIMsgImapMailFolder> childImapFolder = do_QueryInterface(child, &rv);
        if (NS_SUCCEEDED(rv) && childImapFolder)
        {
          uint32_t flags;
          nsCOMPtr <nsIMsgFolder> childFolder = do_QueryInterface(child, &rv);
          rv = childFolder->GetFlags(&flags);
          childIsNoSelect = NS_SUCCEEDED(rv) && (flags & nsMsgFolderFlags::ImapNoselect);
          allDescendentsAreNoSelect = !childIsNoSelect && AllDescendentsAreNoSelect(childFolder);
        }
      }
    }
  }
#if 0
  int numberOfSubfolders = parentFolder->GetNumSubFolders();

  for (int childIndex=0; allDescendantsAreNoSelect && (childIndex < numberOfSubfolders); childIndex++)
  {
    MSG_IMAPFolderInfoMail *currentChild = (MSG_IMAPFolderInfoMail *) parentFolder->GetSubFolder(childIndex);
    allDescendentsAreNoSelect = (currentChild->GetFolderPrefFlags() & MSG_FOLDER_PREF_IMAPNOSELECT) &&
      AllDescendentsAreNoSelect(currentChild);
  }
#endif // 0
  return allDescendentsAreNoSelect;
}

NS_IMETHODIMP
nsImapIncomingServer::PromptLoginFailed(nsIMsgWindow *aMsgWindow,
                                        int32_t *aResult)
{
  nsAutoCString hostName;
  GetRealHostName(hostName);

  return MsgPromptLoginFailed(aMsgWindow, hostName, aResult);
}

NS_IMETHODIMP
nsImapIncomingServer::FEAlert(const nsAString& aAlertString,
                              nsIMsgMailNewsUrl *aUrl)
{
  GetStringBundle();

  if (m_stringBundle)
  {
    nsAutoString hostName;
    nsresult rv = GetPrettyName(hostName);
    if (NS_SUCCEEDED(rv))
    {
      nsString message;
      nsString tempString(aAlertString);
      const char16_t *params[] = { hostName.get(), tempString.get() };

      rv = m_stringBundle->FormatStringFromName(
        u"imapServerAlert",
        params, 2, getter_Copies(message));
      if (NS_SUCCEEDED(rv))
        return AlertUser(message, aUrl);
    }
  }
  return AlertUser(aAlertString, aUrl);
}

nsresult nsImapIncomingServer::AlertUser(const nsAString& aString,
                                         nsIMsgMailNewsUrl *aUrl)
{
  nsresult rv;
  nsCOMPtr <nsIMsgMailSession> mailSession =
    do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
  NS_ENSURE_SUCCESS(rv, rv);

  return mailSession->AlertUser(aString, aUrl);
}

NS_IMETHODIMP
nsImapIncomingServer::FEAlertWithName(const char* aMsgName, nsIMsgMailNewsUrl *aUrl)
{
  // don't bother the user if we're shutting down.
  if (m_shuttingDown)
    return NS_OK;

  GetStringBundle();

  nsString message;

  if (m_stringBundle)
  {
    nsAutoString hostName;
    nsresult rv = GetPrettyName(hostName);
    if (NS_SUCCEEDED(rv))
    {
      const char16_t *params[] = { hostName.get() };
      rv = m_stringBundle->FormatStringFromName(
        NS_ConvertASCIItoUTF16(aMsgName).get(),
        params, 1,getter_Copies(message));
      if (NS_SUCCEEDED(rv))
        return AlertUser(message, aUrl);
    }
  }

  // Error condition
  message.AssignLiteral("String Name ");
  message.AppendASCII(aMsgName);
  FEAlert(message, aUrl);
  return NS_OK;
}

NS_IMETHODIMP  nsImapIncomingServer::FEAlertFromServer(const nsACString& aServerString,
                                                       nsIMsgMailNewsUrl *aUrl)
{
  NS_ENSURE_TRUE(!aServerString.IsEmpty(), NS_OK);

  nsCString message(aServerString);
  message.Trim(" \t\b\r\n");
  if (message.Last() != '.')
    message.Append('.');

  // Skip over the first two words (the command tag and "NO").
  // Find the first word break.
  int32_t pos = message.FindChar(' ');

  // Find the second word break.
  if (pos != -1)
    pos = message.FindChar(' ', pos + 1);

  // Adjust the message.
  if (pos != -1)
    message = Substring(message, pos + 1);

  nsString hostName;
  GetPrettyName(hostName);

  const char16_t *formatStrings[] =
  {
    hostName.get(),
    nullptr,
    nullptr
  };

  nsString msgName;
  int32_t numStrings;
  nsString fullMessage;
  nsCOMPtr<nsIImapUrl> imapUrl = do_QueryInterface(aUrl);
  NS_ENSURE_TRUE(imapUrl, NS_ERROR_INVALID_ARG);

  nsImapState imapState;
  nsImapAction imapAction;

  imapUrl->GetRequiredImapState(&imapState);
  imapUrl->GetImapAction(&imapAction);
  nsString folderName;

  NS_ConvertUTF8toUTF16 unicodeMsg(message);

  nsCOMPtr<nsIMsgFolder> folder;
  if (imapState == nsIImapUrl::nsImapSelectedState ||
      imapAction == nsIImapUrl::nsImapFolderStatus)
  {
    aUrl->GetFolder(getter_AddRefs(folder));
    if (folder)
      folder->GetPrettyName(folderName);
    numStrings = 3;
    msgName.AssignLiteral("imapFolderCommandFailed");
    formatStrings[1] = folderName.get();
  }
  else
  {
    msgName.AssignLiteral("imapServerCommandFailed");
    numStrings = 2;
  }

  formatStrings[numStrings -1] = unicodeMsg.get();

  nsresult rv = GetStringBundle();
  NS_ENSURE_SUCCESS(rv, rv);
  if (m_stringBundle)
  {
    rv = m_stringBundle->FormatStringFromName(msgName.get(),
      formatStrings, numStrings, getter_Copies(fullMessage));
    NS_ENSURE_SUCCESS(rv, rv);
  }

  return AlertUser(fullMessage, aUrl);
}

#define IMAP_MSGS_URL       "chrome://messenger/locale/imapMsgs.properties"

nsresult nsImapIncomingServer::GetStringBundle()
{
  if (m_stringBundle)
    return NS_OK;

  nsCOMPtr<nsIStringBundleService> sBundleService =
    mozilla::services::GetStringBundleService();
  NS_ENSURE_TRUE(sBundleService, NS_ERROR_UNEXPECTED);
  return sBundleService->CreateBundle(IMAP_MSGS_URL, getter_AddRefs(m_stringBundle));
}

NS_IMETHODIMP
nsImapIncomingServer::GetImapStringByName(const char* msgName, nsAString& aString)
{
  nsresult rv = NS_OK;
  GetStringBundle();
  if (m_stringBundle)
  {
    nsString res_str;
    rv = m_stringBundle->GetStringFromName(
       NS_ConvertASCIItoUTF16(msgName).get(),
       getter_Copies(res_str));
    aString.Assign(res_str);
    if (NS_SUCCEEDED(rv))
      return rv;
  }
  aString.AssignLiteral("String Name ");
  // mscott: FIX ME
  aString.AppendASCII(msgName);
  return NS_OK;
}

nsresult nsImapIncomingServer::ResetFoldersToUnverified(nsIMsgFolder *parentFolder)
{
  nsresult rv = NS_OK;
  if (!parentFolder)
  {
    nsCOMPtr<nsIMsgFolder> rootFolder;
    rv = GetRootFolder(getter_AddRefs(rootFolder));
    NS_ENSURE_SUCCESS(rv, rv);
    return ResetFoldersToUnverified(rootFolder);
  }
  else
  {
    nsCOMPtr<nsISimpleEnumerator> subFolders;
    nsCOMPtr<nsIMsgImapMailFolder> imapFolder = do_QueryInterface(parentFolder, &rv);
    NS_ENSURE_SUCCESS(rv, rv);
    rv = imapFolder->SetVerifiedAsOnlineFolder(false);
    rv = parentFolder->GetSubFolders(getter_AddRefs(subFolders));
    NS_ENSURE_SUCCESS(rv, rv);

    bool moreFolders = false;
    while (NS_SUCCEEDED(subFolders->HasMoreElements(&moreFolders)) && moreFolders)
    {
      nsCOMPtr<nsISupports> child;
      rv = subFolders->GetNext(getter_AddRefs(child));
      if (NS_SUCCEEDED(rv) && child)
      {
        nsCOMPtr<nsIMsgFolder> childFolder = do_QueryInterface(child, &rv);
        if (NS_SUCCEEDED(rv) && childFolder)
        {
          rv = ResetFoldersToUnverified(childFolder);
          if (NS_FAILED(rv))
            break;
        }
      }
    }
  }
  return rv;
}

void
nsImapIncomingServer::GetUnverifiedFolders(nsCOMArray<nsIMsgImapMailFolder> &aFoldersArray)
{
  nsCOMPtr<nsIMsgFolder> rootFolder;
  if (NS_FAILED(GetRootFolder(getter_AddRefs(rootFolder))) || !rootFolder)
    return;

  nsCOMPtr<nsIMsgImapMailFolder> imapRoot(do_QueryInterface(rootFolder));
  // don't need to verify the root.
  if (imapRoot)
    imapRoot->SetVerifiedAsOnlineFolder(true);

  GetUnverifiedSubFolders(rootFolder, aFoldersArray);
}

void
nsImapIncomingServer::GetUnverifiedSubFolders(nsIMsgFolder *parentFolder,
                                              nsCOMArray<nsIMsgImapMailFolder> &aFoldersArray)
{
  nsCOMPtr<nsIMsgImapMailFolder> imapFolder(do_QueryInterface(parentFolder));

  bool verified = false, explicitlyVerify = false;
  if (imapFolder)
  {
    nsresult rv = imapFolder->GetVerifiedAsOnlineFolder(&verified);
    if (NS_SUCCEEDED(rv))
      rv = imapFolder->GetExplicitlyVerify(&explicitlyVerify);

    if (NS_SUCCEEDED(rv) && (!verified || explicitlyVerify))
      aFoldersArray.AppendObject(imapFolder);
  }

  nsCOMPtr<nsISimpleEnumerator> subFolders;
  if (NS_SUCCEEDED(parentFolder->GetSubFolders(getter_AddRefs(subFolders))))
  {
    bool moreFolders;

    while (NS_SUCCEEDED(subFolders->HasMoreElements(&moreFolders)) && moreFolders)
    {
      nsCOMPtr<nsISupports> child;
      subFolders->GetNext(getter_AddRefs(child));
      if (child)
      {
        nsCOMPtr<nsIMsgFolder> childFolder(do_QueryInterface(child));
        if (childFolder)
          GetUnverifiedSubFolders(childFolder, aFoldersArray);
      }
    }
  }
}

NS_IMETHODIMP nsImapIncomingServer::ForgetSessionPassword()
{
  nsresult rv = nsMsgIncomingServer::ForgetSessionPassword();
  NS_ENSURE_SUCCESS(rv,rv);

  // fix for bugscape bug #15485
  // if we use turbo, and we logout, we need to make sure
  // the server doesn't think it's authenticated.
  // the biff timer continues to fire when you use turbo
  // (see #143848).  if we exited, we've set the password to null
  // but if we're authenticated, and the biff timer goes off
  // we'll still perform biff, because we use m_userAuthenticated
  // to determine if we require a password for biff.
  // (if authenticated, we don't require a password
  // see nsMsgBiffManager::PerformBiff())
  // performing biff without a password will pop up the prompt dialog
  // which is pretty wacky, when it happens after you quit the application
  m_userAuthenticated = false;
  return NS_OK;
}

NS_IMETHODIMP nsImapIncomingServer::GetServerRequiresPasswordForBiff(bool *aServerRequiresPasswordForBiff)
{
  NS_ENSURE_ARG_POINTER(aServerRequiresPasswordForBiff);
  // if the user has already been authenticated, we've got the password
  *aServerRequiresPasswordForBiff = !m_userAuthenticated;
  return NS_OK;
}

NS_IMETHODIMP nsImapIncomingServer::ForgetPassword()
{
  return nsMsgIncomingServer::ForgetPassword();
}


NS_IMETHODIMP
nsImapIncomingServer::AsyncGetPassword(nsIImapProtocol *aProtocol,
                                       bool aNewPasswordRequested,
                                       nsACString &aPassword)
{
  if (m_password.IsEmpty())
  {
    // We're now going to need to do something that will end up with us either
    // poking login manager or prompting the user. We need to ensure we only
    // do one prompt at a time (and login manager could cause a master password
    // prompt), so we need to use the async prompter.
    nsresult rv;
    nsCOMPtr<nsIMsgAsyncPrompter> asyncPrompter =
      do_GetService(NS_MSGASYNCPROMPTER_CONTRACTID, &rv);
    NS_ENSURE_SUCCESS(rv, rv);
    nsCOMPtr<nsIMsgAsyncPromptListener> promptListener(do_QueryInterface(aProtocol));
    rv = asyncPrompter->QueueAsyncAuthPrompt(m_serverKey, aNewPasswordRequested,
                                             promptListener);
    // Explict NS_ENSURE_SUCCESS for debug purposes as errors tend to get
    // hidden.
    NS_ENSURE_SUCCESS(rv, rv);
  }
  if (!m_password.IsEmpty())
    aPassword = m_password;
  return NS_OK;
}

NS_IMETHODIMP
nsImapIncomingServer::PromptPassword(nsIMsgWindow *aMsgWindow,
                                     nsACString &aPassword)
{
  nsString passwordTitle;
  GetImapStringByName("imapEnterPasswordPromptTitle", passwordTitle);
  NS_ENSURE_STATE(m_stringBundle);

  nsAutoCString userName;
  GetRealUsername(userName);

  nsAutoCString hostName;
  GetRealHostName(hostName);

  nsresult rv = GetStringBundle();
  NS_ENSURE_SUCCESS(rv, rv);

  NS_ConvertASCIItoUTF16 finalUserName(userName);
  NS_ConvertASCIItoUTF16 finalHostName(hostName);

  const char16_t *formatStrings[] = { finalUserName.get(), finalHostName.get() };

  nsString passwordText;
  rv = m_stringBundle->FormatStringFromName(
    u"imapEnterServerPasswordPrompt",
    formatStrings, 2, getter_Copies(passwordText));
  NS_ENSURE_SUCCESS(rv, rv);

  rv = GetPasswordWithUI(passwordText, passwordTitle, aMsgWindow, aPassword);
  if (NS_SUCCEEDED(rv))
    m_password = aPassword;
  return rv;
}

// for the nsIImapServerSink interface
NS_IMETHODIMP nsImapIncomingServer::SetCapability(eIMAPCapabilityFlags capability)
{
  m_capability = capability;
  SetIsGMailServer((capability & kGmailImapCapability) != 0);
  SetCapabilityACL(capability & kACLCapability);
  SetCapabilityQuota(capability & kQuotaCapability);
  return NS_OK;
}

NS_IMETHODIMP nsImapIncomingServer::SetServerID(const nsACString &aServerID)
{
  return SetServerIDPref(aServerID);
}

NS_IMETHODIMP  nsImapIncomingServer::CommitNamespaces()
{
  nsresult rv;
  nsCOMPtr<nsIImapHostSessionList> hostSession = do_GetService(kCImapHostSessionListCID, &rv);
  NS_ENSURE_SUCCESS(rv, rv);
  return hostSession->CommitNamespacesForHost(this);
}

NS_IMETHODIMP nsImapIncomingServer::PseudoInterruptMsgLoad(nsIMsgFolder *aImapFolder, nsIMsgWindow *aMsgWindow,
                                                          bool *interrupted)
{
  nsresult rv = NS_OK;
  nsCOMPtr<nsIImapProtocol> connection;
  PR_CEnterMonitor(this);
  // iterate through the connection cache for a connection that is loading
  // a message in this folder and should be pseudo-interrupted.
  int32_t cnt = m_connectionCache.Count();

  for (int32_t i = 0; i < cnt; ++i)
  {
    connection = m_connectionCache[i];
    if (connection)
      rv = connection->PseudoInterruptMsgLoad(aImapFolder, aMsgWindow, interrupted);
  }

  PR_CExitMonitor(this);
  return rv;
}

NS_IMETHODIMP nsImapIncomingServer::ResetNamespaceReferences()
{
  nsCOMPtr <nsIMsgFolder> rootFolder;
  nsresult rv = GetRootFolder(getter_AddRefs(rootFolder));
  if (NS_SUCCEEDED(rv) && rootFolder)
  {
    nsCOMPtr <nsIMsgImapMailFolder> imapFolder = do_QueryInterface(rootFolder);
    if (imapFolder)
      rv = imapFolder->ResetNamespaceReferences();
  }
  return rv;
}

NS_IMETHODIMP nsImapIncomingServer::SetUserAuthenticated(bool aUserAuthenticated)
{
  m_userAuthenticated = aUserAuthenticated;
  if (aUserAuthenticated)
  {
    nsresult rv;
    nsCOMPtr<nsIMsgAccountManager> accountManager =
      do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
    NS_ENSURE_SUCCESS(rv, rv);

    accountManager->SetUserNeedsToAuthenticate(false);
  }
  return NS_OK;
}

NS_IMETHODIMP nsImapIncomingServer::GetUserAuthenticated(bool *aUserAuthenticated)
{
  NS_ENSURE_ARG_POINTER(aUserAuthenticated);
  *aUserAuthenticated = m_userAuthenticated;
  return NS_OK;
}

/* void SetMailServerUrls (in string manageMailAccount, in string manageLists, in string manageFilters); */
NS_IMETHODIMP  nsImapIncomingServer::SetMailServerUrls(const nsACString& manageMailAccount, const nsACString& manageLists,
                                                       const nsACString& manageFilters)
{
  return SetManageMailAccountUrl(manageMailAccount);
}

NS_IMETHODIMP nsImapIncomingServer::SetManageMailAccountUrl(const nsACString& manageMailAccountUrl)
{
  m_manageMailAccountUrl = manageMailAccountUrl;
  return NS_OK;
}

NS_IMETHODIMP nsImapIncomingServer::GetManageMailAccountUrl(nsACString& manageMailAccountUrl)
{
  manageMailAccountUrl = m_manageMailAccountUrl;
  return NS_OK;
}

NS_IMETHODIMP
nsImapIncomingServer::StartPopulatingWithUri(nsIMsgWindow *aMsgWindow, bool aForceToServer /*ignored*/, const char *uri)
{
  NS_ENSURE_ARG_POINTER (uri);

  nsresult rv;
  mDoingSubscribeDialog = true;

  rv = EnsureInner();
  NS_ENSURE_SUCCESS(rv,rv);
  rv = mInner->StartPopulatingWithUri(aMsgWindow, aForceToServer, uri);
  NS_ENSURE_SUCCESS(rv,rv);

  // imap always uses the canonical delimiter form of paths for subscribe ui.
  rv = SetDelimiter('/');
  NS_ENSURE_SUCCESS(rv,rv);

  rv = SetShowFullName(false);
  NS_ENSURE_SUCCESS(rv,rv);

  nsCString serverUri;
  rv = GetServerURI(serverUri);
  NS_ENSURE_SUCCESS(rv,rv);

  nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
  NS_ENSURE_SUCCESS(rv,rv);

/*
    if uri = imap://user@host/foo/bar, the serverUri is imap://user@host
    to get path from uri, skip over imap://user@host + 1 (for the /)
*/
  const char *path = uri + serverUri.Length() + 1;
  return imapService->GetListOfFoldersWithPath(this, aMsgWindow, nsDependentCString(path));
}

NS_IMETHODIMP
nsImapIncomingServer::StartPopulating(nsIMsgWindow *aMsgWindow, bool aForceToServer /*ignored*/, bool aGetOnlyNew)
{
  nsresult rv;
  mDoingSubscribeDialog = true;

  rv = EnsureInner();
  NS_ENSURE_SUCCESS(rv,rv);
  rv = mInner->StartPopulating(aMsgWindow, aForceToServer, aGetOnlyNew);
  NS_ENSURE_SUCCESS(rv,rv);

  // imap always uses the canonical delimiter form of paths for subscribe ui.
  rv = SetDelimiter('/');
  NS_ENSURE_SUCCESS(rv,rv);

  rv = SetShowFullName(false);
  NS_ENSURE_SUCCESS(rv,rv);

  nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
  NS_ENSURE_SUCCESS(rv,rv);
  return imapService->GetListOfFoldersOnServer(this, aMsgWindow);
}

NS_IMETHODIMP
nsImapIncomingServer::OnStartRunningUrl(nsIURI *url)
{
  return NS_OK;
}

NS_IMETHODIMP
nsImapIncomingServer::OnStopRunningUrl(nsIURI *url, nsresult exitCode)
{
  nsresult rv = exitCode;

  // xxx todo get msgWindow from url
  nsCOMPtr<nsIMsgWindow> msgWindow;
  nsCOMPtr<nsIImapUrl> imapUrl = do_QueryInterface(url);
  if (imapUrl) {
    nsImapAction imapAction = nsIImapUrl::nsImapTest;
    imapUrl->GetImapAction(&imapAction);
    switch (imapAction) {
    case nsIImapUrl::nsImapDiscoverAllAndSubscribedBoxesUrl:
    case nsIImapUrl::nsImapDiscoverChildrenUrl:
      rv = UpdateSubscribed();
      NS_ENSURE_SUCCESS(rv, rv);
      mDoingSubscribeDialog = false;
      rv = StopPopulating(msgWindow);
      NS_ENSURE_SUCCESS(rv, rv);
      break;
    case nsIImapUrl::nsImapDiscoverAllBoxesUrl:
      if (NS_SUCCEEDED(exitCode))
        DiscoveryDone();
      break;
    case nsIImapUrl::nsImapFolderStatus:
    {
      nsCOMPtr<nsIMsgFolder> msgFolder;
      nsCOMPtr<nsIMsgMailNewsUrl> mailUrl = do_QueryInterface(imapUrl);
      mailUrl->GetFolder(getter_AddRefs(msgFolder));
      if (msgFolder)
      {
        nsresult rv;
        nsCOMPtr<nsIMsgMailSession> session =
                 do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
        NS_ENSURE_SUCCESS(rv, rv);
        bool folderOpen;
        rv = session->IsFolderOpenInWindow(msgFolder, &folderOpen);
        if (NS_SUCCEEDED(rv) && !folderOpen && msgFolder)
          msgFolder->SetMsgDatabase(nullptr);
        nsCOMPtr<nsIMsgImapMailFolder> imapFolder = do_QueryInterface(msgFolder);
        m_foldersToStat.RemoveObject(imapFolder);
      }
      // if we get an error running the url, it's better
      // not to chain the next url.
      if (NS_FAILED(exitCode) && exitCode != NS_MSG_ERROR_IMAP_COMMAND_FAILED)
        m_foldersToStat.Clear();
      if (m_foldersToStat.Count() > 0)
        m_foldersToStat[0]->UpdateStatus(this, nullptr);
      break;
    }
    default:
        break;
    }
  }
  return NS_OK;
}

NS_IMETHODIMP
nsImapIncomingServer::SetIncomingServer(nsIMsgIncomingServer *aServer)
{
  nsresult rv = EnsureInner();
  NS_ENSURE_SUCCESS(rv,rv);
  return mInner->SetIncomingServer(aServer);
}

NS_IMETHODIMP
nsImapIncomingServer::SetShowFullName(bool showFullName)
{
  nsresult rv = EnsureInner();
  NS_ENSURE_SUCCESS(rv,rv);
  return mInner->SetShowFullName(showFullName);
}

NS_IMETHODIMP
nsImapIncomingServer::GetDelimiter(char *aDelimiter)
{
  nsresult rv = EnsureInner();
  NS_ENSURE_SUCCESS(rv,rv);
  return mInner->GetDelimiter(aDelimiter);
}

NS_IMETHODIMP
nsImapIncomingServer::SetDelimiter(char aDelimiter)
{
  nsresult rv = EnsureInner();
  NS_ENSURE_SUCCESS(rv,rv);
  return mInner->SetDelimiter(aDelimiter);
}

NS_IMETHODIMP
nsImapIncomingServer::SetAsSubscribed(const nsACString &path)
{
  nsresult rv = EnsureInner();
  NS_ENSURE_SUCCESS(rv,rv);
  return mInner->SetAsSubscribed(path);
}

NS_IMETHODIMP
nsImapIncomingServer::UpdateSubscribed()
{
  return NS_OK;
}

NS_IMETHODIMP
nsImapIncomingServer::AddTo(const nsACString &aName, bool addAsSubscribed,
                            bool aSubscribable, bool changeIfExists)
{
  nsresult rv = EnsureInner();
  NS_ENSURE_SUCCESS(rv,rv);

  // RFC 3501 allows UTF-8 in addition to modified UTF-7
  // If it's not UTF-8, it cannot be MUTF7, either. We just ignore it.
  // (otherwise we'll crash. see #63186)
  if (!MsgIsUTF8(aName))
    return NS_OK;

  if (!NS_IsAscii(aName.BeginReading(), aName.Length())) {
    nsAutoCString name;
    CopyUTF16toMUTF7(NS_ConvertUTF8toUTF16(aName), name);
    return mInner->AddTo(name, addAsSubscribed, aSubscribable, changeIfExists);
  }
  return mInner->AddTo(aName, addAsSubscribed, aSubscribable, changeIfExists);
}

NS_IMETHODIMP
nsImapIncomingServer::StopPopulating(nsIMsgWindow *aMsgWindow)
{
  nsCOMPtr<nsISubscribeListener> listener;
  (void) GetSubscribeListener(getter_AddRefs(listener));

  if (listener)
    listener->OnDonePopulating();

  nsresult rv = EnsureInner();
  NS_ENSURE_SUCCESS(rv,rv);
  return mInner->StopPopulating(aMsgWindow);
}


NS_IMETHODIMP
nsImapIncomingServer::SubscribeCleanup()
{
  m_subscribeFolders.Clear();
  return ClearInner();
}

NS_IMETHODIMP
nsImapIncomingServer::SetSubscribeListener(nsISubscribeListener *aListener)
{
  nsresult rv = EnsureInner();
  NS_ENSURE_SUCCESS(rv,rv);
  return mInner->SetSubscribeListener(aListener);
}

NS_IMETHODIMP
nsImapIncomingServer::GetSubscribeListener(nsISubscribeListener **aListener)
{
  nsresult rv = EnsureInner();
  NS_ENSURE_SUCCESS(rv,rv);
  return mInner->GetSubscribeListener(aListener);
}

NS_IMETHODIMP
nsImapIncomingServer::Subscribe(const char16_t *aName)
{
  NS_ENSURE_ARG_POINTER(aName);
  
  return SubscribeToFolder(nsDependentString(aName), true, nullptr);
}

NS_IMETHODIMP
nsImapIncomingServer::Unsubscribe(const char16_t *aName)
{
  NS_ENSURE_ARG_POINTER(aName);

  return SubscribeToFolder(nsDependentString(aName), false, nullptr);
}

NS_IMETHODIMP
nsImapIncomingServer::SubscribeToFolder(const nsAString& aName, bool subscribe, nsIURI **aUri)
{
  nsresult rv;
  nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<nsIMsgFolder> rootMsgFolder;
  rv = GetRootFolder(getter_AddRefs(rootMsgFolder));
  NS_ENSURE_SUCCESS(rv, rv);

  // Locate the folder so that the correct hierarchical delimiter is used in the
  // folder pathnames, otherwise root's (ie, '^') is used and this is wrong.

  // aName is not a genuine UTF-16 but just a zero-padded modified UTF-7
  NS_LossyConvertUTF16toASCII folderCName(aName);
  nsCOMPtr<nsIMsgFolder> msgFolder;
  if (rootMsgFolder && !aName.IsEmpty())
    rv = rootMsgFolder->FindSubFolder(folderCName, getter_AddRefs(msgFolder));

  nsCOMPtr<nsIThread> thread(do_GetCurrentThread());

  nsAutoString unicodeName;
  rv = CopyMUTF7toUTF16(folderCName, unicodeName);
  NS_ENSURE_SUCCESS(rv, rv);

  if (subscribe)
    rv = imapService->SubscribeFolder(msgFolder, unicodeName, nullptr, aUri);
  else
    rv = imapService->UnsubscribeFolder(msgFolder, unicodeName, nullptr, nullptr);
  return rv;
}

NS_IMETHODIMP
nsImapIncomingServer::SetDoingLsub(bool doingLsub)
{
  mDoingLsub = doingLsub;
  return NS_OK;
}

NS_IMETHODIMP
nsImapIncomingServer::GetDoingLsub(bool *doingLsub)
{
  NS_ENSURE_ARG_POINTER(doingLsub);
  *doingLsub = mDoingLsub;
  return NS_OK;
}

NS_IMETHODIMP
nsImapIncomingServer::ReDiscoverAllFolders()
{
  return PerformExpand(nullptr);
}

NS_IMETHODIMP
nsImapIncomingServer::SetState(const nsACString &path, bool state,
                               bool *stateChanged)
{
  nsresult rv = EnsureInner();
  NS_ENSURE_SUCCESS(rv,rv);
  return mInner->SetState(path, state, stateChanged);
}

NS_IMETHODIMP
nsImapIncomingServer::HasChildren(const nsACString &path, bool *aHasChildren)
{
  nsresult rv = EnsureInner();
  NS_ENSURE_SUCCESS(rv,rv);
  return mInner->HasChildren(path, aHasChildren);
}

NS_IMETHODIMP
nsImapIncomingServer::IsSubscribed(const nsACString &path,
                                   bool *aIsSubscribed)
{
  nsresult rv = EnsureInner();
  NS_ENSURE_SUCCESS(rv,rv);
  return mInner->IsSubscribed(path, aIsSubscribed);
}

NS_IMETHODIMP
nsImapIncomingServer::IsSubscribable(const nsACString &path, bool *aIsSubscribable)
{
  nsresult rv = EnsureInner();
  NS_ENSURE_SUCCESS(rv,rv);
  return mInner->IsSubscribable(path, aIsSubscribable);
}

NS_IMETHODIMP
nsImapIncomingServer::GetLeafName(const nsACString &path, nsAString &aLeafName)
{
  nsresult rv = EnsureInner();
  NS_ENSURE_SUCCESS(rv,rv);
  return mInner->GetLeafName(path, aLeafName);
}

NS_IMETHODIMP
nsImapIncomingServer::GetFirstChildURI(const nsACString &path, nsACString &aResult)
{
  nsresult rv = EnsureInner();
  NS_ENSURE_SUCCESS(rv,rv);
  return mInner->GetFirstChildURI(path, aResult);
}


NS_IMETHODIMP
nsImapIncomingServer::GetChildren(const nsACString &aPath,
                                  nsISimpleEnumerator **aResult)
{
  nsresult rv = EnsureInner();
  NS_ENSURE_SUCCESS(rv,rv);
  return mInner->GetChildren(aPath, aResult);
}

nsresult
nsImapIncomingServer::EnsureInner()
{
  nsresult rv = NS_OK;

  if (mInner)
    return NS_OK;

  mInner = do_CreateInstance(kSubscribableServerCID,&rv);
  NS_ENSURE_SUCCESS(rv,rv);
  return SetIncomingServer(this);
}

nsresult
nsImapIncomingServer::ClearInner()
{
  nsresult rv = NS_OK;
  if (mInner)
  {
    rv = mInner->SetSubscribeListener(nullptr);
    NS_ENSURE_SUCCESS(rv,rv);
    rv = mInner->SetIncomingServer(nullptr);
    NS_ENSURE_SUCCESS(rv,rv);
    mInner = nullptr;
  }
  return NS_OK;
}

NS_IMETHODIMP
nsImapIncomingServer::CommitSubscribeChanges()
{
  return ReDiscoverAllFolders();
}

NS_IMETHODIMP
nsImapIncomingServer::GetCanBeDefaultServer(bool *canBeDefaultServer)
{
  NS_ENSURE_ARG_POINTER(canBeDefaultServer);
  *canBeDefaultServer = true;
  return NS_OK;
}

NS_IMETHODIMP
nsImapIncomingServer::GetCanCompactFoldersOnServer(bool *canCompactFoldersOnServer)
{
  NS_ENSURE_ARG_POINTER(canCompactFoldersOnServer);
  // Initialize canCompactFoldersOnServer true, a default value for IMAP
  *canCompactFoldersOnServer = true;
  GetPrefForServerAttribute("canCompactFoldersOnServer", canCompactFoldersOnServer);
  return NS_OK;
}

NS_IMETHODIMP
nsImapIncomingServer::GetCanUndoDeleteOnServer(bool *canUndoDeleteOnServer)
{
  NS_ENSURE_ARG_POINTER(canUndoDeleteOnServer);
  // Initialize canUndoDeleteOnServer true, a default value for IMAP
  *canUndoDeleteOnServer = true;
  GetPrefForServerAttribute("canUndoDeleteOnServer", canUndoDeleteOnServer);
  return NS_OK;
}

NS_IMETHODIMP
nsImapIncomingServer::GetCanSearchMessages(bool *canSearchMessages)
{
  NS_ENSURE_ARG_POINTER(canSearchMessages);
  // Initialize canSearchMessages true, a default value for IMAP
  *canSearchMessages = true;
  GetPrefForServerAttribute("canSearchMessages", canSearchMessages);
  return NS_OK;
}

NS_IMETHODIMP
nsImapIncomingServer::GetCanEmptyTrashOnExit(bool *canEmptyTrashOnExit)
{
  NS_ENSURE_ARG_POINTER(canEmptyTrashOnExit);
  // Initialize canEmptyTrashOnExit true, a default value for IMAP
  *canEmptyTrashOnExit = true;
  GetPrefForServerAttribute("canEmptyTrashOnExit", canEmptyTrashOnExit);
  return NS_OK;
}

nsresult
nsImapIncomingServer::CreateHostSpecificPrefName(const char *prefPrefix, nsAutoCString &prefName)
{
  NS_ENSURE_ARG_POINTER(prefPrefix);

  nsCString hostName;
  nsresult rv = GetHostName(hostName);
  NS_ENSURE_SUCCESS(rv,rv);

  prefName = prefPrefix;
  prefName.Append('.');
  prefName.Append(hostName);
  return NS_OK;
}

NS_IMETHODIMP
nsImapIncomingServer::GetSupportsDiskSpace(bool *aSupportsDiskSpace)
{
  NS_ENSURE_ARG_POINTER(aSupportsDiskSpace);
  nsAutoCString prefName;
  nsresult rv = CreateHostSpecificPrefName("default_supports_diskspace", prefName);
  NS_ENSURE_SUCCESS(rv,rv);

  nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
  if (NS_SUCCEEDED(rv))
     rv = prefBranch->GetBoolPref(prefName.get(), aSupportsDiskSpace);

  // Couldn't get the default value with the hostname.
  // Fall back on IMAP default value
  if (NS_FAILED(rv)) // set default value
     *aSupportsDiskSpace = true;
  return NS_OK;
}

// count number of non-busy connections in cache
NS_IMETHODIMP
nsImapIncomingServer::GetNumIdleConnections(int32_t *aNumIdleConnections)
{
  NS_ENSURE_ARG_POINTER(aNumIdleConnections);
  *aNumIdleConnections = 0;

  nsresult rv = NS_OK;
  nsCOMPtr<nsIImapProtocol> connection;
  bool isBusy = false;
  bool isInboxConnection;
  PR_CEnterMonitor(this);

  int32_t cnt = m_connectionCache.Count();

  // loop counting idle connections
  for (int32_t i = 0; i < cnt; ++i)
  {
    connection = m_connectionCache[i];
    if (connection)
    {
      rv = connection->IsBusy(&isBusy, &isInboxConnection);
      if (NS_FAILED(rv))
        continue;
      if (!isBusy)
        (*aNumIdleConnections)++;
    }
  }
  PR_CExitMonitor(this);
  return rv;
}


/**
 * Get the preference that tells us whether the imap server in question allows
 * us to create subfolders. Some ISPs might not want users to create any folders
 * besides the existing ones.
 * We do want to identify all those servers that don't allow creation of subfolders
 * and take them out of the account picker in the Copies and Folder panel.
 */
NS_IMETHODIMP
nsImapIncomingServer::GetCanCreateFoldersOnServer(bool *aCanCreateFoldersOnServer)
{
  NS_ENSURE_ARG_POINTER(aCanCreateFoldersOnServer);
  // Initialize aCanCreateFoldersOnServer true, a default value for IMAP
  *aCanCreateFoldersOnServer = true;
  GetPrefForServerAttribute("canCreateFolders", aCanCreateFoldersOnServer);
  return NS_OK;
}

NS_IMETHODIMP
nsImapIncomingServer::GetOfflineSupportLevel(int32_t *aSupportLevel)
{
  NS_ENSURE_ARG_POINTER(aSupportLevel);
  nsresult rv = NS_OK;

  rv = GetIntValue("offline_support_level", aSupportLevel);
  if (*aSupportLevel != OFFLINE_SUPPORT_LEVEL_UNDEFINED)
    return rv;

  nsAutoCString prefName;
  rv = CreateHostSpecificPrefName("default_offline_support_level", prefName);
  NS_ENSURE_SUCCESS(rv,rv);

  nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
  if (NS_SUCCEEDED(rv))
    rv = prefBranch->GetIntPref(prefName.get(), aSupportLevel);

  // Couldn't get the pref value with the hostname.
  // Fall back on IMAP default value
  if (NS_FAILED(rv)) // set default value
    *aSupportLevel = OFFLINE_SUPPORT_LEVEL_REGULAR;
  return NS_OK;
}

// Called only during the migration process. This routine enables the generation of
// unique account name based on the username, hostname and the port. If the port
// is valid and not a default one, it will be appended to the account name.
NS_IMETHODIMP
nsImapIncomingServer::GeneratePrettyNameForMigration(nsAString& aPrettyName)
{
  nsCString userName;
  nsCString hostName;

/**
  * Pretty name for migrated account is of format username@hostname:<port>,
  * provided the port is valid and not the default
*/
  // Get user name to construct pretty name
  nsresult rv = GetUsername(userName);
  NS_ENSURE_SUCCESS(rv, rv);

  // Get host name to construct pretty name
  rv = GetHostName(hostName);
  NS_ENSURE_SUCCESS(rv, rv);

  int32_t defaultServerPort;
  int32_t defaultSecureServerPort;

  // Here, the final contract ID is already known, so use it directly for efficiency.
  nsCOMPtr <nsIMsgProtocolInfo> protocolInfo = do_GetService(NS_IMAPPROTOCOLINFO_CONTRACTID, &rv);
  NS_ENSURE_SUCCESS(rv,rv);

  // Get the default port
  rv = protocolInfo->GetDefaultServerPort(false, &defaultServerPort);
  NS_ENSURE_SUCCESS(rv,rv);

  // Get the default secure port
  rv = protocolInfo->GetDefaultServerPort(true, &defaultSecureServerPort);
  NS_ENSURE_SUCCESS(rv,rv);

  // Get the current server port
  int32_t serverPort = PORT_NOT_SET;
  rv = GetPort(&serverPort);
  NS_ENSURE_SUCCESS(rv,rv);

  // Is the server secure ?
  int32_t socketType;
  rv = GetSocketType(&socketType);
  NS_ENSURE_SUCCESS(rv,rv);
  bool isSecure = (socketType == nsMsgSocketType::SSL);

  // Is server port a default port ?
  bool isItDefaultPort = false;
  if (((serverPort == defaultServerPort) && !isSecure)||
      ((serverPort == defaultSecureServerPort) && isSecure))
      isItDefaultPort = true;

  // Construct pretty name from username and hostname
  nsAutoString constructedPrettyName;
  CopyASCIItoUTF16(userName,constructedPrettyName);
  constructedPrettyName.Append('@');
  constructedPrettyName.Append(NS_ConvertASCIItoUTF16(hostName));

  // If the port is valid and not default, add port value to the pretty name
  if ((serverPort > 0) && (!isItDefaultPort)) {
    constructedPrettyName.Append(':');
    constructedPrettyName.AppendInt(serverPort);
  }

  // Format the pretty name
  return GetFormattedStringFromName(constructedPrettyName,
                                    "imapDefaultAccountName",
                                    aPrettyName);
}

nsresult
nsImapIncomingServer::GetFormattedStringFromName(const nsAString& aValue,
                                                 const char* aName,
                                                 nsAString& aResult)
{
  nsresult rv = GetStringBundle();
  if (m_stringBundle)
  {
    nsString tmpVal (aValue);
    const char16_t *formatStrings[] = { tmpVal.get() };

    nsString result;
    rv = m_stringBundle->FormatStringFromName(
      NS_ConvertASCIItoUTF16(aName).get(),
      formatStrings, 1, getter_Copies(result));
    aResult.Assign(result);
  }
  return rv;
}

nsresult
nsImapIncomingServer::GetPrefForServerAttribute(const char *prefSuffix, bool *prefValue)
{
  // Any caller of this function must initialize prefValue with a default value
  // as this code will not set prefValue when the pref does not exist and return
  // NS_OK anyway

  if (!mPrefBranch)
    return NS_ERROR_NOT_INITIALIZED;

  NS_ENSURE_ARG_POINTER(prefValue);

  if (NS_FAILED(mPrefBranch->GetBoolPref(prefSuffix, prefValue)))
    mDefPrefBranch->GetBoolPref(prefSuffix, prefValue);

  return NS_OK;
}

NS_IMETHODIMP
nsImapIncomingServer::GetCanFileMessagesOnServer(bool *aCanFileMessagesOnServer)
{
  NS_ENSURE_ARG_POINTER(aCanFileMessagesOnServer);
  // Initialize aCanFileMessagesOnServer true, a default value for IMAP
  *aCanFileMessagesOnServer = true;
  GetPrefForServerAttribute("canFileMessages", aCanFileMessagesOnServer);
  return NS_OK;
}

NS_IMETHODIMP
nsImapIncomingServer::SetSearchValue(const nsAString &searchValue)
{
  return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP
nsImapIncomingServer::GetSupportsSubscribeSearch(bool *retVal)
{
  NS_ENSURE_ARG_POINTER(retVal);
  *retVal = false;
  return NS_OK;
}

NS_IMETHODIMP
nsImapIncomingServer::GetFilterScope(nsMsgSearchScopeValue *filterScope)
{
  NS_ENSURE_ARG_POINTER(filterScope);
  // If the inbox is enabled for offline use, then use the offline filter
  // scope, else use the online filter scope.
  //
  // XXX We use the same scope for all folders with the same incoming server,
  // yet it is possible to set the offline flag separately for each folder.
  // Manual filters could perhaps check the offline status of each folder,
  // though it's hard to see how to make that work since we only store filters
  // per server.
  //
  nsCOMPtr<nsIMsgFolder> rootMsgFolder;
  nsresult rv = GetRootMsgFolder(getter_AddRefs(rootMsgFolder));
  NS_ENSURE_SUCCESS(rv, rv);
  nsCOMPtr<nsIMsgFolder> offlineInboxMsgFolder;
  rv = rootMsgFolder->GetFolderWithFlags(nsMsgFolderFlags::Inbox |
                                           nsMsgFolderFlags::Offline,
                                         getter_AddRefs(offlineInboxMsgFolder));

  *filterScope = offlineInboxMsgFolder ? nsMsgSearchScope::offlineMailFilter
                                       : nsMsgSearchScope::onlineMailFilter;
  return NS_OK;
}

NS_IMETHODIMP
nsImapIncomingServer::GetSearchScope(nsMsgSearchScopeValue *searchScope)
{
  NS_ENSURE_ARG_POINTER(searchScope);
 *searchScope = WeAreOffline() ? nsMsgSearchScope::offlineMail : nsMsgSearchScope::onlineMail;
  return NS_OK;
}

// This is a recursive function. It gets new messages for current folder
// first if it is marked, then calls itself recursively for each subfolder.
NS_IMETHODIMP
nsImapIncomingServer::GetNewMessagesForNonInboxFolders(nsIMsgFolder *aFolder,
                                                       nsIMsgWindow *aWindow,
                                                       bool forceAllFolders,
                                                       bool performingBiff)
{
  NS_ENSURE_ARG_POINTER(aFolder);
  static bool gGotStatusPref = false;
  static bool gUseStatus = false;

  bool isServer;
  (void) aFolder->GetIsServer(&isServer);
  // Check this folder for new messages if it is marked to be checked
  // or if we are forced to check all folders
  uint32_t flags = 0;
  aFolder->GetFlags(&flags);
  nsresult rv;
  nsCOMPtr<nsIMsgImapMailFolder> imapFolder = do_QueryInterface(aFolder, &rv);
  NS_ENSURE_SUCCESS(rv, rv);
  bool canOpen;
  imapFolder->GetCanOpenFolder(&canOpen);
  if (canOpen && ((forceAllFolders &&
                 !(flags & (nsMsgFolderFlags::Inbox | nsMsgFolderFlags::Trash |
                   nsMsgFolderFlags::Junk | nsMsgFolderFlags::Virtual))) ||
                flags & nsMsgFolderFlags::CheckNew))
  {
    // Get new messages for this folder.
    aFolder->SetGettingNewMessages(true);
    if (performingBiff)
      imapFolder->SetPerformingBiff(true);
    bool isOpen = false;
    nsCOMPtr <nsIMsgMailSession> mailSession = do_GetService(NS_MSGMAILSESSION_CONTRACTID);
    if (mailSession && aFolder)
      mailSession->IsFolderOpenInWindow(aFolder, &isOpen);
    // eventually, the gGotStatusPref should go away, once we work out the kinks
    // from using STATUS.
    if (!gGotStatusPref)
    {
      nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID);
      if(prefBranch)
        prefBranch->GetBoolPref("mail.imap.use_status_for_biff", &gUseStatus);
      gGotStatusPref = true;
    }
    if (gUseStatus && !isOpen)
    {
      if (!isServer && m_foldersToStat.IndexOf(imapFolder) == -1)
        m_foldersToStat.AppendObject(imapFolder);
    }
    else
      aFolder->UpdateFolder(aWindow);
  }

  // Loop through all subfolders to get new messages for them.
  nsCOMPtr<nsISimpleEnumerator> enumerator;
  rv = aFolder->GetSubFolders(getter_AddRefs(enumerator));
  if (NS_FAILED(rv))
    return rv;

  bool hasMore;
  while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore)
  {
    nsCOMPtr<nsISupports> item;
    enumerator->GetNext(getter_AddRefs(item));

    nsCOMPtr<nsIMsgFolder> msgFolder(do_QueryInterface(item));
    if (!msgFolder)
    {
      NS_WARNING("Not an nsIMsgFolder");
      continue;
    }
    GetNewMessagesForNonInboxFolders(msgFolder, aWindow, forceAllFolders,
                                     performingBiff);
  }
  if (isServer && m_foldersToStat.Count() > 0)
    m_foldersToStat[0]->UpdateStatus(this, nullptr);
  return NS_OK;
}

NS_IMETHODIMP
nsImapIncomingServer::GetArbitraryHeaders(nsACString &aResult)
{
  nsCOMPtr <nsIMsgFilterList> filterList;
  nsresult rv = GetFilterList(nullptr, getter_AddRefs(filterList));
  NS_ENSURE_SUCCESS(rv,rv);
  return filterList->GetArbitraryHeaders(aResult);
}

NS_IMETHODIMP
nsImapIncomingServer::GetShowAttachmentsInline(bool *aResult)
{
  NS_ENSURE_ARG_POINTER(aResult);
  *aResult = true; // true per default
  nsresult rv;
  nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
  NS_ENSURE_SUCCESS(rv,rv);

  prefBranch->GetBoolPref("mail.inline_attachments", aResult);
  return NS_OK; // In case this pref is not set we need to return NS_OK.
}

NS_IMETHODIMP nsImapIncomingServer::SetSocketType(int32_t aSocketType)
{
  int32_t oldSocketType;
  nsresult rv = GetSocketType(&oldSocketType);
  if (NS_SUCCEEDED(rv) && oldSocketType != aSocketType)
    CloseCachedConnections();
  return nsMsgIncomingServer::SetSocketType(aSocketType);
}

NS_IMETHODIMP
nsImapIncomingServer::OnUserOrHostNameChanged(const nsACString& oldName,
                                              const nsACString& newName,
                                              bool hostnameChanged)
{
  nsresult rv;
  // 1. Do common things in the base class.
  rv = nsMsgIncomingServer::OnUserOrHostNameChanged(oldName, newName, hostnameChanged);
  NS_ENSURE_SUCCESS(rv,rv);

  // 2. Reset 'HaveWeEverDiscoveredFolders' flag so the new folder list can be
  //    reloaded (ie, DiscoverMailboxList() will be invoked in nsImapProtocol).
  nsCOMPtr<nsIImapHostSessionList> hostSessionList = do_GetService(kCImapHostSessionListCID, &rv);
  NS_ENSURE_SUCCESS(rv, rv);
  nsAutoCString serverKey;
  rv = GetKey(serverKey);
  NS_ENSURE_SUCCESS(rv, rv);
  hostSessionList->SetHaveWeEverDiscoveredFoldersForHost(serverKey.get(), false);
  // 3. Make all the existing folders 'unverified' so that they can be
  //    removed from the folder pane after users log into the new server.
  ResetFoldersToUnverified(nullptr);
  return NS_OK;
}

// use canonical format in originalUri & convertedUri
NS_IMETHODIMP
nsImapIncomingServer::GetUriWithNamespacePrefixIfNecessary(int32_t namespaceType,
                                                           const nsACString& originalUri,
                                                           nsACString& convertedUri)
{
  nsresult rv = NS_OK;
  nsAutoCString serverKey;
  rv = GetKey(serverKey);
  NS_ENSURE_SUCCESS(rv, rv);
  nsCOMPtr<nsIImapHostSessionList> hostSessionList = do_GetService(kCImapHostSessionListCID, &rv);
  nsIMAPNamespace *ns = nullptr;
  rv = hostSessionList->GetDefaultNamespaceOfTypeForHost(serverKey.get(), (EIMAPNamespaceType)namespaceType, ns);
  if (ns)
  {
    nsAutoCString namespacePrefix(ns->GetPrefix());
    if (!namespacePrefix.IsEmpty())
    {
      // check if namespacePrefix is the same as the online directory; if so, ignore it.
      nsAutoCString onlineDir;
      rv = GetServerDirectory(onlineDir);
      NS_ENSURE_SUCCESS(rv, rv);
      if (!onlineDir.IsEmpty())
      {
        char delimiter = ns->GetDelimiter();
          if ( onlineDir.Last() != delimiter )
            onlineDir += delimiter;
          if (onlineDir.Equals(namespacePrefix))
            return NS_OK;
      }

      MsgReplaceChar(namespacePrefix, ns->GetDelimiter(), '/'); // use canonical format
      nsCString uri(originalUri);
      int32_t index = uri.Find("//");           // find scheme
      index = uri.FindChar('/', index + 2);       // find '/' after scheme
      // it may be the case that this is the INBOX uri, in which case
      // we don't want to prepend the namespace. In that case, the uri ends with "INBOX",
      // but the namespace is "INBOX/", so they don't match.
      if (MsgFind(uri, namespacePrefix, false, index + 1) != index + 1 &&
          !MsgLowerCaseEqualsLiteral(Substring(uri, index + 1), "inbox"))
        uri.Insert(namespacePrefix, index + 1);   // insert namespace prefix
      convertedUri = uri;
    }
  }
  return rv;
}

NS_IMETHODIMP nsImapIncomingServer::GetTrashFolderName(nsAString& retval)
{
  nsresult rv = GetUnicharValue(PREF_TRASH_FOLDER_NAME, retval);
  if (NS_FAILED(rv))
    return rv;
  if (retval.IsEmpty())
    retval = NS_LITERAL_STRING(DEFAULT_TRASH_FOLDER_NAME);
  return NS_OK;
}

NS_IMETHODIMP nsImapIncomingServer::SetTrashFolderName(const nsAString& chvalue)
{
  // clear trash flag from the old pref
  nsAutoString oldTrashName;
  nsresult rv = GetTrashFolderName(oldTrashName);
  if (NS_SUCCEEDED(rv))
  {
    nsAutoCString oldTrashNameUtf7;
    rv = CopyUTF16toMUTF7(oldTrashName, oldTrashNameUtf7);
    if (NS_SUCCEEDED(rv))
    {
      nsCOMPtr<nsIMsgFolder> oldFolder;
      rv = GetFolder(oldTrashNameUtf7, getter_AddRefs(oldFolder));
      if (NS_SUCCEEDED(rv) && oldFolder)
        oldFolder->ClearFlag(nsMsgFolderFlags::Trash);
    }
  }
  return SetUnicharValue(PREF_TRASH_FOLDER_NAME, chvalue);
}

NS_IMETHODIMP
nsImapIncomingServer::GetMsgFolderFromURI(nsIMsgFolder *aFolderResource,
                                          const nsACString& aURI,
                                          nsIMsgFolder **aFolder)
{
  nsCOMPtr<nsIMsgFolder> msgFolder;
  bool namespacePrefixAdded = false;
  nsCString folderUriWithNamespace;

  // Check if the folder exists as is...
  nsresult rv = GetExistingMsgFolder(aURI, folderUriWithNamespace,
                                     namespacePrefixAdded, false,
                                     getter_AddRefs(msgFolder));

  // Or try again with a case-insensitive lookup
  if (NS_FAILED(rv) || !msgFolder)
    rv = GetExistingMsgFolder(aURI, folderUriWithNamespace,
                              namespacePrefixAdded, true,
                              getter_AddRefs(msgFolder));

  if (NS_FAILED(rv) || !msgFolder) {
    // we didn't find the folder so we will have to create a new one.
    if (namespacePrefixAdded)
    {
      nsCOMPtr <nsIRDFService> rdf = do_GetService("@mozilla.org/rdf/rdf-service;1", &rv);
      NS_ENSURE_SUCCESS(rv,rv);

      nsCOMPtr<nsIRDFResource> resource;
      rv = rdf->GetResource(folderUriWithNamespace, getter_AddRefs(resource));
      NS_ENSURE_SUCCESS(rv,rv);

      nsCOMPtr <nsIMsgFolder> folderResource;
      folderResource = do_QueryInterface(resource, &rv);
      NS_ENSURE_SUCCESS(rv,rv);
      msgFolder = folderResource;
    }
    else
      msgFolder = aFolderResource;
  }

  msgFolder.swap(*aFolder);
  return NS_OK;
}

nsresult
nsImapIncomingServer::GetExistingMsgFolder(const nsACString& aURI,
                                           nsACString& aFolderUriWithNamespace,
                                           bool& aNamespacePrefixAdded,
                                           bool aCaseInsensitive,
                                           nsIMsgFolder **aFolder)
{
  nsCOMPtr<nsIMsgFolder> rootMsgFolder;
  nsresult rv = GetRootMsgFolder(getter_AddRefs(rootMsgFolder));
  NS_ENSURE_SUCCESS(rv, rv);

  aNamespacePrefixAdded = false;
  // Check if the folder exists as is...Even if we have a personal namespace,
  // it might be in another namespace (e.g., shared) and this will catch that.
  rv = rootMsgFolder->GetChildWithURI(aURI, true, aCaseInsensitive, aFolder);

  // If we couldn't find the folder as is, check if we need to prepend the
  // personal namespace
  if (!*aFolder)
  {
    GetUriWithNamespacePrefixIfNecessary(kPersonalNamespace, aURI,
                                         aFolderUriWithNamespace);
    if (!aFolderUriWithNamespace.IsEmpty())
    {
      aNamespacePrefixAdded = true;
      rv = rootMsgFolder->GetChildWithURI(aFolderUriWithNamespace, true,
                                          aCaseInsensitive, aFolder);
    }
  }
  return rv;
}

NS_IMETHODIMP
nsImapIncomingServer::CramMD5Hash(const char *decodedChallenge, const char *key, char **result)
{
  NS_ENSURE_ARG_POINTER(decodedChallenge);
  NS_ENSURE_ARG_POINTER(key);

  unsigned char resultDigest[DIGEST_LENGTH];
  nsresult rv = MSGCramMD5(decodedChallenge, strlen(decodedChallenge), key, strlen(key), resultDigest);
  NS_ENSURE_SUCCESS(rv, rv);
  *result = (char *) malloc(DIGEST_LENGTH);
  if (*result)
    memcpy(*result, resultDigest, DIGEST_LENGTH);
  return (*result) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}

NS_IMETHODIMP
nsImapIncomingServer::GetLoginUsername(nsACString &aLoginUsername)
{
  return GetRealUsername(aLoginUsername);
}

NS_IMETHODIMP
nsImapIncomingServer::GetOriginalUsername(nsACString &aUsername)
{
  return GetUsername(aUsername);
}

NS_IMETHODIMP
nsImapIncomingServer::GetServerKey(nsACString &aServerKey)
{
  return GetKey(aServerKey);
}

NS_IMETHODIMP
nsImapIncomingServer::GetServerPassword(nsACString &aPassword)
{
  return GetPassword(aPassword);
}

NS_IMETHODIMP
nsImapIncomingServer::RemoveServerConnection(nsIImapProtocol* aProtocol)
{
  return RemoveConnection(aProtocol);
}

NS_IMETHODIMP
nsImapIncomingServer::GetServerShuttingDown(bool* aShuttingDown)
{
  return GetShuttingDown(aShuttingDown);
}

NS_IMETHODIMP
nsImapIncomingServer::ResetServerConnection(const nsACString& aFolderName)
{
  return ResetConnection(aFolderName);
}

NS_IMETHODIMP
nsImapIncomingServer::SetServerDoingLsub(bool aDoingLsub)
{
  return SetDoingLsub(aDoingLsub);
}
