/* 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 = ["migrateServerUris", "MsgIncomingServer"]; var { MailServices } = ChromeUtils.import( "resource:///modules/MailServices.jsm" ); /** * When hostname/username changes, update the corresponding entry in * nsILoginManager. * * @param {string} localStoreType - The store type of the current server. * @param {string} oldHostname - The hostname before the change. * @param {string} oldUsername - The username before the change. * @param {string} newHostname - The hostname after the change. * @param {string} newUsername - The username after the change. */ function migratePassword( localStoreType, oldHostname, oldUsername, newHostname, newUsername ) { // When constructing nsIURI, need to wrap IPv6 address in []. oldHostname = oldHostname.includes(":") ? `[${oldHostname}]` : oldHostname; let oldServerUri = `${localStoreType}://${encodeURIComponent(oldHostname)}`; newHostname = newHostname.includes(":") ? `[${newHostname}]` : newHostname; let newServerUri = `${localStoreType}://${encodeURIComponent(newHostname)}`; let logins = Services.logins.findLogins(oldServerUri, "", oldServerUri); for (let login of logins) { if (login.username == oldUsername) { // If a nsILoginInfo exists for the old hostname/username, update it to // use the new hostname/username. let newLogin = Cc[ "@mozilla.org/login-manager/loginInfo;1" ].createInstance(Ci.nsILoginInfo); newLogin.init( newServerUri, null, newServerUri, newUsername, login.password, "", "" ); Services.logins.modifyLogin(login, newLogin); } } } /** * When hostname/username changes, update the folder attributes in related * identities. * * @param {string} oldServerUri - The server uri before the change. * @param {string} newServerUri - The server uri after the change. */ function migrateIdentities(oldServerUri, newServerUri) { for (let identity of MailServices.accounts.allIdentities) { let attributes = [ "fcc_folder", "draft_folder", "archive_folder", "stationery_folder", ]; for (let attr of attributes) { let folderUri = identity.getUnicharAttribute(attr); if (folderUri.startsWith(oldServerUri)) { identity.setUnicharAttribute( attr, folderUri.replace(oldServerUri, newServerUri) ); } } } } /** * When hostname/username changes, update .spamActionTargetAccount and * .spamActionTargetFolder prefs. * * @param {string} oldServerUri - The server uri before the change. * @param {string} newServerUri - The server uri after the change. */ function migrateSpamActions(oldServerUri, newServerUri) { for (let server of MailServices.accounts.allServers) { let targetAccount = server.getCharValue("spamActionTargetAccount"); let targetFolder = server.getUnicharValue("spamActionTargetFolder"); if (targetAccount.startsWith(oldServerUri)) { server.setCharValue( "spamActionTargetAccount", targetAccount.replace(oldServerUri, newServerUri) ); } if (targetFolder.startsWith(oldServerUri)) { server.setUnicharValue( "spamActionTargetFolder", targetFolder.replace(oldServerUri, newServerUri) ); } } } /** * When hostname/username changes, update targetFolderUri in related filters * to the new folder uri. * * @param {string} oldServerUri - The server uri before the change. * @param {string} newServerUri - The server uri after the change. */ function migrateFilters(oldServerUri, newServerUri) { for (let server of MailServices.accounts.allServers) { let filterList; try { filterList = server.getFilterList(null); if (!server.canHaveFilters || !filterList) { continue; } } catch (e) { continue; } let changed = false; for (let i = 0; i < filterList.filterCount; i++) { let filter = filterList.getFilterAt(i); for (let action of filter.sortedActionList) { let targetFolderUri; try { targetFolderUri = action.targetFolderUri; } catch (e) { continue; } if (targetFolderUri.startsWith(oldServerUri)) { action.targetFolderUri = targetFolderUri.replace( oldServerUri, newServerUri ); changed = true; } } } if (changed) { filterList.saveToDefaultFile(); } } } /** * Migrate server uris in LoginManager and various account/folder prefs. * * @param {string} localStoreType - The store type of the current server. * @param {string} oldHostname - The hostname before the change. * @param {string} oldUsername - The username before the change. * @param {string} newHostname - The hostname after the change. * @param {string} newUsername - The username after the change. */ function migrateServerUris( localStoreType, oldHostname, oldUsername, newHostname, newUsername ) { try { migratePassword( localStoreType, oldHostname, oldUsername, newHostname, newUsername ); } catch (e) { console.error(e); } let oldAuth = oldUsername ? `${encodeURIComponent(oldUsername)}@` : ""; let newAuth = newUsername ? `${encodeURIComponent(newUsername)}@` : ""; // When constructing nsIURI, need to wrap IPv6 address in []. oldHostname = oldHostname.includes(":") ? `[${oldHostname}]` : oldHostname; let oldServerUri = `${localStoreType}://${oldAuth}${encodeURIComponent( oldHostname )}`; newHostname = newHostname.includes(":") ? `[${newHostname}]` : newHostname; let newServerUri = `${localStoreType}://${newAuth}${encodeURIComponent( newHostname )}`; try { migrateIdentities(oldServerUri, newServerUri); } catch (e) { console.error(e); } try { migrateSpamActions(oldServerUri, newServerUri); } catch (e) { console.error(e); } try { migrateFilters(oldServerUri, newServerUri); } catch (e) { console.error(e); } } /** * A base class for incoming server, should not be used directly. * * @implements {nsIMsgIncomingServer} * @implements {nsISupportsWeakReference} * @implements {nsIObserver} * @abstract */ class MsgIncomingServer { QueryInterface = ChromeUtils.generateQI([ "nsIMsgIncomingServer", "nsISupportsWeakReference", "nsIObserver", ]); constructor() { // nsIMsgIncomingServer attributes that map directly to pref values. this._mapAttrsToPrefs([ ["Char", "type"], ["Char", "clientid"], ["Int", "authMethod"], ["Int", "biffMinutes", "check_time"], ["Int", "maxMessageSize", "max_size"], ["Int", "incomingDuplicateAction", "dup_action"], ["Bool", "clientidEnabled"], ["Bool", "downloadOnBiff", "download_on_biff"], ["Bool", "valid"], ["Bool", "emptyTrashOnExit", "empty_trash_on_exit"], ["Bool", "canDelete"], ["Bool", "loginAtStartUp", "login_at_startup"], [ "Bool", "defaultCopiesAndFoldersPrefsToServer", "allows_specialfolders_usage", ], ["Bool", "canCreateFoldersOnServer", "canCreateFolders"], ["Bool", "canFileMessagesOnServer", "canFileMessages"], ["Bool", "limitOfflineMessageSize", "limit_offline_message_size"], ["Bool", "hidden"], ]); // nsIMsgIncomingServer attributes. this.performingBiff = false; this.accountManagerChrome = "am-main.xhtml"; this.biffState = Ci.nsIMsgFolder.nsMsgBiffState_Unknown; this.downloadMessagesAtStartup = false; this.canHaveFilters = true; this.canBeDefaultServer = false; this.displayStartupPage = true; this.supportsDiskSpace = true; this.canCompactFoldersOnServer = true; this.canUndoDeleteOnServer = true; this.sortOrder = 100000000; // @type {Map} - The key is MsgId+Subject, the value is // this._hdrIndex. this._knownHdrMap = new Map(); this._hdrIndex = 0; Services.obs.addObserver(this, "passwordmgr-storage-changed"); } /** * Observe() receives notifications for all accounts, not just this server's * account. So we ignore all notifications not intended for this server. * When the state of the password manager changes we need to clear the * this server's password from the cache in case the user just changed or * removed the password or username. * OAuth2 servers often automatically change the password manager's stored * password (the token). */ observe(subject, topic, data) { if (topic == "passwordmgr-storage-changed") { // Check that the notification is for this server and user. let otherFullName = ""; let otherUsername = ""; if (subject instanceof Ci.nsILoginInfo) { // The login info for a server has been removed with data being // "removeLogin" or "removeAllLogins". otherFullName = subject.origin; otherUsername = subject.username; } else if (subject instanceof Ci.nsIArray) { // Probably a 2 element array containing old and new login info due to // data being "modifyLogin". E.g., a user has modified the password or // username in the password manager or an OAuth2 token string has // automatically changed. Only need to look at names in first array // element (login info before any modification) since the user might // have changed the username as found in the 2nd elements. (The // hostname can't be modified in the password manager. otherFullName = subject.queryElementAt(0, Ci.nsISupports).origin; otherUsername = subject.queryElementAt(0, Ci.nsISupports).username; } if (otherFullName) { if ( otherFullName != "mailbox://" + this.hostName || otherUsername != this.username ) { // Not for this server; keep this server's cached password. return; } } else if (data != "hostSavingDisabled") { // "hostSavingDisabled" only occurs during test_smtpServer.js and // expects the password to be removed from memory cache. Otherwise, we // don't have enough information to decide to remove the cached // password, so keep it. return; } // Remove the password for this server cached in memory. this.password = ""; } } /** * Set up getters/setters for attributes that map directly to pref values. * * @param {string[]} attributes - An array of attributes. Each attribute is * defined by its type, name and corresponding prefName. */ _mapAttrsToPrefs(attributes) { for (let [type, attrName, prefName] of attributes) { prefName = prefName || attrName; Object.defineProperty(this, attrName, { configurable: true, get: () => this[`get${type}Value`](prefName), set: value => { this[`set${type}Value`](prefName, value); }, }); } } get key() { return this._key; } set key(key) { this._key = key; this._prefs = Services.prefs.getBranch(`mail.server.${key}.`); this._defaultPrefs = Services.prefs.getBranch("mail.server.default."); } get UID() { let uid = this._prefs.getStringPref("uid", ""); if (uid) { return uid; } return (this.UID = Services.uuid .generateUUID() .toString() .substring(1, 37)); } set UID(uid) { if (this._prefs.prefHasUserValue("uid")) { throw new Components.Exception("uid is already set", Cr.NS_ERROR_ABORT); } this._prefs.setStringPref("uid", uid); } get hostName() { let hostname = this.getUnicharValue("hostname"); if (hostname.includes(":")) { // Reformat the hostname if it contains a port number. this.hostName = hostname; return this.hostName; } return hostname; } set hostName(value) { let oldName = this.hostName; this._setHostName("hostname", value); if (oldName && oldName != value) { this.onUserOrHostNameChanged(oldName, value, true); } } _setHostName(prefName, value) { let [host, port] = value.split(":"); if (port) { this.port = Number(port); } this.setUnicharValue(prefName, host); } get username() { return this.getUnicharValue("userName"); } set username(value) { let oldName = this.username; if (oldName && oldName != value) { this.setUnicharValue("userName", value); this.onUserOrHostNameChanged(oldName, value, false); } else { this.setUnicharValue("userName", value); } } get port() { let port = this.getIntValue("port"); if (port > 1) { return port; } // If the port isn't set, use the default port based on the protocol. return this.protocolInfo.getDefaultServerPort( this.socketType == Ci.nsMsgSocketType.SSL ); } set port(value) { this.setIntValue("port", value); } get protocolInfo() { return Cc[ `@mozilla.org/messenger/protocol/info;1?type=${this.type}` ].getService(Ci.nsIMsgProtocolInfo); } get socketType() { try { return this._prefs.getIntPref("socketType"); } catch (e) { // socketType is set to default value. Look at isSecure setting. if (this._prefs.getBoolPref("isSecure", false)) { return Ci.nsMsgSocketType.SSL; } return this._defaultPrefs.getIntPref( "socketType", Ci.nsMsgSocketType.plain ); } } set socketType(value) { let wasSecure = this.isSecure; this._prefs.setIntPref("socketType", value); let isSecure = this.isSecure; if (wasSecure != isSecure) { this.rootFolder.NotifyBoolPropertyChanged( "isSecure", wasSecure, isSecure ); } } get isSecure() { return [Ci.nsMsgSocketType.alwaysSTARTTLS, Ci.nsMsgSocketType.SSL].includes( this.socketType ); } get serverURI() { return this._getServerURI(true); } /** * Get server URI in the form of localStoreType://[user@]hostname. * * @param {boolean} includeUsername - Whether to include the username. * @returns {string} */ _getServerURI(includeUsername) { let auth = includeUsername && this.username ? `${encodeURIComponent(this.username)}@` : ""; // When constructing nsIURI, need to wrap IPv6 address in []. let hostname = this.hostName.includes(":") ? `[${this.hostName}]` : this.hostName; return `${this.localStoreType}://${auth}${encodeURIComponent(hostname)}`; } get prettyName() { return this.getUnicharValue("name") || this.constructedPrettyName; } set prettyName(value) { this.setUnicharValue("name", value); this.rootFolder.prettyName = value; } /** * Construct a pretty name from username and hostname. * * @param {string} username - The user name. * @param {string} hostname - The host name. * @returns {string} */ _constructPrettyName(username, hostname) { let prefix = username ? `${username} on ` : ""; return `${prefix}${hostname}`; } get constructedPrettyName() { return this._constructPrettyName(this.username, this.hostName); } get localPath() { let localPath = this.getFileValue("directory-rel", "directory"); if (localPath) { // If the local path has already been set, use it. return localPath; } // Create the path using protocol info and hostname. localPath = this.protocolInfo.defaultLocalPath; if (!localPath.exists()) { localPath.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755); } localPath.append(this.hostName); localPath.createUnique(Ci.nsIFile.DIRECTORY_TYPE, 0o755); this.localPath = localPath; return localPath; } set localPath(localPath) { this.setFileValue("directory-rel", "directory", localPath); } get rootFolder() { if (!this._rootFolder) { this._rootFolder = MailServices.folderLookup.getOrCreateFolderForURL( this.serverURI ); } return this._rootFolder; } get rootMsgFolder() { return this.rootFolder; } get msgStore() { if (!this._msgStore) { let contractId = this.getCharValue("storeContractID"); if (!contractId) { contractId = "@mozilla.org/msgstore/berkeleystore;1"; this.setCharValue("storeContractID", contractId); } // After someone starts using the pluggable store, we can no longer // change the value. this.setBoolValue("canChangeStoreType", false); this._msgStore = Cc[contractId].createInstance(Ci.nsIMsgPluggableStore); } return this._msgStore; } get doBiff() { try { return this._prefs.getBoolPref("check_new_mail"); } catch (e) { return this.protocolInfo.defaultDoBiff; } } set doBiff(value) { let biffManager = Cc["@mozilla.org/messenger/biffManager;1"].getService( Ci.nsIMsgBiffManager ); if (value) { biffManager.addServerBiff(this); } else { biffManager.removeServerBiff(this); } this._prefs.setBoolPref("check_new_mail", value); } /** * type, attribute name, pref name */ _retentionSettingsPrefs = [ ["Int", "retainByPreference", "retainBy"], ["Int", "numHeadersToKeep", "numHdrsToKeep"], ["Int", "daysToKeepHdrs"], ["Int", "daysToKeepBodies"], ["Bool", "cleanupBodiesByDays", "cleanupBodies"], ["Bool", "applyToFlaggedMessages"], ]; get retentionSettings() { let settings = Cc[ "@mozilla.org/msgDatabase/retentionSettings;1" ].createInstance(Ci.nsIMsgRetentionSettings); for (let [type, attrName, prefName] of this._retentionSettingsPrefs) { prefName = prefName || attrName; settings[attrName] = this[`get${type}Value`](prefName); } return settings; } set retentionSettings(settings) { for (let [type, attrName, prefName] of this._retentionSettingsPrefs) { prefName = prefName || attrName; this[`set${type}Value`](prefName, settings[attrName]); } } get spamSettings() { if (!this.getCharValue("spamActionTargetAccount")) { this.setCharValue("spamActionTargetAccount", this.serverURI); } if (!this._spamSettings) { this._spamSettings = Cc[ "@mozilla.org/messenger/spamsettings;1" ].createInstance(Ci.nsISpamSettings); try { this._spamSettings.initialize(this); } catch (e) { console.error(e); } } return this._spamSettings; } get spamFilterPlugin() { if (!this._spamFilterPlugin) { this._spamFilterPlugin = Cc[ "@mozilla.org/messenger/filter-plugin;1?name=bayesianfilter" ].getService(Ci.nsIMsgFilterPlugin); } return this._spamFilterPlugin; } get isDeferredTo() { let account = MailServices.accounts.FindAccountForServer(this); if (!account) { return false; } return MailServices.accounts.allServers.some( server => server.getCharValue("deferred_to_account") == account.key ); } get serverRequiresPasswordForBiff() { return true; } /** * type, attribute name, pref name */ _downloadSettingsPrefs = [ ["Int", "ageLimitOfMsgsToDownload", "ageLimit"], ["Bool", "downloadUnreadOnly"], ["Bool", "downloadByDate"], ]; get downloadSettings() { if (!this._downloadSettings) { this._downloadSettings = Cc[ "@mozilla.org/msgDatabase/downloadSettings;1" ].createInstance(Ci.nsIMsgDownloadSettings); for (let [type, attrName, prefName] of this._downloadSettingsPrefs) { prefName = prefName || attrName; this._downloadSettings[attrName] = this[`get${type}Value`](prefName); } } return this._downloadSettings; } set downloadSettings(settings) { this._downloadSettings = settings; for (let [type, attrName, prefName] of this._downloadSettingsPrefs) { prefName = prefName || attrName; this[`set${type}Value`](prefName, settings[attrName]); } } get offlineSupportLevel() { const OFFLINE_SUPPORT_LEVEL_NONE = 0; const OFFLINE_SUPPORT_LEVEL_UNDEFINED = -1; let level = this.getIntValue("offline_support_level"); return level == OFFLINE_SUPPORT_LEVEL_UNDEFINED ? OFFLINE_SUPPORT_LEVEL_NONE : level; } set offlineSupportLevel(value) { this.setIntValue("offline_support_level", value); } get filterScope() { return Ci.nsMsgSearchScope.offlineMailFilter; } get searchScope() { return Ci.nsMsgSearchScope.offlineMail; } get passwordPromptRequired() { if (!this.serverRequiresPasswordForBiff) { // If the password is not even required for biff we don't need to check // any further. return false; } if (!this.password) { // If the password is empty, check to see if it is stored. this.password = this._getPasswordWithoutUI(); } if (this.password) { return false; } return this.authMethod != Ci.nsMsgAuthMethod.OAuth2; } getCharValue(prefName) { try { return this._prefs.getCharPref(prefName); } catch (e) { return this._defaultPrefs.getCharPref(prefName, ""); } } setCharValue(prefName, value) { let defaultValue = this._defaultPrefs.getCharPref(prefName, ""); if (!value || value == defaultValue) { this._prefs.clearUserPref(prefName); } else { this._prefs.setCharPref(prefName, value); } } getUnicharValue(prefName) { try { return this._prefs.getStringPref(prefName); } catch (e) { return this._defaultPrefs.getStringPref(prefName, ""); } } setUnicharValue(prefName, value) { let defaultValue = this._defaultPrefs.getStringPref(prefName, ""); if (!value || value == defaultValue) { this._prefs.clearUserPref(prefName); } else { this._prefs.setStringPref(prefName, value); } } getIntValue(prefName) { try { return this._prefs.getIntPref(prefName); } catch (e) { return this._defaultPrefs.getIntPref(prefName, 0); } } setIntValue(prefName, value) { let defaultValue = this._defaultPrefs.getIntPref(prefName, value - 1); if (defaultValue == value) { this._prefs.clearUserPref(prefName); } else { this._prefs.setIntPref(prefName, value); } } getBoolValue(prefName) { try { return this._prefs.getBoolPref(prefName); } catch (e) { return this._defaultPrefs.getBoolPref(prefName, false); } } setBoolValue(prefName, value) { let defaultValue = this._defaultPrefs.getBoolPref(prefName, !value); if (defaultValue == value) { this._prefs.clearUserPref(prefName); } else { this._prefs.setBoolPref(prefName, value); } } getFileValue(relPrefName, absPrefName) { try { let file = this._prefs.getComplexValue( relPrefName, Ci.nsIRelativeFilePref ).file; file.normalize(); return file; } catch (e) { try { let file = this._prefs.getComplexValue(absPrefName, Ci.nsIFile); this._prefs.setComplexValue(relPrefName, Ci.nsIRelativeFilePref, { QueryInterface: ChromeUtils.generateQI(["nsIRelativeFilePref"]), file, relativeToKey: "ProfD", }); return file; } catch (e) { return null; } } } setFileValue(relPrefName, absPrefName, file) { this._prefs.setComplexValue(relPrefName, Ci.nsIRelativeFilePref, { QueryInterface: ChromeUtils.generateQI(["nsIRelativeFilePref"]), file, relativeToKey: "ProfD", }); this._prefs.setComplexValue(absPrefName, Ci.nsIFile, file); } onUserOrHostNameChanged(oldValue, newValue, hostnameChanged) { migrateServerUris( this.localStoreType, hostnameChanged ? oldValue : this.hostName, hostnameChanged ? this.username : oldValue, this.hostName, this.username ); this._spamSettings = null; // Clear the clientid because the user or host have changed. this.clientid = ""; let atIndex = newValue.indexOf("@"); if (!this.prettyName || (!hostnameChanged && atIndex != -1)) { // If new username contains @ then better not update the pretty name. return; } atIndex = this.prettyName.indexOf("@"); if ( !hostnameChanged && atIndex != -1 && oldValue == this.prettyName.slice(0, atIndex) ) { // If username changed and the pretty name has the old username before @, // update to the new username. this.prettyName = newValue + this.prettyName.slice(atIndex); } else if ( hostnameChanged && oldValue == this.prettyName.slice(atIndex + 1) ) { // If hostname changed and the pretty name has the old hostname after @, // update to the new hostname. this.prettyName = this.prettyName.slice(0, atIndex + 1) + newValue; } else { // Set the `name` pref anyway, to make tests happy. // eslint-disable-next-line no-self-assign this.prettyName = this.prettyName; } } /** * Try to get the password from nsILoginManager. * * @returns {string} */ _getPasswordWithoutUI() { let serverURI = this._getServerURI(); let logins = Services.logins.findLogins(serverURI, "", serverURI); for (let login of logins) { if (login.username == this.username) { return login.password; } } return null; } getPasswordWithUI(promptMessage, promptTitle) { let password = this._getPasswordWithoutUI(); if (password) { this.password = password; return this.password; } let outUsername = {}; let outPassword = {}; let ok; let authPrompt; try { // This prompt has a checkbox for saving password. authPrompt = Cc["@mozilla.org/messenger/msgAuthPrompt;1"].getService( Ci.nsIAuthPrompt ); } catch (e) { // Often happens in tests. This prompt has no checkbox for saving password. authPrompt = Services.ww.getNewAuthPrompter(null); } if (this.username) { ok = authPrompt.promptPassword( promptTitle, promptMessage, this.serverURI, Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, outPassword ); } else { ok = authPrompt.promptUsernameAndPassword( promptTitle, promptMessage, this.serverURI, Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, outUsername, outPassword ); } if (ok) { if (outUsername.value) { this.username = outUsername.value; } this.password = outPassword.value; } else { throw Components.Exception("Password dialog canceled", Cr.NS_ERROR_ABORT); } return this.password; } forgetPassword() { let serverURI = this._getServerURI(); let logins = Services.logins.findLogins(serverURI, "", serverURI); for (let login of logins) { if (login.username == this.username) { Services.logins.removeLogin(login); } } this.password = ""; } forgetSessionPassword() { this.password = ""; } closeCachedConnections() {} shutdown() { this.closeCachedConnections(); if (this._filterList) { this._filterList.logStream = null; this._filterList = null; } if (this._spamSettings) { this._spamSettings.logStream = null; this._spamSettings = null; } } getFilterList(msgWindow) { if (!this._filterList) { if (!this.rootFolder.filePath.path) { // Happens in tests. return null; } let filterFile = this.rootFolder.filePath.clone(); filterFile.append("msgFilterRules.dat"); try { this._filterList = MailServices.filters.OpenFilterList( filterFile, this.rootFolder, msgWindow ); } catch (e) { console.error(e); const NS_ERROR_FILE_FS_CORRUPTED = 0x80520016; if (e.result == NS_ERROR_FILE_FS_CORRUPTED && filterFile.exists()) { // OpenFilterList will create a new one next time. filterFile.renameTo(filterFile.parent, "msgFilterRules.dat.orig"); } } } return this._filterList; } setFilterList(value) { this._filterList = value; } getEditableFilterList(msgWindow) { if (!this._editableFilterList) { return this.getFilterList(msgWindow); } return this._editableFilterList; } setEditableFilterList(value) { this._editableFilterList = value; } setDefaultLocalPath(value) { this.protocolInfo.setDefaultLocalPath(value); } getNewMessages(folder, msgWindow, urlListener) { folder.getNewMessages(msgWindow, urlListener); } writeToFolderCache(folderCache) { this.rootFolder.writeToFolderCache(folderCache, true); } clearAllValues() { for (let prefName of this._prefs.getChildList("")) { this._prefs.clearUserPref(prefName); } } removeFiles() { if (this.getCharValue("deferred_to_account") || this.isDeferredTo) { throw Components.Exception( "Should not remove files for a deferred account", Cr.NS_ERROR_FAILURE ); } this.localPath.remove(true); } getMsgFolderFromURI(folder, uri) { try { return this.rootMsgFolder.getChildWithURI(uri, true, true) || folder; } catch (e) { return folder; } } isNewHdrDuplicate(newHdr) { // If the message has been partially downloaded, the message should not // be considered a duplicated message. See bug 714090. if (newHdr.flags & Ci.nsMsgMessageFlags.Partial) { return false; } if (!newHdr.subject || !newHdr.messageId) { return false; } let key = `${newHdr.messageId}${newHdr.subject}`; if (this._knownHdrMap.get(key)) { return true; } this._knownHdrMap.set(key, ++this._hdrIndex); const MAX_SIZE = 500; if (this._knownHdrMap.size > MAX_SIZE) { // Release the oldest half of downloaded hdrs. for (let [k, v] of this._knownHdrMap) { if (v < this._hdrIndex - MAX_SIZE / 2) { this._knownHdrMap.delete(k); } else if (this._knownHdrMap.size <= MAX_SIZE / 2) { break; } } } return false; } equals(server) { return this.key == server.key; } _configureTemporaryReturnReceiptsFilter(filterList) { let identity = MailServices.accounts.getFirstIdentityForServer(this); if (!identity) { return; } let incorp = Ci.nsIMsgMdnGenerator.eIncorporateInbox; if (identity.getBoolAttribute("use_custom_prefs")) { incorp = this.getIntValue("incorporate_return_receipt"); } else { incorp = Services.prefs.getIntPref("mail.incorporate.return_receipt"); } let enable = incorp == Ci.nsIMsgMdnGenerator.eIncorporateSent; const FILTER_NAME = "mozilla-temporary-internal-MDN-receipt-filter"; let filter = filterList.getFilterNamed(FILTER_NAME); if (filter) { filter.enabled = enable; return; } else if (!enable || !identity.fccFolder) { return; } filter = filterList.createFilter(FILTER_NAME); if (!filter) { return; } filter.enabled = true; filter.temporary = true; let term = filter.createTerm(); let value = term.value; value.attrib = Ci.nsMsgSearchAttrib.OtherHeader + 1; value.str = "multipart/report"; term.attrib = Ci.nsMsgSearchAttrib.OtherHeader + 1; term.op = Ci.nsMsgSearchOp.Contains; term.booleanAnd = true; term.arbitraryHeader = "Content-Type"; term.value = value; filter.appendTerm(term); term = filter.createTerm(); value = term.value; value.attrib = Ci.nsMsgSearchAttrib.OtherHeader + 1; value.str = "disposition-notification"; term.attrib = Ci.nsMsgSearchAttrib.OtherHeader + 1; term.op = Ci.nsMsgSearchOp.Contains; term.booleanAnd = true; term.arbitraryHeader = "Content-Type"; term.value = value; filter.appendTerm(term); let action = filter.createAction(); action.type = Ci.nsMsgFilterAction.MoveToFolder; action.targetFolderUri = identity.fccFolder; filter.appendAction(action); filterList.insertFilterAt(0, filter); } _configureTemporaryServerSpamFilters(filterList) { let spamSettings = this.spamSettings; if (!spamSettings.useServerFilter) { return; } let serverFilterName = spamSettings.serverFilterName; let serverFilterTrustFlags = spamSettings.serverFilterTrustFlags; if (!serverFilterName || !serverFilterName) { return; } // Check if filters have been setup already. let yesFilterName = `${serverFilterName}Yes`; let noFilterName = `${serverFilterName}No`; let filter = filterList.getFilterNamed(yesFilterName); if (!filter) { filter = filterList.getFilterNamed(noFilterName); } if (filter) { return; } let serverFilterList = MailServices.filters.OpenFilterList( spamSettings.serverFilterFile, null, null ); filter = serverFilterList.getFilterNamed(yesFilterName); if (filter && serverFilterTrustFlags & Ci.nsISpamSettings.TRUST_POSITIVES) { filter.temporary = true; // Check if we're supposed to move junk mail to junk folder; if so, add // filter action to do so. let searchTerms = filter.searchTerms; if (searchTerms.length) { searchTerms[0].beginsGrouping = true; searchTerms.at(-1).endsGrouping = true; } // Create a new term, checking if the user set junk status. The term will // search for junkscoreorigin != "user". let term = filter.createTerm(); term.attrib = Ci.nsMsgSearchAttrib.JunkScoreOrigin; term.op = Ci.nsMsgSearchOp.Isnt; term.booleanAnd = true; let value = term.value; value.attrib = Ci.nsMsgSearchAttrib.JunkScoreOrigin; value.str = "user"; term.value = value; filter.appendTerm(term); if (spamSettings.moveOnSpam) { let spamFolderURI = spamSettings.spamFolderURI; if (spamFolderURI) { let action = filter.createAction(); action.type = Ci.nsMsgFilterAction.MoveToFolder; action.targetFolderUri = spamFolderURI; filter.appendAction(action); } } if (spamSettings.markAsReadOnSpam) { let action = filter.createAction(); action.type = Ci.nsMsgFilterAction.MarkRead; filter.appendAction(action); } filterList.insertFilterAt(0, filter); } filter = serverFilterList.getFilterNamed(noFilterName); if (filter && serverFilterTrustFlags & Ci.nsISpamSettings.TRUST_NEGATIVES) { filter.temporary = true; filterList.insertFilterAt(0, filter); } } configureTemporaryFilters(filterList) { this._configureTemporaryReturnReceiptsFilter(filterList); this._configureTemporaryServerSpamFilters(filterList); } clearTemporaryReturnReceiptsFilter() { if (!this._filterList) { return; } let filter = this._filterList.getFilterNamed( "mozilla-temporary-internal-MDN-receipt-filter" ); if (filter) { this._filterList.removeFilter(filter); } } getForcePropertyEmpty(name) { return this.getCharValue(`${name}.empty`) == "true"; } setForcePropertyEmpty(name, value) { return this.setCharValue(`${name}.empty`, value ? "true" : ""); } performExpand(msgWindow) {} get wrappedJSObject() { return this; } _passwordPromise = null; /** * Show a password prompt. If a prompt is currently shown, just wait for it. * * @param {string} message - The text inside the prompt. * @param {string} title - The title of the prompt. */ async getPasswordWithUIAsync(message, title) { if (this._passwordPromise) { await this._passwordPromise; return this.password; } let deferred = {}; this._passwordPromise = new Promise((resolve, reject) => { deferred.resolve = resolve; deferred.reject = reject; }); try { this.getPasswordWithUI(message, title); } catch (e) { deferred.reject(e); throw e; } finally { this._passwordPromise = null; } deferred.resolve(); return this.password; } }