summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/imap/src/ImapIncomingServer.jsm
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mailnews/imap/src/ImapIncomingServer.jsm')
-rw-r--r--comm/mailnews/imap/src/ImapIncomingServer.jsm783
1 files changed, 783 insertions, 0 deletions
diff --git a/comm/mailnews/imap/src/ImapIncomingServer.jsm b/comm/mailnews/imap/src/ImapIncomingServer.jsm
new file mode 100644
index 0000000000..c9cf86bfdc
--- /dev/null
+++ b/comm/mailnews/imap/src/ImapIncomingServer.jsm
@@ -0,0 +1,783 @@
+/* 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/. */
+
+const EXPORTED_SYMBOLS = ["ImapIncomingServer"];
+
+const { XPCOMUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/XPCOMUtils.sys.mjs"
+);
+const { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+);
+const { MsgIncomingServer } = ChromeUtils.import(
+ "resource:///modules/MsgIncomingServer.jsm"
+);
+
+const lazy = {};
+
+XPCOMUtils.defineLazyModuleGetters(lazy, {
+ ImapClient: "resource:///modules/ImapClient.jsm",
+ ImapCapFlags: "resource:///modules/ImapUtils.jsm",
+ ImapUtils: "resource:///modules/ImapUtils.jsm",
+ MailUtils: "resource:///modules/MailUtils.jsm",
+});
+
+/**
+ * @implements {nsIImapServerSink}
+ * @implements {nsIImapIncomingServer}
+ * @implements {nsIMsgIncomingServer}
+ * @implements {nsIUrlListener}
+ * @implements {nsISupportsWeakReference}
+ * @implements {nsISubscribableServer}
+ */
+class ImapIncomingServer extends MsgIncomingServer {
+ QueryInterface = ChromeUtils.generateQI([
+ "nsIImapServerSink",
+ "nsIImapIncomingServer",
+ "nsIMsgIncomingServer",
+ "nsIUrlListener",
+ "nsISupportsWeakReference",
+ "nsISubscribableServer",
+ ]);
+
+ _logger = lazy.ImapUtils.logger;
+
+ constructor() {
+ super();
+
+ this._userAuthenticated = false;
+
+ // nsIMsgIncomingServer attributes.
+ this.localStoreType = "imap";
+ this.localDatabaseType = "imap";
+ this.canBeDefaultServer = true;
+ this.canSearchMessages = true;
+
+ // nsISubscribableServer attributes.
+ this.supportsSubscribeSearch = false;
+
+ // nsIImapIncomingServer attributes that map directly to pref values.
+ this._mapAttrsToPrefs([
+ ["Bool", "allowUTF8Accept", "allow_utf8_accept"],
+ ["Bool", "autoSyncOfflineStores", "autosync_offline_stores"],
+ ["Bool", "checkAllFoldersForNew", "check_all_folders_for_new"],
+ ["Bool", "cleanupInboxOnExit", "cleanup_inbox_on_exit"],
+ ["Bool", "downloadBodiesOnGetNewMail", "download_bodies_on_get_new_mail"],
+ ["Bool", "dualUseFolders", "dual_use_folders"],
+ ["Bool", "fetchByChunks", "fetch_by_chunks"],
+ ["Bool", "forceSelect", "force_select_imap"],
+ ["Bool", "isGMailServer", "is_gmail"],
+ ["Bool", "offlineDownload", "offline_download"],
+ ["Bool", "sendID", "send_client_info"],
+ ["Bool", "useCompressDeflate", "use_compress_deflate"],
+ ["Bool", "useCondStore", "use_condstore"],
+ ["Bool", "useIdle", "use_idle"],
+ ["Char", "adminUrl", "admin_url"],
+ ["Char", "otherUsersNamespace", "namespace.other_users"],
+ ["Char", "personalNamespace", "namespace.personal"],
+ ["Char", "publicNamespace", "namespace.public"],
+ ["Char", "serverIDPref", "serverIDResponse"],
+ ["Int", "autoSyncMaxAgeDays", "autosync_max_age_days"],
+ ["Int", "timeOutLimits", "timeout"],
+ ]);
+ }
+
+ /**
+ * Most of nsISubscribableServer interfaces are delegated to
+ * this._subscribable.
+ */
+ get _subscribable() {
+ if (!this._subscribableServer) {
+ this._subscribableServer = Cc[
+ "@mozilla.org/messenger/subscribableserver;1"
+ ].createInstance(Ci.nsISubscribableServer);
+ this._subscribableServer.setIncomingServer(this);
+ }
+ return this._subscribableServer;
+ }
+
+ /** @see nsISubscribableServer */
+ get folderView() {
+ return this._subscribable.folderView;
+ }
+
+ get subscribeListener() {
+ return this._subscribable.subscribeListener;
+ }
+
+ set subscribeListener(value) {
+ this._subscribable.subscribeListener = value;
+ }
+
+ set delimiter(value) {
+ this._subscribable.delimiter = value;
+ }
+
+ subscribeCleanup() {
+ this._subscribableServer = null;
+ }
+
+ startPopulating(msgWindow, forceToServer, getOnlyNew) {
+ this._loadingInSubscribeDialog = true;
+ this._subscribable.startPopulating(msgWindow, forceToServer, getOnlyNew);
+ this.delimiter = "/";
+ this.setShowFullName(false);
+ MailServices.imap.getListOfFoldersOnServer(this, msgWindow);
+ }
+
+ stopPopulating(msgWindow) {
+ this._loadingInSubscribeDialog = false;
+ this._subscribable.stopPopulating(msgWindow);
+ }
+
+ addTo(name, addAsSubscribed, subscribable, changeIfExists) {
+ this._subscribable.addTo(
+ name,
+ addAsSubscribed,
+ subscribable,
+ changeIfExists
+ );
+ }
+
+ subscribe(name) {
+ this.subscribeToFolder(name, true);
+ }
+
+ unsubscribe(name) {
+ this.subscribeToFolder(name, false);
+ }
+
+ commitSubscribeChanges() {
+ this.performExpand();
+ }
+
+ setAsSubscribed(path) {
+ this._subscribable.setAsSubscribed(path);
+ }
+
+ updateSubscribed() {}
+
+ setState(path, state) {
+ return this._subscribable.setState(path, state);
+ }
+
+ setShowFullName(showFullName) {
+ this._subscribable.setShowFullName(showFullName);
+ }
+
+ hasChildren(path) {
+ return this._subscribable.hasChildren(path);
+ }
+
+ isSubscribed(path) {
+ return this._subscribable.isSubscribed(path);
+ }
+
+ isSubscribable(path) {
+ return this._subscribable.isSubscribable(path);
+ }
+
+ getLeafName(path) {
+ return this._subscribable.getLeafName(path);
+ }
+
+ getFirstChildURI(path) {
+ return this._subscribable.getFirstChildURI(path);
+ }
+
+ getChildURIs(path) {
+ return this._subscribable.getChildURIs(path);
+ }
+
+ /** @see nsIUrlListener */
+ OnStartRunningUrl() {}
+
+ OnStopRunningUrl(url, exitCode) {
+ switch (url.QueryInterface(Ci.nsIImapUrl).imapAction) {
+ case Ci.nsIImapUrl.nsImapDiscoverAllAndSubscribedBoxesUrl:
+ this.stopPopulating();
+ break;
+ }
+ }
+
+ /** @see nsIMsgIncomingServer */
+ get serverRequiresPasswordForBiff() {
+ return !this._userAuthenticated;
+ }
+
+ get offlineSupportLevel() {
+ const OFFLINE_SUPPORT_LEVEL_UNDEFINED = -1;
+ const OFFLINE_SUPPORT_LEVEL_REGULAR = 10;
+ let level = this.getIntValue("offline_support_level");
+ return level != OFFLINE_SUPPORT_LEVEL_UNDEFINED
+ ? level
+ : OFFLINE_SUPPORT_LEVEL_REGULAR;
+ }
+
+ get constructedPrettyName() {
+ let identity = MailServices.accounts.getFirstIdentityForServer(this);
+ let email;
+ if (identity) {
+ email = identity.email;
+ } else {
+ email = `${this.username}`;
+ if (this.hostName) {
+ email += `@${this.hostName}`;
+ }
+ }
+ let bundle = Services.strings.createBundle(
+ "chrome://messenger/locale/imapMsgs.properties"
+ );
+ return bundle.formatStringFromName("imapDefaultAccountName", [email]);
+ }
+
+ performBiff(msgWindow) {
+ this.performExpand(msgWindow);
+ }
+
+ performExpand(msgWindow) {
+ this._setFolderToUnverified();
+ this.hasDiscoveredFolders = false;
+ MailServices.imap.discoverAllFolders(this.rootFolder, this, msgWindow);
+ }
+
+ /**
+ * Recursively set a folder and its subFolders to unverified state.
+ *
+ * @param {nsIMsgFolder} folder - The folder to operate on.
+ */
+ _setFolderToUnverified(folder) {
+ if (!folder) {
+ this._setFolderToUnverified(this.rootFolder);
+ return;
+ }
+
+ folder.QueryInterface(
+ Ci.nsIMsgImapMailFolder
+ ).verifiedAsOnlineFolder = false;
+ for (let child of folder.subFolders) {
+ this._setFolderToUnverified(child);
+ }
+ }
+
+ closeCachedConnections() {
+ // Close all connections.
+ for (let client of this._connections) {
+ client.logout();
+ }
+ // Cancel all waitings in queue.
+ for (let resolve of this._connectionWaitingQueue) {
+ resolve(false);
+ }
+ this._connections = [];
+ }
+
+ verifyLogon(urlListener, msgWindow) {
+ return MailServices.imap.verifyLogon(
+ this.rootFolder,
+ urlListener,
+ msgWindow
+ );
+ }
+
+ subscribeToFolder(name, subscribe) {
+ let folder = this.rootMsgFolder.findSubFolder(name);
+ if (subscribe) {
+ return MailServices.imap.subscribeFolder(folder, name, null);
+ }
+ return MailServices.imap.unsubscribeFolder(folder, name, null);
+ }
+
+ /** @see nsIImapServerSink */
+
+ /** @type {boolean} - User has authenticated with the server. */
+ get userAuthenticated() {
+ return this._userAuthenticated;
+ }
+
+ set userAuthenticated(value) {
+ this._userAuthenticated = value;
+ if (value) {
+ MailServices.accounts.userNeedsToAuthenticate = false;
+ }
+ }
+
+ possibleImapMailbox(folderPath, delimiter, boxFlags) {
+ let explicitlyVerify = false;
+
+ if (folderPath.endsWith("/")) {
+ folderPath = folderPath.slice(0, -1);
+ if (!folderPath) {
+ throw Components.Exception(
+ "Empty folder path",
+ Cr.NS_ERROR_INVALID_ARG
+ );
+ }
+ explicitlyVerify = !(boxFlags & lazy.ImapUtils.FLAG_NAMESPACE);
+ }
+
+ if (this.hasDiscoveredFolders && this._loadingInSubscribeDialog) {
+ // Populate the subscribe dialog.
+ let noSelect = boxFlags & lazy.ImapUtils.FLAG_NO_SELECT;
+ this.addTo(
+ folderPath,
+ this.doingLsub && !noSelect,
+ !noSelect,
+ this.doingLsub
+ );
+ return false;
+ }
+
+ let slashIndex = folderPath.indexOf("/");
+ let token = folderPath;
+ let rest = "";
+ if (slashIndex > 0) {
+ token = folderPath.slice(0, slashIndex);
+ rest = folderPath.slice(slashIndex);
+ }
+
+ folderPath = (/^inbox/i.test(token) ? "INBOX" : token) + rest;
+
+ let uri = this.rootFolder.URI;
+ let parentName = folderPath;
+ let parentUri = uri;
+ let hasParent = false;
+ let lastSlashIndex = folderPath.lastIndexOf("/");
+ if (lastSlashIndex > 0) {
+ parentName = parentName.slice(0, lastSlashIndex);
+ hasParent = true;
+ parentUri += "/" + parentName;
+ }
+
+ if (/^inbox/i.test(folderPath) && delimiter == "|") {
+ delimiter = "/";
+ this.rootFolder.QueryInterface(
+ Ci.nsIMsgImapMailFolder
+ ).hierarchyDelimiter = delimiter;
+ }
+
+ uri += "/" + folderPath;
+ let child = this.rootFolder.getChildWithURI(
+ uri,
+ true,
+ /^inbox/i.test(folderPath)
+ );
+
+ let isNewFolder = !child;
+ if (isNewFolder) {
+ if (hasParent) {
+ let parent = this.rootFolder.getChildWithURI(
+ parentUri,
+ true,
+ /^inbox/i.test(parentName)
+ );
+
+ if (!parent) {
+ this.possibleImapMailbox(
+ parentName,
+ delimiter,
+ lazy.ImapUtils.FLAG_NO_SELECT |
+ (boxFlags &
+ (lazy.ImapUtils.FLAG_PUBLIC_MAILBOX |
+ lazy.ImapUtils.FLAG_OTHER_USERS_MAILBOX |
+ lazy.ImapUtils.FLAG_PERSONAL_MAILBOX))
+ );
+ }
+ }
+ this.rootFolder
+ .QueryInterface(Ci.nsIMsgImapMailFolder)
+ .createClientSubfolderInfo(folderPath, delimiter, boxFlags, false);
+ child = this.rootFolder.getChildWithURI(
+ uri,
+ true,
+ /^inbox/i.test(folderPath)
+ );
+ }
+ if (child) {
+ let imapFolder = child.QueryInterface(Ci.nsIMsgImapMailFolder);
+ imapFolder.verifiedAsOnlineFolder = true;
+ imapFolder.hierarchyDelimiter = delimiter;
+ if (boxFlags & lazy.ImapUtils.FLAG_IMAP_TRASH) {
+ if (this.deleteModel == Ci.nsMsgImapDeleteModels.MoveToTrash) {
+ child.setFlag(Ci.nsMsgFolderFlags.Trash);
+ }
+ }
+ imapFolder.boxFlags = boxFlags;
+ imapFolder.explicitlyVerify = explicitlyVerify;
+ let onlineName = imapFolder.onlineName;
+ folderPath = folderPath.replaceAll("/", delimiter);
+ if (delimiter != "/") {
+ folderPath = decodeURIComponent(folderPath);
+ }
+
+ if (boxFlags & lazy.ImapUtils.FLAG_IMAP_INBOX) {
+ // GMail gives us a localized name for the inbox but doesn't let
+ // us select that localized name.
+ imapFolder.onlineName = "INBOX";
+ } else if (!onlineName || onlineName != folderPath) {
+ imapFolder.onlineName = folderPath;
+ }
+
+ child.prettyName = imapFolder.name;
+ if (isNewFolder) {
+ // Close the db so we don't hold open all the .msf files for new folders.
+ child.msgDatabase = null;
+ }
+ }
+
+ return isNewFolder;
+ }
+
+ discoveryDone() {
+ this.hasDiscoveredFolders = true;
+ // No need to verify the root.
+ this.rootFolder.QueryInterface(
+ Ci.nsIMsgImapMailFolder
+ ).verifiedAsOnlineFolder = true;
+ let unverified = this._getUnverifiedFolders(this.rootFolder);
+ this._logger.debug(
+ `discoveryDone, unverified folders count=${unverified.length}.`
+ );
+ for (let folder of unverified) {
+ if (folder.flags & Ci.nsMsgFolderFlags.Virtual) {
+ // Do not remove virtual folders.
+ continue;
+ }
+ let imapFolder = folder.QueryInterface(Ci.nsIMsgImapMailFolder);
+ if (
+ !this.usingSubscription ||
+ imapFolder.explicitlyVerify ||
+ (folder.hasSubFolders && this._noDescendentsAreVerified(folder))
+ ) {
+ imapFolder.explicitlyVerify = false;
+ imapFolder.list();
+ } else if (folder.parent) {
+ imapFolder.removeLocalSelf();
+ this._logger.debug(`Removed unverified folder name=${folder.name}`);
+ }
+ }
+ }
+
+ /**
+ * Find local folders that do not exist on the server.
+ *
+ * @param {nsIMsgFolder} parentFolder - The folder to check.
+ * @returns {nsIMsgFolder[]}
+ */
+ _getUnverifiedFolders(parentFolder) {
+ let folders = [];
+ let imapFolder = parentFolder.QueryInterface(Ci.nsIMsgImapMailFolder);
+ if (!imapFolder.verifiedAsOnlineFolder || imapFolder.explicitlyVerify) {
+ folders.push(imapFolder);
+ }
+ for (let folder of parentFolder.subFolders) {
+ folders.push(...this._getUnverifiedFolders(folder));
+ }
+ return folders;
+ }
+
+ /**
+ * Returns true if all sub folders are unverified.
+ *
+ * @param {nsIMsgFolder} parentFolder - The folder to check.
+ * @returns {nsIMsgFolder[]}
+ */
+ _noDescendentsAreVerified(parentFolder) {
+ for (let folder of parentFolder.subFolders) {
+ let imapFolder = folder.QueryInterface(Ci.nsIMsgImapMailFolder);
+ if (
+ imapFolder.verifiedAsOnlineFolder ||
+ !this._noDescendentsAreVerified(folder)
+ ) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ onlineFolderRename(msgWindow, oldName, newName) {
+ let folder = this._getFolder(oldName).QueryInterface(
+ Ci.nsIMsgImapMailFolder
+ );
+ let index = newName.lastIndexOf("/");
+ let parent =
+ index > 0 ? this._getFolder(newName.slice(0, index)) : this.rootFolder;
+ folder.renameLocal(newName, parent);
+ if (parent instanceof Ci.nsIMsgImapMailFolder) {
+ try {
+ parent.renameClient(msgWindow, folder, oldName, newName);
+ } catch (e) {
+ this._logger.error("renameClient failed", e);
+ }
+ }
+
+ this._getFolder(newName).NotifyFolderEvent("RenameCompleted");
+ }
+
+ /**
+ * Given a canonical folder name, returns the corresponding msg folder.
+ *
+ * @param {string} name - The canonical folder name, e.g. a/b/c.
+ * @returns {nsIMsgFolder} The corresponding msg folder.
+ */
+ _getFolder(name) {
+ return lazy.MailUtils.getOrCreateFolder(this.rootFolder.URI + "/" + name);
+ }
+
+ abortQueuedUrls() {}
+
+ setCapability(capabilityFlags) {
+ this._capabilityFlags = capabilityFlags;
+ if (capabilityFlags & lazy.ImapCapFlags.Gmail) {
+ this.isGMailServer = true;
+ }
+ }
+
+ /** @see nsIImapIncomingServer */
+ getCapability() {
+ return this._capabilityFlags;
+ }
+
+ get deleteModel() {
+ return this.getIntValue("delete_model");
+ }
+
+ set deleteModel(value) {
+ this.setIntValue("delete_model", value);
+ let trashFolder = this._getFolder(this.trashFolderName);
+ if (trashFolder) {
+ if (value == Ci.nsMsgImapDeleteModels.MoveToTrash) {
+ trashFolder.setFlag(Ci.nsMsgFolderFlags.Trash);
+ } else {
+ trashFolder.clearFlag(Ci.nsMsgFolderFlags.Trash);
+ }
+ }
+ }
+
+ get usingSubscription() {
+ return this.getBoolValue("using_subscription");
+ }
+
+ get trashFolderName() {
+ return this.getUnicharValue("trash_folder_name") || "Trash";
+ }
+
+ get maximumConnectionsNumber() {
+ let maxConnections = this.getIntValue("max_cached_connections", 0);
+ if (maxConnections > 0) {
+ return maxConnections;
+ }
+ // The default is 5 connections, if the pref value is 0, we use the default.
+ // If it's negative, treat it as 1.
+ maxConnections = maxConnections == 0 ? 5 : 1;
+ this.maximumConnectionsNumber = maxConnections;
+ return maxConnections;
+ }
+
+ set maximumConnectionsNumber(value) {
+ this.setIntValue("max_cached_connections", value);
+ }
+
+ GetNewMessagesForNonInboxFolders(
+ folder,
+ msgWindow,
+ forceAllFolders,
+ performingBiff
+ ) {
+ let flags = folder.flags;
+
+ if (
+ folder.QueryInterface(Ci.nsIMsgImapMailFolder).canOpenFolder &&
+ ((forceAllFolders &&
+ !(
+ flags &
+ (Ci.nsMsgFolderFlags.Inbox |
+ Ci.nsMsgFolderFlags.Trash |
+ Ci.nsMsgFolderFlags.Junk |
+ Ci.nsMsgFolderFlags.Virtual)
+ )) ||
+ flags & Ci.nsMsgFolderFlags.CheckNew)
+ ) {
+ folder.gettingNewMessages = true;
+ if (performingBiff) {
+ folder.performingBiff = true;
+ }
+ }
+
+ if (
+ Services.prefs.getBoolPref("mail.imap.use_status_for_biff", false) &&
+ !MailServices.mailSession.IsFolderOpenInWindow(folder)
+ ) {
+ folder.updateStatus(this, msgWindow);
+ } else {
+ folder.updateFolder(msgWindow);
+ }
+
+ for (let subFolder of folder.subFolders) {
+ this.GetNewMessagesForNonInboxFolders(
+ subFolder,
+ msgWindow,
+ forceAllFolders,
+ performingBiff
+ );
+ }
+ }
+
+ CloseConnectionForFolder(folder) {
+ for (let client of this._connections) {
+ if (client.folder == folder) {
+ client.logout();
+ }
+ }
+ }
+
+ _capabilities = [];
+
+ set capabilities(value) {
+ this._capabilities = value;
+ this.setCapability(lazy.ImapCapFlags.stringsToFlags(value));
+ }
+
+ // @type {ImapClient[]} - An array of connections.
+ _connections = [];
+ // @type {Function[]} - An array of Promise.resolve functions.
+ _connectionWaitingQueue = [];
+
+ /**
+ * Wait for a free connection.
+ *
+ * @param {nsIMsgFolder} folder - The folder to operate on.
+ * @returns {ImapClient}
+ */
+ async _waitForNextClient(folder) {
+ // Wait until a connection is available. canGetNext is false when
+ // closeCachedConnections is called.
+ let canGetNext = await new Promise(resolve =>
+ this._connectionWaitingQueue.push(resolve)
+ );
+ if (canGetNext) {
+ return this._getNextClient(folder);
+ }
+ return null;
+ }
+
+ /**
+ * Check if INBOX folder is selected in a connection.
+ *
+ * @param {ImapClient} client - The client to check.
+ * @returns {boolean}
+ */
+ _isInboxConnection(client) {
+ return client.folder?.onlineName.toUpperCase() == "INBOX";
+ }
+
+ /**
+ * Get a free connection that can be used.
+ *
+ * @param {nsIMsgFolder} folder - The folder to operate on.
+ * @returns {ImapClient}
+ */
+ async _getNextClient(folder) {
+ let client;
+
+ for (client of this._connections) {
+ if (folder && client.folder == folder) {
+ if (client.busy) {
+ // Prevent operating on the same folder in two connections.
+ return this._waitForNextClient(folder);
+ }
+ // If we're idling in the target folder, reuse it.
+ client.busy = true;
+ return client;
+ }
+ }
+
+ // Create a new client if the pool is not full.
+ if (this._connections.length < this.maximumConnectionsNumber) {
+ client = new lazy.ImapClient(this);
+ this._connections.push(client);
+ client.busy = true;
+ return client;
+ }
+
+ let freeConnections = this._connections.filter(c => !c.busy);
+
+ // Wait if no free connection.
+ if (!freeConnections.length) {
+ return this._waitForNextClient(folder);
+ }
+
+ // Reuse any free connection if only have one connection or IDLE not used.
+ if (
+ this.maximumConnectionsNumber <= 1 ||
+ !this.useIdle ||
+ !this._capabilities.includes("IDLE")
+ ) {
+ freeConnections[0].busy = true;
+ return freeConnections[0];
+ }
+
+ // Reuse non-inbox free connection.
+ client = freeConnections.find(c => !this._isInboxConnection(c));
+ if (client) {
+ client.busy = true;
+ return client;
+ }
+ return this._waitForNextClient(folder);
+ }
+
+ /**
+ * Do some actions with a connection.
+ *
+ * @param {nsIMsgFolder} folder - The folder to operate on.
+ * @param {Function} handler - A callback function to take a ImapClient
+ * instance, and do some actions.
+ */
+ async withClient(folder, handler) {
+ let client = await this._getNextClient(folder);
+ if (!client) {
+ return;
+ }
+ let startIdle = async () => {
+ if (!this.useIdle || !this._capabilities.includes("IDLE")) {
+ return;
+ }
+
+ // IDLE is configed and supported, use IDLE to receive server pushes.
+ let hasInboxConnection = this._connections.some(c =>
+ this._isInboxConnection(c)
+ );
+ let alreadyIdling =
+ client.folder &&
+ this._connections.find(
+ c => c != client && !c.busy && c.folder == client.folder
+ );
+ if (!hasInboxConnection) {
+ client.selectFolder(
+ this.rootFolder.getFolderWithFlags(Ci.nsMsgFolderFlags.Inbox)
+ );
+ } else if (client.folder && !alreadyIdling) {
+ client.idle();
+ } else if (alreadyIdling) {
+ client.folder = null;
+ }
+ };
+ client.onFree = () => {
+ client.busy = false;
+ let resolve = this._connectionWaitingQueue.shift();
+ if (resolve) {
+ // Resolve the first waiting in queue.
+ resolve(true);
+ } else if (client.isOnline) {
+ startIdle();
+ }
+ };
+ handler(client);
+ client.connect();
+ }
+}
+
+ImapIncomingServer.prototype.classID = Components.ID(
+ "{b02a4e1c-0d9e-498c-8b9d-18917ba9f65b}"
+);