summaryrefslogtreecommitdiffstats
path: root/mailnews/base/prefs/content/AccountManager.js
diff options
context:
space:
mode:
authorMatt A. Tobin <email@mattatobin.com>2019-11-03 00:17:46 -0400
committerMatt A. Tobin <email@mattatobin.com>2019-11-03 00:17:46 -0400
commit302bf1b523012e11b60425d6eee1221ebc2724eb (patch)
treeb191a895f8716efcbe42f454f37597a545a6f421 /mailnews/base/prefs/content/AccountManager.js
parent21b3f6247403c06f85e1f45d219f87549862198f (diff)
downloadUXP-302bf1b523012e11b60425d6eee1221ebc2724eb.tar
UXP-302bf1b523012e11b60425d6eee1221ebc2724eb.tar.gz
UXP-302bf1b523012e11b60425d6eee1221ebc2724eb.tar.lz
UXP-302bf1b523012e11b60425d6eee1221ebc2724eb.tar.xz
UXP-302bf1b523012e11b60425d6eee1221ebc2724eb.zip
Issue #1258 - Part 1: Import mailnews, ldap, and mork from comm-esr52.9.1
Diffstat (limited to 'mailnews/base/prefs/content/AccountManager.js')
-rw-r--r--mailnews/base/prefs/content/AccountManager.js1628
1 files changed, 1628 insertions, 0 deletions
diff --git a/mailnews/base/prefs/content/AccountManager.js b/mailnews/base/prefs/content/AccountManager.js
new file mode 100644
index 000000000..c5335039b
--- /dev/null
+++ b/mailnews/base/prefs/content/AccountManager.js
@@ -0,0 +1,1628 @@
+/* -*- Mode: C++; tab-width: 4; 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/. */
+
+/*
+ * here's how this dialog works:
+ * The main dialog contains a tree on the left (accounttree) and a
+ * deck on the right. Each card in the deck on the right contains an
+ * IFRAME which loads a particular preference document (such as am-main.xul)
+ *
+ * when the user clicks on items in the tree on the right, two things have
+ * to be determined before the UI can be updated:
+ * - the relevant account
+ * - the relevant page
+ *
+ * when both of these are known, this is what happens:
+ * - every form element of the previous page is saved in the account value
+ * hashtable for the previous account
+ * - the card containing the relevant page is brought to the front
+ * - each form element in the page is filled in with an appropriate value
+ * from the current account's hashtable
+ * - in the IFRAME inside the page, if there is an onInit() method,
+ * it is called. The onInit method can further update this page based
+ * on values set in the previous step.
+ */
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource://gre/modules/BrowserUtils.jsm");
+Components.utils.import("resource:///modules/iteratorUtils.jsm");
+Components.utils.import("resource:///modules/mailServices.js");
+Components.utils.import("resource:///modules/folderUtils.jsm");
+Components.utils.import("resource:///modules/hostnameUtils.jsm");
+
+// If Local directory has changed the app needs to restart. Once this is set
+// a restart will be attempted at each attempt to close the Account manager with OK.
+var gRestartNeeded = false;
+
+// This is a hash-map for every account we've touched in the pane. Each entry
+// has additional maps of attribute-value pairs that we're going to want to save
+// when the user hits OK.
+var accountArray;
+var gGenericAttributeTypes;
+
+var currentAccount;
+var currentPageId;
+
+var pendingAccount;
+var pendingPageId;
+
+/**
+ * This array contains filesystem folders that are deemed inappropriate
+ * for use as the local directory pref for message storage.
+ * It is global to allow extensions to add to/remove from it if needed.
+ * Extentions adding new server types should first consider setting
+ * nsIMsgProtocolInfo(of the server type).defaultLocalPath properly
+ * so that the test will allow that directory automatically.
+ * See the checkLocalDirectoryIsSafe function for description of the members.
+ */
+var gDangerousLocalStorageDirs = [
+ // profile folder
+ { dirsvc: "ProfD", OS: null },
+ // GRE install folder
+ { dirsvc: "GreD", OS: null },
+ // Application install folder
+ { dirsvc: "CurProcD", OS: null },
+ // system temporary folder
+ { dirsvc: "TmpD", OS: null },
+ // Windows system folder
+ { dirsvc: "SysD", OS: "WINNT" },
+ // Windows folder
+ { dirsvc: "WinD", OS: "WINNT" },
+ // Program Files folder
+ { dirsvc: "ProgF", OS: "WINNT" },
+ // trash folder
+ { dirsvc: "Trsh", OS: "Darwin" },
+ // Mac OS system folder
+ { dir: "/System", OS: "Darwin" },
+ // devices folder
+ { dir: "/dev", OS: "Darwin,Linux" },
+ // process info folder
+ { dir: "/proc", OS: "Linux" },
+ // system state folder
+ { dir: "/sys", OS: "Linux" }
+];
+
+// This sets an attribute in a xul element so that we can later
+// know what value to substitute in a prefstring. Different
+// preference types set different attributes. We get the value
+// in the same way as the function getAccountValue() determines it.
+function updateElementWithKeys(account, element, type) {
+ switch (type)
+ {
+ case "identity":
+ element["identitykey"] = account.defaultIdentity.key;
+ break;
+ case "pop3":
+ case "imap":
+ case "nntp":
+ case "server":
+ element["serverkey"] = account.incomingServer.key;
+ break;
+ case "smtp":
+ if (MailServices.smtp.defaultServer)
+ element["serverkey"] = MailServices.smtp.defaultServer.key;
+ break;
+ default:
+// dump("unknown element type! "+type+"\n");
+ }
+}
+
+function hideShowControls(serverType) {
+ let controls = document.querySelectorAll("[hidefor]");
+ for (let controlNo = 0; controlNo < controls.length; controlNo++) {
+ let control = controls[controlNo];
+ let hideFor = control.getAttribute("hidefor");
+
+ // Hide unsupported server types using hideFor="servertype1,servertype2".
+ let hide = false;
+ let hideForTokens = hideFor.split(",");
+ for (let tokenNo = 0; tokenNo < hideForTokens.length; tokenNo++) {
+ if (hideForTokens[tokenNo] == serverType) {
+ hide = true;
+ break;
+ }
+ }
+
+ if (hide)
+ control.setAttribute("hidden", "true");
+ else
+ control.removeAttribute("hidden");
+ }
+}
+
+// called when the whole document loads
+// perform initialization here
+function onLoad() {
+ var selectedServer;
+ var selectPage = null;
+
+ // Arguments can have two properties: (1) "server," the nsIMsgIncomingServer
+ // to select initially and (2) "selectPage," the page for that server to that
+ // should be selected.
+ if ("arguments" in window && window.arguments[0]) {
+ selectedServer = window.arguments[0].server;
+ selectPage = window.arguments[0].selectPage;
+ }
+
+ accountArray = new Object();
+ gGenericAttributeTypes = new Object();
+
+ gAccountTree.load();
+
+ setTimeout(selectServer, 0, selectedServer, selectPage);
+
+ // Make sure the account manager window fits the screen.
+ document.getElementById("accountManager").style.maxHeight =
+ (window.screen.availHeight - 30) + "px";
+}
+
+function onUnload() {
+ gAccountTree.unload();
+}
+
+function selectServer(server, selectPageId)
+{
+ let childrenNode = document.getElementById("account-tree-children");
+
+ // Default to showing the first account.
+ let accountNode = childrenNode.firstChild;
+
+ // Find the tree-node for the account we want to select
+ if (server) {
+ for (let i = 0; i < childrenNode.childNodes.length; i++) {
+ let account = childrenNode.childNodes[i]._account;
+ if (account && server == account.incomingServer) {
+ accountNode = childrenNode.childNodes[i];
+ // Make sure all the panes of the account to be selected are shown.
+ accountNode.setAttribute("open", "true");
+ break;
+ }
+ }
+ }
+
+ let pageToSelect = accountNode;
+
+ if (selectPageId) {
+ // Find the page that also corresponds to this server.
+ // It either is the accountNode itself...
+ let pageId = accountNode.getAttribute("PageTag");
+ if (pageId != selectPageId) {
+ // ... or one of its children.
+ pageToSelect = accountNode.querySelector('[PageTag="' + selectPageId + '"]');
+ }
+ }
+
+ let accountTree = document.getElementById("accounttree");
+ let index = accountTree.contentView.getIndexOfItem(pageToSelect);
+ accountTree.view.selection.select(index);
+ accountTree.treeBoxObject.ensureRowIsVisible(index);
+
+ let lastItem = accountNode.lastChild.lastChild;
+ if (lastItem.localName == "treeitem")
+ index = accountTree.contentView.getIndexOfItem(lastItem);
+
+ accountTree.treeBoxObject.ensureRowIsVisible(index);
+}
+
+function replaceWithDefaultSmtpServer(deletedSmtpServerKey)
+{
+ // First we replace the smtpserverkey in every identity.
+ let am = MailServices.accounts;
+ for (let identity in fixIterator(am.allIdentities,
+ Components.interfaces.nsIMsgIdentity)) {
+ if (identity.smtpServerKey == deletedSmtpServerKey)
+ identity.smtpServerKey = "";
+ }
+
+ // When accounts have already been loaded in the panel then the first
+ // replacement will be overwritten when the accountvalues are written out
+ // from the pagedata. We get the loaded accounts and check to make sure
+ // that the account exists for the accountid and that it has a default
+ // identity associated with it (to exclude smtpservers and local folders)
+ // Then we check only for the identity[type] and smtpServerKey[slot] and
+ // replace that with the default smtpserverkey if necessary.
+
+ for (var accountid in accountArray) {
+ var account = accountArray[accountid]._account;
+ if (account && account.defaultIdentity) {
+ var accountValues = accountArray[accountid];
+ var smtpServerKey = getAccountValue(account, accountValues, "identity",
+ "smtpServerKey", null, false);
+ if (smtpServerKey == deletedSmtpServerKey)
+ setAccountValue(accountValues, "identity", "smtpServerKey", "");
+ }
+ }
+}
+
+/**
+ * Called when OK is clicked on the dialog.
+ *
+ * @param aDoChecks If true, execute checks on data, otherwise hope they
+ * were already done elsewhere and proceed directly to saving
+ * the data.
+ */
+function onAccept(aDoChecks) {
+ if (aDoChecks) {
+ // Check if user/host have been modified correctly.
+ if (!checkUserServerChanges(true))
+ return false;
+
+ if (!checkAccountNameIsValid())
+ return false;
+ }
+
+ if (!onSave())
+ return false;
+
+ // hack hack - save the prefs file NOW in case we crash
+ Services.prefs.savePrefFile(null);
+
+ if (gRestartNeeded) {
+ gRestartNeeded = !BrowserUtils.restartApplication();
+ // returns false so that Account manager is not exited when restart failed
+ return !gRestartNeeded;
+ }
+
+ return true;
+}
+
+/**
+ * See if the given path to a directory is usable on the current OS.
+ *
+ * aLocalPath the nsIFile of a directory to check.
+ */
+function checkDirectoryIsValid(aLocalPath) {
+ // Any directory selected in the file picker already exists.
+ // Any directory specified in prefs.js will be created at start if it does
+ // not exist yet.
+ // If at the time of entering Account Manager the directory does not exist,
+ // it must be invalid in the current OS or not creatable due to permissions.
+ // Even then, the backend sometimes tries to create a new one
+ // under the current profile.
+ if (!aLocalPath.exists() || !aLocalPath.isDirectory())
+ return false;
+
+ if (Services.appinfo.OS == "WINNT") {
+ // Do not allow some special filenames on Windows.
+ // Taken from mozilla/widget/windows/nsDataObj.cpp::MangleTextToValidFilename()
+ let dirLeafName = aLocalPath.leafName;
+ const kForbiddenNames = [
+ "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
+ "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
+ "CON", "PRN", "AUX", "NUL", "CLOCK$" ];
+ if (kForbiddenNames.indexOf(dirLeafName) != -1)
+ return false;
+ }
+
+ // The directory must be readable and writable to work as a mail store.
+ if (!(aLocalPath.isReadable() && aLocalPath.isWritable()))
+ return false;
+
+ return true;
+}
+
+/**
+ * Even if the local path is usable, there are some special folders we do not
+ * want to allow for message storage as they cause problems (see e.g. bug 750781).
+ *
+ * aLocalPath The nsIFile of a directory to check.
+ */
+function checkDirectoryIsAllowed(aLocalPath) {
+ /**
+ * Check if the local path (aLocalPath) is 'safe' i.e. NOT a parent
+ * or subdirectory of the given special system/app directory (aDirToCheck).
+ *
+ * @param aDirToCheck An object describing the special directory.
+ * The object has the following members:
+ * dirsvc : A path keyword to retrieve from the Directory service.
+ * dir : An absolute filesystem path.
+ * Only one of 'dirsvc' or 'dir' can be specified.
+ * OS : A string of comma separated values defining on which
+ * Operating systems the folder is unusable:
+ * null = all
+ * WINNT = Windows
+ * Darwin = OS X
+ * Linux = Linux
+ * safeSubdirs : An array of directory names that are allowed to be used
+ * under the tested directory.
+ * @param aLocalPath An nsIFile of the directory to check, intended for message storage.
+ */
+ function checkLocalDirectoryIsSafe(aDirToCheck, aLocalPath) {
+ if (aDirToCheck.OS) {
+ if (aDirToCheck.OS.split(",").indexOf(Services.appinfo.OS) == -1)
+ return true;
+ }
+
+ let testDir = null;
+ if ("dirsvc" in aDirToCheck) {
+ try {
+ testDir = Services.dirsvc.get(aDirToCheck.dirsvc, Components.interfaces.nsIFile);
+ } catch (e) {
+ Components.utils.reportError("The special folder " + aDirToCheck.dirsvc +
+ " cannot be retrieved on this platform: " + e);
+ }
+
+ if (!testDir)
+ return true;
+ }
+ else if ("dir" in aDirToCheck) {
+ testDir = Components.classes["@mozilla.org/file/local;1"]
+ .createInstance(Components.interfaces.nsIFile);
+ testDir.initWithPath(aDirToCheck.dir);
+ if (!testDir.exists())
+ return true;
+ } else {
+ Components.utils.reportError("No directory to check?");
+ return true;
+ }
+
+ testDir.normalize();
+
+ if (testDir.equals(aLocalPath) || aLocalPath.contains(testDir))
+ return false;
+
+ if (testDir.contains(aLocalPath)) {
+ if (!("safeSubdirs" in aDirToCheck))
+ return false;
+
+ // While the tested directory may not be safe,
+ // a subdirectory of some safe subdirectories may be fine.
+ let isInSubdir = false;
+ for (let subDir of aDirToCheck.safeSubdirs) {
+ let checkDir = testDir.clone();
+ checkDir.append(subDir);
+ if (checkDir.contains(aLocalPath)) {
+ isInSubdir = true;
+ break;
+ }
+ }
+ return isInSubdir;
+ }
+
+ return true;
+ } // end of checkDirectoryIsNotSpecial
+
+ // If the server type has a nsIMsgProtocolInfo.defaultLocalPath set,
+ // allow that directory.
+ if (currentAccount.incomingServer) {
+ try {
+ let defaultPath = currentAccount.incomingServer.protocolInfo.defaultLocalPath;
+ if (defaultPath) {
+ defaultPath.normalize();
+ if (defaultPath.contains(aLocalPath))
+ return true;
+ }
+ } catch (e) { /* No problem if this fails. */ }
+ }
+
+ for (let tryDir of gDangerousLocalStorageDirs) {
+ if (!checkLocalDirectoryIsSafe(tryDir, aLocalPath))
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Check if the specified directory does meet all the requirements
+ * for safe mail storage.
+ *
+ * aLocalPath the nsIFile of a directory to check.
+ */
+function checkDirectoryIsUsable(aLocalPath) {
+ const kAlertTitle = document.getElementById("bundle_prefs")
+ .getString("prefPanel-server");
+ const originalPath = aLocalPath;
+
+ let invalidPath = false;
+ try{
+ aLocalPath.normalize();
+ } catch (e) { invalidPath = true; }
+
+ if (invalidPath || !checkDirectoryIsValid(aLocalPath)) {
+ let alertString = document.getElementById("bundle_prefs")
+ .getFormattedString("localDirectoryInvalid",
+ [originalPath.path]);
+ Services.prompt.alert(window, kAlertTitle, alertString);
+ return false;
+ }
+
+ if (!checkDirectoryIsAllowed(aLocalPath)) {
+ let alertNotAllowed = document.getElementById("bundle_prefs")
+ .getFormattedString("localDirectoryNotAllowed",
+ [originalPath.path]);
+ Services.prompt.alert(window, kAlertTitle, alertNotAllowed);
+ return false;
+ }
+
+ // Check that no other account has this same or dependent local directory.
+ let allServers = MailServices.accounts.allServers;
+
+ for (let server in fixIterator(allServers,
+ Components.interfaces.nsIMsgIncomingServer))
+ {
+ if (server.key == currentAccount.incomingServer.key)
+ continue;
+
+ let serverPath = server.localPath;
+ try {
+ serverPath.normalize();
+ let alertStringID = null;
+ if (serverPath.equals(aLocalPath))
+ alertStringID = "directoryAlreadyUsedByOtherAccount";
+ else if (serverPath.contains(aLocalPath))
+ alertStringID = "directoryParentUsedByOtherAccount";
+ else if (aLocalPath.contains(serverPath))
+ alertStringID = "directoryChildUsedByOtherAccount";
+
+ if (alertStringID) {
+ let alertString = document.getElementById("bundle_prefs")
+ .getFormattedString(alertStringID,
+ [server.prettyName]);
+
+ Services.prompt.alert(window, kAlertTitle, alertString);
+ return false;
+ }
+ } catch (e) {
+ // The other account's path is seriously broken, so we can't compare it.
+ Components.utils.reportError("The Local Directory path of the account " +
+ server.prettyName + " seems invalid.");
+ }
+ }
+
+ return true;
+}
+
+/**
+ * Check if the user and/or host names have been changed and if so check
+ * if the new names already exists for an account or are empty.
+ * Also check if the Local Directory path was changed.
+ *
+ * @param showAlert show and alert if a problem with the host / user name is found
+ */
+function checkUserServerChanges(showAlert) {
+ const prefBundle = document.getElementById("bundle_prefs");
+ const alertTitle = prefBundle.getString("prefPanel-server");
+ var alertText = null;
+
+ var accountValues = getValueArrayFor(currentAccount);
+ if (!accountValues)
+ return true;
+
+ let currentServer = currentAccount ? currentAccount.incomingServer : null;
+
+ // If this type doesn't exist (just removed) then return.
+ if (!("server" in accountValues) || !accountValues["server"])
+ return true;
+
+ // Get the new username, hostname and type from the page.
+ var typeElem = getPageFormElement("server.type");
+ var hostElem = getPageFormElement("server.realHostName");
+ var userElem = getPageFormElement("server.realUsername");
+ if (typeElem && userElem && hostElem) {
+ var newType = getFormElementValue(typeElem);
+ var oldHost = getAccountValue(currentAccount, accountValues, "server", "realHostName",
+ null, false);
+ var newHost = getFormElementValue(hostElem);
+ var oldUser = getAccountValue(currentAccount, accountValues, "server", "realUsername",
+ null, false);
+
+ var newUser = getFormElementValue(userElem);
+ var checkUser = true;
+ // There is no username needed for e.g. news so reset it.
+ if (currentServer && !currentServer.protocolInfo.requiresUsername) {
+ oldUser = newUser = "";
+ checkUser = false;
+ }
+ alertText = null;
+ // If something is changed then check if the new user/host already exists.
+ if ((oldUser != newUser) || (oldHost != newHost)) {
+ newUser = newUser.trim();
+ newHost = cleanUpHostName(newHost);
+ if (checkUser && (newUser == "")) {
+ alertText = prefBundle.getString("userNameEmpty");
+ }
+ else if (!isLegalHostNameOrIP(newHost)) {
+ alertText = prefBundle.getString("enterValidServerName");
+ }
+ else {
+ let sameServer = MailServices.accounts
+ .findRealServer(newUser, newHost, newType, 0);
+ if (sameServer && (sameServer != currentServer)) {
+ alertText = prefBundle.getString("modifiedAccountExists");
+ } else {
+ // New hostname passed all checks. We may have cleaned it up so set
+ // the new value back into the input element.
+ setFormElementValue(hostElem, newHost);
+ }
+ }
+
+ if (alertText) {
+ if (showAlert)
+ Services.prompt.alert(window, alertTitle, alertText);
+ // Restore the old values before return
+ if (checkUser)
+ setFormElementValue(userElem, oldUser);
+ setFormElementValue(hostElem, oldHost);
+ // If no message is shown to the user, silently revert the values
+ // and consider the check a success.
+ return !showAlert;
+ }
+
+ // If username is changed remind users to change Your Name and Email Address.
+ // If server name is changed and has defined filters then remind users
+ // to edit rules.
+ if (showAlert) {
+ let filterList;
+ if (currentServer && checkUser) {
+ filterList = currentServer.getEditableFilterList(null);
+ }
+ let changeText = "";
+ if ((oldHost != newHost) &&
+ (filterList != undefined) && filterList.filterCount)
+ changeText = prefBundle.getString("serverNameChanged");
+ // In the event that oldHost == newHost or oldUser == newUser,
+ // the \n\n will be trimmed off before the message is shown.
+ if (oldUser != newUser)
+ changeText = changeText + "\n\n" + prefBundle.getString("userNameChanged");
+
+ if (changeText != "")
+ Services.prompt.alert(window, alertTitle, changeText.trim());
+ }
+ }
+ }
+
+ // Check the new value of the server.localPath field for validity.
+ var pathElem = getPageFormElement("server.localPath");
+ if (!pathElem)
+ return true;
+
+ if (!checkDirectoryIsUsable(getFormElementValue(pathElem))) {
+// return false; // Temporarily disable this. Just show warning but do not block. See bug 921371.
+ Components.utils.reportError("Local directory '" +
+ getFormElementValue(pathElem).path + "' of account " +
+ currentAccount.key + " is not safe to use. Consider changing it.");
+ }
+
+ // Warn if the Local directory path was changed.
+ // This can be removed once bug 2654 is fixed.
+ let oldLocalDir = getAccountValue(currentAccount, accountValues, "server", "localPath",
+ null, false); // both return nsIFile
+ let newLocalDir = getFormElementValue(pathElem);
+ if (oldLocalDir && newLocalDir && (oldLocalDir.path != newLocalDir.path)) {
+ let brandName = document.getElementById("bundle_brand").getString("brandShortName");
+ alertText = prefBundle.getFormattedString("localDirectoryChanged", [brandName]);
+
+ let cancel = Services.prompt.confirmEx(window, alertTitle, alertText,
+ (Services.prompt.BUTTON_POS_0 * Services.prompt.BUTTON_TITLE_IS_STRING) +
+ (Services.prompt.BUTTON_POS_1 * Services.prompt.BUTTON_TITLE_CANCEL),
+ prefBundle.getString("localDirectoryRestart"), null, null, null, {});
+ if (cancel) {
+ setFormElementValue(pathElem, oldLocalDir);
+ return false;
+ }
+ gRestartNeeded = true;
+ }
+
+ return true;
+}
+
+/**
+ * If account name is not valid, alert the user.
+ */
+function checkAccountNameIsValid() {
+ if (!currentAccount)
+ return true;
+
+ const prefBundle = document.getElementById("bundle_prefs");
+ let alertText = null;
+
+ let serverNameElem = getPageFormElement("server.prettyName");
+ if (serverNameElem) {
+ let accountName = getFormElementValue(serverNameElem);
+
+ if (!accountName)
+ alertText = prefBundle.getString("accountNameEmpty");
+ else if (accountNameExists(accountName, currentAccount.key))
+ alertText = prefBundle.getString("accountNameExists");
+
+ if (alertText) {
+ const alertTitle = prefBundle.getString("accountWizard");
+ Services.prompt.alert(window, alertTitle, alertText);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+function onSave() {
+ if (pendingPageId) {
+ dump("ERROR: " + pendingPageId + " hasn't loaded yet! Not saving.\n");
+ return false;
+ }
+
+ // make sure the current visible page is saved
+ savePage(currentAccount);
+
+ for (var accountid in accountArray) {
+ var accountValues = accountArray[accountid];
+ var account = accountArray[accountid]._account;
+ if (!saveAccount(accountValues, account))
+ return false;
+ }
+
+ return true;
+}
+
+function onAddAccount() {
+ MsgAccountWizard();
+}
+
+function AddMailAccount()
+{
+ NewMailAccount(MailServices.mailSession.topmostMsgWindow);
+}
+
+function AddIMAccount()
+{
+ window.openDialog("chrome://messenger/content/chat/imAccountWizard.xul",
+ "", "chrome,modal,titlebar,centerscreen");
+}
+
+/**
+ * Highlight the default account row in the account tree,
+ * optionally un-highlight the previous one.
+ *
+ * @param newDefault The account that has become the new default.
+ * Can be given as null if there is none.
+ * @param oldDefault The account that has stopped being the default.
+ * Can be given as null if there was none.
+ */
+function markDefaultServer(newDefault, oldDefault) {
+ if (oldDefault == newDefault)
+ return;
+
+ let accountTreeNodes = document.getElementById("account-tree-children")
+ .childNodes;
+ for (let i = 0; i < accountTreeNodes.length; i++) {
+ let accountNode = accountTreeNodes[i];
+ if (newDefault && newDefault == accountNode._account) {
+ let props = accountNode.firstChild.firstChild.getAttribute("properties");
+ accountNode.firstChild.firstChild
+ .setAttribute("properties", props + " isDefaultServer-true");
+ }
+ if (oldDefault && oldDefault == accountNode._account) {
+ let props = accountNode.firstChild.firstChild.getAttribute("properties");
+ props = props.replace(/isDefaultServer-true/, "");
+ accountNode.firstChild.firstChild.setAttribute("properties", props);
+ }
+ }
+}
+
+/**
+ * Make currentAccount (currently selected in the account tree) the default one.
+ */
+function onSetDefault(event) {
+ // Make sure this function was not called while the control item is disabled
+ if (event.target.getAttribute("disabled") == "true")
+ return;
+
+ let previousDefault = getDefaultAccount();
+ MailServices.accounts.defaultAccount = currentAccount;
+ markDefaultServer(currentAccount, previousDefault);
+
+ // This is only needed on Seamonkey which has this button.
+ setEnabled(document.getElementById("setDefaultButton"), false);
+}
+
+function onRemoveAccount(event) {
+ if (event.target.getAttribute("disabled") == "true" || !currentAccount)
+ return;
+
+ let server = currentAccount.incomingServer;
+
+ let canDelete = server.protocolInfo.canDelete || server.canDelete;
+ if (!canDelete)
+ return;
+
+ let serverList = [];
+ let accountTreeNode = document.getElementById("account-tree-children");
+ // build the list of servers in the account tree (order is important)
+ for (let i = 0; i < accountTreeNode.childNodes.length; i++) {
+ if ("_account" in accountTreeNode.childNodes[i]) {
+ let curServer = accountTreeNode.childNodes[i]._account.incomingServer;
+ if (serverList.indexOf(curServer) == -1)
+ serverList.push(curServer);
+ }
+ }
+
+ // get position of the current server in the server list
+ let serverIndex = serverList.indexOf(server);
+
+ // After the current server is deleted, choose the next server/account,
+ // or the previous one if the last one was deleted.
+ if (serverIndex == serverList.length - 1)
+ serverIndex--;
+ else
+ serverIndex++;
+
+ // Need to save these before the account and its server is removed.
+ let serverId = server.serverURI;
+
+ // Confirm account deletion.
+ let removeArgs = { server: server, account: currentAccount,
+ result: false };
+
+ window.openDialog("chrome://messenger/content/removeAccount.xul",
+ "removeAccount",
+ "chrome,titlebar,modal,centerscreen,resizable=no",
+ removeArgs);
+
+ // If result is true, the account was removed.
+ if (!removeArgs.result)
+ return;
+
+ // clear cached data out of the account array
+ currentAccount = currentPageId = null;
+ if (serverId in accountArray) {
+ delete accountArray[serverId];
+ }
+
+ if ((serverIndex >= 0) && (serverIndex < serverList.length))
+ selectServer(serverList[serverIndex], null);
+
+ // Either the default account was deleted so there is a new one
+ // or the default account was not changed. Either way, there is
+ // no need to unmark the old one.
+ markDefaultServer(getDefaultAccount(), null);
+}
+
+function saveAccount(accountValues, account)
+{
+ var identity = null;
+ var server = null;
+
+ if (account) {
+ identity = account.defaultIdentity;
+ server = account.incomingServer;
+ }
+
+ for (var type in accountValues) {
+ var typeArray = accountValues[type];
+
+ for (var slot in typeArray) {
+ var dest;
+ try {
+ if (type == "identity")
+ dest = identity;
+ else if (type == "server")
+ dest = server;
+ else if (type == "pop3")
+ dest = server.QueryInterface(Components.interfaces.nsIPop3IncomingServer);
+ else if (type == "imap")
+ dest = server.QueryInterface(Components.interfaces.nsIImapIncomingServer);
+ else if (type == "none")
+ dest = server.QueryInterface(Components.interfaces.nsINoIncomingServer);
+ else if (type == "nntp")
+ dest = server.QueryInterface(Components.interfaces.nsINntpIncomingServer);
+ else if (type == "smtp")
+ dest = MailServices.smtp.defaultServer;
+
+ } catch (ex) {
+ // don't do anything, just means we don't support that
+ }
+ if (dest == undefined) continue;
+
+ if ((type in gGenericAttributeTypes) && (slot in gGenericAttributeTypes[type])) {
+ var methodName = "get";
+ switch (gGenericAttributeTypes[type][slot]) {
+ case "int":
+ methodName += "Int";
+ break;
+ case "wstring":
+ methodName += "Unichar";
+ break;
+ case "string":
+ methodName += "Char";
+ break;
+ case "bool":
+ // in some cases
+ // like for radiogroups of type boolean
+ // the value will be "false" instead of false
+ // we need to convert it.
+ if (typeArray[slot] == "false")
+ typeArray[slot] = false;
+ else if (typeArray[slot] == "true")
+ typeArray[slot] = true;
+
+ methodName += "Bool";
+ break;
+ default:
+ dump("unexpected preftype: " + preftype + "\n");
+ break;
+ }
+ methodName += ((methodName + "Value") in dest ? "Value" : "Attribute");
+ if (dest[methodName](slot) != typeArray[slot]) {
+ methodName = methodName.replace("get", "set");
+ dest[methodName](slot, typeArray[slot]);
+ }
+ }
+ else if (slot in dest && typeArray[slot] != undefined && dest[slot] != typeArray[slot]) {
+ try {
+ dest[slot] = typeArray[slot];
+ } catch (ex) {
+ // hrm... need to handle special types here
+ }
+ }
+ }
+ }
+
+ // if we made account changes to the spam settings, we'll need to re-initialize
+ // our settings object
+ if (server && server.spamSettings) {
+ try {
+ server.spamSettings.initialize(server);
+ } catch(e) {
+ let accountName = getAccountValue(account, getValueArrayFor(account), "server",
+ "prettyName", null, false);
+ let alertText = document.getElementById("bundle_prefs")
+ .getFormattedString("junkSettingsBroken", [accountName]);
+ let review = Services.prompt.confirmEx(window, null, alertText,
+ (Services.prompt.BUTTON_POS_0 * Services.prompt.BUTTON_TITLE_YES) +
+ (Services.prompt.BUTTON_POS_1 * Services.prompt.BUTTON_TITLE_NO),
+ null, null, null, null, {});
+ if (!review) {
+ onAccountTreeSelect("am-junk.xul", account);
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+/**
+ * Set enabled/disabled state for account actions buttons.
+ * Called by all apps, but if the buttons do not exist, exits early.
+ */
+function updateButtons(tree, account) {
+ let addAccountButton = document.getElementById("addAccountButton");
+ let removeButton = document.getElementById("removeButton");
+ let setDefaultButton = document.getElementById("setDefaultButton");
+
+ if (!addAccountButton && !removeButton && !setDefaultButton)
+ return; // Thunderbird isn't using these.
+
+ updateItems(tree, account, addAccountButton, setDefaultButton, removeButton);
+ updateBlockedItems([addAccountButton, setDefaultButton, removeButton], false);
+}
+
+/**
+ * Set enabled/disabled state for the actions in the Account Actions menu.
+ * Called only by Thunderbird.
+ */
+function initAccountActionsButtons(menupopup) {
+ if (!Services.prefs.getBoolPref("mail.chat.enabled"))
+ document.getElementById("accountActionsAddIMAccount").hidden = true;
+
+ updateItems(
+ document.getElementById("accounttree"),
+ getCurrentAccount(),
+ document.getElementById("accountActionsAddMailAccount"),
+ document.getElementById("accountActionsDropdownSetDefault"),
+ document.getElementById("accountActionsDropdownRemove"));
+
+ updateBlockedItems(menupopup.childNodes, true);
+}
+
+/**
+ * Determine enabled/disabled state for the passed in elements
+ * representing account actions.
+ */
+function updateItems(tree, account, addAccountItem, setDefaultItem, removeItem) {
+ // Start with items disabled and then find out what can be enabled.
+ let canSetDefault = false;
+ let canDelete = false;
+
+ if (account && (tree.view.selection.count >= 1)) {
+ // Only try to check properties if there was anything selected in the tree
+ // and it belongs to an account.
+ // Otherwise we have either selected a SMTP server, or there is some
+ // problem. Either way, we don't want the user to act on it.
+ let server = account.incomingServer;
+
+ if (account != getDefaultAccount() &&
+ server.canBeDefaultServer && account.identities.length > 0)
+ canSetDefault = true;
+
+ canDelete = server.protocolInfo.canDelete || server.canDelete;
+ }
+
+ setEnabled(addAccountItem, true);
+ setEnabled(setDefaultItem, canSetDefault);
+ setEnabled(removeItem, canDelete);
+}
+
+/**
+ * Disable buttons/menu items if their control preference is locked.
+ * SeaMonkey: Not currently handled by WSM or the main loop yet
+ * since these buttons aren't under the IFRAME.
+ *
+ * @param aItems an array or NodeList of elements to be checked
+ * @param aMustBeTrue if true then the pref must be boolean and set to true
+ * to trigger the disabling (TB requires this, SM not)
+ */
+function updateBlockedItems(aItems, aMustBeTrue) {
+ for (let item of aItems) {
+ let prefstring = item.getAttribute("prefstring");
+ if (!prefstring)
+ continue;
+
+ if (Services.prefs.prefIsLocked(prefstring) &&
+ (!aMustBeTrue || Services.prefs.getBoolPref(prefstring)))
+ item.setAttribute("disabled", true);
+ }
+}
+
+/**
+ * Set enabled/disabled state for the control.
+ */
+function setEnabled(control, enabled)
+{
+ if (!control)
+ return;
+
+ if (enabled)
+ control.removeAttribute("disabled");
+ else
+ control.setAttribute("disabled", true);
+}
+
+// Called when someone clicks on an account. Figure out context by what they
+// clicked on. This is also called when an account is removed. In this case,
+// nothing is selected.
+function onAccountTreeSelect(pageId, account)
+{
+ let tree = document.getElementById("accounttree");
+
+ let changeView = pageId && account;
+ if (!changeView) {
+ if (tree.view.selection.count < 1)
+ return false;
+
+ let node = tree.contentView.getItemAtIndex(tree.currentIndex);
+ account = ("_account" in node) ? node._account : null;
+
+ pageId = node.getAttribute("PageTag")
+ }
+
+ if (pageId == currentPageId && account == currentAccount)
+ return true;
+
+ if (document.getElementById("contentFrame").contentDocument.getElementById("server.localPath")) {
+ // Check if user/host names have been changed or the Local Directory is invalid.
+ if (!checkUserServerChanges(false)) {
+ changeView = true;
+ account = currentAccount;
+ pageId = currentPageId;
+ }
+
+ if (gRestartNeeded)
+ onAccept(false);
+ }
+
+ if (document.getElementById("contentFrame").contentDocument.getElementById("server.prettyName")) {
+ // Check if account name is valid.
+ if (!checkAccountNameIsValid()) {
+ changeView = true;
+ account = currentAccount;
+ pageId = currentPageId;
+ }
+ }
+
+ if (currentPageId) {
+ // Change focus to the account tree first so that any 'onchange' handlers
+ // on elements in the current page have a chance to run before the page
+ // is saved and replaced by the new one.
+ tree.focus();
+ }
+
+ // save the previous page
+ savePage(currentAccount);
+
+ let changeAccount = (account != currentAccount);
+
+ if (changeView)
+ selectServer(account.incomingServer, pageId);
+
+ if (pageId != currentPageId) {
+ // loading a complete different page
+
+ // prevent overwriting with bad stuff
+ currentAccount = currentPageId = null;
+
+ pendingAccount = account;
+ pendingPageId = pageId;
+ loadPage(pageId);
+ } else if (changeAccount) {
+ // same page, different server
+ restorePage(pageId, account);
+ }
+
+ if (changeAccount)
+ updateButtons(tree, account);
+
+ return true;
+}
+
+// page has loaded
+function onPanelLoaded(pageId) {
+ if (pageId != pendingPageId) {
+ // if we're reloading the current page, we'll assume the
+ // page has asked itself to be completely reloaded from
+ // the prefs. to do this, clear out the the old entry in
+ // the account data, and then restore theh page
+ if (pageId == currentPageId) {
+ var serverId = currentAccount ?
+ currentAccount.incomingServer.serverURI : "global"
+ delete accountArray[serverId];
+ restorePage(currentPageId, currentAccount);
+ }
+ } else {
+ restorePage(pendingPageId, pendingAccount);
+ }
+
+ // probably unnecessary, but useful for debugging
+ pendingAccount = null;
+ pendingPageId = null;
+}
+
+function pageURL(pageId)
+{
+ // If we have a special non account manager pane (e.g. about:blank),
+ // do not translate it into ChromePackageName URL.
+ if (!pageId.startsWith("am-"))
+ return pageId;
+
+ let chromePackageName;
+ try {
+ // we could compare against "main","server","copies","offline","addressing",
+ // "smtp" and "advanced" first to save the work, but don't,
+ // as some of these might be turned into extensions (for thunderbird)
+ let packageName = pageId.split("am-")[1].split(".xul")[0];
+ chromePackageName = MailServices.accounts.getChromePackageName(packageName);
+ }
+ catch (ex) {
+ chromePackageName = "messenger";
+ }
+ return "chrome://" + chromePackageName + "/content/" + pageId;
+}
+
+function loadPage(pageId)
+{
+ const LOAD_FLAGS_NONE = Components.interfaces.nsIWebNavigation.LOAD_FLAGS_NONE;
+ document.getElementById("contentFrame").webNavigation.loadURI(pageURL(pageId),
+ LOAD_FLAGS_NONE, null, null, null);
+}
+
+// save the values of the widgets to the given server
+function savePage(account)
+{
+ if (!account)
+ return;
+
+ // tell the page that it's about to save
+ if ("onSave" in top.frames["contentFrame"])
+ top.frames["contentFrame"].onSave();
+
+ var accountValues = getValueArrayFor(account);
+ if (!accountValues)
+ return;
+
+ var pageElements = getPageFormElements();
+ if (!pageElements)
+ return;
+
+ // store the value in the account
+ for (let i = 0; i < pageElements.length; i++) {
+ if (pageElements[i].id) {
+ let vals = pageElements[i].id.split(".");
+ if (vals.length >= 2) {
+ let type = vals[0];
+ let slot = pageElements[i].id.slice(type.length + 1);
+
+ setAccountValue(accountValues,
+ type, slot,
+ getFormElementValue(pageElements[i]));
+ }
+ }
+ }
+}
+
+function setAccountValue(accountValues, type, slot, value) {
+ if (!(type in accountValues))
+ accountValues[type] = new Object();
+
+ accountValues[type][slot] = value;
+}
+
+function getAccountValue(account, accountValues, type, slot, preftype, isGeneric) {
+ if (!(type in accountValues))
+ accountValues[type] = new Object();
+
+ // fill in the slot from the account if necessary
+ if (!(slot in accountValues[type]) || accountValues[type][slot] == undefined) {
+ var server;
+ if (account)
+ server= account.incomingServer;
+ var source = null;
+ try {
+ if (type == "identity")
+ source = account.defaultIdentity;
+ else if (type == "server")
+ source = account.incomingServer;
+ else if (type == "pop3")
+ source = server.QueryInterface(Components.interfaces.nsIPop3IncomingServer);
+ else if (type == "imap")
+ source = server.QueryInterface(Components.interfaces.nsIImapIncomingServer);
+ else if (type == "none")
+ source = server.QueryInterface(Components.interfaces.nsINoIncomingServer);
+ else if (type == "nntp")
+ source = server.QueryInterface(Components.interfaces.nsINntpIncomingServer);
+ else if (type == "smtp")
+ source = MailServices.smtp.defaultServer;
+ } catch (ex) {
+ }
+
+ if (source) {
+ if (isGeneric) {
+ if (!(type in gGenericAttributeTypes))
+ gGenericAttributeTypes[type] = new Object();
+
+ // we need the preftype later, for setting when we save.
+ gGenericAttributeTypes[type][slot] = preftype;
+ var methodName = "get";
+ switch (preftype) {
+ case "int":
+ methodName += "Int";
+ break;
+ case "wstring":
+ methodName += "Unichar";
+ break;
+ case "string":
+ methodName += "Char";
+ break;
+ case "bool":
+ methodName += "Bool";
+ break;
+ default:
+ dump("unexpected preftype: " + preftype + "\n");
+ break;
+ }
+ methodName += ((methodName + "Value") in source ? "Value" : "Attribute");
+ accountValues[type][slot] = source[methodName](slot);
+ }
+ else if (slot in source) {
+ accountValues[type][slot] = source[slot];
+ } else {
+ accountValues[type][slot] = null;
+ }
+ }
+ else {
+ accountValues[type][slot] = null;
+ }
+ }
+ return accountValues[type][slot];
+}
+
+// restore the values of the widgets from the given server
+function restorePage(pageId, account)
+{
+ if (!account)
+ return;
+
+ var accountValues = getValueArrayFor(account);
+ if (!accountValues)
+ return;
+
+ if ("onPreInit" in top.frames["contentFrame"])
+ top.frames["contentFrame"].onPreInit(account, accountValues);
+
+ var pageElements = getPageFormElements();
+ if (!pageElements)
+ return;
+
+ // restore the value from the account
+ for (let i = 0; i < pageElements.length; i++) {
+ if (pageElements[i].id) {
+ let vals = pageElements[i].id.split(".");
+ if (vals.length >= 2) {
+ let type = vals[0];
+ let slot = pageElements[i].id.slice(type.length + 1);
+
+ // buttons are lockable, but don't have any data so we skip that part.
+ // elements that do have data, we get the values at poke them in.
+ if (pageElements[i].localName != "button") {
+ var value = getAccountValue(account, accountValues, type, slot, pageElements[i].getAttribute("preftype"), (pageElements[i].getAttribute("genericattr") == "true"));
+ setFormElementValue(pageElements[i], value);
+ }
+ var element = pageElements[i];
+ switch (type) {
+ case "identity":
+ element["identitykey"] = account.defaultIdentity.key;
+ break;
+ case "pop3":
+ case "imap":
+ case "nntp":
+ case "server":
+ element["serverkey"] = account.incomingServer.key;
+ break;
+ case "smtp":
+ if (MailServices.smtp.defaultServer)
+ element["serverkey"] = MailServices.smtp.defaultServer.key;
+ break;
+ }
+ var isLocked = getAccountValueIsLocked(pageElements[i]);
+ setEnabled(pageElements[i], !isLocked);
+ }
+ }
+ }
+
+ // tell the page that new values have been loaded
+ if ("onInit" in top.frames["contentFrame"])
+ top.frames["contentFrame"].onInit(pageId, account.incomingServer.serverURI);
+
+ // everything has succeeded, vervied by setting currentPageId
+ currentPageId = pageId;
+ currentAccount = account;
+}
+
+/**
+ * Gets the value of a widget in current the account settings page,
+ * automatically setting the right property of it depending on element type.
+ *
+ * @param formElement A XUL input element.
+ */
+function getFormElementValue(formElement) {
+ try {
+ var type = formElement.localName;
+ if (type == "checkbox") {
+ if (formElement.getAttribute("reversed"))
+ return !formElement.checked;
+ return formElement.checked;
+ }
+ if (type == "textbox" &&
+ formElement.getAttribute("datatype") == "nsIFile") {
+ if (formElement.value) {
+ let localfile = Components.classes["@mozilla.org/file/local;1"]
+ .createInstance(Components.interfaces.nsIFile);
+
+ localfile.initWithPath(formElement.value);
+ return localfile;
+ }
+ return null;
+ }
+ if ((type == "textbox") || ("value" in formElement)) {
+ return formElement.value;
+ }
+ return null;
+ }
+ catch (ex) {
+ Components.utils.reportError("getFormElementValue failed, ex=" + ex + "\n");
+ }
+ return null;
+}
+
+/**
+ * Sets the value of a widget in current the account settings page,
+ * automatically setting the right property of it depending on element type.
+ *
+ * @param formElement A XUL input element.
+ * @param value The value to store in the element.
+ */
+function setFormElementValue(formElement, value) {
+ var type = formElement.localName;
+ if (type == "checkbox") {
+ if (value == null) {
+ formElement.checked = false;
+ } else {
+ if (formElement.getAttribute("reversed"))
+ formElement.checked = !value;
+ else
+ formElement.checked = value;
+ }
+ }
+ else if (type == "radiogroup" || type == "menulist") {
+ if (value == null)
+ formElement.selectedIndex = 0;
+ else
+ formElement.value = value;
+ }
+ // handle nsIFile
+ else if (type == "textbox" &&
+ formElement.getAttribute("datatype") == "nsIFile") {
+ if (value) {
+ let localfile = value.QueryInterface(Components.interfaces.nsIFile);
+ try {
+ formElement.value = localfile.path;
+ } catch (ex) {
+ dump("Still need to fix uninitialized nsIFile problem!\n");
+ }
+ } else {
+ formElement.value = "";
+ }
+ }
+ else if (type == "textbox") {
+ if (value == null)
+ formElement.value = null;
+ else
+ formElement.value = value;
+ }
+ else if (type == "label") {
+ formElement.value = value || "";
+ }
+ // let the form figure out what to do with it
+ else {
+ if (value == null)
+ formElement.value = null;
+ else
+ formElement.value = value;
+ }
+}
+
+//
+// conversion routines - get data associated
+// with a given pageId, serverId, etc
+//
+
+// helper routine for account manager panels to get the current account for the selected server
+function getCurrentAccount()
+{
+ return currentAccount;
+}
+
+/**
+ * Returns the default account without throwing exception if there is none.
+ * The account manager can be opened even if there are no account yet.
+ */
+function getDefaultAccount() {
+ try {
+ return MailServices.accounts.defaultAccount;
+ } catch (e) {
+ return null; // No default account yet.
+ }
+}
+
+/**
+ * Get the array of persisted form elements for the given page.
+ */
+function getPageFormElements() {
+ // Uses getElementsByAttribute() which returns a live NodeList which is usually
+ // faster than e.g. querySelector().
+ if ("getElementsByAttribute" in top.frames["contentFrame"].document)
+ return top.frames["contentFrame"].document
+ .getElementsByAttribute("wsm_persist", "true");
+
+ return null;
+}
+
+/**
+ * Get a single persisted form element in the current page.
+ *
+ * @param aId ID of the element requested.
+ */
+function getPageFormElement(aId) {
+ let elem = top.frames["contentFrame"].document.getElementById(aId);
+ if (elem && (elem.getAttribute("wsm_persist") == "true"))
+ return elem;
+
+ return null;
+}
+
+// get the value array for the given account
+function getValueArrayFor(account) {
+ var serverId = account ? account.incomingServer.serverURI : "global";
+
+ if (!(serverId in accountArray)) {
+ accountArray[serverId] = new Object();
+ accountArray[serverId]._account = account;
+ }
+
+ return accountArray[serverId];
+}
+
+var gAccountTree = {
+ load: function at_load() {
+ this._build();
+
+ MailServices.accounts.addIncomingServerListener(this);
+ },
+ unload: function at_unload() {
+ MailServices.accounts.removeIncomingServerListener(this);
+ },
+ onServerLoaded: function at_onServerLoaded(aServer) {
+ this._build();
+ },
+ onServerUnloaded: function at_onServerUnloaded(aServer) {
+ this._build();
+ },
+ onServerChanged: function at_onServerChanged(aServer) {},
+
+ _dataStore: Components.classes["@mozilla.org/xul/xulstore;1"]
+ .getService(Components.interfaces.nsIXULStore),
+
+ /**
+ * Retrieve from XULStore.json whether the account should be expanded (open)
+ * in the account tree.
+ *
+ * @param aAccountKey key of the account to check
+ */
+ _getAccountOpenState: function at_getAccountOpenState(aAccountKey) {
+ if (!this._dataStore.hasValue(document.documentURI, aAccountKey, "open")) {
+ // If there was no value stored, use opened state.
+ return "true";
+ } else {
+ // Retrieve the persisted value from XULStore.json.
+ // It is stored under the URI of the current document and ID of the XUL element.
+ return this._dataStore
+ .getValue(document.documentURI, aAccountKey, "open");
+ }
+ },
+
+ _build: function at_build() {
+ const Ci = Components.interfaces;
+ var bundle = document.getElementById("bundle_prefs");
+ function getString(aString) { return bundle.getString(aString); }
+ var panels = [{string: getString("prefPanel-server"), src: "am-server.xul"},
+ {string: getString("prefPanel-copies"), src: "am-copies.xul"},
+ {string: getString("prefPanel-synchronization"), src: "am-offline.xul"},
+ {string: getString("prefPanel-diskspace"), src: "am-offline.xul"},
+ {string: getString("prefPanel-addressing"), src: "am-addressing.xul"},
+ {string: getString("prefPanel-junk"), src: "am-junk.xul"}];
+
+ let accounts = allAccountsSorted(false);
+
+ let mainTree = document.getElementById("account-tree-children");
+ // Clear off all children...
+ while (mainTree.hasChildNodes())
+ mainTree.lastChild.remove();
+
+ for (let account of accounts) {
+ let accountName = null;
+ let accountKey = account.key;
+ let amChrome = "about:blank";
+ let panelsToKeep = [];
+ let server = null;
+
+ // This "try {} catch {}" block is intentionally very long to catch
+ // unknown exceptions and confine them to this single account.
+ // This may happen from broken accounts. See e.g. bug 813929.
+ // Other accounts can still be shown properly if they are valid.
+ try {
+ server = account.incomingServer;
+
+ if (server.type == "im" && !Services.prefs.getBoolPref("mail.chat.enabled"))
+ continue;
+
+ accountName = server.prettyName;
+
+ // Now add our panels.
+ let idents = MailServices.accounts.getIdentitiesForServer(server);
+ if (idents.length) {
+ panelsToKeep.push(panels[0]); // The server panel is valid
+ panelsToKeep.push(panels[1]); // also the copies panel
+ panelsToKeep.push(panels[4]); // and addresssing
+ }
+
+ // Everyone except News, RSS and IM has a junk panel
+ // XXX: unextensible!
+ // The existence of server.spamSettings can't currently be used for this.
+ if (server.type != "nntp" && server.type != "rss" && server.type != "im")
+ panelsToKeep.push(panels[5]);
+
+ // Check offline/diskspace support level.
+ let diskspace = server.supportsDiskSpace;
+ if (server.offlineSupportLevel >= 10 && diskspace)
+ panelsToKeep.push(panels[2]);
+ else if (diskspace)
+ panelsToKeep.push(panels[3]);
+
+ // extensions
+ let catMan = Components.classes["@mozilla.org/categorymanager;1"]
+ .getService(Ci.nsICategoryManager);
+ const CATEGORY = "mailnews-accountmanager-extensions";
+ let catEnum = catMan.enumerateCategory(CATEGORY);
+ while (catEnum.hasMoreElements()) {
+ let entryName = null;
+ try {
+ entryName = catEnum.getNext().QueryInterface(Ci.nsISupportsCString).data;
+ let svc = Components.classes[catMan.getCategoryEntry(CATEGORY, entryName)]
+ .getService(Ci.nsIMsgAccountManagerExtension);
+ if (svc.showPanel(server)) {
+ let bundleName = "chrome://" + svc.chromePackageName +
+ "/locale/am-" + svc.name + ".properties";
+ let bundle = Services.strings.createBundle(bundleName);
+ let title = bundle.GetStringFromName("prefPanel-" + svc.name);
+ panelsToKeep.push({string: title, src: "am-" + svc.name + ".xul"});
+ }
+ } catch(e) {
+ // Fetching of this extension panel failed so do not show it,
+ // just log error.
+ let extName = entryName || "(unknown)";
+ Components.utils.reportError("Error accessing panel from extension '" +
+ extName + "': " + e);
+ }
+ }
+ amChrome = server.accountManagerChrome;
+ } catch(e) {
+ // Show only a placeholder in the account list saying this account
+ // is broken, with no child panels.
+ let accountID = (accountName || accountKey);
+ Components.utils.reportError("Error accessing account " + accountID + ": " + e);
+ accountName = "Invalid account " + accountID;
+ panelsToKeep.length = 0;
+ }
+
+ // Create the top level tree-item.
+ var treeitem = document.createElement("treeitem");
+ mainTree.appendChild(treeitem);
+ var treerow = document.createElement("treerow");
+ treeitem.appendChild(treerow);
+ var treecell = document.createElement("treecell");
+ treerow.appendChild(treecell);
+ treecell.setAttribute("label", accountName);
+ treeitem.setAttribute("PageTag", amChrome);
+ // Add icons based on account type.
+ if (server) {
+ treecell.setAttribute("properties", "folderNameCol isServer-true" +
+ " serverType-" + server.type);
+ // For IM accounts, we can try to fetch a protocol specific icon.
+ if (server.type == "im") {
+ treecell.setAttribute("src", server.wrappedJSObject.imAccount
+ .protocol.iconBaseURI + "icon.png");
+ }
+ }
+
+ if (panelsToKeep.length > 0) {
+ var treekids = document.createElement("treechildren");
+ treeitem.appendChild(treekids);
+ for (let panel of panelsToKeep) {
+ var kidtreeitem = document.createElement("treeitem");
+ treekids.appendChild(kidtreeitem);
+ var kidtreerow = document.createElement("treerow");
+ kidtreeitem.appendChild(kidtreerow);
+ var kidtreecell = document.createElement("treecell");
+ kidtreerow.appendChild(kidtreecell);
+ kidtreecell.setAttribute("label", panel.string);
+ kidtreeitem.setAttribute("PageTag", panel.src);
+ kidtreeitem._account = account;
+ }
+ treeitem.setAttribute("container", "true");
+ treeitem.id = accountKey;
+ // Load the 'open' state of the account from XULStore.json.
+ treeitem.setAttribute("open", this._getAccountOpenState(accountKey));
+ // Let the XULStore.json automatically save the 'open' state of the
+ // account when it is changed.
+ treeitem.setAttribute("persist", "open");
+ }
+ treeitem._account = account;
+ }
+
+ markDefaultServer(getDefaultAccount(), null);
+
+ // Now add the outgoing server node.
+ var treeitem = document.createElement("treeitem");
+ mainTree.appendChild(treeitem);
+ var treerow = document.createElement("treerow");
+ treeitem.appendChild(treerow);
+ var treecell = document.createElement("treecell");
+ treerow.appendChild(treecell);
+ treecell.setAttribute("label", getString("prefPanel-smtp"));
+ treeitem.setAttribute("PageTag", "am-smtp.xul");
+ treecell.setAttribute("properties",
+ "folderNameCol isServer-true serverType-smtp");
+ }
+};