mailnews/imap/src/nsImapUrl.cpp
author Rob Lemley <rob@thunderbird.net>
Mon, 27 Apr 2020 19:51:09 -0400
changeset 38064 39e1d01fb0c37b7f70bc92d58a9a06f8148e6981
parent 37611 2891d703d7591ad5492211961c7bfc1593ab8193
child 38590 9c08c80ec341936cd58f8a8fbdf01a59bec5fd17
permissions -rw-r--r--
Bug 1577518 - Update libgcrypt to version 1.8.5. r=kaie

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

#include "msgCore.h"  // precompiled header...
#include "nsMsgImapCID.h"

#include "nsImapUrl.h"
#include "nsIIMAPHostSessionList.h"
#include "nsThreadUtils.h"
#include "nsString.h"
#include "prmem.h"
#include "plstr.h"
#include "prprf.h"
#include "nsMemory.h"
#include "nsCOMPtr.h"
#include "nsMsgBaseCID.h"
#include "nsImapUtils.h"
#include "nsIImapMockChannel.h"
#include "nsIImapMailFolderSink.h"
#include "nsIImapMessageSink.h"
#include "nsIImapServerSink.h"
#include "nsIMAPNamespace.h"
#include "nsICacheEntry.h"
#include "nsIMsgFolder.h"
#include "nsMsgUtils.h"
#include "nsIMsgHdr.h"
#include "nsServiceManagerUtils.h"

using namespace mozilla;
extern LazyLogModule IMAPCache;  // defined in nsImapProtocol.cpp

static NS_DEFINE_CID(kCImapHostSessionListCID, NS_IIMAPHOSTSESSIONLIST_CID);

nsImapUrl::nsImapUrl() : mLock("nsImapUrl.mLock") {
  m_listOfMessageIds = nullptr;
  m_sourceCanonicalFolderPathSubString = nullptr;
  m_destinationCanonicalFolderPathSubString = nullptr;
  m_listOfMessageIds = nullptr;
  m_tokenPlaceHolder = nullptr;
  m_searchCriteriaString = nullptr;
  m_idsAreUids = false;
  m_mimePartSelectorDetected = false;
  m_allowContentChange = true;   // assume we can do MPOD.
  m_fetchPartsOnDemand = false;  // but assume we're not doing it :-)
  m_msgLoadingFromCache = false;
  m_storeResultsOffline = false;
  m_storeOfflineOnFallback = false;
  m_localFetchOnly = false;
  m_rerunningUrl = false;
  m_moreHeadersToDownload = false;
  m_externalLinkUrl = true;  // we'll start this at true, and set it false in
                             // nsImapService::CreateStartOfImapUrl
  m_contentModified = IMAP_CONTENT_NOT_MODIFIED;
  m_validUrl = true;  // assume the best.
  m_flags = 0;
  m_extraStatus = ImapStatusNone;
  m_onlineSubDirSeparator = '/';
  m_imapAction = 0;

  // ** jt - the following are not ref counted
  m_copyState = nullptr;
  m_file = nullptr;
  m_imapMailFolderSink = nullptr;
  m_imapMessageSink = nullptr;
  m_addDummyEnvelope = false;
  m_canonicalLineEnding = false;
}

nsImapUrl::~nsImapUrl() {
  PR_FREEIF(m_listOfMessageIds);
  PR_FREEIF(m_destinationCanonicalFolderPathSubString);
  PR_FREEIF(m_sourceCanonicalFolderPathSubString);
  PR_FREEIF(m_searchCriteriaString);
}

NS_IMPL_ADDREF_INHERITED(nsImapUrl, nsMsgMailNewsUrl)

NS_IMPL_RELEASE_INHERITED(nsImapUrl, nsMsgMailNewsUrl)

NS_INTERFACE_MAP_BEGIN(nsImapUrl)
  NS_INTERFACE_MAP_ENTRY(nsIImapUrl)
  NS_INTERFACE_MAP_ENTRY(nsIMsgMessageUrl)
  NS_INTERFACE_MAP_ENTRY(nsIMsgI18NUrl)
NS_INTERFACE_MAP_END_INHERITING(nsMsgMailNewsUrl)

////////////////////////////////////////////////////////////////////////////////////
// Begin nsIImapUrl specific support
////////////////////////////////////////////////////////////////////////////////////

NS_IMETHODIMP nsImapUrl::GetRequiredImapState(nsImapState *aImapUrlState) {
  if (aImapUrlState) {
    // the imap action determines the state we must be in...check the
    // the imap action.

    if (m_imapAction & 0x10000000)
      *aImapUrlState = nsImapSelectedState;
    else
      *aImapUrlState = nsImapAuthenticatedState;
  }

  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::GetImapAction(nsImapAction *aImapAction) {
  *aImapAction = m_imapAction;
  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::SetImapAction(nsImapAction aImapAction) {
  m_imapAction = aImapAction;
  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::GetFolder(nsIMsgFolder **aMsgFolder) {
  NS_ENSURE_ARG_POINTER(aMsgFolder);
  NS_ENSURE_ARG_POINTER(m_imapFolder);

  nsCOMPtr<nsIMsgFolder> folder = do_QueryReferent(m_imapFolder);
  folder.forget(aMsgFolder);
  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::SetFolder(nsIMsgFolder *aMsgFolder) {
  nsresult rv;
  m_imapFolder = do_GetWeakReference(aMsgFolder, &rv);
  if (aMsgFolder) {
    nsCOMPtr<nsIMsgIncomingServer> incomingServer;
    aMsgFolder->GetServer(getter_AddRefs(incomingServer));
    if (incomingServer) incomingServer->GetKey(m_serverKey);
  }
  return rv;
}

NS_IMETHODIMP nsImapUrl::GetImapMailFolderSink(
    nsIImapMailFolderSink **aImapMailFolderSink) {
  NS_ENSURE_ARG_POINTER(aImapMailFolderSink);
  if (!m_imapMailFolderSink)
    return NS_ERROR_NULL_POINTER;  // no assert, so don't use NS_ENSURE_POINTER.

  nsCOMPtr<nsIImapMailFolderSink> folderSink =
      do_QueryReferent(m_imapMailFolderSink);
  folderSink.forget(aImapMailFolderSink);
  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::SetImapMailFolderSink(
    nsIImapMailFolderSink *aImapMailFolderSink) {
  nsresult rv;
  m_imapMailFolderSink = do_GetWeakReference(aImapMailFolderSink, &rv);
  return rv;
}

NS_IMETHODIMP nsImapUrl::GetImapMessageSink(
    nsIImapMessageSink **aImapMessageSink) {
  NS_ENSURE_ARG_POINTER(aImapMessageSink);
  NS_ENSURE_ARG_POINTER(m_imapMessageSink);

  nsCOMPtr<nsIImapMessageSink> messageSink =
      do_QueryReferent(m_imapMessageSink);
  messageSink.forget(aImapMessageSink);
  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::SetImapMessageSink(
    nsIImapMessageSink *aImapMessageSink) {
  nsresult rv;
  m_imapMessageSink = do_GetWeakReference(aImapMessageSink, &rv);
  return rv;
}

NS_IMETHODIMP nsImapUrl::GetImapServerSink(
    nsIImapServerSink **aImapServerSink) {
  NS_ENSURE_ARG_POINTER(aImapServerSink);
  NS_ENSURE_ARG_POINTER(m_imapServerSink);

  nsCOMPtr<nsIImapServerSink> serverSink = do_QueryReferent(m_imapServerSink);
  serverSink.forget(aImapServerSink);
  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::SetImapServerSink(nsIImapServerSink *aImapServerSink) {
  nsresult rv;
  m_imapServerSink = do_GetWeakReference(aImapServerSink, &rv);
  return rv;
}

////////////////////////////////////////////////////////////////////////////////////
// End nsIImapUrl specific support
////////////////////////////////////////////////////////////////////////////////////

nsresult nsImapUrl::SetSpecInternal(const nsACString &aSpec) {
  nsresult rv = nsMsgMailNewsUrl::SetSpecInternal(aSpec);
  if (NS_SUCCEEDED(rv)) {
    m_validUrl = true;  // assume the best.
    rv = ParseUrl();
  }
  return rv;
}

nsresult nsImapUrl::SetQuery(const nsACString &aQuery) {
  nsresult rv = nsMsgMailNewsUrl::SetQuery(aQuery);
  if (NS_SUCCEEDED(rv)) rv = ParseUrl();
  return rv;
}

nsresult nsImapUrl::ParseUrl() {
  nsresult rv = NS_OK;
  // extract the user name
  GetUserPass(m_userName);

  nsAutoCString imapPartOfUrl;
  rv = GetPathQueryRef(imapPartOfUrl);
  nsAutoCString unescapedImapPartOfUrl;
  MsgUnescapeString(imapPartOfUrl, 0, unescapedImapPartOfUrl);
  if (NS_SUCCEEDED(rv) && !unescapedImapPartOfUrl.IsEmpty()) {
    ParseImapPart(unescapedImapPartOfUrl.BeginWriting() +
                  1);  // GetPath leaves leading '/' in the path!!!
  }

  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::CreateSearchCriteriaString(char **aResult) {
  // this method should only be called from the imap thread...
  // o.t. add lock protection..
  if (nullptr == aResult || !m_searchCriteriaString)
    return NS_ERROR_NULL_POINTER;
  *aResult = strdup(m_searchCriteriaString);
  return NS_OK;
}

// this method gets called from the UI thread and the imap thread
NS_IMETHODIMP nsImapUrl::GetListOfMessageIds(nsACString &aResult) {
  MutexAutoLock mon(mLock);
  if (!m_listOfMessageIds) return NS_ERROR_NULL_POINTER;

  int32_t bytesToCopy = strlen(m_listOfMessageIds);

  // mime may have glommed a "&part=" for a part download
  // we return the entire message and let mime extract
  // the part. Pop and news work this way also.
  // this algorithm truncates the "&part" string.
  char *currentChar = m_listOfMessageIds;
  while (*currentChar && (*currentChar != '?')) currentChar++;
  if (*currentChar == '?') bytesToCopy = currentChar - m_listOfMessageIds;

  // we should also strip off anything after "/;section="
  // since that can specify an IMAP MIME part
  char *wherePart = PL_strstr(m_listOfMessageIds, "/;section=");
  if (wherePart)
    bytesToCopy =
        std::min(bytesToCopy, int32_t(wherePart - m_listOfMessageIds));

  aResult.Assign(m_listOfMessageIds, bytesToCopy);
  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::GetCommand(nsACString &result) {
  result = m_command;
  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::GetCustomAttributeToFetch(nsACString &result) {
  result = m_msgFetchAttribute;
  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::GetCustomAttributeResult(nsACString &result) {
  result = m_customAttributeResult;
  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::SetCustomAttributeResult(const nsACString &result) {
  m_customAttributeResult = result;
  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::GetCustomCommandResult(nsACString &result) {
  result = m_customCommandResult;
  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::SetCustomCommandResult(const nsACString &result) {
  m_customCommandResult = result;
  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::GetCustomAddFlags(nsACString &aResult) {
  aResult = m_customAddFlags;
  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::GetCustomSubtractFlags(nsACString &aResult) {
  aResult = m_customSubtractFlags;
  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::GetImapPartToFetch(char **result) {
  //  here's the old code....

  // unfortunately an imap part can have the form: /;section= OR
  // it can have the form ?section=. We need to look for both.
  if (m_listOfMessageIds) {
    char *wherepart = PL_strstr(m_listOfMessageIds, ";section=");
    if (!wherepart)  // look for ?section too....
      wherepart = PL_strstr(m_listOfMessageIds, "?section=");
    if (wherepart) {
      wherepart += 9;  // strlen("/;section=")
      char *wherelibmimepart = PL_strstr(wherepart, "&part=");
      if (!wherelibmimepart) wherelibmimepart = PL_strstr(wherepart, "?part=");
      int numCharsToCopy = (wherelibmimepart)
                               ? wherelibmimepart - wherepart
                               : PL_strlen(m_listOfMessageIds) -
                                     (wherepart - m_listOfMessageIds);
      if (numCharsToCopy) {
        *result = (char *)PR_Malloc(sizeof(char) * (numCharsToCopy + 1));
        if (*result) {
          PL_strncpy(*result, wherepart, numCharsToCopy + 1);  // appends a \0
          (*result)[numCharsToCopy] = '\0';
        }
      }
    }  // if we got a wherepart
  }    // if we got a m_listOfMessageIds
  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::GetOnlineSubDirSeparator(char *separator) {
  if (separator) {
    *separator = m_onlineSubDirSeparator;
    return NS_OK;
  }
  return NS_ERROR_NULL_POINTER;
}

NS_IMETHODIMP nsImapUrl::GetNumBytesToFetch(int32_t *aNumBytesToFetch) {
  NS_ENSURE_ARG_POINTER(aNumBytesToFetch);
  *aNumBytesToFetch = m_numBytesToFetch;
  return NS_OK;
}

NS_IMETHODIMP
nsImapUrl::SetOnlineSubDirSeparator(char onlineDirSeparator) {
  m_onlineSubDirSeparator = onlineDirSeparator;
  return NS_OK;
}

// this method is only called from the imap thread
NS_IMETHODIMP nsImapUrl::MessageIdsAreUids(bool *result) {
  *result = m_idsAreUids;
  return NS_OK;
}

NS_IMETHODIMP
nsImapUrl::SetExtraStatus(int32_t aExtraStatus) {
  m_extraStatus = aExtraStatus;
  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::GetExtraStatus(int32_t *aResult) {
  NS_ENSURE_ARG_POINTER(aResult);
  *aResult = m_extraStatus;
  return NS_OK;
}

// this method is only called from the imap thread
NS_IMETHODIMP nsImapUrl::GetMsgFlags(
    imapMessageFlagsType *result)  // kAddMsgFlags or kSubtractMsgFlags only
{
  *result = m_flags;
  return NS_OK;
}

void nsImapUrl::ParseImapPart(char *imapPartOfUrl) {
  m_tokenPlaceHolder = imapPartOfUrl;
  m_urlidSubString = m_tokenPlaceHolder ? NS_strtok(IMAP_URL_TOKEN_SEPARATOR,
                                                    &m_tokenPlaceHolder)
                                        : (char *)NULL;

  if (!m_urlidSubString) {
    m_validUrl = false;
    return;
  }

  if (!PL_strcasecmp(m_urlidSubString, "fetch")) {
    m_imapAction = nsImapMsgFetch;
    ParseUidChoice();
    PR_FREEIF(m_sourceCanonicalFolderPathSubString);
    ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
    ParseListOfMessageIds();
    // if fetched by spam filter, the action will be changed to
    // nsImapMsgFetchPeek
  } else {
    if (!PL_strcasecmp(m_urlidSubString, "header")) {
      m_imapAction = nsImapMsgHeader;
      ParseUidChoice();
      ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
      ParseListOfMessageIds();
    } else if (!PL_strcasecmp(m_urlidSubString, "customFetch")) {
      ParseUidChoice();
      ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
      ParseListOfMessageIds();
      ParseCustomMsgFetchAttribute();
    } else if (!PL_strcasecmp(m_urlidSubString, "previewBody")) {
      ParseUidChoice();
      ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
      ParseListOfMessageIds();
      ParseNumBytes();
    } else if (!PL_strcasecmp(m_urlidSubString, "deletemsg")) {
      m_imapAction = nsImapDeleteMsg;
      ParseUidChoice();
      ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
      ParseListOfMessageIds();
    } else if (!PL_strcasecmp(m_urlidSubString, "uidexpunge")) {
      m_imapAction = nsImapUidExpunge;
      ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
      ParseListOfMessageIds();
    } else if (!PL_strcasecmp(m_urlidSubString, "deleteallmsgs")) {
      m_imapAction = nsImapDeleteAllMsgs;
      ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
    } else if (!PL_strcasecmp(m_urlidSubString, "addmsgflags")) {
      m_imapAction = nsImapAddMsgFlags;
      ParseUidChoice();
      ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
      ParseListOfMessageIds();
      ParseMsgFlags();
    } else if (!PL_strcasecmp(m_urlidSubString, "subtractmsgflags")) {
      m_imapAction = nsImapSubtractMsgFlags;
      ParseUidChoice();
      ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
      ParseListOfMessageIds();
      ParseMsgFlags();
    } else if (!PL_strcasecmp(m_urlidSubString, "setmsgflags")) {
      m_imapAction = nsImapSetMsgFlags;
      ParseUidChoice();
      ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
      ParseListOfMessageIds();
      ParseMsgFlags();
    } else if (!PL_strcasecmp(m_urlidSubString, "onlinecopy")) {
      m_imapAction = nsImapOnlineCopy;
      ParseUidChoice();
      ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
      ParseListOfMessageIds();
      ParseFolderPath(&m_destinationCanonicalFolderPathSubString);
    } else if (!PL_strcasecmp(m_urlidSubString, "onlinemove")) {
      m_imapAction = nsImapOnlineMove;
      ParseUidChoice();
      ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
      ParseListOfMessageIds();
      ParseFolderPath(&m_destinationCanonicalFolderPathSubString);
    } else if (!PL_strcasecmp(m_urlidSubString, "onlinetoofflinecopy")) {
      m_imapAction = nsImapOnlineToOfflineCopy;
      ParseUidChoice();
      ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
      ParseListOfMessageIds();
      ParseFolderPath(&m_destinationCanonicalFolderPathSubString);
    } else if (!PL_strcasecmp(m_urlidSubString, "onlinetoofflinemove")) {
      m_imapAction = nsImapOnlineToOfflineMove;
      ParseUidChoice();
      ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
      ParseListOfMessageIds();
      ParseFolderPath(&m_destinationCanonicalFolderPathSubString);
    } else if (!PL_strcasecmp(m_urlidSubString, "offlinetoonlinecopy")) {
      m_imapAction = nsImapOfflineToOnlineMove;
      ParseFolderPath(&m_destinationCanonicalFolderPathSubString);
    } else if (!PL_strcasecmp(m_urlidSubString, "search")) {
      m_imapAction = nsImapSearch;
      ParseUidChoice();
      ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
      ParseSearchCriteriaString();
    } else if (!PL_strcasecmp(m_urlidSubString, "test")) {
      m_imapAction = nsImapTest;
    } else if (!PL_strcasecmp(m_urlidSubString, "select")) {
      m_imapAction = nsImapSelectFolder;
      ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
      if (m_tokenPlaceHolder && *m_tokenPlaceHolder)
        ParseListOfMessageIds();
      else
        m_listOfMessageIds = PL_strdup("");
    } else if (!PL_strcasecmp(m_urlidSubString, "liteselect")) {
      m_imapAction = nsImapLiteSelectFolder;
      ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
    } else if (!PL_strcasecmp(m_urlidSubString, "selectnoop")) {
      m_imapAction = nsImapSelectNoopFolder;
      m_listOfMessageIds = PL_strdup("");
      ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
    } else if (!PL_strcasecmp(m_urlidSubString, "expunge")) {
      m_imapAction = nsImapExpungeFolder;
      ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
      m_listOfMessageIds = PL_strdup("");  // no ids to UNDO
    } else if (!PL_strcasecmp(m_urlidSubString, "create")) {
      m_imapAction = nsImapCreateFolder;
      ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
    } else if (!PL_strcasecmp(m_urlidSubString, "ensureExists")) {
      m_imapAction = nsImapEnsureExistsFolder;
      ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
    } else if (!PL_strcasecmp(m_urlidSubString, "discoverchildren")) {
      m_imapAction = nsImapDiscoverChildrenUrl;
      ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
    } else if (!PL_strcasecmp(m_urlidSubString, "discoverallboxes")) {
      m_imapAction = nsImapDiscoverAllBoxesUrl;
    } else if (!PL_strcasecmp(m_urlidSubString,
                              "discoverallandsubscribedboxes")) {
      m_imapAction = nsImapDiscoverAllAndSubscribedBoxesUrl;
    } else if (!PL_strcasecmp(m_urlidSubString, "delete")) {
      m_imapAction = nsImapDeleteFolder;
      ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
    } else if (!PL_strcasecmp(m_urlidSubString, "deletefolder")) {
      m_imapAction = nsImapDeleteFolderAndMsgs;
      ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
    } else if (!PL_strcasecmp(m_urlidSubString, "rename")) {
      m_imapAction = nsImapRenameFolder;
      ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
      ParseFolderPath(&m_destinationCanonicalFolderPathSubString);
    } else if (!PL_strcasecmp(m_urlidSubString, "movefolderhierarchy")) {
      m_imapAction = nsImapMoveFolderHierarchy;
      ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
      if (m_tokenPlaceHolder && *m_tokenPlaceHolder)  // handle promote to root
        ParseFolderPath(&m_destinationCanonicalFolderPathSubString);
    } else if (!PL_strcasecmp(m_urlidSubString, "list")) {
      m_imapAction = nsImapLsubFolders;
      ParseFolderPath(&m_destinationCanonicalFolderPathSubString);
    } else if (!PL_strcasecmp(m_urlidSubString, "biff")) {
      m_imapAction = nsImapBiff;
      ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
      ParseListOfMessageIds();
    } else if (!PL_strcasecmp(m_urlidSubString, "netscape")) {
      m_imapAction = nsImapGetMailAccountUrl;
    } else if (!PL_strcasecmp(m_urlidSubString, "appendmsgfromfile")) {
      m_imapAction = nsImapAppendMsgFromFile;
      ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
    } else if (!PL_strcasecmp(m_urlidSubString, "appenddraftfromfile")) {
      m_imapAction = nsImapAppendDraftFromFile;
      ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
      ParseUidChoice();
      if (m_tokenPlaceHolder && *m_tokenPlaceHolder)
        ParseListOfMessageIds();
      else
        m_listOfMessageIds = strdup("");
    } else if (!PL_strcasecmp(m_urlidSubString, "subscribe")) {
      m_imapAction = nsImapSubscribe;
      ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
    } else if (!PL_strcasecmp(m_urlidSubString, "unsubscribe")) {
      m_imapAction = nsImapUnsubscribe;
      ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
    } else if (!PL_strcasecmp(m_urlidSubString, "refreshacl")) {
      m_imapAction = nsImapRefreshACL;
      ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
    } else if (!PL_strcasecmp(m_urlidSubString, "refreshfolderurls")) {
      m_imapAction = nsImapRefreshFolderUrls;
      ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
    } else if (!PL_strcasecmp(m_urlidSubString, "refreshallacls")) {
      m_imapAction = nsImapRefreshAllACLs;
    } else if (!PL_strcasecmp(m_urlidSubString, "listfolder")) {
      m_imapAction = nsImapListFolder;
      ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
    } else if (!PL_strcasecmp(m_urlidSubString, "upgradetosubscription")) {
      m_imapAction = nsImapUpgradeToSubscription;
      ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
    } else if (!PL_strcasecmp(m_urlidSubString, "folderstatus")) {
      m_imapAction = nsImapFolderStatus;
      ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
    } else if (!PL_strcasecmp(m_urlidSubString, "verifyLogon")) {
      m_imapAction = nsImapVerifylogon;
    } else if (m_imapAction == nsIImapUrl::nsImapUserDefinedMsgCommand) {
      m_command = m_urlidSubString;  // save this
      ParseUidChoice();
      ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
      ParseListOfMessageIds();
    } else if (m_imapAction == nsIImapUrl::nsImapMsgStoreCustomKeywords) {
      ParseUidChoice();
      ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
      ParseListOfMessageIds();
      bool addKeyword = (m_tokenPlaceHolder && *m_tokenPlaceHolder != '>');
      // if we're not adding a keyword, m_tokenPlaceHolder will now look like
      // >keywordToSubtract> and strtok will leave flagsPtr pointing to
      // keywordToSubtract. So detect this case and only set the
      // customSubtractFlags.
      char *flagsPtr = m_tokenPlaceHolder ? NS_strtok(IMAP_URL_TOKEN_SEPARATOR,
                                                      &m_tokenPlaceHolder)
                                          : (char *)nullptr;
      if (addKeyword) {
        m_customAddFlags.Assign(flagsPtr);
        flagsPtr = m_tokenPlaceHolder ? NS_strtok(IMAP_URL_TOKEN_SEPARATOR,
                                                  &m_tokenPlaceHolder)
                                      : (char *)nullptr;
      }
      m_customSubtractFlags.Assign(flagsPtr);
    } else {
      m_validUrl = false;
    }
  }
}

// Returns NULL if nothing was done.
// Otherwise, returns a newly allocated name.
NS_IMETHODIMP nsImapUrl::AddOnlineDirectoryIfNecessary(
    const char *onlineMailboxName, char **directory) {
  nsresult rv;
  nsString onlineDirString;
  char *newOnlineName = nullptr;

  nsCOMPtr<nsIImapHostSessionList> hostSessionList =
      do_GetService(kCImapHostSessionListCID, &rv);
  if (NS_FAILED(rv)) return rv;
  rv = hostSessionList->GetOnlineDirForHost(m_serverKey.get(), onlineDirString);
  nsAutoCString onlineDir;
  LossyCopyUTF16toASCII(onlineDirString, onlineDir);

  nsIMAPNamespace *ns = nullptr;
  rv = hostSessionList->GetNamespaceForMailboxForHost(m_serverKey.get(),
                                                      onlineMailboxName, ns);
  if (!ns)
    hostSessionList->GetDefaultNamespaceOfTypeForHost(m_serverKey.get(),
                                                      kPersonalNamespace, ns);

  if (onlineDir.IsEmpty() && ns) onlineDir = ns->GetPrefix();

  // If this host has an online server directory configured
  if (onlineMailboxName && !onlineDir.IsEmpty()) {
    if (PL_strcasecmp(onlineMailboxName, "INBOX")) {
      NS_ASSERTION(ns, "couldn't find namespace for host");
      nsAutoCString onlineDirWithDelimiter(onlineDir);
      // make sure the onlineDir ends with the hierarchy delimiter
      if (ns) {
        char delimiter = ns->GetDelimiter();
        if (delimiter && delimiter != kOnlineHierarchySeparatorUnknown) {
          // try to change the canonical online dir name to real dir name first
          onlineDirWithDelimiter.ReplaceChar('/', delimiter);
          // make sure the last character is the delimiter
          if (onlineDirWithDelimiter.Last() != delimiter)
            onlineDirWithDelimiter += delimiter;
          if (!*onlineMailboxName)
            onlineDirWithDelimiter.SetLength(onlineDirWithDelimiter.Length() -
                                             1);
        }
      }
      if (ns && (PL_strlen(ns->GetPrefix()) != 0) &&
          !onlineDirWithDelimiter.Equals(ns->GetPrefix())) {
        // check that onlineMailboxName doesn't start with the namespace. If
        // that's the case, we don't want to prepend the online dir.
        if (PL_strncmp(onlineMailboxName, ns->GetPrefix(),
                       PL_strlen(ns->GetPrefix()))) {
          // The namespace for this mailbox is the root ("").
          // Prepend the online server directory
          int finalLen =
              onlineDirWithDelimiter.Length() + strlen(onlineMailboxName) + 1;
          newOnlineName = (char *)PR_Malloc(finalLen);
          if (newOnlineName) {
            PL_strcpy(newOnlineName, onlineDirWithDelimiter.get());
            PL_strcat(newOnlineName, onlineMailboxName);
          }
        }
      }
      // just prepend the online server directory if it doesn't start with it
      // already
      else if (strncmp(onlineMailboxName, onlineDirWithDelimiter.get(),
                       onlineDirWithDelimiter.Length())) {
        newOnlineName = (char *)PR_Malloc(strlen(onlineMailboxName) +
                                          onlineDirWithDelimiter.Length() + 1);
        if (newOnlineName) {
          PL_strcpy(newOnlineName, onlineDirWithDelimiter.get());
          PL_strcat(newOnlineName, onlineMailboxName);
        }
      }
    }
  }
  if (directory)
    *directory = newOnlineName;
  else if (newOnlineName)
    free(newOnlineName);
  return rv;
}

// Converts from canonical format (hierarchy is indicated by '/' and all real
// slashes ('/') are escaped) to the real online name on the server.
NS_IMETHODIMP nsImapUrl::AllocateServerPath(const char *canonicalPath,
                                            char onlineDelimiter,
                                            char **aAllocatedPath) {
  nsresult retVal = NS_OK;
  char *rv = NULL;
  char delimiterToUse = onlineDelimiter;
  if (onlineDelimiter == kOnlineHierarchySeparatorUnknown)
    GetOnlineSubDirSeparator(&delimiterToUse);
  NS_ASSERTION(delimiterToUse != kOnlineHierarchySeparatorUnknown,
               "hierarchy separator unknown");
  if (canonicalPath)
    rv = ReplaceCharsInCopiedString(canonicalPath, '/', delimiterToUse);
  else
    rv = strdup("");

  if (delimiterToUse != '/') UnescapeSlashes(rv);
  char *onlineNameAdded = nullptr;
  AddOnlineDirectoryIfNecessary(rv, &onlineNameAdded);
  if (onlineNameAdded) {
    free(rv);
    rv = onlineNameAdded;
  }

  if (aAllocatedPath)
    *aAllocatedPath = rv;
  else
    free(rv);

  return retVal;
}

// escape '/' as ^, ^ -> ^^ - use UnescapeSlashes to revert
/* static */ nsresult nsImapUrl::EscapeSlashes(const char *sourcePath,
                                               char **resultPath) {
  NS_ENSURE_ARG(sourcePath);
  NS_ENSURE_ARG(resultPath);
  int32_t extra = 0;
  int32_t len = strlen(sourcePath);
  const char *src = sourcePath;
  int32_t i;
  for (i = 0; i < len; i++) {
    if (*src == '^') extra += 1; /* ^ -> ^^ */
    src++;
  }
  char *result = (char *)moz_xmalloc(len + extra + 1);
  if (!result) return NS_ERROR_OUT_OF_MEMORY;

  unsigned char *dst = (unsigned char *)result;
  src = sourcePath;
  for (i = 0; i < len; i++) {
    unsigned char c = *src++;
    if (c == '/')
      *dst++ = '^';
    else if (c == '^') {
      *dst++ = '^';
      *dst++ = '^';
    } else
      *dst++ = c;
  }
  *dst = '\0'; /* tack on eos */
  *resultPath = result;
  return NS_OK;
}

/* static */ nsresult nsImapUrl::UnescapeSlashes(char *sourcePath) {
  char *src = sourcePath;
  char *dst = sourcePath;

  while (*src) {
    if (*src == '^') {
      if (*(src + 1) == '^') {
        *dst++ = '^';
        src++;  // skip over second '^'
      } else
        *dst++ = '/';
      src++;
    } else
      *dst++ = *src++;
  }

  *dst = 0;
  return NS_OK;
}

/*  static */ nsresult nsImapUrl::ConvertToCanonicalFormat(
    const char *folderName, char onlineDelimiter,
    char **resultingCanonicalPath) {
  // Now, start the conversion to canonical form.

  char *canonicalPath;
  if (onlineDelimiter != '/') {
    nsCString escapedPath;

    EscapeSlashes(folderName, getter_Copies(escapedPath));
    canonicalPath =
        ReplaceCharsInCopiedString(escapedPath.get(), onlineDelimiter, '/');
  } else {
    canonicalPath = strdup(folderName);
  }
  if (canonicalPath) *resultingCanonicalPath = canonicalPath;

  return (canonicalPath) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}

// Converts the real online name on the server to canonical format:
// result is hierarchy is indicated by '/' and all real slashes ('/') are
// escaped. The caller has already converted m-utf-7 to 8 bit ascii, which is a
// problem. this method is only called from the imap thread
NS_IMETHODIMP nsImapUrl::AllocateCanonicalPath(const char *serverPath,
                                               char onlineDelimiter,
                                               char **allocatedPath) {
  nsresult rv = NS_ERROR_NULL_POINTER;
  char delimiterToUse = onlineDelimiter;
  char *serverKey = nullptr;
  nsString aString;
  char *currentPath = (char *)serverPath;
  nsAutoCString onlineDir;
  nsCOMPtr<nsIMsgIncomingServer> server;

  nsCOMPtr<nsIImapHostSessionList> hostSessionList =
      do_GetService(kCImapHostSessionListCID, &rv);

  *allocatedPath = nullptr;

  if (onlineDelimiter == kOnlineHierarchySeparatorUnknown ||
      onlineDelimiter == 0)
    GetOnlineSubDirSeparator(&delimiterToUse);

  NS_ASSERTION(serverPath, "Oops... null serverPath");

  if (!serverPath || NS_FAILED(rv)) goto done;

  hostSessionList->GetOnlineDirForHost(m_serverKey.get(), aString);
  // First we have to check to see if we should strip off an online server
  // subdirectory
  // If this host has an online server directory configured
  LossyCopyUTF16toASCII(aString, onlineDir);

  if (currentPath && !onlineDir.IsEmpty()) {
    // By definition, the online dir must be at the root.
    if (delimiterToUse && delimiterToUse != kOnlineHierarchySeparatorUnknown) {
      // try to change the canonical online dir name to real dir name first
      onlineDir.ReplaceChar('/', delimiterToUse);
      // Add the delimiter
      if (onlineDir.Last() != delimiterToUse) onlineDir += delimiterToUse;
    }
    int len = onlineDir.Length();
    if (!PL_strncmp(onlineDir.get(), currentPath, len)) {
      // This online path begins with the server sub directory
      currentPath += len;

      // This might occur, but it's most likely something not good.
      // Basically, it means we're doing something on the online sub directory
      // itself.
      NS_ASSERTION(*currentPath, "Oops ... null currentPath");
      // Also make sure that the first character in the mailbox name is not '/'.
      NS_ASSERTION(*currentPath != '/',
                   "Oops ... currentPath starts with a slash");
    }
  }

  if (!currentPath) goto done;

  rv = ConvertToCanonicalFormat(currentPath, delimiterToUse, allocatedPath);

done:
  PR_Free(serverKey);
  return rv;
}

// this method is only called from the imap thread
NS_IMETHODIMP nsImapUrl::CreateServerSourceFolderPathString(char **result) {
  NS_ENSURE_ARG_POINTER(result);
  AllocateServerPath(m_sourceCanonicalFolderPathSubString,
                     kOnlineHierarchySeparatorUnknown, result);
  return NS_OK;
}

// this method is called from the imap thread AND the UI thread...
NS_IMETHODIMP nsImapUrl::CreateCanonicalSourceFolderPathString(char **result) {
  NS_ENSURE_ARG_POINTER(result);
  MutexAutoLock mon(mLock);
  *result = strdup(m_sourceCanonicalFolderPathSubString
                       ? m_sourceCanonicalFolderPathSubString
                       : "");
  return (*result) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}

// this method is called from the imap thread AND the UI thread...
NS_IMETHODIMP nsImapUrl::CreateServerDestinationFolderPathString(
    char **result) {
  NS_ENSURE_ARG_POINTER(result);
  MutexAutoLock mon(mLock);
  nsresult rv = AllocateServerPath(m_destinationCanonicalFolderPathSubString,
                                   kOnlineHierarchySeparatorUnknown, result);
  return (*result) ? rv : NS_ERROR_OUT_OF_MEMORY;
}

// for enabling or disabling mime parts on demand. Setting this to true says we
// can use mime parts on demand, if we chose.
NS_IMETHODIMP nsImapUrl::SetAllowContentChange(bool allowContentChange) {
  m_allowContentChange = allowContentChange;
  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::SetContentModified(
    nsImapContentModifiedType contentModified) {
  m_contentModified = contentModified;
  nsCOMPtr<nsICacheEntry> cacheEntry;
  nsresult res = GetMemCacheEntry(getter_AddRefs(cacheEntry));
  if (NS_SUCCEEDED(res) && cacheEntry) {
    const char *contentModifiedAnnotation = "";
    switch (m_contentModified) {
      case IMAP_CONTENT_NOT_MODIFIED:
        contentModifiedAnnotation = "Not Modified";
        break;
      case IMAP_CONTENT_MODIFIED_VIEW_INLINE:
        contentModifiedAnnotation = "Modified View Inline";
        break;
      case IMAP_CONTENT_MODIFIED_VIEW_AS_LINKS:
        contentModifiedAnnotation = "Modified View As Link";
        break;
      case IMAP_CONTENT_FORCE_CONTENT_NOT_MODIFIED:
        contentModifiedAnnotation = "Force Content Not Modified";
        break;
    }
    MOZ_LOG(IMAPCache, LogLevel::Debug,
            ("SetContentModified(): Set annotation to |%s|",
             contentModifiedAnnotation));
    cacheEntry->SetMetaDataElement("ContentModified",
                                   contentModifiedAnnotation);
  } else {
    MOZ_LOG(IMAPCache, LogLevel::Debug,
            ("SetContentModified(): Set annotation FAILED -- no cacheEntry"));
  }
  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::GetContentModified(
    nsImapContentModifiedType *contentModified) {
  if (!contentModified) return NS_ERROR_NULL_POINTER;

  *contentModified = m_contentModified;
  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::SetFetchPartsOnDemand(bool fetchPartsOnDemand) {
  m_fetchPartsOnDemand = fetchPartsOnDemand;
  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::GetFetchPartsOnDemand(bool *fetchPartsOnDemand) {
  if (!fetchPartsOnDemand) return NS_ERROR_NULL_POINTER;

  *fetchPartsOnDemand = m_fetchPartsOnDemand;
  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::SetMimePartSelectorDetected(
    bool mimePartSelectorDetected) {
  m_mimePartSelectorDetected = mimePartSelectorDetected;
  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::GetMimePartSelectorDetected(
    bool *mimePartSelectorDetected) {
  if (!mimePartSelectorDetected) return NS_ERROR_NULL_POINTER;

  *mimePartSelectorDetected = m_mimePartSelectorDetected;
  return NS_OK;
}

// this method is only called from the UI thread.
NS_IMETHODIMP nsImapUrl::SetCopyState(nsISupports *copyState) {
  MutexAutoLock mon(mLock);
  m_copyState = copyState;
  return NS_OK;
}

// this method is only called from the imap thread..but we still
// need a monitor 'cause the setter is called from the UI thread.
NS_IMETHODIMP nsImapUrl::GetCopyState(nsISupports **copyState) {
  NS_ENSURE_ARG_POINTER(copyState);
  MutexAutoLock mon(mLock);
  NS_IF_ADDREF(*copyState = m_copyState);

  return NS_OK;
}

NS_IMETHODIMP
nsImapUrl::SetMsgFile(nsIFile *aFile) {
  nsresult rv = NS_OK;
  MutexAutoLock mon(mLock);
  m_file = aFile;
  return rv;
}

NS_IMETHODIMP
nsImapUrl::GetMsgFile(nsIFile **aFile) {
  NS_ENSURE_ARG_POINTER(aFile);

  MutexAutoLock mon(mLock);
  NS_IF_ADDREF(*aFile = m_file);
  return NS_OK;
}

// this method is called from the UI thread..
NS_IMETHODIMP nsImapUrl::GetMockChannel(nsIImapMockChannel **aChannel) {
  NS_ENSURE_ARG_POINTER(aChannel);
  MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread(),
                        "should only access mock channel on ui thread");
  *aChannel = nullptr;
  nsCOMPtr<nsIImapMockChannel> channel(do_QueryReferent(m_channelWeakPtr));
  channel.forget(aChannel);
  return *aChannel ? NS_OK : NS_ERROR_FAILURE;
}

NS_IMETHODIMP nsImapUrl::SetMockChannel(nsIImapMockChannel *aChannel) {
  MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread(),
                        "should only access mock channel on ui thread");
  m_channelWeakPtr = do_GetWeakReference(aChannel);
  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::GetAllowContentChange(bool *result) {
  NS_ENSURE_ARG_POINTER(result);
  *result = m_allowContentChange;
  return NS_OK;
}

nsresult nsImapUrl::Clone(nsIURI **_retval) {
  nsresult rv = nsMsgMailNewsUrl::Clone(_retval);
  NS_ENSURE_SUCCESS(rv, rv);
  // also clone the mURI member, because GetUri below won't work if
  // mURI isn't set due to escaping issues.
  nsCOMPtr<nsIMsgMessageUrl> clonedUrl = do_QueryInterface(*_retval);
  if (clonedUrl) clonedUrl->SetUri(mURI.get());
  return rv;
}

NS_IMETHODIMP nsImapUrl::GetNormalizedSpec(nsACString &aPrincipalSpec) {
  // URLs look like this:
  // imap://user@domain@server:port/fetch>UID>folder>nn
  // We simply strip any query part beginning with ? & or /;
  // Normalized spec: imap://user@domain@server:port/fetch>UID>folder>nn
  nsCOMPtr<nsIMsgMailNewsUrl> mailnewsURL;
  QueryInterface(NS_GET_IID(nsIMsgMailNewsUrl), getter_AddRefs(mailnewsURL));

  nsAutoCString spec;
  mailnewsURL->GetSpecIgnoringRef(spec);

  // Strip any query part beginning with ? or /;
  MsgRemoveQueryPart(spec);

  aPrincipalSpec.Assign(spec);
  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::SetUri(const char *aURI) {
  mURI = aURI;
  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::GetUri(char **aURI) {
  nsresult rv = NS_OK;
  if (!mURI.IsEmpty())
    *aURI = ToNewCString(mURI);
  else {
    *aURI = nullptr;
    uint32_t key =
        m_listOfMessageIds ? strtoul(m_listOfMessageIds, nullptr, 10) : 0;
    nsCString canonicalPath;
    AllocateCanonicalPath(m_sourceCanonicalFolderPathSubString,
                          m_onlineSubDirSeparator,
                          (getter_Copies(canonicalPath)));
    nsCString fullFolderPath("/");
    fullFolderPath.Append(m_userName);
    nsAutoCString hostName;
    rv = GetHost(hostName);
    fullFolderPath.Append('@');
    fullFolderPath.Append(hostName);
    fullFolderPath.Append('/');
    fullFolderPath.Append(canonicalPath);

    nsCString baseMessageURI;
    nsCreateImapBaseMessageURI(fullFolderPath, baseMessageURI);
    nsAutoCString uriStr;
    rv = nsBuildImapMessageURI(baseMessageURI.get(), key, uriStr);
    *aURI = ToNewCString(uriStr);
  }
  return rv;
}

NS_IMPL_GETSET(nsImapUrl, AddDummyEnvelope, bool, m_addDummyEnvelope)
NS_IMPL_GETSET(nsImapUrl, CanonicalLineEnding, bool, m_canonicalLineEnding)
NS_IMETHODIMP nsImapUrl::GetMsgLoadingFromCache(bool *result) {
  NS_ENSURE_ARG_POINTER(result);
  *result = m_msgLoadingFromCache;
  return NS_OK;
}
NS_IMPL_GETSET(nsImapUrl, LocalFetchOnly, bool, m_localFetchOnly)
NS_IMPL_GETSET(nsImapUrl, ExternalLinkUrl, bool, m_externalLinkUrl)
NS_IMPL_GETSET(nsImapUrl, RerunningUrl, bool, m_rerunningUrl)
NS_IMPL_GETSET(nsImapUrl, ValidUrl, bool, m_validUrl)
NS_IMPL_GETSET(nsImapUrl, MoreHeadersToDownload, bool, m_moreHeadersToDownload)

NS_IMETHODIMP nsImapUrl::SetMsgLoadingFromCache(bool loadingFromCache) {
  nsresult rv = NS_OK;
  m_msgLoadingFromCache = loadingFromCache;
  return rv;
}

NS_IMETHODIMP nsImapUrl::SetMessageFile(nsIFile *aFile) {
  m_messageFile = aFile;
  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::GetMessageFile(nsIFile **aFile) {
  if (aFile) NS_IF_ADDREF(*aFile = m_messageFile);
  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::IsUrlType(uint32_t type, bool *isType) {
  NS_ENSURE_ARG(isType);

  switch (type) {
    case nsIMsgMailNewsUrl::eCopy:
      *isType = ((m_imapAction == nsIImapUrl::nsImapOnlineCopy) ||
                 (m_imapAction == nsIImapUrl::nsImapOnlineToOfflineCopy) ||
                 (m_imapAction == nsIImapUrl::nsImapOfflineToOnlineCopy));
      break;
    case nsIMsgMailNewsUrl::eMove:
      *isType = ((m_imapAction == nsIImapUrl::nsImapOnlineMove) ||
                 (m_imapAction == nsIImapUrl::nsImapOnlineToOfflineMove) ||
                 (m_imapAction == nsIImapUrl::nsImapOfflineToOnlineMove));
      break;
    case nsIMsgMailNewsUrl::eDisplay:
      *isType = (m_imapAction == nsIImapUrl::nsImapMsgFetch ||
                 m_imapAction == nsIImapUrl::nsImapMsgFetchPeek);
      break;
    default:
      *isType = false;
  };

  return NS_OK;
}

NS_IMETHODIMP
nsImapUrl::GetOriginalSpec(char **aSpec) { return NS_ERROR_NOT_IMPLEMENTED; }

NS_IMETHODIMP
nsImapUrl::SetOriginalSpec(const char *aSpec) {
  return NS_ERROR_NOT_IMPLEMENTED;
}

char *nsImapUrl::ReplaceCharsInCopiedString(const char *stringToCopy,
                                            char oldChar, char newChar) {
  char oldCharString[2];
  *oldCharString = oldChar;
  *(oldCharString + 1) = 0;

  char *translatedString = PL_strdup(stringToCopy);
  char *currentSeparator = PL_strstr(translatedString, oldCharString);

  while (currentSeparator) {
    *currentSeparator = newChar;
    currentSeparator = PL_strstr(currentSeparator + 1, oldCharString);
  }

  return translatedString;
}

////////////////////////////////////////////////////////////////////////////////////
// End of functions which should be made obsolete after modifying nsIURI
////////////////////////////////////////////////////////////////////////////////////

void nsImapUrl::ParseFolderPath(char **resultingCanonicalPath) {
  char *resultPath = m_tokenPlaceHolder ? NS_strtok(IMAP_URL_TOKEN_SEPARATOR,
                                                    &m_tokenPlaceHolder)
                                        : (char *)NULL;

  if (!resultPath) {
    m_validUrl = false;
    return;
  }
  NS_ASSERTION(*resultingCanonicalPath == nullptr, "whoops, mem leak");

  char dirSeparator = *resultPath;

  nsCString unescapedResultingCanonicalPath;
  MsgUnescapeString(nsDependentCString(resultPath + 1), 0,
                    unescapedResultingCanonicalPath);
  *resultingCanonicalPath = ToNewCString(unescapedResultingCanonicalPath);
  // The delimiter will be set for a given URL, but will not be statically
  // available from an arbitrary URL.  It is the creator's responsibility to
  // fill in the correct delimiter from the folder's namespace when creating the
  // URL.
  if (dirSeparator != kOnlineHierarchySeparatorUnknown)
    SetOnlineSubDirSeparator(dirSeparator);

  // if dirSeparator == kOnlineHierarchySeparatorUnknown, then this must be a
  // create of a top level imap box.  If there is an online subdir, we will
  // automatically use its separator.  If there is not an online subdir, we
  // don't need a separator.
}

void nsImapUrl::ParseSearchCriteriaString() {
  if (m_tokenPlaceHolder) {
    int quotedFlag = false;

    // skip initial separator
    while (*m_tokenPlaceHolder == *IMAP_URL_TOKEN_SEPARATOR)
      m_tokenPlaceHolder++;

    char *saveTokenPlaceHolder = m_tokenPlaceHolder;

    //    m_searchCriteriaString = m_tokenPlaceHolder;

    // looking for another separator outside quoted string
    while (*m_tokenPlaceHolder) {
      if (*m_tokenPlaceHolder == '\\' && *(m_tokenPlaceHolder + 1) == '"')
        m_tokenPlaceHolder++;
      else if (*m_tokenPlaceHolder == '"')
        quotedFlag = !quotedFlag;
      else if (!quotedFlag &&
               *m_tokenPlaceHolder == *IMAP_URL_TOKEN_SEPARATOR) {
        *m_tokenPlaceHolder = '\0';
        m_tokenPlaceHolder++;
        break;
      }
      m_tokenPlaceHolder++;
    }
    m_searchCriteriaString = PL_strdup(saveTokenPlaceHolder);
    if (*m_tokenPlaceHolder == '\0') m_tokenPlaceHolder = NULL;

    if (*m_searchCriteriaString == '\0') m_searchCriteriaString = (char *)NULL;
  } else
    m_searchCriteriaString = (char *)NULL;
  if (!m_searchCriteriaString) m_validUrl = false;
}

void nsImapUrl::ParseUidChoice() {
  char *uidChoiceString =
      m_tokenPlaceHolder
          ? NS_strtok(IMAP_URL_TOKEN_SEPARATOR, &m_tokenPlaceHolder)
          : (char *)NULL;
  if (!uidChoiceString)
    m_validUrl = false;
  else
    m_idsAreUids = strcmp(uidChoiceString, "UID") == 0;
}

void nsImapUrl::ParseMsgFlags() {
  char *flagsPtr = m_tokenPlaceHolder ? NS_strtok(IMAP_URL_TOKEN_SEPARATOR,
                                                  &m_tokenPlaceHolder)
                                      : (char *)NULL;
  if (flagsPtr) {
    // the url is encodes the flags byte as ascii
    int intFlags = atoi(flagsPtr);
    m_flags = (imapMessageFlagsType)intFlags;  // cast here
  } else
    m_flags = 0;
}

void nsImapUrl::ParseListOfMessageIds() {
  m_listOfMessageIds = m_tokenPlaceHolder ? NS_strtok(IMAP_URL_TOKEN_SEPARATOR,
                                                      &m_tokenPlaceHolder)
                                          : (char *)NULL;
  if (!m_listOfMessageIds)
    m_validUrl = false;
  else {
    m_listOfMessageIds = strdup(m_listOfMessageIds);
    m_mimePartSelectorDetected = PL_strstr(m_listOfMessageIds, "&part=") != 0 ||
                                 PL_strstr(m_listOfMessageIds, "?part=") != 0;

    // if we're asking for just the body, don't download the whole message. see
    // nsMsgQuote::QuoteMessage() for the "header=" settings when replying to
    // msgs.
    if (!m_fetchPartsOnDemand)
      m_fetchPartsOnDemand =
          (PL_strstr(m_listOfMessageIds, "?header=quotebody") != 0 ||
           PL_strstr(m_listOfMessageIds, "?header=only") != 0);
    // if it's a spam filter trying to fetch the msg, don't let it get marked
    // read.
    if (PL_strstr(m_listOfMessageIds, "?header=filter") != 0)
      m_imapAction = nsImapMsgFetchPeek;
  }
}

void nsImapUrl::ParseCustomMsgFetchAttribute() {
  m_msgFetchAttribute = m_tokenPlaceHolder ? NS_strtok(IMAP_URL_TOKEN_SEPARATOR,
                                                       &m_tokenPlaceHolder)
                                           : (char *)nullptr;
}

void nsImapUrl::ParseNumBytes() {
  const char *numBytes =
      (m_tokenPlaceHolder)
          ? NS_strtok(IMAP_URL_TOKEN_SEPARATOR, &m_tokenPlaceHolder)
          : 0;
  m_numBytesToFetch = numBytes ? atoi(numBytes) : 0;
}

// nsIMsgI18NUrl support

nsresult nsImapUrl::GetMsgFolder(nsIMsgFolder **msgFolder) {
  // if we have a RDF URI, then try to get the folder for that URI and then ask
  // the folder for it's charset....

  nsCString uri;
  GetUri(getter_Copies(uri));
  NS_ENSURE_TRUE(!uri.IsEmpty(), NS_ERROR_FAILURE);

  nsCOMPtr<nsIMsgDBHdr> msg;
  GetMsgDBHdrFromURI(uri.get(), getter_AddRefs(msg));
  NS_ENSURE_TRUE(msg, NS_ERROR_FAILURE);
  nsresult rv = msg->GetFolder(msgFolder);
  NS_ENSURE_SUCCESS(rv, rv);
  NS_ENSURE_TRUE(msgFolder, NS_ERROR_FAILURE);

  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::GetFolderCharset(char **aCharacterSet) {
  nsCOMPtr<nsIMsgFolder> folder;
  nsresult rv = GetMsgFolder(getter_AddRefs(folder));
  NS_ENSURE_SUCCESS(rv, rv);
  nsCString tmpStr;
  folder->GetCharset(tmpStr);
  *aCharacterSet = ToNewCString(tmpStr);
  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::GetFolderCharsetOverride(bool *aCharacterSetOverride) {
  nsCOMPtr<nsIMsgFolder> folder;
  nsresult rv = GetMsgFolder(getter_AddRefs(folder));
  NS_ENSURE_SUCCESS(rv, rv);
  NS_ENSURE_TRUE(folder, NS_ERROR_FAILURE);
  folder->GetCharsetOverride(aCharacterSetOverride);
  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::GetCharsetOverRide(char **aCharacterSet) {
  if (!mCharsetOverride.IsEmpty())
    *aCharacterSet = ToNewCString(mCharsetOverride);
  else
    *aCharacterSet = nullptr;
  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::SetCharsetOverRide(const char *aCharacterSet) {
  mCharsetOverride = aCharacterSet;
  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::GetStoreResultsOffline(bool *aStoreResultsOffline) {
  NS_ENSURE_ARG_POINTER(aStoreResultsOffline);
  *aStoreResultsOffline = m_storeResultsOffline;
  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::SetStoreResultsOffline(bool aStoreResultsOffline) {
  m_storeResultsOffline = aStoreResultsOffline;
  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::GetStoreOfflineOnFallback(
    bool *aStoreOfflineOnFallback) {
  NS_ENSURE_ARG_POINTER(aStoreOfflineOnFallback);
  *aStoreOfflineOnFallback = m_storeOfflineOnFallback;
  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::SetStoreOfflineOnFallback(
    bool aStoreOfflineOnFallback) {
  m_storeOfflineOnFallback = aStoreOfflineOnFallback;
  return NS_OK;
}

NS_IMETHODIMP nsImapUrl::GetMessageHeader(nsIMsgDBHdr **aMsgHdr) {
  nsCString uri;
  nsresult rv = GetUri(getter_Copies(uri));
  NS_ENSURE_SUCCESS(rv, rv);
  return GetMsgDBHdrFromURI(uri.get(), aMsgHdr);
}

NS_IMETHODIMP nsImapUrl::SetMessageHeader(nsIMsgDBHdr *aMsgHdr) {
  return NS_ERROR_NOT_IMPLEMENTED;
}