/* -*- 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/. */

#ifndef nsImapProtocol_h___
#define nsImapProtocol_h___

#include "mozilla/Attributes.h"
#include "nsIImapProtocol.h"
#include "nsIImapUrl.h"

#include "nsMsgProtocol.h"
#include "nsIStreamListener.h"
#include "nsIAsyncOutputStream.h"
#include "nsIAsyncInputStream.h"
#include "nsImapCore.h"
#include "nsString.h"
#include "nsIProgressEventSink.h"
#include "nsIInterfaceRequestor.h"
#include "nsISocketTransport.h"

// UI Thread proxy helper
#include "nsIImapProtocolSink.h"

#include "../public/nsIImapHostSessionList.h"
#include "nsImapServerResponseParser.h"
#include "nsImapFlagAndUidState.h"
#include "nsImapNamespace.h"
#include "nsTArray.h"
#include "nsIWeakReferenceUtils.h"
#include "nsMsgLineBuffer.h"  // we need this to use the nsMsgLineStreamBuffer helper class...
#include "nsIInputStream.h"
#include "nsIMsgIncomingServer.h"
#include "nsCOMArray.h"
#include "nsIThread.h"
#include "nsIRunnable.h"
#include "nsIImapMockChannel.h"
#include "nsILoadGroup.h"
#include "nsCOMPtr.h"
#include "nsIMsgWindow.h"
#include "nsIImapHeaderXferInfo.h"
#include "nsMsgLineBuffer.h"
#include "nsIAsyncInputStream.h"
#include "nsIMsgFolder.h"
#include "nsIMsgAsyncPrompter.h"
#include "mozilla/ReentrantMonitor.h"
#include "nsSyncRunnableHelpers.h"
#include "nsICacheEntryOpenCallback.h"
#include "nsIProtocolProxyCallback.h"
#include "nsIStringBundle.h"
#include "nsHashPropertyBag.h"
#include "nsMailChannel.h"

#include "mozilla/Monitor.h"

class nsIMAPMessagePartID;
class nsIPrefBranch;

#define kDownLoadCacheSize 16000u  // was 1536 - try making it bigger

typedef struct _msg_line_info {
  const char* adoptedMessageLine;
  uint32_t uidOfMessage;
} msg_line_info;

class nsMsgImapLineDownloadCache : public nsIImapHeaderInfo,
                                   public nsByteArray {
 public:
  NS_DECL_THREADSAFE_ISUPPORTS
  NS_DECL_NSIIMAPHEADERINFO
  nsMsgImapLineDownloadCache();
  uint32_t CurrentUID();
  uint32_t SpaceAvailable();
  bool CacheEmpty();

  msg_line_info* GetCurrentLineInfo();

 private:
  virtual ~nsMsgImapLineDownloadCache();

  msg_line_info* fLineInfo;
  int32_t m_msgSize;
};

#define kNumHdrsToXfer 10

class nsMsgImapHdrXferInfo : public nsIImapHeaderXferInfo {
 public:
  NS_DECL_THREADSAFE_ISUPPORTS
  NS_DECL_NSIIMAPHEADERXFERINFO
  nsMsgImapHdrXferInfo();
  void ResetAll();    // reset HeaderInfos for re-use
  void ReleaseAll();  // release HeaderInfos (frees up memory)
  // this will return null if we're full, in which case the client code
  // should transfer the headers and retry.
  nsIImapHeaderInfo* StartNewHdr();
  // call when we've finished adding lines to current hdr
  void FinishCurrentHdr();

 private:
  virtual ~nsMsgImapHdrXferInfo();
  nsCOMArray<nsIImapHeaderInfo> m_hdrInfos;
  int32_t m_nextFreeHdrInfo;
};

// This class contains the name of a mailbox and whether or not
// its children have been listed.
class nsIMAPMailboxInfo {
 public:
  nsIMAPMailboxInfo(const nsACString& aName, char aDelimiter);
  virtual ~nsIMAPMailboxInfo();

  void SetChildrenListed(bool childrenListed);
  bool GetChildrenListed();
  const nsACString& GetMailboxName();
  char GetDelimiter();

 protected:
  nsCString mMailboxName;
  bool mChildrenListed;
  char mDelimiter;
};

// State Flags (Note, I use the word state in terms of storing
// state information about the connection (authentication, have we sent
// commands, etc. I do not intend it to refer to protocol state)
// Use these flags in conjunction with SetFlag/TestFlag/ClearFlag instead
// of creating PRBools for everything....
// clang-format off
#define IMAP_RECEIVED_GREETING        0x00000001  // should we pause for the next read
#define  IMAP_CONNECTION_IS_OPEN      0x00000004  // is the connection currently open?
#define IMAP_WAITING_FOR_DATA         0x00000008
#define IMAP_CLEAN_UP_URL_STATE       0x00000010  // processing clean up url state
#define IMAP_ISSUED_LANGUAGE_REQUEST  0x00000020  // make sure we only issue the language
                                                  // request once per connection...
#define IMAP_ISSUED_COMPRESS_REQUEST  0x00000040  // make sure we only request compression once

// There are 3 types of progress strings for items downloaded from IMAP servers.
// An index is needed to keep track of the current count of the number of each
// item type downloaded. The IMAP_EMPTY_STRING_INDEX means no string displayed.
#define IMAP_NUMBER_OF_PROGRESS_STRINGS 4
#define IMAP_HEADERS_STRING_INDEX       0
#define IMAP_FLAGS_STRING_INDEX         1
#define IMAP_MESSAGES_STRING_INDEX      2
#define IMAP_EMPTY_STRING_INDEX         3
// clang-format on

/**
 * nsImapProtocol is, among other things, the underlying nsIChannel
 * implementation for the IMAP protocol. However, it's usually hidden away
 * behind nsImapMockChannel objects. It also represents the 'real' connection
 * to the IMAP server - it maintains the nsISocketTransport.
 * Because there can be multiple IMAP requests queued up, NS_NewChannel()
 * will return nsImapMockChannel objects instead, to keep the request in a
 * holding pattern until the connection is free. At which time the mock
 * channel will just forward calls onward to the nsImapProtocol.
 *
 * The url scheme we implement here encodes various IMAP commands as URLs.
 * Some URLs are just traditional I/O based nsIChannel transactions, but
 * many others have side effects. For example, an IMAP folder discovery
 * command might cause the creation of the nsImapMailFolder hierarchy under
 * the the nsImapIncomingServer.
 * Such side effects are communicated via the various "Sink" interfaces. This
 * helps decouple the IMAP code from the rest of the system:
 *
 * - nsIImapServerSink      (implemented by nsImapIncomingServer)
 * - nsIImapMailFolderSink  (implemented by nsImapMailFolder)
 * - nsIImapMessageSink     (implemented by nsImapMailFolder)
 *
 * Internal to nsImapProtocol, these sink classes all have corresponding proxy
 * implementations (ImapServerSinkProxy, ImapMailFolderSinkProxy and
 * ImapMessageSinkProxy). These allow us to safely call the sink objects, in
 * a synchronous fashion, from I/O threads (threads other than the main one).
 * When an IMAP routine calls a member function of one of these sink proxies,
 * it dispatches a call to the real sink object on the main thread, then
 * blocks until the call is completed.
 */
class nsImapProtocol : public nsIImapProtocol,
                       public nsIInputStreamCallback,
                       public nsSupportsWeakReference,
                       public nsMsgProtocol,
                       public nsIImapProtocolSink,
                       public nsIMsgAsyncPromptListener,
                       public nsIProtocolProxyCallback {
 public:
  struct TCPKeepalive {
    // For enabling and setting TCP keepalive (not related to IMAP IDLE).
    std::atomic<bool> enabled;
    std::atomic<int32_t> idleTimeS;
    std::atomic<int32_t> retryIntervalS;
  };

  NS_DECL_ISUPPORTS_INHERITED
  NS_DECL_NSIINPUTSTREAMCALLBACK
  NS_DECL_NSIPROTOCOLPROXYCALLBACK
  nsImapProtocol();

  virtual nsresult ProcessProtocolState(nsIURI* url,
                                        nsIInputStream* inputStream,
                                        uint64_t sourceOffset,
                                        uint32_t length) override;

  //////////////////////////////////////////////////////////////////////////////////
  // we support the nsIImapProtocol interface
  //////////////////////////////////////////////////////////////////////////////////
  NS_DECL_NSIIMAPPROTOCOL

  //////////////////////////////////////////////////////////////////////////////////
  // we support the nsIImapProtocolSink interface
  //////////////////////////////////////////////////////////////////////////////////
  NS_DECL_NSIIMAPPROTOCOLSINK

  NS_DECL_NSIMSGASYNCPROMPTLISTENER

  // message id string utilities.
  uint32_t CountMessagesInIdString(const char* idString);
  static bool HandlingMultipleMessages(const nsCString& messageIdString);
  // escape slashes and double quotes in username/passwords for insecure login.
  static void EscapeUserNamePasswordString(const char* strToEscape,
                                           nsCString* resultStr);

  // used to start fetching a message.
  void GetShouldDownloadAllHeaders(bool* aResult);
  void GetArbitraryHeadersToDownload(nsCString& aResult);
  virtual void AdjustChunkSize();
  virtual void FetchMessage(const nsCString& messageIds,
                            nsIMAPeFetchFields whatToFetch,
                            const char* fetchModifier = nullptr,
                            uint32_t startByte = 0, uint32_t numBytes = 0,
                            char* part = 0);
  void FetchTryChunking(const nsCString& messageIds,
                        nsIMAPeFetchFields whatToFetch, bool idIsUid,
                        char* part, uint32_t downloadSize, bool tryChunking);
  void FallbackToFetchWholeMsg(const nsCString& messageId,
                               uint32_t messageSize);
  // used when streaming a message fetch
  virtual nsresult BeginMessageDownLoad(
      uint32_t totalSize,        // for user, headers and body
      const char* contentType);  // some downloads are header only
  virtual void HandleMessageDownLoadLine(const char* line, bool isPartialLine,
                                         char* lineCopy = nullptr);
  virtual void NormalMessageEndDownload();
  virtual void AbortMessageDownLoad();
  virtual void PostLineDownLoadEvent(const char* line, uint32_t uid);
  void FlushDownloadCache();

  virtual void SetMailboxDiscoveryStatus(EMailboxDiscoverStatus status);
  virtual EMailboxDiscoverStatus GetMailboxDiscoveryStatus();

  virtual void ProcessMailboxUpdate(bool handlePossibleUndo);
  // Send log output...
  void Log(const char* logSubName, const char* extraInfo, const char* logData);
  static void LogImapUrl(const char* logMsg, nsIImapUrl* imapUrl);
  // Comment from 4.5: We really need to break out the thread synchronizer from
  // the connection class...Not sure what this means
  bool GetPseudoInterrupted();

  uint32_t GetMessageSize(const nsACString& messageId);
  bool GetSubscribingNow();

  bool DeathSignalReceived();
  void ResetProgressInfo();
  void SetActive(bool active);
  bool GetActive();

  bool GetShowAttachmentsInline();

  // Sets whether or not the content referenced by the current ActiveEntry has
  // been modified. Used for MIME parts on demand.
  void SetContentModified(IMAP_ContentModifiedType modified);
  bool GetShouldFetchAllParts();
  bool GetIgnoreExpunges() { return m_ignoreExpunges; }
  // Generic accessors required by the imap parser
  char* CreateNewLineFromSocket();
  nsresult GetConnectionStatus();
  void SetConnectionStatus(nsresult status);

  // Cleanup the connection and shutdown the thread.
  void TellThreadToDie();

  const nsCString&
  GetImapHostName();  // return the host name from the url for the
  // current connection
  const nsCString& GetImapUserName();  // return the user name from the identity
  const char*
  GetImapServerKey();  // return the user name from the incoming server;

  // state set by the imap parser...
  void NotifyMessageFlags(imapMessageFlagsType flags,
                          const nsACString& keywords, nsMsgKey key,
                          uint64_t highestModSeq);
  void NotifySearchHit(const char* hitLine);

  // Event handlers for the imap parser.
  void DiscoverMailboxSpec(nsImapMailboxSpec* adoptedBoxSpec);
  void AlertUserEventUsingName(const char* aMessageId);
  void AlertUserEvent(const char* message);
  void AlertUserEventFromServer(const char* aServerEvent,
                                bool aForIdle = false);

  void ProgressEventFunctionUsingName(const char* aMsgId);
  void ProgressEventFunctionUsingNameWithString(const char* aMsgName,
                                                const char* aExtraInfo);
  void PercentProgressUpdateEvent(nsACString const& fmtStringName,
                                  nsAString const& mailbox,
                                  int64_t currentProgress, int64_t maxProgress);
  void ShowProgress();

  // utility function calls made by the server

  void Copy(const char* messageList, const char* destinationMailbox,
            bool idsAreUid);
  void Search(const char* searchCriteria, bool useUID, bool notifyHit = true);
  // imap commands issued by the parser
  void Store(const nsCString& aMessageList, const char* aMessageData,
             bool aIdsAreUid);
  void ProcessStoreFlags(const nsCString& messageIds, bool idsAreUids,
                         imapMessageFlagsType flags, bool addFlags);
  void IssueUserDefinedMsgCommand(const char* command, const char* messageList);
  void FetchMsgAttribute(const nsCString& messageIds,
                         const nsCString& attribute);
  void Expunge();
  void UidExpunge(const nsCString& messageSet);
  void ImapClose(bool shuttingDown = false, bool waitForResponse = true);
  void Check();
  void SelectMailbox(const char* mailboxName);
  // more imap commands
  void Logout(bool shuttingDown = false, bool waitForResponse = true);
  void Noop();
  void XServerInfo();
  void Netscape();
  void XMailboxInfo(const char* mailboxName);
  void XAOL_Option(const char* option);
  void MailboxData();
  void GetMyRightsForFolder(const char* mailboxName);
  void Bodystructure(const nsCString& messageId, bool idIsUid);

  // this function does not ref count!!! be careful!!!
  nsIImapUrl* GetCurrentUrl() { return m_runningUrl; }

  // acl and namespace stuff
  // notifies libmsg that we have a new personal/default namespace that we're
  // using
  void CommitNamespacesForHostEvent();
  // notifies libmsg that we have new capability data for the current host
  void CommitCapability();

  // Adds a set of rights for a given user on a given mailbox on the current
  // host. if userName is NULL, it means "me," or MYRIGHTS. rights is a single
  // string of rights, as specified by RFC2086, the IMAP ACL extension.
  void AddFolderRightsForUser(const char* mailboxName, const char* userName,
                              const char* rights);
  // Clears all rights for the current folder, for all users.
  void ClearAllFolderRights();
  void RefreshFolderACLView(const char* mailboxName,
                            nsImapNamespace* nsForMailbox);

  nsresult SetFolderAdminUrl(const char* mailboxName);
  void HandleMemoryFailure();
  void HandleCurrentUrlError();

  // UIDPLUS extension
  void SetCopyResponseUid(const char* msgIdString);

  // Quota support
  void UpdateFolderQuotaData(nsImapQuotaAction aAction, nsCString& aQuotaRoot,
                             uint64_t aUsed, uint64_t aMax);

  bool GetPreferPlainText() { return m_preferPlainText; }

  int32_t GetCurFetchSize() { return m_curFetchSize; }

  const nsString& GetEmptyMimePartString() { return m_emptyMimePartString; }
  void SetCapabilityResponseOccurred();

  // Start event loop. This is called on IMAP thread
  bool RunImapThreadMainLoop();

 private:
  virtual ~nsImapProtocol();
  // the following flag is used to determine when a url is currently being run.
  // It is cleared when we finish processng a url and it is set whenever we call
  // Load on a url
  bool m_urlInProgress;

  /** The nsIImapURL that is currently running. */
  nsCOMPtr<nsIImapUrl> m_runningUrl;
  nsCOMPtr<nsIImapUrl> m_runningUrlLatest;
  /** Current imap action associated with this connection. */
  nsImapAction m_imapAction;

  nsCString m_hostName;
  nsCString m_userName;
  nsCString m_serverKey;
  char* m_dataOutputBuf;
  RefPtr<nsMsgLineStreamBuffer> m_inputStreamBuffer;
  nsCString m_trashFolderPath;

  /** The socket connection to the IMAP server. */
  nsCOMPtr<nsISocketTransport> m_transport;
  nsCOMPtr<nsITransportSecurityInfo> m_securityInfo;

  /** Stream to handle data coming in from the IMAP server. */
  nsCOMPtr<nsIInputStream> m_inputStream;

  nsCOMPtr<nsIAsyncInputStream> m_channelInputStream;
  nsCOMPtr<nsIAsyncOutputStream> m_channelOutputStream;

  /** The currently running request. */
  nsCOMPtr<nsIImapMockChannel> m_mockChannel;

  uint32_t m_bytesToChannel;
  bool m_fetchingWholeMessage;
  // nsCOMPtr<nsIRequest> mAsyncReadRequest; // we're going to cancel this when
  // we're done with the conn.

  // ******* Thread support *******
  PRThread* m_thread;
  mozilla::ReentrantMonitor
      m_dataAvailableMonitor;  // used to notify the arrival of data from the
                               // server
  mozilla::ReentrantMonitor
      m_urlReadyToRunMonitor;  // used to notify the arrival of a new url to be
                               // processed
  mozilla::ReentrantMonitor m_pseudoInterruptMonitor;
  mozilla::ReentrantMonitor m_dataMemberMonitor;
  mozilla::ReentrantMonitor m_threadDeathMonitor;
  mozilla::ReentrantMonitor m_waitForBodyIdsMonitor;
  mozilla::ReentrantMonitor m_fetchBodyListMonitor;
  mozilla::ReentrantMonitor m_passwordReadyMonitor;
  mozilla::Mutex mLock;
  // If we get an async password prompt, this is where the UI thread
  // stores the password, before notifying the imap thread of the password
  // via the m_passwordReadyMonitor.
  nsString m_password;
  // Set to the result of nsImapServer::PromptPassword
  nsresult m_passwordStatus;
  bool m_passwordObtained;

  bool m_imapThreadIsRunning;
  void ImapThreadMainLoop(void);
  nsresult m_connectionStatus;
  nsCString m_connectionType;

  bool m_nextUrlReadyToRun;
  bool m_idleResponseReadyToHandle;
  nsWeakPtr m_server;

  RefPtr<ImapMailFolderSinkProxy> m_imapMailFolderSink;
  RefPtr<ImapMailFolderSinkProxy> m_imapMailFolderSinkSelected;
  RefPtr<ImapMessageSinkProxy> m_imapMessageSink;
  RefPtr<ImapServerSinkProxy> m_imapServerSink;
  RefPtr<ImapServerSinkProxy> m_imapServerSinkLatest;
  RefPtr<ImapProtocolSinkProxy> m_imapProtocolSink;

  // helper function to setup imap sink interface proxies
  nsresult SetupSinkProxy();
  // End thread support stuff
  nsresult LoadImapUrlInternal();

  bool GetDeleteIsMoveToTrash();
  bool GetShowDeletedMessages();
  nsCString m_currentCommand;
  nsImapServerResponseParser m_parser;
  nsImapServerResponseParser& GetServerStateParser() { return m_parser; }

  bool HandleIdleResponses();
  virtual bool ProcessCurrentURL();
  void EstablishServerConnection();
  virtual void ParseIMAPandCheckForNewMail(const char* commandString = nullptr,
                                           bool ignoreBadNOResponses = false);
  // biff
  void PeriodicBiff();
  void SendSetBiffIndicatorEvent(nsMsgBiffState newState);

  // folder opening and listing header functions
  void FolderHeaderDump(uint32_t* msgUids, uint32_t msgCount);
  void FolderMsgDump(uint32_t* msgUids, uint32_t msgCount,
                     nsIMAPeFetchFields fields);
  void FolderMsgDumpLoop(uint32_t* msgUids, uint32_t msgCount,
                         nsIMAPeFetchFields fields);
  void WaitForPotentialListOfBodysToFetch(nsTArray<nsMsgKey>& msgIdList);
  void HeaderFetchCompleted();
  void UploadMessageFromFile(nsIFile* file, const char* mailboxName,
                             PRTime date, imapMessageFlagsType flags,
                             nsCString& keywords);

  // mailbox name utilities.
  void CreateEscapedMailboxName(const char* rawName, nsCString& escapedName);
  void SetupMessageFlagsString(nsCString& flagString,
                               imapMessageFlagsType flags, uint16_t userFlags);

  // body fetching listing data
  bool m_fetchBodyListIsNew;
  nsTArray<nsMsgKey> m_fetchBodyIdList;

  // initialization function given a new url and transport layer
  nsresult SetupWithUrl(nsIURI* aURL, nsISupports* aConsumer);
  nsresult SetupWithUrlCallback(nsIProxyInfo* proxyInfo);
  void ReleaseUrlState(bool rerunningUrl);  // release any state that is stored
                                            // on a per action basis.
  /**
   * Last ditch effort to run the url without using an imap connection.
   * If it turns out that we don't need to run the url at all (e.g., we're
   * trying to download a single message for offline use and it has already
   * been downloaded, this function will send the appropriate notifications.
   *
   * @returns true if the url has been run locally, or doesn't need to be run.
   */
  bool TryToRunUrlLocally(nsIURI* aURL, nsISupports* aConsumer);

  ////////////////////////////////////////////////////////////////////////////////////////
  // Communication methods --> Reading and writing protocol
  ////////////////////////////////////////////////////////////////////////////////////////

  // SendData not only writes the NULL terminated data in dataBuffer to our
  // output stream but it also informs the consumer that the data has been
  // written to the stream. aSuppressLogging --> set to true if you wish to
  // suppress logging for this particular command. this is useful for making
  // sure we don't log authentication information like the user's password
  // (which was encoded anyway), but still we shouldn't add that information to
  // the log.
  nsresult SendData(const char* dataBuffer,
                    bool aSuppressLogging = false) override;

  // state ported over from 4.5
  bool m_pseudoInterrupted;
  bool m_active;
  bool m_folderNeedsSubscribing;
  bool m_folderNeedsACLRefreshed;

  bool m_threadShouldDie;

  // use to prevent re-entering TellThreadToDie.
  bool m_inThreadShouldDie;
  // If the UI thread has signalled the IMAP thread to die, and the
  // connection has timed out, this will be set to FALSE.
  bool m_safeToCloseConnection;

  RefPtr<nsImapFlagAndUidState> m_flagState;
  nsMsgBiffState m_currentBiffState;
  // manage the IMAP server command tags
  // 11 = enough memory for the decimal representation of MAX_UINT + trailing
  // nul
  char m_currentServerCommandTag[11];
  uint32_t m_currentServerCommandTagNumber;
  void IncrementCommandTagNumber();
  const char* GetServerCommandTag();

  void StartTLS();

  // login related methods.
  nsresult GetPassword(nsString& password, bool aNewPasswordRequested);
  void InitPrefAuthMethods(int32_t authMethodPrefValue,
                           nsIMsgIncomingServer* aServer);
  nsresult ChooseAuthMethod();
  void MarkAuthMethodAsFailed(eIMAPCapabilityFlags failedAuthMethod);
  void ResetAuthMethods();

  // All of these methods actually issue protocol
  void Capability();  // query host for capabilities.
  void ID();          // send RFC 2971 app info to server
  void EnableUTF8Accept();
  void EnableCondStore();
  void StartCompressDeflate();
  nsresult BeginCompressing();
  void Language();  // set the language on the server if it supports it
  void Namespace();
  void InsecureLogin(const char* userName, const nsCString& password);
  nsresult ClientID();
  nsresult AuthLogin(const char* userName, const nsString& password,
                     eIMAPCapabilityFlag flag);
  nsresult SendDataParseIMAPandCheckForNewMail(const char* data,
                                               const char* command);
  void ProcessAuthenticatedStateURL();
  void ProcessAfterAuthenticated();
  void ProcessSelectedStateURL();
  bool TryToLogon();

  // ProcessAuthenticatedStateURL() used to be one giant if statement. I've
  // broken out a set of actions based on the imap action passed into the url.
  // The following functions are imap protocol handlers for each action. They
  // are called by ProcessAuthenticatedStateUrl.
  void OnLSubFolders();
  void OnAppendMsgFromFile();

  char* GetFolderPathString();  // OK to call from UI thread

  char* OnCreateServerSourceFolderPathString();
  char* OnCreateServerDestinationFolderPathString();
  nsresult CreateServerSourceFolderPathString(char** result);
  void OnCreateFolder(const char* aSourceMailbox);
  void OnEnsureExistsFolder(const char* aSourceMailbox);
  void OnSubscribe(const char* aSourceMailbox);
  void OnUnsubscribe(const char* aSourceMailbox);
  void RefreshACLForFolderIfNecessary(const char* mailboxName);
  void RefreshACLForFolder(const char* aSourceMailbox);
  void GetACLForFolder(const char* aMailboxName);
  void OnRefreshAllACLs();
  void OnListFolder(const char* aSourceMailbox, bool aBool);
  void OnStatusForFolder(const char* sourceMailbox);
  void OnDeleteFolder(const char* aSourceMailbox);
  void OnRenameFolder(const char* aSourceMailbox);
  void OnMoveFolderHierarchy(const char* aSourceMailbox);
  void DeleteFolderAndMsgs(const char* aSourceMailbox);
  void RemoveMsgsAndExpunge();
  void FindMailboxesIfNecessary();
  void CreateMailbox(const char* mailboxName);
  void DeleteMailbox(const char* mailboxName);
  void RenameMailbox(const char* existingName, const char* newName);
  void RemoveHierarchyDelimiter(nsCString& mailboxName);
  bool CreateMailboxRespectingSubscriptions(const char* mailboxName);
  bool DeleteMailboxRespectingSubscriptions(const char* mailboxName);
  bool RenameMailboxRespectingSubscriptions(const char* existingName,
                                            const char* newName,
                                            bool reallyRename);
  // notify the fe that a folder was deleted
  void FolderDeleted(const char* mailboxName);
  // notify the fe that a folder creation failed
  void FolderNotCreated(const char* mailboxName);
  // notify the fe that a folder was deleted
  void FolderRenamed(const char* oldName, const char* newName);

  bool FolderIsSelected(const char* mailboxName);

  bool MailboxIsNoSelectMailbox(const char* mailboxName);
  bool FolderNeedsACLInitialized(const char* folderName);
  void DiscoverMailboxList();
  void DiscoverAllAndSubscribedBoxes();
  void MailboxDiscoveryFinished();
  void NthLevelChildList(const char* onlineMailboxPrefix, int32_t depth);
  // LIST SUBSCRIBED command (from RFC 5258) crashes some servers. so we need to
  // identify those servers
  bool GetListSubscribedIsBrokenOnServer();
  void Lsub(const char* mailboxPattern, bool addDirectoryIfNecessary);
  void List(const char* mailboxPattern, bool addDirectoryIfNecessary,
            bool useXLIST = false);
  void Subscribe(const char* mailboxName);
  void Unsubscribe(const char* mailboxName);
  void Idle();
  void EndIdle(bool waitForResponse = true);
  // Some imap servers include the mailboxName following the dir-separator in
  // the list of subfolders of the mailboxName. In fact, they are the same. So
  // we should decide if we should delete such subfolder and provide feedback if
  // the delete operation succeed.
  bool DeleteSubFolders(const char* aMailboxName, bool& aDeleteSelf);
  bool RenameHierarchyByHand(const char* oldParentMailboxName,
                             const char* newParentMailboxName);
  bool RetryUrl();

  nsresult GlobalInitialization(nsIPrefBranch* aPrefBranch);
  nsresult Configure(int32_t TooFastTime, int32_t IdealTime,
                     int32_t ChunkAddSize, int32_t ChunkSize,
                     int32_t ChunkThreshold, bool FetchByChunks);
  nsresult GetMsgWindow(nsIMsgWindow** aMsgWindow);
  // End Process AuthenticatedState Url helper methods

  virtual char const* GetType() override { return "imap"; }

  // Quota support
  void GetQuotaDataIfSupported(const char* aBoxName);

  // CondStore support - true if server supports it, and the user hasn't
  // disabled it.
  bool UseCondStore();
  // false if pref "mail.server.serverxxx.use_condstore" is false;
  bool m_useCondStore;
  // COMPRESS=DEFLATE support - true if server supports it, and the user hasn't
  // disabled it.
  bool UseCompressDeflate();
  // false if pref "mail.server.serverxxx.use_compress_deflate" is false;
  bool m_useCompressDeflate;
  // these come from the nsIDBFolderInfo in the msgDatabase and
  // are initialized in nsImapProtocol::SetupWithUrl.
  uint64_t mFolderLastModSeq;
  int32_t mFolderTotalMsgCount;
  uint32_t mFolderHighestUID;
  bool m_allowUTF8Accept;

  bool m_isGmailServer;
  nsTArray<nsCString> mCustomDBHeaders;
  nsTArray<nsCString> mCustomHeaders;
  bool m_trackingTime;
  PRTime m_startTime;
  PRTime m_endTime;
  PRTime m_lastActiveTime;
  int32_t m_tooFastTime;
  int32_t m_idealTime;
  int32_t m_chunkAddSize;
  int32_t m_chunkStartSize;
  bool m_fetchByChunks;
  bool m_sendID;
  int32_t m_curFetchSize;
  bool m_ignoreExpunges;
  eIMAPCapabilityFlags m_prefAuthMethods;    // set of capability flags (in
                                             // nsImapCore.h) for auth methods
  eIMAPCapabilityFlags m_failedAuthMethods;  // ditto
  eIMAPCapabilityFlag m_currentAuthMethod;  // exactly one capability flag, or 0
  int32_t m_socketType;
  int32_t m_chunkSize;
  int32_t m_chunkThreshold;
  RefPtr<nsMsgImapLineDownloadCache> m_downloadLineCache;
  RefPtr<nsMsgImapHdrXferInfo> m_hdrDownloadCache;
  nsCOMPtr<nsIImapHeaderInfo> m_curHdrInfo;
  // mapping between mailboxes and the corresponding folder flags
  nsTHashMap<nsCStringHashKey, int32_t> m_standardListMailboxes;
  // mapping between special xlist mailboxes and the corresponding folder flags
  nsTHashMap<nsCStringHashKey, int32_t> m_specialXListMailboxes;

  nsCOMPtr<nsIImapHostSessionList> m_hostSessionList;

  bool m_fromHeaderSeen;

  nsString mAcceptLanguages;

  nsCString m_clientId;

  // progress stuff
  void SetProgressString(uint32_t aStringIndex);

  nsCString m_progressStringName;
  uint32_t m_stringIndex;
  int32_t m_progressCurrentNumber[IMAP_NUMBER_OF_PROGRESS_STRINGS];
  int32_t m_progressExpectedNumber;
  nsCString m_lastProgressStringName;
  int32_t m_lastPercent;
  int64_t m_lastProgressTime;

  bool m_notifySearchHit;
  bool m_needNoop;
  bool m_idle;
  bool m_useIdle;
  int32_t m_noopCount;
  bool m_autoSubscribe, m_autoUnsubscribe, m_autoSubscribeOnOpen;
  bool m_closeNeededBeforeSelect;
  bool m_retryUrlOnError;
  bool m_preferPlainText;
  bool m_forceSelect;

  int32_t m_uidValidity;  // stored uid validity for the selected folder.

  enum EMailboxHierarchyNameState {
    kNoOperationInProgress,
    // kDiscoverBaseFolderInProgress, - Unused. Keeping for historical reasons.
    kDiscoverTrashFolderInProgress,
    kDeleteSubFoldersInProgress,
    kListingForInfoOnly,
    kListingForInfoAndDiscovery,
    kDiscoveringNamespacesOnly,
    kXListing,
    kListingForFolderFlags,
    kListingForCreate
  };
  EMailboxHierarchyNameState m_hierarchyNameState;
  EMailboxDiscoverStatus m_discoveryStatus;
  nsTArray<nsIMAPMailboxInfo*> m_listedMailboxList;
  nsTArray<nsCString>* m_deletableChildren;
  uint32_t m_flagChangeCount;
  PRTime m_lastCheckTime;

  bool CheckNeeded();

  nsString m_emptyMimePartString;

  RefPtr<mozilla::mailnews::OAuth2ThreadHelper> mOAuth2Support;
  bool m_capabilityResponseOccurred;

  nsresult IsTransportAlive(bool* alive);
  nsresult TransportStartTLS();
  void GetTransportSecurityInfo(nsITransportSecurityInfo** aSecurityInfo);
};

// This small class is a "mock" channel because it is a mockery of the imap
// channel's implementation... it's a light weight channel that we can return to
// necko when they ask for a channel on a url before we actually have an imap
// protocol instance around which can run the url. Please see my comments in
// nsIImapMockChannel.idl for more details..
//
// Threading concern: This class lives entirely in the UI thread.

class nsICacheEntry;

class nsImapMockChannel : public nsIImapMockChannel,
                          public nsICacheEntryOpenCallback,
                          public nsITransportEventSink,
                          public nsSupportsWeakReference,
                          public nsMailChannel,
                          public nsHashPropertyBag {
 public:
  friend class nsImapProtocol;

  NS_DECL_ISUPPORTS_INHERITED
  NS_DECL_NSIIMAPMOCKCHANNEL
  NS_DECL_NSICHANNEL
  NS_DECL_NSIREQUEST
  NS_DECL_NSICACHEENTRYOPENCALLBACK
  NS_DECL_NSITRANSPORTEVENTSINK

  nsImapMockChannel();
  static nsresult Create(const nsIID& iid, void** result);
  nsresult RunOnStopRequestFailure();

 protected:
  virtual ~nsImapMockChannel();
  nsCOMPtr<nsIURI> m_url;

  nsCOMPtr<nsIURI> m_originalUrl;
  nsCOMPtr<nsILoadGroup> m_loadGroup;
  nsCOMPtr<nsILoadInfo> m_loadInfo;
  nsCOMPtr<nsIStreamListener> m_channelListener;
  nsresult m_cancelStatus;
  nsLoadFlags mLoadFlags;
  nsCOMPtr<nsIProgressEventSink> mProgressEventSink;
  nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
  nsCOMPtr<nsISupports> mOwner;
  nsCOMPtr<nsITransportSecurityInfo> mSecurityInfo;
  nsCString mContentType;
  nsCString mCharset;
  nsWeakPtr mProtocol;

  bool mChannelClosed;
  bool mReadingFromCache;
  int64_t mContentLength;
  bool mWritingToCache;

  mozilla::Monitor mSuspendedMonitor;
  bool mSuspended;

  nsresult ResumeAndNotifyOne();

  // cache related helper methods
  nsresult OpenCacheEntry();  // makes a request to the cache service for a
                              // cache entry for a url
  bool ReadFromLocalCache();  // attempts to read the url out of our local
                              // (offline) cache....
  nsresult ReadFromCache2(nsICacheEntry* entry);  // pipes message from cache2
                                                  // entry to channel listener
  nsresult NotifyStartEndReadFromCache(bool start);

  // we end up daisy chaining multiple nsIStreamListeners into the load process.
  nsresult SetupPartExtractorListener(nsIImapUrl* aUrl,
                                      nsIStreamListener* aConsumer);

  uint32_t mContentDisposition;
};

#endif  // nsImapProtocol_h___
