mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-20 07:17:32 +09:00
1604 lines
54 KiB
JavaScript
1604 lines
54 KiB
JavaScript
/* -*- 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);
|
|
}
|
|
|
|
/**
|
|
* 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 = MailServices.accounts.defaultAccount;
|
|
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(MailServices.accounts.defaultAccount, 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) {
|
|
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 != MailServices.accounts.defaultAccount &&
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
|
|
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(MailServices.accounts.defaultAccount, 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");
|
|
}
|
|
};
|