summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/base/prefs/content/AccountWizard.js
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mailnews/base/prefs/content/AccountWizard.js')
-rw-r--r--comm/mailnews/base/prefs/content/AccountWizard.js605
1 files changed, 605 insertions, 0 deletions
diff --git a/comm/mailnews/base/prefs/content/AccountWizard.js b/comm/mailnews/base/prefs/content/AccountWizard.js
new file mode 100644
index 0000000000..69047c3f9e
--- /dev/null
+++ b/comm/mailnews/base/prefs/content/AccountWizard.js
@@ -0,0 +1,605 @@
+/* -*- Mode: JavaScript; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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/. */
+
+/* import-globals-from accountUtils.js */
+/* import-globals-from amUtils.js */
+/* import-globals-from aw-identity.js */
+/* import-globals-from aw-incoming.js */
+/* import-globals-from aw-accname.js */
+/* import-globals-from aw-done.js */
+
+/* NOTE: This Account Wizard is *only* for Newsgroup accounts.
+ * Historically, it was a generic Account Wizard, hence the generic naming.
+ */
+
+/*
+ data flow into the account wizard like this:
+
+ For new accounts:
+ * pageData -> accountData -> createAccount -> finishAccount
+
+ for "unfinished accounts"
+ * account -> accountData -> pageData -> accountData -> finishAccount
+*/
+
+var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+);
+var { NntpUtils } = ChromeUtils.import("resource:///modules/NntpUtils.jsm");
+
+var contentWindow;
+
+var gPageData;
+
+var nsIMsgIdentity = Ci.nsIMsgIdentity;
+var nsIMsgIncomingServer = Ci.nsIMsgIncomingServer;
+var gPrefsBundle, gMessengerBundle;
+
+// the current nsIMsgAccount
+var gCurrentAccount;
+
+// The default account before we create a new account.
+// We need to store this as just asking for the default account may switch
+// it to the newly created one if there was none before.
+var gDefaultAccount;
+
+// the current associative array that
+// will eventually be dumped into the account
+var gCurrentAccountData = null;
+
+// default picker mode for copies and folders
+var gDefaultSpecialFolderPickerMode = "0";
+
+// event handlers
+function onAccountWizardLoad() {
+ document.querySelector("wizard").addEventListener("wizardcancel", onCancel);
+ document
+ .querySelector("wizard")
+ .addEventListener("wizardfinish", FinishAccount);
+ let identityPage = document.getElementById("identitypage");
+ identityPage.addEventListener("pageshow", identityPageInit);
+ identityPage.addEventListener("pageadvanced", identityPageUnload);
+ identityPage.next = "newsserver";
+ let newsserverPage = document.getElementById("newsserver");
+ newsserverPage.addEventListener("pageshow", incomingPageInit);
+ newsserverPage.addEventListener("pageadvanced", incomingPageUnload);
+ newsserverPage.next = "accnamepage";
+ let accnamePage = document.getElementById("accnamepage");
+ accnamePage.addEventListener("pageshow", acctNamePageInit);
+ accnamePage.addEventListener("pageadvanced", acctNamePageUnload);
+ accnamePage.next = "done";
+ let donePage = document.getElementById("done");
+ donePage.addEventListener("pageshow", donePageInit);
+
+ gPrefsBundle = document.getElementById("bundle_prefs");
+ gMessengerBundle = document.getElementById("bundle_messenger");
+
+ checkForInvalidAccounts();
+
+ // It is fine if there is no default account, this is expected the first
+ // time you launch mail on a new profile.
+ gDefaultAccount = MailServices.accounts.defaultAccount;
+
+ identityPageInit();
+}
+
+function onCancel() {
+ if ("ActivationOnCancel" in this && this.ActivationOnCancel()) {
+ return false;
+ }
+ var firstInvalidAccount = getInvalidAccounts(
+ MailServices.accounts.accounts
+ ).find(account => account.incomingServer.type == "nntp");
+ var closeWizard = true;
+
+ // if the user cancels the the wizard when it pops up because of
+ // an invalid account (example, a webmail account that activation started)
+ // we just force create it by setting some values and calling the FinishAccount()
+ // see bug #47521 for the full discussion
+ if (firstInvalidAccount) {
+ var pageData = GetPageData();
+ // set the fullName if it doesn't exist
+ if (!pageData.fullName) {
+ pageData.fullName = "";
+ }
+
+ // set the email if it doesn't exist
+ if (!pageData.email) {
+ pageData.email = "user@domain.invalid";
+ }
+
+ // call FinishAccount() and not onFinish(), since the "finish"
+ // button may be disabled
+ FinishAccount();
+ } else if (!MailServices.accounts.accounts.length) {
+ // since this is not an invalid account
+ // really cancel if the user hits the "cancel" button
+ // if the length of the account list is less than 1, there are no accounts
+ let confirmMsg = gPrefsBundle.getString("cancelWizard");
+ let confirmTitle = gPrefsBundle.getString("accountWizard");
+ let result = Services.prompt.confirmEx(
+ window,
+ confirmTitle,
+ confirmMsg,
+ Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0 +
+ Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_1,
+ gPrefsBundle.getString("WizardExit"),
+ gPrefsBundle.getString("WizardContinue"),
+ null,
+ null,
+ { value: 0 }
+ );
+
+ if (result == 1) {
+ closeWizard = false;
+ }
+ }
+
+ return closeWizard;
+}
+
+function FinishAccount() {
+ try {
+ var pageData = GetPageData();
+
+ var accountData = gCurrentAccountData;
+
+ if (!accountData) {
+ accountData = {};
+ }
+
+ // we may need local folders before account is "Finished"
+ // if it's a pop3 account which defers to Local Folders.
+ verifyLocalFoldersAccount();
+
+ PageDataToAccountData(pageData, accountData);
+
+ // we might be simply finishing another account
+ if (!gCurrentAccount) {
+ gCurrentAccount = createAccount(accountData);
+ }
+
+ // transfer all attributes from the accountdata
+ finishAccount(gCurrentAccount, accountData);
+
+ setupCopiesAndFoldersServer(gCurrentAccount, accountData);
+
+ if (gCurrentAccount.incomingServer.canBeDefaultServer) {
+ EnableCheckMailAtStartUpIfNeeded(gCurrentAccount);
+ }
+
+ // in case we crash, force us a save of the prefs file NOW
+ try {
+ MailServices.accounts.saveAccountInfo();
+ } catch (ex) {
+ dump("Error saving account info: " + ex + "\n");
+ }
+ let openerWindow = window.opener.top;
+ // The following block is the same as in feedAccountWizard.js.
+ if ("selectServer" in openerWindow) {
+ // Opened from Account Settings.
+ openerWindow.selectServer(gCurrentAccount.incomingServer);
+ }
+
+ // Post a message to the main window on successful account setup.
+ openerWindow.postMessage("account-created", "*");
+
+ window.close();
+ } catch (ex) {
+ dump("FinishAccount failed, " + ex + "\n");
+ }
+}
+
+// prepopulate pageData with stuff from accountData
+// use: to prepopulate the wizard with account information
+function AccountDataToPageData(accountData, pageData) {
+ var server = accountData.incomingServer;
+ pageData.hostname = server.hostName;
+ pageData.prettyName = server.prettyName || "";
+
+ var identity;
+
+ if (accountData.identity) {
+ dump("This is an accountdata\n");
+ identity = accountData.identity;
+ } else if (accountData.identities) {
+ identity = accountData.identities[0];
+ dump("this is an account, id= " + identity + "\n");
+ }
+
+ pageData.email = identity.email || "";
+ pageData.fullName = identity.fullName || "";
+}
+
+// take data from each page of pageData and dump it into accountData
+// use: to put results of wizard into a account-oriented object
+function PageDataToAccountData(pageData, accountData) {
+ if (!accountData.identity) {
+ accountData.identity = {};
+ }
+ if (!accountData.incomingServer) {
+ accountData.incomingServer = {};
+ }
+
+ var identity = accountData.identity;
+ var server = accountData.incomingServer;
+
+ if (pageData.email) {
+ identity.email = pageData.email;
+ }
+ if (pageData.fullName) {
+ identity.fullName = pageData.fullName;
+ }
+
+ server.hostName = pageData.hostname;
+ if (pageData.prettyName) {
+ server.prettyName = pageData.prettyName;
+ }
+}
+
+// given an accountData structure, create an account
+// (but don't fill in any fields, that's for finishAccount()
+function createAccount(accountData) {
+ let hostName = accountData.incomingServer.hostName;
+ // If we're here, the server must not be associated with any account, so reuse
+ // it.
+ let server = NntpUtils.findServer(hostName);
+
+ if (!server) {
+ dump(`MailServices.accounts.createIncomingServer(${hostName})\n`);
+ // Create a (actual) server.
+ server = MailServices.accounts.createIncomingServer(null, hostName, "nntp");
+ }
+
+ dump("MailServices.accounts.createAccount()\n");
+ // Create an account.
+ let account = MailServices.accounts.createAccount();
+
+ // only create an identity for this account if we really have one
+ // (use the email address as a check)
+ if (accountData.identity && accountData.identity.email) {
+ dump("MailServices.accounts.createIdentity()\n");
+ // Create an identity.
+ let identity = MailServices.accounts.createIdentity();
+
+ // New nntp identities should use plain text by default;
+ // we want that GNKSA (The Good Net-Keeping Seal of Approval).
+ identity.composeHtml = false;
+
+ account.addIdentity(identity);
+ }
+
+ // we mark the server as invalid so that the account manager won't
+ // tell RDF about the new server - it's not quite finished getting
+ // set up yet, in particular, the deferred storage pref hasn't been set.
+ server.valid = false;
+ // Set the new account to use the new server.
+ account.incomingServer = server;
+ server.valid = true;
+ return account;
+}
+
+// given an accountData structure, copy the data into the
+// given account, incoming server, and so forth
+function finishAccount(account, accountData) {
+ if (accountData.incomingServer) {
+ var destServer = account.incomingServer;
+ var srcServer = accountData.incomingServer;
+ copyObjectToInterface(destServer, srcServer, true);
+
+ // See if there are any protocol-specific attributes.
+ // If so, we use the type to get the IID, QueryInterface
+ // as appropriate, then copy the data over.
+ const typeProperty = "ServerType-" + srcServer.type;
+ let serverAttrs =
+ typeProperty in srcServer ? srcServer[typeProperty] : null;
+ dump(`srcServer.${typeProperty} = ${serverAttrs}\n`);
+ if (serverAttrs) {
+ // handle server-specific stuff
+ var IID;
+ try {
+ IID = destServer.protocolInfo.serverIID;
+ } catch (ex) {
+ console.error("Could not get IID for " + srcServer.type + ": " + ex);
+ }
+
+ if (IID) {
+ let destProtocolServer = destServer.QueryInterface(IID);
+ let srcProtocolServer = srcServer["ServerType-" + srcServer.type];
+
+ dump("Copying over " + srcServer.type + "-specific data\n");
+ copyObjectToInterface(destProtocolServer, srcProtocolServer, false);
+ }
+ }
+
+ account.incomingServer.valid = true;
+ // hack to cause an account loaded notification now the server is valid
+ account.incomingServer = account.incomingServer; // eslint-disable-line no-self-assign
+ }
+
+ // copy identity info
+ var destIdentity = account.identities.length ? account.identities[0] : null;
+
+ if (destIdentity) {
+ // does this account have an identity?
+ if (accountData.identity && accountData.identity.email) {
+ // fixup the email address if we have a default domain
+ let emailArray = accountData.identity.email.split("@");
+ if (emailArray.length < 2 && accountData.domain) {
+ accountData.identity.email += "@" + accountData.domain;
+ }
+
+ copyObjectToInterface(destIdentity, accountData.identity, true);
+ destIdentity.valid = true;
+ }
+
+ /**
+ * If signature file need to be set, get the path to the signature file.
+ * Signature files, if exist, are placed under default location. Get
+ * default files location for messenger using directory service. Signature
+ * file name should be extracted from the account data to build the complete
+ * path for signature file. Once the path is built, set the identity's signature pref.
+ */
+ if (destIdentity.attachSignature) {
+ var sigFileName = accountData.signatureFileName;
+ let sigFile = MailServices.mailSession.getDataFilesDir("messenger");
+ sigFile.append(sigFileName);
+ destIdentity.signature = sigFile;
+ }
+ } // if the account has an identity...
+
+ if (this.FinishAccountHook != undefined) {
+ this.FinishAccountHook(accountData.domain);
+ }
+}
+
+// Helper method used by copyObjectToInterface which attempts to set dest[attribute] as a generic
+// attribute on the xpconnect object, src.
+// This routine skips any attribute that begins with ServerType-
+function setGenericAttribute(dest, src, attribute) {
+ if (!attribute.toLowerCase().startsWith("servertype-") && src[attribute]) {
+ switch (typeof src[attribute]) {
+ case "string":
+ dest.setUnicharAttribute(attribute, src[attribute]);
+ break;
+ case "boolean":
+ dest.setBoolAttribute(attribute, src[attribute]);
+ break;
+ case "number":
+ dest.setIntAttribute(attribute, src[attribute]);
+ break;
+ default:
+ dump(
+ "Error: No Generic attribute " +
+ attribute +
+ " found for: " +
+ dest +
+ "\n"
+ );
+ break;
+ }
+ }
+}
+
+// copy over all attributes from dest into src that already exist in src
+// the assumption is that src is an XPConnect interface full of attributes
+// @param useGenericFallback if we can't set an attribute directly on src, then fall back
+// and try setting it generically. This assumes that src supports setIntAttribute, setUnicharAttribute
+// and setBoolAttribute.
+function copyObjectToInterface(dest, src, useGenericFallback) {
+ if (!dest) {
+ return;
+ }
+ if (!src) {
+ return;
+ }
+
+ var attribute;
+ for (attribute in src) {
+ if (dest.__lookupSetter__(attribute)) {
+ if (dest[attribute] != src[attribute]) {
+ dest[attribute] = src[attribute];
+ }
+ } else if (useGenericFallback) {
+ // fall back to setting the attribute generically
+ setGenericAttribute(dest, src, attribute);
+ }
+ } // for each attribute in src we want to copy
+}
+
+// check if there already is a "Local Folders"
+// if not, create it.
+function verifyLocalFoldersAccount() {
+ var localMailServer = null;
+ try {
+ localMailServer = MailServices.accounts.localFoldersServer;
+ } catch (ex) {
+ // dump("exception in findserver: " + ex + "\n");
+ localMailServer = null;
+ }
+
+ try {
+ if (!localMailServer) {
+ // dump("Creating local mail account\n");
+ // creates a copy of the identity you pass in
+ MailServices.accounts.createLocalMailAccount();
+ try {
+ localMailServer = MailServices.accounts.localFoldersServer;
+ } catch (ex) {
+ dump(
+ "error! we should have found the local mail server after we created it.\n"
+ );
+ localMailServer = null;
+ }
+ }
+ } catch (ex) {
+ dump("Error in verifyLocalFoldersAccount" + ex + "\n");
+ }
+}
+
+function setupCopiesAndFoldersServer(account, accountData) {
+ try {
+ var server = account.incomingServer;
+
+ if (!account.identities.length) {
+ return false;
+ }
+
+ let identity = account.identities[0];
+ // For this server, do we default the folder prefs to this server, or to the "Local Folders" server
+ // If it's deferred, we use the local folders account.
+ var defaultCopiesAndFoldersPrefsToServer =
+ server.defaultCopiesAndFoldersPrefsToServer;
+
+ var copiesAndFoldersServer = null;
+ if (defaultCopiesAndFoldersPrefsToServer) {
+ copiesAndFoldersServer = server;
+ } else {
+ if (!MailServices.accounts.localFoldersServer) {
+ dump("error! we should have a local mail server at this point\n");
+ return false;
+ }
+ copiesAndFoldersServer = MailServices.accounts.localFoldersServer;
+ }
+
+ setDefaultCopiesAndFoldersPrefs(
+ identity,
+ copiesAndFoldersServer,
+ accountData
+ );
+ } catch (ex) {
+ // return false (meaning we did not setupCopiesAndFoldersServer)
+ // on any error
+ dump("Error in setupCopiesAndFoldersServer: " + ex + "\n");
+ return false;
+ }
+ return true;
+}
+
+function setDefaultCopiesAndFoldersPrefs(identity, server, accountData) {
+ var rootFolder = server.rootFolder;
+
+ // we need to do this or it is possible that the server's draft,
+ // stationery fcc folder will not be in rdf
+ //
+ // this can happen in a couple cases
+ // 1) the first account we create, creates the local mail. since
+ // local mail was just created, it obviously hasn't been opened,
+ // or in rdf..
+ // 2) the account we created is of a type where
+ // defaultCopiesAndFoldersPrefsToServer is true
+ // this since we are creating the server, it obviously hasn't been
+ // opened, or in rdf.
+ //
+ // this makes the assumption that the server's draft, stationery fcc folder
+ // are at the top level (ie subfolders of the root folder.) this works
+ // because we happen to be doing things that way, and if the user changes
+ // that, it will work because to change the folder, it must be in rdf,
+ // coming from the folder cache, in the worst case.
+ var msgFolder = rootFolder.QueryInterface(Ci.nsIMsgFolder);
+
+ /**
+ * When a new account is created, folders 'Sent', 'Drafts'
+ * and 'Templates' are not created then, but created on demand at runtime.
+ * But we do need to present them as possible choices in the Copies and Folders
+ * UI. To do that, folder URIs have to be created and stored in the prefs file.
+ * So, if there is a need to build special folders, append the special folder
+ * names and create right URIs.
+ */
+ var folderDelim = "/";
+
+ /* we use internal names known to everyone like Sent, Templates and Drafts */
+ /* if folder names were already given in isp rdf, we use them,
+ otherwise we use internal names known to everyone like Sent, Templates and Drafts */
+
+ // Note the capital F, D and S!
+ var draftFolder =
+ accountData.identity && accountData.identity.DraftFolder
+ ? accountData.identity.DraftFolder
+ : "Drafts";
+ var stationeryFolder =
+ accountData.identity && accountData.identity.StationeryFolder
+ ? accountData.identity.StationeryFolder
+ : "Templates";
+ var fccFolder =
+ accountData.identity && accountData.identity.FccFolder
+ ? accountData.identity.FccFolder
+ : "Sent";
+
+ identity.draftFolder = msgFolder.server.serverURI + folderDelim + draftFolder;
+ identity.stationeryFolder =
+ msgFolder.server.serverURI + folderDelim + stationeryFolder;
+ identity.fccFolder = msgFolder.server.serverURI + folderDelim + fccFolder;
+
+ // Note the capital F, D and S!
+ identity.fccFolderPickerMode =
+ accountData.identity && accountData.identity.FccFolder
+ ? 1
+ : gDefaultSpecialFolderPickerMode;
+ identity.draftsFolderPickerMode =
+ accountData.identity && accountData.identity.DraftFolder
+ ? 1
+ : gDefaultSpecialFolderPickerMode;
+ identity.tmplFolderPickerMode =
+ accountData.identity && accountData.identity.StationeryFolder
+ ? 1
+ : gDefaultSpecialFolderPickerMode;
+}
+
+function checkForInvalidAccounts() {
+ var firstInvalidAccount = getInvalidAccounts(
+ MailServices.accounts.accounts
+ ).find(account => account.incomingServer.type == "nntp");
+
+ if (firstInvalidAccount) {
+ var pageData = GetPageData();
+ dump(
+ "We have an invalid account, " +
+ firstInvalidAccount +
+ ", let's use that!\n"
+ );
+ gCurrentAccount = firstInvalidAccount;
+
+ var accountData = {};
+ accountData.incomingServer = firstInvalidAccount.incomingServer;
+ accountData.identity = firstInvalidAccount.identities[0];
+ AccountDataToPageData(accountData, pageData);
+
+ gCurrentAccountData = accountData;
+ }
+}
+
+function getUsernameFromEmail(email) {
+ return email && email.substr(0, email.indexOf("@"));
+}
+
+function GetPageData() {
+ if (!gPageData) {
+ gPageData = {};
+ }
+
+ return gPageData;
+}
+
+// flush the XUL cache - just for debugging purposes - not called
+function onFlush() {
+ Services.prefs.setBoolPref("nglayout.debug.disable_xul_cache", true);
+ Services.prefs.setBoolPref("nglayout.debug.disable_xul_cache", false);
+}
+
+/** If there are no default accounts..
+ * this is will be the new default, so enable
+ * check for mail at startup
+ */
+function EnableCheckMailAtStartUpIfNeeded(newAccount) {
+ // Check if default account existed.
+ // If no such account, make this one the default account
+ // and turn on the new mail check at startup for the current account
+ if (!gDefaultAccount) {
+ MailServices.accounts.defaultAccount = newAccount;
+ newAccount.incomingServer.loginAtStartUp = true;
+ newAccount.incomingServer.downloadOnBiff = true;
+ }
+}