mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-28 03:17:31 +09:00
Issue #1258 - Part 1: Import mailnews, ldap, and mork from comm-esr52.9.1
This commit is contained in:
parent
23e0d82436
commit
e400f4130a
1564 changed files with 510348 additions and 0 deletions
|
|
@ -0,0 +1,41 @@
|
|||
/* -*- Mode: C++; tab-width: 2; 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/. */
|
||||
|
||||
/**
|
||||
* This class implements nsIBadCertListener. It's job is to prevent "bad cert"
|
||||
* security dialogs from being shown to the user. We call back to the
|
||||
* 'callback' object's method "processCertError" so that it can deal with it as
|
||||
* needed (in the case of autoconfig, setting up temporary overrides).
|
||||
*/
|
||||
function BadCertHandler(callback)
|
||||
{
|
||||
this._init(callback);
|
||||
}
|
||||
|
||||
BadCertHandler.prototype =
|
||||
{
|
||||
_init: function(callback) {
|
||||
this._callback = callback;
|
||||
},
|
||||
|
||||
// Suppress any certificate errors
|
||||
notifyCertProblem: function(socketInfo, status, targetSite) {
|
||||
return this._callback.processCertError(socketInfo, status, targetSite);
|
||||
},
|
||||
|
||||
// nsIInterfaceRequestor
|
||||
getInterface: function(iid) {
|
||||
return this.QueryInterface(iid);
|
||||
},
|
||||
|
||||
// nsISupports
|
||||
QueryInterface: function(iid) {
|
||||
if (!iid.equals(Components.interfaces.nsIBadCertListener2) &&
|
||||
!iid.equals(Components.interfaces.nsIInterfaceRequestor) &&
|
||||
!iid.equals(Components.interfaces.nsISupports))
|
||||
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||
return this;
|
||||
}
|
||||
};
|
||||
259
mailnews/base/prefs/content/accountcreation/accountConfig.js
Normal file
259
mailnews/base/prefs/content/accountcreation/accountConfig.js
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
/* -*- Mode: Java; tab-width: 2; 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/. */
|
||||
|
||||
/**
|
||||
* This file creates the class AccountConfig, which is a JS object that holds
|
||||
* a configuration for a certain account. It is *not* created in the backend
|
||||
* yet (use aw-createAccount.js for that), and it may be incomplete.
|
||||
*
|
||||
* Several AccountConfig objects may co-exist, e.g. for autoconfig.
|
||||
* One AccountConfig object is used to prefill and read the widgets
|
||||
* in the Wizard UI.
|
||||
* When we autoconfigure, we autoconfig writes the values into a
|
||||
* new object and returns that, and the caller can copy these
|
||||
* values into the object used by the UI.
|
||||
*
|
||||
* See also
|
||||
* <https://wiki.mozilla.org/Thunderbird:Autoconfiguration:ConfigFileFormat>
|
||||
* for values stored.
|
||||
*/
|
||||
|
||||
function AccountConfig()
|
||||
{
|
||||
this.incoming = this.createNewIncoming();
|
||||
this.incomingAlternatives = [];
|
||||
this.outgoing = this.createNewOutgoing();
|
||||
this.outgoingAlternatives = [];
|
||||
this.identity =
|
||||
{
|
||||
// displayed real name of user
|
||||
realname : "%REALNAME%",
|
||||
// email address of user, as shown in From of outgoing mails
|
||||
emailAddress : "%EMAILADDRESS%",
|
||||
};
|
||||
this.inputFields = [];
|
||||
this.domains = [];
|
||||
};
|
||||
AccountConfig.prototype =
|
||||
{
|
||||
// @see createNewIncoming()
|
||||
incoming : null,
|
||||
// @see createNewOutgoing()
|
||||
outgoing : null,
|
||||
/**
|
||||
* Other servers which can be used instead of |incoming|,
|
||||
* in order of decreasing preference.
|
||||
* (|incoming| itself should not be included here.)
|
||||
* { Array of incoming/createNewIncoming() }
|
||||
*/
|
||||
incomingAlternatives : null,
|
||||
outgoingAlternatives : null,
|
||||
// OAuth2 configuration, if needed.
|
||||
oauthSettings : null,
|
||||
// just an internal string to refer to this. Do not show to user.
|
||||
id : null,
|
||||
// who created the config.
|
||||
// { one of kSource* }
|
||||
source : 0,
|
||||
displayName : null,
|
||||
// { Array of { varname (value without %), displayName, exampleValue } }
|
||||
inputFields : null,
|
||||
// email address domains for which this config is applicable
|
||||
// { Array of Strings }
|
||||
domains : null,
|
||||
|
||||
/**
|
||||
* Factory function for incoming and incomingAlternatives
|
||||
*/
|
||||
createNewIncoming : function()
|
||||
{
|
||||
return {
|
||||
// { String-enum: "pop3", "imap", "nntp" }
|
||||
type : null,
|
||||
hostname : null,
|
||||
// { Integer }
|
||||
port : null,
|
||||
// May be a placeholder (starts and ends with %). { String }
|
||||
username : null,
|
||||
password : null,
|
||||
// { enum: 1 = plain, 2 = SSL/TLS, 3 = STARTTLS always, 0 = not inited }
|
||||
// ('TLS when available' is insecure and not supported here)
|
||||
socketType : 0,
|
||||
/**
|
||||
* true when the cert is invalid (and thus SSL useless), because it's
|
||||
* 1) not from an accepted CA (including self-signed certs)
|
||||
* 2) for a different hostname or
|
||||
* 3) expired.
|
||||
* May go back to false when user explicitly accepted the cert.
|
||||
*/
|
||||
badCert : false,
|
||||
/**
|
||||
* How to log in to the server: plaintext or encrypted pw, GSSAPI etc.
|
||||
* Defined by Ci.nsMsgAuthMethod
|
||||
* Same as server pref "authMethod".
|
||||
*/
|
||||
auth : 0,
|
||||
/**
|
||||
* Other auth methods that we think the server supports.
|
||||
* They are ordered by descreasing preference.
|
||||
* (|auth| itself is not included in |authAlternatives|)
|
||||
* {Array of Ci.nsMsgAuthMethod} (same as .auth)
|
||||
*/
|
||||
authAlternatives : null,
|
||||
// in minutes { Integer }
|
||||
checkInterval : 10,
|
||||
loginAtStartup : true,
|
||||
// POP3 only:
|
||||
// Not yet implemented. { Boolean }
|
||||
useGlobalInbox : false,
|
||||
leaveMessagesOnServer : true,
|
||||
daysToLeaveMessagesOnServer : 14,
|
||||
deleteByAgeFromServer : true,
|
||||
// When user hits delete, delete from local store and from server
|
||||
deleteOnServerWhenLocalDelete : true,
|
||||
downloadOnBiff : true,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* Factory function for outgoing and outgoingAlternatives
|
||||
*/
|
||||
createNewOutgoing : function()
|
||||
{
|
||||
return {
|
||||
type : "smtp",
|
||||
hostname : null,
|
||||
port : null, // see incoming
|
||||
username : null, // see incoming. may be null, if auth is 0.
|
||||
password : null, // see incoming. may be null, if auth is 0.
|
||||
socketType : 0, // see incoming
|
||||
badCert : false, // see incoming
|
||||
auth : 0, // see incoming
|
||||
authAlternatives : null, // see incoming
|
||||
addThisServer : true, // if we already have an SMTP server, add this
|
||||
// if we already have an SMTP server, use it.
|
||||
useGlobalPreferredServer : false,
|
||||
// we should reuse an already configured SMTP server.
|
||||
// nsISmtpServer.key
|
||||
existingServerKey : null,
|
||||
// user display value for existingServerKey
|
||||
existingServerLabel : null,
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a deep copy of this object,
|
||||
* i.e. modifying the copy will not affect the original object.
|
||||
*/
|
||||
copy : function()
|
||||
{
|
||||
// Workaround: deepCopy() fails to preserve base obj (instanceof)
|
||||
var result = new AccountConfig();
|
||||
for (var prop in this)
|
||||
result[prop] = deepCopy(this[prop]);
|
||||
|
||||
return result;
|
||||
},
|
||||
isComplete : function()
|
||||
{
|
||||
return (!!this.incoming.hostname && !!this.incoming.port &&
|
||||
!!this.incoming.socketType && !!this.incoming.auth &&
|
||||
!!this.incoming.username &&
|
||||
(!!this.outgoing.existingServerKey ||
|
||||
(!!this.outgoing.hostname && !!this.outgoing.port &&
|
||||
!!this.outgoing.socketType && !!this.outgoing.auth &&
|
||||
!!this.outgoing.username)));
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
// enum consts
|
||||
|
||||
// .source
|
||||
AccountConfig.kSourceUser = 1; // user manually entered the config
|
||||
AccountConfig.kSourceXML = 2; // config from XML from ISP or Mozilla DB
|
||||
AccountConfig.kSourceGuess = 3; // guessConfig()
|
||||
|
||||
|
||||
/**
|
||||
* Some fields on the account config accept placeholders (when coming from XML).
|
||||
*
|
||||
* These are the predefined ones
|
||||
* * %EMAILADDRESS% (full email address of the user, usually entered by user)
|
||||
* * %EMAILLOCALPART% (email address, part before @)
|
||||
* * %EMAILDOMAIN% (email address, part after @)
|
||||
* * %REALNAME%
|
||||
* as well as those defined in account.inputFields.*.varname, with % added
|
||||
* before and after.
|
||||
*
|
||||
* These must replaced with real values, supplied by the user or app,
|
||||
* before the account is created. This is done here. You call this function once
|
||||
* you have all the data - gathered the standard vars mentioned above as well as
|
||||
* all listed in account.inputFields, and pass them in here. This function will
|
||||
* insert them in the fields, returning a fully filled-out account ready to be
|
||||
* created.
|
||||
*
|
||||
* @param account {AccountConfig}
|
||||
* The account data to be modified. It may or may not contain placeholders.
|
||||
* After this function, it should not contain placeholders anymore.
|
||||
* This object will be modified in-place.
|
||||
*
|
||||
* @param emailfull {String}
|
||||
* Full email address of this account, e.g. "joe@example.com".
|
||||
* Empty of incomplete email addresses will/may be rejected.
|
||||
*
|
||||
* @param realname {String}
|
||||
* Real name of user, as will appear in From of outgoing messages
|
||||
*
|
||||
* @param password {String}
|
||||
* The password for the incoming server and (if necessary) the outgoing server
|
||||
*/
|
||||
function replaceVariables(account, realname, emailfull, password)
|
||||
{
|
||||
sanitize.nonemptystring(emailfull);
|
||||
let emailsplit = emailfull.split("@");
|
||||
assert(emailsplit.length == 2,
|
||||
"email address not in expected format: must contain exactly one @");
|
||||
let emaillocal = sanitize.nonemptystring(emailsplit[0]);
|
||||
let emaildomain = sanitize.hostname(emailsplit[1]);
|
||||
sanitize.label(realname);
|
||||
sanitize.nonemptystring(realname);
|
||||
|
||||
let otherVariables = {};
|
||||
otherVariables.EMAILADDRESS = emailfull;
|
||||
otherVariables.EMAILLOCALPART = emaillocal;
|
||||
otherVariables.EMAILDOMAIN = emaildomain;
|
||||
otherVariables.REALNAME = realname;
|
||||
|
||||
if (password) {
|
||||
account.incoming.password = password;
|
||||
account.outgoing.password = password; // set member only if auth required?
|
||||
}
|
||||
account.incoming.username = _replaceVariable(account.incoming.username,
|
||||
otherVariables);
|
||||
account.outgoing.username = _replaceVariable(account.outgoing.username,
|
||||
otherVariables);
|
||||
account.incoming.hostname =
|
||||
_replaceVariable(account.incoming.hostname, otherVariables);
|
||||
if (account.outgoing.hostname) // will be null if user picked existing server.
|
||||
account.outgoing.hostname =
|
||||
_replaceVariable(account.outgoing.hostname, otherVariables);
|
||||
account.identity.realname =
|
||||
_replaceVariable(account.identity.realname, otherVariables);
|
||||
account.identity.emailAddress =
|
||||
_replaceVariable(account.identity.emailAddress, otherVariables);
|
||||
account.displayName = _replaceVariable(account.displayName, otherVariables);
|
||||
}
|
||||
|
||||
function _replaceVariable(variable, values)
|
||||
{
|
||||
let str = variable;
|
||||
if (typeof(str) != "string")
|
||||
return str;
|
||||
|
||||
for (let varname in values)
|
||||
str = str.replace("%" + varname + "%", values[varname]);
|
||||
|
||||
return str;
|
||||
}
|
||||
333
mailnews/base/prefs/content/accountcreation/createInBackend.js
Normal file
333
mailnews/base/prefs/content/accountcreation/createInBackend.js
Normal file
|
|
@ -0,0 +1,333 @@
|
|||
/* -*- Mode: Java; tab-width: 2; 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/. */
|
||||
|
||||
/**
|
||||
* Takes an |AccountConfig| JS object and creates that account in the
|
||||
* Thunderbird backend (which also writes it to prefs).
|
||||
*
|
||||
* @param config {AccountConfig} The account to create
|
||||
*
|
||||
* @return - the account created.
|
||||
*/
|
||||
|
||||
Components.utils.import("resource:///modules/mailServices.js");
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
function createAccountInBackend(config)
|
||||
{
|
||||
// incoming server
|
||||
let inServer = MailServices.accounts.createIncomingServer(
|
||||
config.incoming.username,
|
||||
config.incoming.hostname,
|
||||
sanitize.enum(config.incoming.type, ["pop3", "imap", "nntp"]));
|
||||
inServer.port = config.incoming.port;
|
||||
inServer.authMethod = config.incoming.auth;
|
||||
inServer.password = config.incoming.password;
|
||||
if (config.rememberPassword && config.incoming.password.length)
|
||||
rememberPassword(inServer, config.incoming.password);
|
||||
|
||||
if (inServer.authMethod == Ci.nsMsgAuthMethod.OAuth2) {
|
||||
inServer.setCharValue("oauth2.scope", config.oauthSettings.scope);
|
||||
inServer.setCharValue("oauth2.issuer", config.oauthSettings.issuer);
|
||||
}
|
||||
|
||||
// SSL
|
||||
if (config.incoming.socketType == 1) // plain
|
||||
inServer.socketType = Ci.nsMsgSocketType.plain;
|
||||
else if (config.incoming.socketType == 2) // SSL / TLS
|
||||
inServer.socketType = Ci.nsMsgSocketType.SSL;
|
||||
else if (config.incoming.socketType == 3) // STARTTLS
|
||||
inServer.socketType = Ci.nsMsgSocketType.alwaysSTARTTLS;
|
||||
//inServer.prettyName = config.displayName;
|
||||
inServer.prettyName = config.identity.emailAddress;
|
||||
|
||||
inServer.doBiff = true;
|
||||
inServer.biffMinutes = config.incoming.checkInterval;
|
||||
const loginAtStartupPrefTemplate =
|
||||
"mail.server.%serverkey%.login_at_startup";
|
||||
var loginAtStartupPref =
|
||||
loginAtStartupPrefTemplate.replace("%serverkey%", inServer.key);
|
||||
Services.prefs.setBoolPref(loginAtStartupPref,
|
||||
config.incoming.loginAtStartup);
|
||||
if (config.incoming.type == "pop3")
|
||||
{
|
||||
const leaveOnServerPrefTemplate =
|
||||
"mail.server.%serverkey%.leave_on_server";
|
||||
const daysToLeaveOnServerPrefTemplate =
|
||||
"mail.server.%serverkey%.num_days_to_leave_on_server";
|
||||
const deleteFromServerPrefTemplate =
|
||||
"mail.server.%serverkey%.delete_mail_left_on_server";
|
||||
const deleteByAgeFromServerPrefTemplate =
|
||||
"mail.server.%serverkey%.delete_by_age_from_server";
|
||||
const downloadOnBiffPrefTemplate =
|
||||
"mail.server.%serverkey%.download_on_biff";
|
||||
var leaveOnServerPref =
|
||||
leaveOnServerPrefTemplate.replace("%serverkey%", inServer.key);
|
||||
var ageFromServerPref =
|
||||
deleteByAgeFromServerPrefTemplate.replace("%serverkey%", inServer.key);
|
||||
var daysToLeaveOnServerPref =
|
||||
daysToLeaveOnServerPrefTemplate.replace("%serverkey%", inServer.key);
|
||||
var deleteFromServerPref =
|
||||
deleteFromServerPrefTemplate.replace("%serverkey%", inServer.key);
|
||||
let downloadOnBiffPref =
|
||||
downloadOnBiffPrefTemplate.replace("%serverkey%", inServer.key);
|
||||
Services.prefs.setBoolPref(leaveOnServerPref,
|
||||
config.incoming.leaveMessagesOnServer);
|
||||
Services.prefs.setIntPref(daysToLeaveOnServerPref,
|
||||
config.incoming.daysToLeaveMessagesOnServer);
|
||||
Services.prefs.setBoolPref(deleteFromServerPref,
|
||||
config.incoming.deleteOnServerWhenLocalDelete);
|
||||
Services.prefs.setBoolPref(ageFromServerPref,
|
||||
config.incoming.deleteByAgeFromServer);
|
||||
Services.prefs.setBoolPref(downloadOnBiffPref,
|
||||
config.incoming.downloadOnBiff);
|
||||
}
|
||||
inServer.valid = true;
|
||||
|
||||
let username = config.outgoing.auth > 1 ? config.outgoing.username : null;
|
||||
let outServer = MailServices.smtp.findServer(username, config.outgoing.hostname);
|
||||
assert(config.outgoing.addThisServer ||
|
||||
config.outgoing.useGlobalPreferredServer ||
|
||||
config.outgoing.existingServerKey,
|
||||
"No SMTP server: inconsistent flags");
|
||||
|
||||
if (config.outgoing.addThisServer && !outServer)
|
||||
{
|
||||
outServer = MailServices.smtp.createServer();
|
||||
outServer.hostname = config.outgoing.hostname;
|
||||
outServer.port = config.outgoing.port;
|
||||
outServer.authMethod = config.outgoing.auth;
|
||||
if (config.outgoing.auth > 1)
|
||||
{
|
||||
outServer.username = username;
|
||||
outServer.password = config.incoming.password;
|
||||
if (config.rememberPassword && config.incoming.password.length)
|
||||
rememberPassword(outServer, config.incoming.password);
|
||||
}
|
||||
|
||||
if (outServer.authMethod == Ci.nsMsgAuthMethod.OAuth2) {
|
||||
let pref = "mail.smtpserver." + outServer.key + ".";
|
||||
Services.prefs.setCharPref(pref + "oauth2.scope",
|
||||
config.oauthSettings.scope);
|
||||
Services.prefs.setCharPref(pref + "oauth2.issuer",
|
||||
config.oauthSettings.issuer);
|
||||
}
|
||||
|
||||
if (config.outgoing.socketType == 1) // no SSL
|
||||
outServer.socketType = Ci.nsMsgSocketType.plain;
|
||||
else if (config.outgoing.socketType == 2) // SSL / TLS
|
||||
outServer.socketType = Ci.nsMsgSocketType.SSL;
|
||||
else if (config.outgoing.socketType == 3) // STARTTLS
|
||||
outServer.socketType = Ci.nsMsgSocketType.alwaysSTARTTLS;
|
||||
|
||||
// API problem: <http://mxr.mozilla.org/seamonkey/source/mailnews/compose/public/nsISmtpServer.idl#93>
|
||||
outServer.description = config.displayName;
|
||||
if (config.password)
|
||||
outServer.password = config.outgoing.password;
|
||||
|
||||
// If this is the first SMTP server, set it as default
|
||||
if (!MailServices.smtp.defaultServer ||
|
||||
!MailServices.smtp.defaultServer.hostname)
|
||||
MailServices.smtp.defaultServer = outServer;
|
||||
}
|
||||
|
||||
// identity
|
||||
// TODO accounts without identity?
|
||||
let identity = MailServices.accounts.createIdentity();
|
||||
identity.fullName = config.identity.realname;
|
||||
identity.email = config.identity.emailAddress;
|
||||
|
||||
// for new accounts, default to replies being positioned above the quote
|
||||
// if a default account is defined already, take its settings instead
|
||||
if (config.incoming.type == "imap" || config.incoming.type == "pop3")
|
||||
{
|
||||
identity.replyOnTop = 1;
|
||||
// identity.sigBottom = false; // don't set this until Bug 218346 is fixed
|
||||
|
||||
if (MailServices.accounts.accounts.length &&
|
||||
MailServices.accounts.defaultAccount)
|
||||
{
|
||||
let defAccount = MailServices.accounts.defaultAccount;
|
||||
let defIdentity = defAccount.defaultIdentity;
|
||||
if (defAccount.incomingServer.canBeDefaultServer &&
|
||||
defIdentity && defIdentity.valid)
|
||||
{
|
||||
identity.replyOnTop = defIdentity.replyOnTop;
|
||||
identity.sigBottom = defIdentity.sigBottom;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// due to accepted conventions, news accounts should default to plain text
|
||||
if (config.incoming.type == "nntp")
|
||||
identity.composeHtml = false;
|
||||
|
||||
identity.valid = true;
|
||||
|
||||
if (config.outgoing.existingServerKey)
|
||||
identity.smtpServerKey = config.outgoing.existingServerKey;
|
||||
else if (!config.outgoing.useGlobalPreferredServer)
|
||||
identity.smtpServerKey = outServer.key;
|
||||
|
||||
// account and hook up
|
||||
// Note: Setting incomingServer will cause the AccountManager to refresh
|
||||
// itself, which could be a problem if we came from it and we haven't set
|
||||
// the identity (see bug 521955), so make sure everything else on the
|
||||
// account is set up before you set the incomingServer.
|
||||
let account = MailServices.accounts.createAccount();
|
||||
account.addIdentity(identity);
|
||||
account.incomingServer = inServer;
|
||||
if (inServer.canBeDefaultServer && (!MailServices.accounts.defaultAccount ||
|
||||
!MailServices.accounts.defaultAccount
|
||||
.incomingServer.canBeDefaultServer))
|
||||
MailServices.accounts.defaultAccount = account;
|
||||
|
||||
verifyLocalFoldersAccount(MailServices.accounts);
|
||||
setFolders(identity, inServer);
|
||||
|
||||
// save
|
||||
MailServices.accounts.saveAccountInfo();
|
||||
try {
|
||||
Services.prefs.savePrefFile(null);
|
||||
} catch (ex) {
|
||||
ddump("Could not write out prefs: " + ex);
|
||||
}
|
||||
return account;
|
||||
}
|
||||
|
||||
function setFolders(identity, server)
|
||||
{
|
||||
// TODO: support for local folders for global inbox (or use smart search
|
||||
// folder instead)
|
||||
|
||||
var baseURI = server.serverURI + "/";
|
||||
|
||||
// Names will be localized in UI, not in folder names on server/disk
|
||||
// TODO allow to override these names in the XML config file,
|
||||
// in case e.g. Google or AOL use different names?
|
||||
// Workaround: Let user fix it :)
|
||||
var fccName = "Sent";
|
||||
var draftName = "Drafts";
|
||||
var templatesName = "Templates";
|
||||
|
||||
identity.draftFolder = baseURI + draftName;
|
||||
identity.stationeryFolder = baseURI + templatesName;
|
||||
identity.fccFolder = baseURI + fccName;
|
||||
|
||||
identity.fccFolderPickerMode = 0;
|
||||
identity.draftsFolderPickerMode = 0;
|
||||
identity.tmplFolderPickerMode = 0;
|
||||
}
|
||||
|
||||
function rememberPassword(server, password)
|
||||
{
|
||||
if (server instanceof Components.interfaces.nsIMsgIncomingServer)
|
||||
var passwordURI = server.localStoreType + "://" + server.hostName;
|
||||
else if (server instanceof Components.interfaces.nsISmtpServer)
|
||||
var passwordURI = "smtp://" + server.hostname;
|
||||
else
|
||||
throw new NotReached("Server type not supported");
|
||||
|
||||
let login = Cc["@mozilla.org/login-manager/loginInfo;1"]
|
||||
.createInstance(Ci.nsILoginInfo);
|
||||
login.init(passwordURI, null, passwordURI, server.username, password, "", "");
|
||||
try {
|
||||
Services.logins.addLogin(login);
|
||||
} catch (e) {
|
||||
if (e.message.includes("This login already exists")) {
|
||||
// TODO modify
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the user's setup already has an incoming server
|
||||
* which matches (hostname, port, username) the primary one
|
||||
* in the config.
|
||||
* (We also check the email address as username.)
|
||||
*
|
||||
* @param config {AccountConfig} filled in (no placeholders)
|
||||
* @return {nsIMsgIncomingServer} If it already exists, the server
|
||||
* object is returned.
|
||||
* If it's a new server, |null| is returned.
|
||||
*/
|
||||
function checkIncomingServerAlreadyExists(config)
|
||||
{
|
||||
assert(config instanceof AccountConfig);
|
||||
let incoming = config.incoming;
|
||||
let existing = MailServices.accounts.findRealServer(incoming.username,
|
||||
incoming.hostname,
|
||||
sanitize.enum(incoming.type, ["pop3", "imap", "nntp"]),
|
||||
incoming.port);
|
||||
|
||||
// if username does not have an '@', also check the e-mail
|
||||
// address form of the name.
|
||||
if (!existing && !incoming.username.includes("@"))
|
||||
existing = MailServices.accounts.findRealServer(config.identity.emailAddress,
|
||||
incoming.hostname,
|
||||
sanitize.enum(incoming.type, ["pop3", "imap", "nntp"]),
|
||||
incoming.port);
|
||||
return existing;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check whether the user's setup already has an outgoing server
|
||||
* which matches (hostname, port, username) the primary one
|
||||
* in the config.
|
||||
*
|
||||
* @param config {AccountConfig} filled in (no placeholders)
|
||||
* @return {nsISmtpServer} If it already exists, the server
|
||||
* object is returned.
|
||||
* If it's a new server, |null| is returned.
|
||||
*/
|
||||
function checkOutgoingServerAlreadyExists(config)
|
||||
{
|
||||
assert(config instanceof AccountConfig);
|
||||
let smtpServers = MailServices.smtp.servers;
|
||||
while (smtpServers.hasMoreElements())
|
||||
{
|
||||
let existingServer = smtpServers.getNext()
|
||||
.QueryInterface(Ci.nsISmtpServer);
|
||||
// TODO check username with full email address, too, like for incoming
|
||||
if (existingServer.hostname == config.outgoing.hostname &&
|
||||
existingServer.port == config.outgoing.port &&
|
||||
existingServer.username == config.outgoing.username)
|
||||
return existingServer;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if there already is a "Local Folders". If not, create it.
|
||||
* Copied from AccountWizard.js with minor updates.
|
||||
*/
|
||||
function verifyLocalFoldersAccount(am)
|
||||
{
|
||||
let localMailServer;
|
||||
try {
|
||||
localMailServer = am.localFoldersServer;
|
||||
}
|
||||
catch (ex) {
|
||||
localMailServer = null;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!localMailServer)
|
||||
{
|
||||
// creates a copy of the identity you pass in
|
||||
am.createLocalMailAccount();
|
||||
try {
|
||||
localMailServer = am.localFoldersServer;
|
||||
}
|
||||
catch (ex) {
|
||||
ddump("Error! we should have found the local mail server " +
|
||||
"after we created it.");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ex) { ddump("Error in verifyLocalFoldersAccount " + ex); }
|
||||
}
|
||||
1959
mailnews/base/prefs/content/accountcreation/emailWizard.js
Normal file
1959
mailnews/base/prefs/content/accountcreation/emailWizard.js
Normal file
File diff suppressed because it is too large
Load diff
493
mailnews/base/prefs/content/accountcreation/emailWizard.xul
Normal file
493
mailnews/base/prefs/content/accountcreation/emailWizard.xul
Normal file
|
|
@ -0,0 +1,493 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://messenger/skin/accountCreation.css"
|
||||
type="text/css"?>
|
||||
|
||||
<!DOCTYPE window [
|
||||
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
|
||||
%brandDTD;
|
||||
<!ENTITY % acDTD SYSTEM "chrome://messenger/locale/accountCreation.dtd">
|
||||
%acDTD;
|
||||
]>
|
||||
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
id="autoconfigWizard"
|
||||
windowtype="mail:autoconfig"
|
||||
title="&autoconfigWizard.title;"
|
||||
onload="gEmailConfigWizard.onLoad();"
|
||||
onkeypress="gEmailConfigWizard.onKeyDown(event);"
|
||||
onclose="gEmailConfigWizard.onWizardShutdown();"
|
||||
onunload="gEmailConfigWizard.onWizardShutdown();"
|
||||
>
|
||||
|
||||
<stringbundleset>
|
||||
<stringbundle id="bundle_brand"
|
||||
src="chrome://branding/locale/brand.properties"/>
|
||||
<stringbundle id="strings"
|
||||
src="chrome://messenger/locale/accountCreation.properties"/>
|
||||
<stringbundle id="utilstrings"
|
||||
src="chrome://messenger/locale/accountCreationUtil.properties"/>
|
||||
<stringbundle id="bundle_messenger"
|
||||
src="chrome://messenger/locale/messenger.properties"/>
|
||||
</stringbundleset>
|
||||
<script type="application/javascript"
|
||||
src="chrome://messenger/content/accountcreation/util.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://messenger/content/accountcreation/accountConfig.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://messenger/content/accountcreation/emailWizard.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://messenger/content/accountcreation/sanitizeDatatypes.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://messenger/content/accountcreation/fetchhttp.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://messenger/content/accountcreation/readFromXML.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://messenger/content/accountcreation/guessConfig.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://messenger/content/accountcreation/verifyConfig.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://messenger/content/accountcreation/fetchConfig.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://messenger/content/accountcreation/createInBackend.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://messenger/content/accountcreation/MyBadCertHandler.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://messenger/content/accountUtils.js" />
|
||||
|
||||
<keyset id="mailKeys">
|
||||
<key keycode="VK_ESCAPE" oncommand="window.close();"/>
|
||||
</keyset>
|
||||
|
||||
<panel id="insecureserver-cleartext-panel" class="popup-panel">
|
||||
<hbox>
|
||||
<image class="insecureLarry"/>
|
||||
<vbox flex="1">
|
||||
<description class="title">&insecureServer.tooltip.title;</description>
|
||||
<description class="details">
|
||||
&insecureUnencrypted.description;</description>
|
||||
</vbox>
|
||||
</hbox>
|
||||
</panel>
|
||||
<panel id="insecureserver-selfsigned-panel" class="popup-panel">
|
||||
<hbox>
|
||||
<image class="insecureLarry"/>
|
||||
<vbox flex="1">
|
||||
<description class="title">&insecureServer.tooltip.title;</description>
|
||||
<description class="details">
|
||||
&insecureSelfSigned.description;</description>
|
||||
</vbox>
|
||||
</hbox>
|
||||
</panel>
|
||||
<panel id="secureserver-panel" class="popup-panel">
|
||||
<hbox>
|
||||
<image class="secureLarry"/>
|
||||
<vbox flex="1">
|
||||
<description class="title">&secureServer.description;</description>
|
||||
</vbox>
|
||||
</hbox>
|
||||
</panel>
|
||||
|
||||
<tooltip id="insecureserver-cleartext">
|
||||
<hbox>
|
||||
<image class="insecureLarry"/>
|
||||
<vbox>
|
||||
<description class="title">&insecureServer.tooltip.title;</description>
|
||||
<description class="details">
|
||||
&insecureServer.tooltip.details;</description>
|
||||
</vbox>
|
||||
</hbox>
|
||||
</tooltip>
|
||||
<tooltip id="insecureserver-selfsigned">
|
||||
<hbox>
|
||||
<image class="insecureLarry"/>
|
||||
<vbox>
|
||||
<description class="title">&insecureServer.tooltip.title;</description>
|
||||
<description class="details">
|
||||
&insecureServer.tooltip.details;</description>
|
||||
</vbox>
|
||||
</hbox>
|
||||
</tooltip>
|
||||
<tooltip id="secureservertooltip">
|
||||
<hbox>
|
||||
<image class="secureLarry"/>
|
||||
<description class="title">&secureServer.description;</description>
|
||||
</hbox>
|
||||
</tooltip>
|
||||
<tooltip id="optional-password">
|
||||
<description>&password.text;</description>
|
||||
</tooltip>
|
||||
|
||||
<spacer id="fullwidth"/>
|
||||
|
||||
<vbox id="mastervbox" class="mastervbox" flex="1">
|
||||
<grid id="initialSettings">
|
||||
<columns>
|
||||
<column/>
|
||||
<column/>
|
||||
<column/>
|
||||
</columns>
|
||||
<rows>
|
||||
<row align="center">
|
||||
<label accesskey="&name.accesskey;"
|
||||
class="autoconfigLabel"
|
||||
value="&name.label;"
|
||||
control="realname"/>
|
||||
<textbox id="realname"
|
||||
class="padded"
|
||||
placeholder="&name.placeholder;"
|
||||
oninput="gEmailConfigWizard.onInputRealname();"
|
||||
onblur="gEmailConfigWizard.onBlurRealname();"/>
|
||||
<hbox>
|
||||
<description id="nametext" class="initialDesc">&name.text;</description>
|
||||
<image id="nameerroricon"
|
||||
hidden="true"
|
||||
class="warningicon"/>
|
||||
<description id="nameerror" class="errordescription" hidden="true"/>
|
||||
</hbox>
|
||||
</row>
|
||||
<row align="center">
|
||||
<label accesskey="&email.accesskey;"
|
||||
class="autoconfigLabel"
|
||||
value="&email.label;"
|
||||
control="email"/>
|
||||
<textbox id="email"
|
||||
class="padded uri-element"
|
||||
placeholder="&email.placeholder;"
|
||||
oninput="gEmailConfigWizard.onInputEmail();"
|
||||
onblur="gEmailConfigWizard.onBlurEmail();"/>
|
||||
<hbox>
|
||||
<image id="emailerroricon"
|
||||
hidden="true"
|
||||
class="warningicon"/>
|
||||
<description id="emailerror" class="errordescription" hidden="true"/>
|
||||
</hbox>
|
||||
</row>
|
||||
<row align="center">
|
||||
<!-- this starts out as text so the emptytext shows, but then
|
||||
changes to type=password once it's not empty -->
|
||||
<label accesskey="&password.accesskey;"
|
||||
class="autoconfigLabel"
|
||||
value="&password.label;"
|
||||
control="password"
|
||||
tooltip="optional-password"/>
|
||||
<textbox id="password"
|
||||
class="padded"
|
||||
placeholder="&password.placeholder;"
|
||||
type="text"
|
||||
oninput="gEmailConfigWizard.onInputPassword();"
|
||||
onfocus="gEmailConfigWizard.onFocusPassword();"
|
||||
onblur="gEmailConfigWizard.onBlurPassword();"/>
|
||||
<hbox>
|
||||
<image id="passworderroricon"
|
||||
hidden="true"
|
||||
class="warningicon"/>
|
||||
<description id="passworderror" class="errordescription" hidden="true"/>
|
||||
</hbox>
|
||||
</row>
|
||||
<row align="center" pack="start">
|
||||
<label class="autoconfigLabel"/>
|
||||
<checkbox id="remember_password"
|
||||
label="&rememberPassword.label;"
|
||||
accesskey="&rememberPassword.accesskey;"
|
||||
checked="true"/>
|
||||
</row>
|
||||
</rows>
|
||||
</grid>
|
||||
<spacer flex="1" />
|
||||
|
||||
<hbox id="status_area" flex="1">
|
||||
<vbox id="status_img_before" pack="start"/>
|
||||
<description id="status_msg"> </description>
|
||||
<!-- Include 160 = nbsp, to make the element occupy the
|
||||
full height, for at least one line. With a normal space,
|
||||
it does not have sufficient height. -->
|
||||
<vbox id="status_img_after" pack="start"/>
|
||||
</hbox>
|
||||
|
||||
<groupbox id="result_area" hidden="true">
|
||||
<radiogroup id="result_imappop" orient="horizontal">
|
||||
<radio id="result_select_imap" label="&imapLong.label;" value="1"
|
||||
oncommand="gEmailConfigWizard.onResultIMAPOrPOP3();"/>
|
||||
<radio id="result_select_pop3" label="&pop3Long.label;" value="2"
|
||||
oncommand="gEmailConfigWizard.onResultIMAPOrPOP3();"/>
|
||||
</radiogroup>
|
||||
<grid>
|
||||
<columns>
|
||||
<column/>
|
||||
<column flex="1"/>
|
||||
</columns>
|
||||
<rows>
|
||||
<row align="center">
|
||||
<label class="textbox-label" value="&incoming.label;"
|
||||
control="result-incoming"/>
|
||||
<textbox id="result-incoming" disabled="true" flex="1"/>
|
||||
</row>
|
||||
<row align="center">
|
||||
<label class="textbox-label" value="&outgoing.label;"
|
||||
control="result-outgoing"/>
|
||||
<textbox id="result-outgoing" disabled="true" flex="1"/>
|
||||
</row>
|
||||
<row align="center">
|
||||
<label class="textbox-label" value="&username.label;"
|
||||
control="result-username"/>
|
||||
<textbox id="result-username" disabled="true" flex="1"/>
|
||||
</row>
|
||||
</rows>
|
||||
</grid>
|
||||
</groupbox>
|
||||
|
||||
<groupbox id="manual-edit_area" hidden="true">
|
||||
<grid>
|
||||
<columns>
|
||||
<column/><!-- row label, e.g. "incoming" -->
|
||||
<column/><!-- protocol, e.g. "IMAP" -->
|
||||
<column flex="1"/><!-- hostname / username -->
|
||||
<column/><!-- port -->
|
||||
<column/><!-- SSL -->
|
||||
<column/><!-- auth method -->
|
||||
</columns>
|
||||
<rows>
|
||||
<row id="labels_row" align="center">
|
||||
<spacer/>
|
||||
<spacer/>
|
||||
<label value="&hostname.label;" class="columnHeader"/>
|
||||
<label value="&port.label;" class="columnHeader"/>
|
||||
<label value="&ssl.label;" class="columnHeader"/>
|
||||
<label value="&auth.label;" class="columnHeader"/>
|
||||
</row>
|
||||
<row id="incoming_server_area">
|
||||
<hbox align="center" pack="end">
|
||||
<label class="textbox-label"
|
||||
value="&incoming.label;"
|
||||
control="incoming_hostname"/>
|
||||
</hbox>
|
||||
<menulist id="incoming_protocol"
|
||||
oncommand="gEmailConfigWizard.onChangedProtocolIncoming();"
|
||||
sizetopopup="always">
|
||||
<menupopup>
|
||||
<menuitem label="&imap.label;" value="1"/>
|
||||
<menuitem label="&pop3.label;" value="2"/>
|
||||
</menupopup>
|
||||
</menulist>
|
||||
<textbox id="incoming_hostname"
|
||||
oninput="gEmailConfigWizard.onInputHostname();"
|
||||
class="host uri-element"/>
|
||||
<menulist id="incoming_port"
|
||||
editable="true"
|
||||
oninput="gEmailConfigWizard.onChangedPortIncoming();"
|
||||
oncommand="gEmailConfigWizard.onChangedPortIncoming();"
|
||||
class="port">
|
||||
<menupopup/>
|
||||
</menulist>
|
||||
<menulist id="incoming_ssl"
|
||||
class="security"
|
||||
oncommand="gEmailConfigWizard.onChangedSSLIncoming();"
|
||||
sizetopopup="always">
|
||||
<menupopup>
|
||||
<!-- values defined in nsMsgSocketType -->
|
||||
<menuitem label="&autodetect.label;" value="0"/>
|
||||
<menuitem label="&noEncryption.label;" value="1"/>
|
||||
<menuitem label="&starttls.label;" value="3"/>
|
||||
<menuitem label="&sslTls.label;" value="2"/>
|
||||
</menupopup>
|
||||
</menulist>
|
||||
<menulist id="incoming_authMethod"
|
||||
class="auth"
|
||||
oncommand="gEmailConfigWizard.onChangedInAuth();"
|
||||
sizetopopup="always">
|
||||
<menupopup>
|
||||
<menuitem label="&autodetect.label;" value="0"/>
|
||||
<!-- values defined in nsMsgAuthMethod -->
|
||||
<!-- labels set from messenger.properties
|
||||
to avoid duplication -->
|
||||
<menuitem id="in-authMethod-password-cleartext" value="3"/>
|
||||
<menuitem id="in-authMethod-password-encrypted" value="4"/>
|
||||
<menuitem id="in-authMethod-kerberos" value="5"/>
|
||||
<menuitem id="in-authMethod-ntlm" value="6"/>
|
||||
<menuitem id="in-authMethod-oauth2" value="10" hidden="true"/>
|
||||
</menupopup>
|
||||
</menulist>
|
||||
</row>
|
||||
<row id="outgoing_server_area" align="center">
|
||||
<label class="textbox-label"
|
||||
value="&outgoing.label;"
|
||||
control="outgoing_hostname"/>
|
||||
<label id="outgoing_protocol"
|
||||
value="&smtp.label;"/>
|
||||
<menulist id="outgoing_hostname"
|
||||
editable="true"
|
||||
sizetopopup="none"
|
||||
oninput="gEmailConfigWizard.onInputHostname();"
|
||||
oncommand="gEmailConfigWizard.onChangedOutgoingDropdown();"
|
||||
onpopupshowing="gEmailConfigWizard.onOpenOutgoingDropdown();"
|
||||
class="host uri-element">
|
||||
<menupopup id="outgoing_hostname_popup"/>
|
||||
</menulist>
|
||||
<menulist id="outgoing_port"
|
||||
editable="true"
|
||||
oninput="gEmailConfigWizard.onChangedPortOutgoing();"
|
||||
oncommand="gEmailConfigWizard.onChangedPortOutgoing();"
|
||||
class="port">
|
||||
<menupopup/>
|
||||
</menulist>
|
||||
<menulist id="outgoing_ssl"
|
||||
class="security"
|
||||
oncommand="gEmailConfigWizard.onChangedSSLOutgoing();"
|
||||
sizetopopup="always">
|
||||
<menupopup>
|
||||
<!-- @see incoming -->
|
||||
<menuitem label="&autodetect.label;" value="0"/>
|
||||
<menuitem label="&noEncryption.label;" value="1"/>
|
||||
<menuitem label="&starttls.label;" value="3"/>
|
||||
<menuitem label="&sslTls.label;" value="2"/>
|
||||
</menupopup>
|
||||
</menulist>
|
||||
<menulist id="outgoing_authMethod"
|
||||
class="auth"
|
||||
oncommand="gEmailConfigWizard.onChangedOutAuth(this.selectedItem);"
|
||||
sizetopopup="always">
|
||||
<menupopup>
|
||||
<menuitem label="&autodetect.label;" value="0"/>
|
||||
<!-- @see incoming -->
|
||||
<menuitem id="out-authMethod-no" value="1"/>
|
||||
<menuitem id="out-authMethod-password-cleartext" value="3"/>
|
||||
<menuitem id="out-authMethod-password-encrypted" value="4"/>
|
||||
<menuitem id="out-authMethod-kerberos" value="5"/>
|
||||
<menuitem id="out-authMethod-ntlm" value="6"/>
|
||||
<menuitem id="out-authMethod-oauth2" value="10" hidden="true"/>
|
||||
</menupopup>
|
||||
</menulist>
|
||||
</row>
|
||||
<row id="username_area" align="center">
|
||||
<label class="textbox-label"
|
||||
value="&username.label;"/>
|
||||
<label class="columnHeader"
|
||||
value="&incoming.label;"
|
||||
control="incoming_username"/>
|
||||
<textbox id="incoming_username"
|
||||
oninput="gEmailConfigWizard.onInputInUsername();"
|
||||
class="username"/>
|
||||
<spacer/>
|
||||
<label class="columnHeader"
|
||||
id="outgoing_label"
|
||||
value="&outgoing.label;"
|
||||
control="outgoing_username"/>
|
||||
<textbox id="outgoing_username"
|
||||
oninput="gEmailConfigWizard.onInputOutUsername();"
|
||||
class="username"/>
|
||||
</row>
|
||||
</rows>
|
||||
</grid>
|
||||
</groupbox>
|
||||
|
||||
<spacer flex="1" />
|
||||
<hbox id="buttons_area">
|
||||
<hbox id="left_buttons_area" align="center" pack="start">
|
||||
<button id="provisioner_button"
|
||||
label="&switch-to-provisioner.label;"
|
||||
accesskey="&switch-to-provisioner.accesskey;"
|
||||
class="larger-button"
|
||||
oncommand="gEmailConfigWizard.onSwitchToProvisioner();"/>
|
||||
<button id="manual-edit_button"
|
||||
label="&manual-edit.label;"
|
||||
accesskey="&manual-edit.accesskey;"
|
||||
hidden="true"
|
||||
oncommand="gEmailConfigWizard.onManualEdit();"/>
|
||||
<button id="advanced-setup_button"
|
||||
label="&advancedSetup.label;"
|
||||
accesskey="&advancedSetup.accesskey;"
|
||||
disabled="true"
|
||||
hidden="true"
|
||||
oncommand="gEmailConfigWizard.onAdvancedSetup();"/>
|
||||
</hbox>
|
||||
<spacer flex="1"/>
|
||||
<hbox id="right_buttons_area" align="center" pack="end">
|
||||
<button id="stop_button"
|
||||
label="&stop.label;"
|
||||
accesskey="&stop.accesskey;"
|
||||
hidden="true"
|
||||
oncommand="gEmailConfigWizard.onStop();"/>
|
||||
<button id="cancel_button"
|
||||
label="&cancel.label;"
|
||||
accesskey="&cancel.accesskey;"
|
||||
oncommand="gEmailConfigWizard.onCancel();"/>
|
||||
<button id="half-manual-test_button"
|
||||
label="&half-manual-test.label;"
|
||||
accesskey="&half-manual-test.accesskey;"
|
||||
hidden="true"
|
||||
oncommand="gEmailConfigWizard.onHalfManualTest();"/>
|
||||
<button id="next_button"
|
||||
label="&continue.label;"
|
||||
accesskey="&continue.accesskey;"
|
||||
hidden="false"
|
||||
oncommand="gEmailConfigWizard.onNext();"/>
|
||||
<button id="create_button"
|
||||
label="&doneAccount.label;"
|
||||
accesskey="&doneAccount.accesskey;"
|
||||
class="important-button"
|
||||
hidden="true"
|
||||
oncommand="gEmailConfigWizard.onCreate();"/>
|
||||
</hbox>
|
||||
</hbox>
|
||||
</vbox>
|
||||
|
||||
|
||||
<vbox id="warningbox" hidden="true" flex="1">
|
||||
<hbox class="warning" flex="1">
|
||||
<vbox class="larrybox">
|
||||
<image id="insecure_larry" class="insecureLarry"/>
|
||||
</vbox>
|
||||
<vbox flex="1" class="warning_text">
|
||||
<label class="warning-heading">&warning.label;</label>
|
||||
<vbox id="incoming_box">
|
||||
<hbox>
|
||||
<label class="warning_settings" value="&incomingSettings.label;"/>
|
||||
<description id="warning_incoming"/>
|
||||
</hbox>
|
||||
<label id="incoming_technical"
|
||||
class="technical_details"
|
||||
value="&technicaldetails.label;"
|
||||
onclick="gSecurityWarningDialog.toggleDetails('incoming');"/>
|
||||
<description id="incoming_details" collapsed="true"/>
|
||||
</vbox>
|
||||
<vbox id="outgoing_box">
|
||||
<hbox>
|
||||
<label class="warning_settings" value="&outgoingSettings.label;"/>
|
||||
<description id="warning_outgoing"/>
|
||||
</hbox>
|
||||
<label id="outgoing_technical"
|
||||
class="technical_details"
|
||||
value="&technicaldetails.label;"
|
||||
onclick="gSecurityWarningDialog.toggleDetails('outgoing');"/>
|
||||
<description id="outgoing_details" collapsed="true"/>
|
||||
</vbox>
|
||||
<spacer flex="10"/>
|
||||
<description id="findoutmore">
|
||||
&contactYourProvider.description;</description>
|
||||
<spacer flex="100"/>
|
||||
<checkbox id="acknowledge_warning"
|
||||
label="&confirmWarning.label;"
|
||||
accesskey="&confirmWarning.accesskey;"
|
||||
class="acknowledge_checkbox"
|
||||
oncommand="gSecurityWarningDialog.toggleAcknowledge()"/>
|
||||
<hbox>
|
||||
<button id="getmeoutofhere"
|
||||
label="&changeSettings.label;"
|
||||
accesskey="&changeSettings.accesskey;"
|
||||
oncommand="gSecurityWarningDialog.onCancel()"/>
|
||||
<spacer flex="1"/>
|
||||
<button id="iknow"
|
||||
label="&doneAccount.label;"
|
||||
accesskey="&doneAccount.accesskey;"
|
||||
disabled="true"
|
||||
oncommand="gSecurityWarningDialog.onOK()"/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
</hbox>
|
||||
</vbox>
|
||||
</window>
|
||||
240
mailnews/base/prefs/content/accountcreation/fetchConfig.js
Normal file
240
mailnews/base/prefs/content/accountcreation/fetchConfig.js
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
/* -*- Mode: Java; tab-width: 2; 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/. */
|
||||
|
||||
/**
|
||||
* Tries to find a configuration for this ISP on the local harddisk, in the
|
||||
* application install directory's "isp" subdirectory.
|
||||
* Params @see fetchConfigFromISP()
|
||||
*/
|
||||
|
||||
Components.utils.import("resource:///modules/mailServices.js");
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
Components.utils.import("resource://gre/modules/JXON.js");
|
||||
|
||||
|
||||
function fetchConfigFromDisk(domain, successCallback, errorCallback)
|
||||
{
|
||||
return new TimeoutAbortable(runAsync(function()
|
||||
{
|
||||
try {
|
||||
// <TB installdir>/isp/example.com.xml
|
||||
var configLocation = Services.dirsvc.get("CurProcD", Ci.nsIFile);
|
||||
configLocation.append("isp");
|
||||
configLocation.append(sanitize.hostname(domain) + ".xml");
|
||||
|
||||
var contents =
|
||||
readURLasUTF8(Services.io.newFileURI(configLocation));
|
||||
let domParser = Cc["@mozilla.org/xmlextras/domparser;1"]
|
||||
.createInstance(Ci.nsIDOMParser);
|
||||
successCallback(readFromXML(JXON.build(
|
||||
domParser.parseFromString(contents, "text/xml"))));
|
||||
} catch (e) { errorCallback(e); }
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to get a configuration from the ISP / mail provider directly.
|
||||
*
|
||||
* Disclaimers:
|
||||
* - To support domain hosters, we cannot use SSL. That means we
|
||||
* rely on insecure DNS and http, which means the results may be
|
||||
* forged when under attack. The same is true for guessConfig(), though.
|
||||
*
|
||||
* @param domain {String} The domain part of the user's email address
|
||||
* @param emailAddress {String} The user's email address
|
||||
* @param successCallback {Function(config {AccountConfig}})} A callback that
|
||||
* will be called when we could retrieve a configuration.
|
||||
* The AccountConfig object will be passed in as first parameter.
|
||||
* @param errorCallback {Function(ex)} A callback that
|
||||
* will be called when we could not retrieve a configuration,
|
||||
* for whatever reason. This is expected (e.g. when there's no config
|
||||
* for this domain at this location),
|
||||
* so do not unconditionally show this to the user.
|
||||
* The first paramter will be an exception object or error string.
|
||||
*/
|
||||
function fetchConfigFromISP(domain, emailAddress, successCallback,
|
||||
errorCallback)
|
||||
{
|
||||
if (!Services.prefs.getBoolPref(
|
||||
"mailnews.auto_config.fetchFromISP.enabled")) {
|
||||
errorCallback("ISP fetch disabled per user preference");
|
||||
return;
|
||||
}
|
||||
|
||||
let url1 = "http://autoconfig." + sanitize.hostname(domain) +
|
||||
"/mail/config-v1.1.xml";
|
||||
// .well-known/ <http://tools.ietf.org/html/draft-nottingham-site-meta-04>
|
||||
let url2 = "http://" + sanitize.hostname(domain) +
|
||||
"/.well-known/autoconfig/mail/config-v1.1.xml";
|
||||
let sucAbortable = new SuccessiveAbortable();
|
||||
var time = Date.now();
|
||||
var urlArgs = { emailaddress: emailAddress };
|
||||
if (!Services.prefs.getBoolPref(
|
||||
"mailnews.auto_config.fetchFromISP.sendEmailAddress")) {
|
||||
delete urlArgs.emailaddress;
|
||||
}
|
||||
let fetch1 = new FetchHTTP(url1, urlArgs, false,
|
||||
function(result)
|
||||
{
|
||||
successCallback(readFromXML(result));
|
||||
},
|
||||
function(e1) // fetch1 failed
|
||||
{
|
||||
ddump("fetchisp 1 <" + url1 + "> took " + (Date.now() - time) +
|
||||
"ms and failed with " + e1);
|
||||
time = Date.now();
|
||||
if (e1 instanceof CancelledException)
|
||||
{
|
||||
errorCallback(e1);
|
||||
return;
|
||||
}
|
||||
|
||||
let fetch2 = new FetchHTTP(url2, urlArgs, false,
|
||||
function(result)
|
||||
{
|
||||
successCallback(readFromXML(result));
|
||||
},
|
||||
function(e2)
|
||||
{
|
||||
ddump("fetchisp 2 <" + url2 + "> took " + (Date.now() - time) +
|
||||
"ms and failed with " + e2);
|
||||
// return the error for the primary call,
|
||||
// unless the fetch was cancelled
|
||||
errorCallback(e2 instanceof CancelledException ? e2 : e1);
|
||||
});
|
||||
sucAbortable.current = fetch2;
|
||||
fetch2.start();
|
||||
});
|
||||
sucAbortable.current = fetch1;
|
||||
fetch1.start();
|
||||
return sucAbortable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to get a configuration for this ISP from a central database at
|
||||
* Mozilla servers.
|
||||
* Params @see fetchConfigFromISP()
|
||||
*/
|
||||
|
||||
function fetchConfigFromDB(domain, successCallback, errorCallback)
|
||||
{
|
||||
let url = Services.prefs.getCharPref("mailnews.auto_config_url");
|
||||
domain = sanitize.hostname(domain);
|
||||
|
||||
// If we don't specify a place to put the domain, put it at the end.
|
||||
if (!url.includes("{{domain}}"))
|
||||
url = url + domain;
|
||||
else
|
||||
url = url.replace("{{domain}}", domain);
|
||||
url = url.replace("{{accounts}}", MailServices.accounts.accounts.length);
|
||||
|
||||
if (!url.length)
|
||||
return errorCallback("no fetch url set");
|
||||
let fetch = new FetchHTTP(url, null, false,
|
||||
function(result)
|
||||
{
|
||||
successCallback(readFromXML(result));
|
||||
},
|
||||
errorCallback);
|
||||
fetch.start();
|
||||
return fetch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does a lookup of DNS MX, to get the server who is responsible for
|
||||
* recieving mail for this domain. Then it takes the domain of that
|
||||
* server, and does another lookup (in ISPDB and possible at ISP autoconfig
|
||||
* server) and if such a config is found, returns that.
|
||||
*
|
||||
* Disclaimers:
|
||||
* - DNS is unprotected, meaning the results could be forged.
|
||||
* The same is true for fetchConfigFromISP() and guessConfig(), though.
|
||||
* - DNS MX tells us the incoming server, not the mailbox (IMAP) server.
|
||||
* They are different. This mechnism is only an approximation
|
||||
* for hosted domains (yourname.com is served by mx.hoster.com and
|
||||
* therefore imap.hoster.com - that "therefore" is exactly the
|
||||
* conclusional jump we make here.) and alternative domains
|
||||
* (e.g. yahoo.de -> yahoo.com).
|
||||
* - We make a look up for the base domain. E.g. if MX is
|
||||
* mx1.incoming.servers.hoster.com, we look up hoster.com.
|
||||
* Thanks to Services.eTLD, we also get bbc.co.uk right.
|
||||
*
|
||||
* Params @see fetchConfigFromISP()
|
||||
*/
|
||||
function fetchConfigForMX(domain, successCallback, errorCallback)
|
||||
{
|
||||
domain = sanitize.hostname(domain);
|
||||
|
||||
var sucAbortable = new SuccessiveAbortable();
|
||||
var time = Date.now();
|
||||
sucAbortable.current = getMX(domain,
|
||||
function(mxHostname) // success
|
||||
{
|
||||
ddump("getmx took " + (Date.now() - time) + "ms");
|
||||
let sld = Services.eTLD.getBaseDomainFromHost(mxHostname);
|
||||
ddump("base domain " + sld + " for " + mxHostname);
|
||||
if (sld == domain)
|
||||
{
|
||||
errorCallback("MX lookup would be no different from domain");
|
||||
return;
|
||||
}
|
||||
sucAbortable.current = fetchConfigFromDB(sld, successCallback,
|
||||
errorCallback);
|
||||
},
|
||||
errorCallback);
|
||||
return sucAbortable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries the DNS MX for the domain
|
||||
*
|
||||
* The current implementation goes to a web service to do the
|
||||
* DNS resolve for us, because Mozilla unfortunately has no implementation
|
||||
* to do it. That's just a workaround. Once bug 545866 is fixed, we make
|
||||
* the DNS query directly on the client. The API of this function should not
|
||||
* change then.
|
||||
*
|
||||
* Returns (in successCallback) the hostname of the MX server.
|
||||
* If there are several entires with different preference values,
|
||||
* only the most preferred (i.e. those with the lowest value)
|
||||
* is returned. If there are several most preferred servers (i.e.
|
||||
* round robin), only one of them is returned.
|
||||
*
|
||||
* @param domain @see fetchConfigFromISP()
|
||||
* @param successCallback {function(hostname {String})
|
||||
* Called when we found an MX for the domain.
|
||||
* For |hostname|, see description above.
|
||||
* @param errorCallback @see fetchConfigFromISP()
|
||||
* @returns @see fetchConfigFromISP()
|
||||
*/
|
||||
function getMX(domain, successCallback, errorCallback)
|
||||
{
|
||||
domain = sanitize.hostname(domain);
|
||||
|
||||
let url = Services.prefs.getCharPref("mailnews.mx_service_url");
|
||||
if (!url)
|
||||
errorCallback("no URL for MX service configured");
|
||||
url += domain;
|
||||
|
||||
let fetch = new FetchHTTP(url, null, false,
|
||||
function(result)
|
||||
{
|
||||
// result is plain text, with one line per server.
|
||||
// So just take the first line
|
||||
ddump("MX query result: \n" + result + "(end)");
|
||||
assert(typeof(result) == "string");
|
||||
let first = result.split("\n")[0];
|
||||
first.toLowerCase().replace(/[^a-z0-9\-_\.]*/g, "");
|
||||
if (first.length == 0)
|
||||
{
|
||||
errorCallback("no MX found");
|
||||
return;
|
||||
}
|
||||
successCallback(first);
|
||||
},
|
||||
errorCallback);
|
||||
fetch.start();
|
||||
return fetch;
|
||||
}
|
||||
267
mailnews/base/prefs/content/accountcreation/fetchhttp.js
Normal file
267
mailnews/base/prefs/content/accountcreation/fetchhttp.js
Normal file
|
|
@ -0,0 +1,267 @@
|
|||
/* -*- Mode: Java; tab-width: 2; 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/. */
|
||||
|
||||
/**
|
||||
* This is a small wrapper around XMLHttpRequest, which solves various
|
||||
* inadequacies of the API, e.g. error handling. It is entirely generic and
|
||||
* can be used for purposes outside of even mail.
|
||||
*
|
||||
* It does not provide download progress, but assumes that the
|
||||
* fetched resource is so small (<1 10 KB) that the roundtrip and
|
||||
* response generation is far more significant than the
|
||||
* download time of the response. In other words, it's fine for RPC,
|
||||
* but not for bigger file downloads.
|
||||
*/
|
||||
|
||||
Components.utils.import("resource://gre/modules/JXON.js");
|
||||
|
||||
/**
|
||||
* Set up a fetch.
|
||||
*
|
||||
* @param url {String} URL of the server function.
|
||||
* ATTENTION: The caller needs to make sure that the URL is secure to call.
|
||||
* @param urlArgs {Object, associative array} Parameters to add
|
||||
* to the end of the URL as query string. E.g.
|
||||
* { foo: "bla", bar: "blub blub" } will add "?foo=bla&bar=blub%20blub"
|
||||
* to the URL
|
||||
* (unless the URL already has a "?", then it adds "&foo...").
|
||||
* The values will be urlComponentEncoded, so pass them unencoded.
|
||||
* @param post {Boolean} HTTP GET or POST
|
||||
* Only influences the HTTP request method,
|
||||
* i.e. first line of the HTTP request, not the body or parameters.
|
||||
* Use POST when you modify server state,
|
||||
* GET when you only request information.
|
||||
*
|
||||
* @param successCallback {Function(result {String})}
|
||||
* Called when the server call worked (no errors).
|
||||
* |result| will contain the body of the HTTP reponse, as string.
|
||||
* @param errorCallback {Function(ex)}
|
||||
* Called in case of error. ex contains the error
|
||||
* with a user-displayable but not localized |.message| and maybe a
|
||||
* |.code|, which can be either
|
||||
* - an nsresult error code,
|
||||
* - an HTTP result error code (0...1000) or
|
||||
* - negative: 0...-100 :
|
||||
* -2 = can't resolve server in DNS etc.
|
||||
* -4 = response body (e.g. XML) malformed
|
||||
*/
|
||||
/* not yet supported:
|
||||
* @param headers {Object, associative array} Like urlArgs,
|
||||
* just that the params will be added as HTTP headers.
|
||||
* { foo: "blub blub" } will add "Foo: Blub blub"
|
||||
* The values will be urlComponentEncoded, apart from space,
|
||||
* so pass them unencoded.
|
||||
* @param headerArgs {Object, associative array} Like urlArgs,
|
||||
* just that the params will be added as HTTP headers.
|
||||
* { foo: "blub blub" } will add "X-Moz-Arg-Foo: Blub blub"
|
||||
* The values will be urlComponentEncoded, apart from space,
|
||||
* so pass them unencoded.
|
||||
* @param bodyArgs {Object, associative array} Like urlArgs,
|
||||
* just that the params will be sent x-url-encoded in the body,
|
||||
* like a HTML form post.
|
||||
* The values will be urlComponentEncoded, so pass them unencoded.
|
||||
* This cannot be used together with |uploadBody|.
|
||||
* @param uploadbody {Object} Arbitrary object, which to use as
|
||||
* body of the HTTP request. Will also set the mimetype accordingly.
|
||||
* Only supported object types, currently only E4X is supported
|
||||
* (sending XML).
|
||||
* Usually, you have nothing to upload, so just pass |null|.
|
||||
*/
|
||||
function FetchHTTP(url, urlArgs, post, successCallback, errorCallback)
|
||||
{
|
||||
assert(typeof(successCallback) == "function", "BUG: successCallback");
|
||||
assert(typeof(errorCallback) == "function", "BUG: errorCallback");
|
||||
this._url = sanitize.string(url);
|
||||
if (!urlArgs)
|
||||
urlArgs = {};
|
||||
|
||||
this._urlArgs = urlArgs;
|
||||
this._post = sanitize.boolean(post);
|
||||
this._successCallback = successCallback;
|
||||
this._errorCallback = errorCallback;
|
||||
}
|
||||
FetchHTTP.prototype =
|
||||
{
|
||||
__proto__: Abortable.prototype,
|
||||
_url : null, // URL as passed to ctor, without arguments
|
||||
_urlArgs : null,
|
||||
_post : null,
|
||||
_successCallback : null,
|
||||
_errorCallback : null,
|
||||
_request : null, // the XMLHttpRequest object
|
||||
result : null,
|
||||
|
||||
start : function()
|
||||
{
|
||||
var url = this._url;
|
||||
for (var name in this._urlArgs)
|
||||
{
|
||||
url += (!url.includes("?") ? "?" : "&") +
|
||||
name + "=" + encodeURIComponent(this._urlArgs[name]);
|
||||
}
|
||||
this._request = new XMLHttpRequest();
|
||||
let request = this._request;
|
||||
request.open(this._post ? "POST" : "GET", url);
|
||||
request.channel.loadGroup = null;
|
||||
// needs bug 407190 patch v4 (or higher) - uncomment if that lands.
|
||||
// try {
|
||||
// var channel = request.channel.QueryInterface(Ci.nsIHttpChannel2);
|
||||
// channel.connectTimeout = 5;
|
||||
// channel.requestTimeout = 5;
|
||||
// } catch (e) { dump(e + "\n"); }
|
||||
|
||||
var me = this;
|
||||
request.onload = function() { me._response(true); }
|
||||
request.onerror = function() { me._response(false); }
|
||||
request.send(null);
|
||||
},
|
||||
_response : function(success, exStored)
|
||||
{
|
||||
try
|
||||
{
|
||||
var errorCode = null;
|
||||
var errorStr = null;
|
||||
|
||||
if (success && this._request.status >= 200 &&
|
||||
this._request.status < 300) // HTTP level success
|
||||
{
|
||||
try
|
||||
{
|
||||
// response
|
||||
var mimetype = this._request.getResponseHeader("Content-Type");
|
||||
if (!mimetype)
|
||||
mimetype = "";
|
||||
mimetype = mimetype.split(";")[0];
|
||||
if (mimetype == "text/xml" ||
|
||||
mimetype == "application/xml" ||
|
||||
mimetype == "text/rdf")
|
||||
{
|
||||
this.result = JXON.build(this._request.responseXML);
|
||||
}
|
||||
else
|
||||
{
|
||||
//ddump("mimetype: " + mimetype + " only supported as text");
|
||||
this.result = this._request.responseText;
|
||||
}
|
||||
//ddump("result:\n" + this.result);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
success = false;
|
||||
errorStr = getStringBundle(
|
||||
"chrome://messenger/locale/accountCreationUtil.properties")
|
||||
.GetStringFromName("bad_response_content.error");
|
||||
errorCode = -4;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
success = false;
|
||||
try
|
||||
{
|
||||
errorCode = this._request.status;
|
||||
errorStr = this._request.statusText;
|
||||
} catch (e) {
|
||||
// If we can't resolve the hostname in DNS etc., .statusText throws
|
||||
errorCode = -2;
|
||||
errorStr = getStringBundle(
|
||||
"chrome://messenger/locale/accountCreationUtil.properties")
|
||||
.GetStringFromName("cannot_contact_server.error");
|
||||
ddump(errorStr);
|
||||
}
|
||||
}
|
||||
|
||||
// Callbacks
|
||||
if (success)
|
||||
{
|
||||
try {
|
||||
this._successCallback(this.result);
|
||||
} catch (e) {
|
||||
logException(e);
|
||||
this._error(e);
|
||||
}
|
||||
}
|
||||
else if (exStored)
|
||||
this._error(exStored);
|
||||
else
|
||||
this._error(new ServerException(errorStr, errorCode, this._url));
|
||||
|
||||
if (this._finishedCallback)
|
||||
{
|
||||
try {
|
||||
this._finishedCallback(this);
|
||||
} catch (e) {
|
||||
logException(e);
|
||||
this._error(e);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
// error in our fetchhttp._response() code
|
||||
logException(e);
|
||||
this._error(e);
|
||||
}
|
||||
},
|
||||
_error : function(e)
|
||||
{
|
||||
try {
|
||||
this._errorCallback(e);
|
||||
} catch (e) {
|
||||
// error in errorCallback, too!
|
||||
logException(e);
|
||||
alertPrompt("Error in errorCallback for fetchhttp", e);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Call this between start() and finishedCallback fired.
|
||||
*/
|
||||
cancel : function(ex)
|
||||
{
|
||||
assert(!this.result, "Call already returned");
|
||||
|
||||
this._request.abort();
|
||||
|
||||
// Need to manually call error handler
|
||||
// <https://bugzilla.mozilla.org/show_bug.cgi?id=218236#c11>
|
||||
this._response(false, ex ? ex : new UserCancelledException());
|
||||
},
|
||||
/**
|
||||
* Allows caller or lib to be notified when the call is done.
|
||||
* This is useful to enable and disable a Cancel button in the UI,
|
||||
* which allows to cancel the network request.
|
||||
*/
|
||||
setFinishedCallback : function(finishedCallback)
|
||||
{
|
||||
this._finishedCallback = finishedCallback;
|
||||
}
|
||||
}
|
||||
|
||||
function CancelledException(msg)
|
||||
{
|
||||
Exception.call(this, msg);
|
||||
}
|
||||
CancelledException.prototype = Object.create(Exception.prototype);
|
||||
CancelledException.prototype.constructor = CancelledException;
|
||||
|
||||
function UserCancelledException(msg)
|
||||
{
|
||||
// The user knows they cancelled so I don't see a need
|
||||
// for a message to that effect.
|
||||
if (!msg)
|
||||
msg = "User cancelled";
|
||||
CancelledException.call(this, msg);
|
||||
}
|
||||
UserCancelledException.prototype = Object.create(CancelledException.prototype);
|
||||
UserCancelledException.prototype.constructor = UserCancelledException;
|
||||
|
||||
function ServerException(msg, code, uri)
|
||||
{
|
||||
Exception.call(this, msg);
|
||||
this.code = code;
|
||||
this.uri = uri;
|
||||
}
|
||||
ServerException.prototype = Object.create(Exception.prototype);
|
||||
ServerException.prototype.constructor = ServerException;
|
||||
|
||||
1145
mailnews/base/prefs/content/accountcreation/guessConfig.js
Normal file
1145
mailnews/base/prefs/content/accountcreation/guessConfig.js
Normal file
File diff suppressed because it is too large
Load diff
238
mailnews/base/prefs/content/accountcreation/readFromXML.js
Normal file
238
mailnews/base/prefs/content/accountcreation/readFromXML.js
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
/* -*- Mode: Java; tab-width: 2; 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/. */
|
||||
|
||||
/**
|
||||
* Takes an XML snipplet (as JXON) and reads the values into
|
||||
* a new AccountConfig object.
|
||||
* It does so securely (or tries to), by trying to avoid remote execution
|
||||
* and similar holes which can appear when reading too naively.
|
||||
* Of course it cannot tell whether the actual values are correct,
|
||||
* e.g. it can't tell whether the host name is a good server.
|
||||
*
|
||||
* The XML format is documented at
|
||||
* <https://wiki.mozilla.org/Thunderbird:Autoconfiguration:ConfigFileFormat>
|
||||
*
|
||||
* @param clientConfigXML {JXON} The <clientConfig> node.
|
||||
* @return AccountConfig object filled with the data from XML
|
||||
*/
|
||||
Components.utils.import("resource:///modules/hostnameUtils.jsm");
|
||||
|
||||
function readFromXML(clientConfigXML)
|
||||
{
|
||||
function array_or_undef(value) {
|
||||
return value === undefined ? [] : value;
|
||||
}
|
||||
var exception;
|
||||
if (typeof(clientConfigXML) != "object" ||
|
||||
!("clientConfig" in clientConfigXML) ||
|
||||
!("emailProvider" in clientConfigXML.clientConfig))
|
||||
{
|
||||
dump("client config xml = " + JSON.stringify(clientConfigXML) + "\n");
|
||||
var stringBundle = getStringBundle(
|
||||
"chrome://messenger/locale/accountCreationModel.properties");
|
||||
throw stringBundle.GetStringFromName("no_emailProvider.error");
|
||||
}
|
||||
var xml = clientConfigXML.clientConfig.emailProvider;
|
||||
|
||||
var d = new AccountConfig();
|
||||
d.source = AccountConfig.kSourceXML;
|
||||
|
||||
d.id = sanitize.hostname(xml["@id"]);
|
||||
d.displayName = d.id;
|
||||
try {
|
||||
d.displayName = sanitize.label(xml.displayName);
|
||||
} catch (e) { logException(e); }
|
||||
for (var domain of xml.$domain)
|
||||
{
|
||||
try {
|
||||
d.domains.push(sanitize.hostname(domain));
|
||||
} catch (e) { logException(e); exception = e; }
|
||||
}
|
||||
if (d.domains.length == 0)
|
||||
throw exception ? exception : "need proper <domain> in XML";
|
||||
exception = null;
|
||||
|
||||
// incoming server
|
||||
for (let iX of array_or_undef(xml.$incomingServer)) // input (XML)
|
||||
{
|
||||
let iO = d.createNewIncoming(); // output (object)
|
||||
try {
|
||||
// throws if not supported
|
||||
iO.type = sanitize.enum(iX["@type"], ["pop3", "imap", "nntp"]);
|
||||
iO.hostname = sanitize.hostname(iX.hostname);
|
||||
iO.port = sanitize.integerRange(iX.port, kMinPort, kMaxPort);
|
||||
// We need a username even for Kerberos, need it even internally.
|
||||
iO.username = sanitize.string(iX.username); // may be a %VARIABLE%
|
||||
|
||||
if ("password" in iX) {
|
||||
d.rememberPassword = true;
|
||||
iO.password = sanitize.string(iX.password);
|
||||
}
|
||||
|
||||
for (let iXsocketType of array_or_undef(iX.$socketType))
|
||||
{
|
||||
try {
|
||||
iO.socketType = sanitize.translate(iXsocketType,
|
||||
{ plain : 1, SSL: 2, STARTTLS: 3 });
|
||||
break; // take first that we support
|
||||
} catch (e) { exception = e; }
|
||||
}
|
||||
if (!iO.socketType)
|
||||
throw exception ? exception : "need proper <socketType> in XML";
|
||||
exception = null;
|
||||
|
||||
for (let iXauth of array_or_undef(iX.$authentication))
|
||||
{
|
||||
try {
|
||||
iO.auth = sanitize.translate(iXauth,
|
||||
{ "password-cleartext" : Ci.nsMsgAuthMethod.passwordCleartext,
|
||||
// @deprecated TODO remove
|
||||
"plain" : Ci.nsMsgAuthMethod.passwordCleartext,
|
||||
"password-encrypted" : Ci.nsMsgAuthMethod.passwordEncrypted,
|
||||
// @deprecated TODO remove
|
||||
"secure" : Ci.nsMsgAuthMethod.passwordEncrypted,
|
||||
"GSSAPI" : Ci.nsMsgAuthMethod.GSSAPI,
|
||||
"NTLM" : Ci.nsMsgAuthMethod.NTLM,
|
||||
"OAuth2" : Ci.nsMsgAuthMethod.OAuth2 });
|
||||
break; // take first that we support
|
||||
} catch (e) { exception = e; }
|
||||
}
|
||||
if (!iO.auth)
|
||||
throw exception ? exception : "need proper <authentication> in XML";
|
||||
exception = null;
|
||||
|
||||
// defaults are in accountConfig.js
|
||||
if (iO.type == "pop3" && "pop3" in iX)
|
||||
{
|
||||
try {
|
||||
if ("leaveMessagesOnServer" in iX.pop3)
|
||||
iO.leaveMessagesOnServer =
|
||||
sanitize.boolean(iX.pop3.leaveMessagesOnServer);
|
||||
if ("daysToLeaveMessagesOnServer" in iX.pop3)
|
||||
iO.daysToLeaveMessagesOnServer =
|
||||
sanitize.integer(iX.pop3.daysToLeaveMessagesOnServer);
|
||||
} catch (e) { logException(e); }
|
||||
try {
|
||||
if ("downloadOnBiff" in iX.pop3)
|
||||
iO.downloadOnBiff = sanitize.boolean(iX.pop3.downloadOnBiff);
|
||||
} catch (e) { logException(e); }
|
||||
}
|
||||
|
||||
// processed successfully, now add to result object
|
||||
if (!d.incoming.hostname) // first valid
|
||||
d.incoming = iO;
|
||||
else
|
||||
d.incomingAlternatives.push(iO);
|
||||
} catch (e) { exception = e; }
|
||||
}
|
||||
if (!d.incoming.hostname)
|
||||
// throw exception for last server
|
||||
throw exception ? exception : "Need proper <incomingServer> in XML file";
|
||||
exception = null;
|
||||
|
||||
// outgoing server
|
||||
for (let oX of array_or_undef(xml.$outgoingServer)) // input (XML)
|
||||
{
|
||||
let oO = d.createNewOutgoing(); // output (object)
|
||||
try {
|
||||
if (oX["@type"] != "smtp")
|
||||
{
|
||||
var stringBundle = getStringBundle(
|
||||
"chrome://messenger/locale/accountCreationModel.properties");
|
||||
throw stringBundle.GetStringFromName("outgoing_not_smtp.error");
|
||||
}
|
||||
oO.hostname = sanitize.hostname(oX.hostname);
|
||||
oO.port = sanitize.integerRange(oX.port, kMinPort, kMaxPort);
|
||||
|
||||
for (let oXsocketType of array_or_undef(oX.$socketType))
|
||||
{
|
||||
try {
|
||||
oO.socketType = sanitize.translate(oXsocketType,
|
||||
{ plain : 1, SSL: 2, STARTTLS: 3 });
|
||||
break; // take first that we support
|
||||
} catch (e) { exception = e; }
|
||||
}
|
||||
if (!oO.socketType)
|
||||
throw exception ? exception : "need proper <socketType> in XML";
|
||||
exception = null;
|
||||
|
||||
for (let oXauth of array_or_undef(oX.$authentication))
|
||||
{
|
||||
try {
|
||||
oO.auth = sanitize.translate(oXauth,
|
||||
{ // open relay
|
||||
"none" : Ci.nsMsgAuthMethod.none,
|
||||
// inside ISP or corp network
|
||||
"client-IP-address" : Ci.nsMsgAuthMethod.none,
|
||||
// hope for the best
|
||||
"smtp-after-pop" : Ci.nsMsgAuthMethod.none,
|
||||
"password-cleartext" : Ci.nsMsgAuthMethod.passwordCleartext,
|
||||
// @deprecated TODO remove
|
||||
"plain" : Ci.nsMsgAuthMethod.passwordCleartext,
|
||||
"password-encrypted" : Ci.nsMsgAuthMethod.passwordEncrypted,
|
||||
// @deprecated TODO remove
|
||||
"secure" : Ci.nsMsgAuthMethod.passwordEncrypted,
|
||||
"GSSAPI" : Ci.nsMsgAuthMethod.GSSAPI,
|
||||
"NTLM" : Ci.nsMsgAuthMethod.NTLM,
|
||||
"OAuth2" : Ci.nsMsgAuthMethod.OAuth2,
|
||||
});
|
||||
|
||||
break; // take first that we support
|
||||
} catch (e) { exception = e; }
|
||||
}
|
||||
if (!oO.auth)
|
||||
throw exception ? exception : "need proper <authentication> in XML";
|
||||
exception = null;
|
||||
|
||||
if ("username" in oX ||
|
||||
// if password-based auth, we need a username,
|
||||
// so go there anyways and throw.
|
||||
oO.auth == Ci.nsMsgAuthMethod.passwordCleartext ||
|
||||
oO.auth == Ci.nsMsgAuthMethod.passwordEncrypted)
|
||||
oO.username = sanitize.string(oX.username);
|
||||
|
||||
if ("password" in oX) {
|
||||
d.rememberPassword = true;
|
||||
oO.password = sanitize.string(oX.password);
|
||||
}
|
||||
|
||||
try {
|
||||
// defaults are in accountConfig.js
|
||||
if ("addThisServer" in oX)
|
||||
oO.addThisServer = sanitize.boolean(oX.addThisServer);
|
||||
if ("useGlobalPreferredServer" in oX)
|
||||
oO.useGlobalPreferredServer =
|
||||
sanitize.boolean(oX.useGlobalPreferredServer);
|
||||
} catch (e) { logException(e); }
|
||||
|
||||
// processed successfully, now add to result object
|
||||
if (!d.outgoing.hostname) // first valid
|
||||
d.outgoing = oO;
|
||||
else
|
||||
d.outgoingAlternatives.push(oO);
|
||||
} catch (e) { logException(e); exception = e; }
|
||||
}
|
||||
if (!d.outgoing.hostname)
|
||||
// throw exception for last server
|
||||
throw exception ? exception : "Need proper <outgoingServer> in XML file";
|
||||
exception = null;
|
||||
|
||||
d.inputFields = new Array();
|
||||
for (let inputField of array_or_undef(xml.$inputField))
|
||||
{
|
||||
try {
|
||||
var fieldset =
|
||||
{
|
||||
varname : sanitize.alphanumdash(inputField["@key"]).toUpperCase(),
|
||||
displayName : sanitize.label(inputField["@label"]),
|
||||
exampleValue : sanitize.label(inputField.value)
|
||||
};
|
||||
d.inputFields.push(fieldset);
|
||||
} catch (e) { logException(e); } // for now, don't throw,
|
||||
// because we don't support custom fields yet anyways.
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
207
mailnews/base/prefs/content/accountcreation/sanitizeDatatypes.js
Normal file
207
mailnews/base/prefs/content/accountcreation/sanitizeDatatypes.js
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
/* -*- Mode: Java; tab-width: 2; 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/. */
|
||||
|
||||
/**
|
||||
* This is a generic input validation lib. Use it when you process
|
||||
* data from the network.
|
||||
*
|
||||
* Just a few functions which verify, for security purposes, that the
|
||||
* input variables (strings, if nothing else is noted) are of the expected
|
||||
* type and syntax.
|
||||
*
|
||||
* The functions take a string (unless noted otherwise) and return
|
||||
* the expected datatype in JS types. If the value is not as expected,
|
||||
* they throw exceptions.
|
||||
*/
|
||||
|
||||
Components.utils.import("resource:///modules/hostnameUtils.jsm");
|
||||
|
||||
var sanitize =
|
||||
{
|
||||
integer : function(unchecked)
|
||||
{
|
||||
if (typeof(unchecked) == "number" && !isNaN(unchecked))
|
||||
return unchecked;
|
||||
|
||||
var r = parseInt(unchecked);
|
||||
if (isNaN(r))
|
||||
throw new MalformedException("no_number.error", unchecked);
|
||||
|
||||
return r;
|
||||
},
|
||||
|
||||
integerRange : function(unchecked, min, max)
|
||||
{
|
||||
var int = this.integer(unchecked);
|
||||
if (int < min)
|
||||
throw new MalformedException("number_too_small.error", unchecked);
|
||||
|
||||
if (int > max)
|
||||
throw new MalformedException("number_too_large.error", unchecked);
|
||||
|
||||
return int;
|
||||
},
|
||||
|
||||
boolean : function(unchecked)
|
||||
{
|
||||
if (typeof(unchecked) == "boolean")
|
||||
return unchecked;
|
||||
|
||||
if (unchecked == "true")
|
||||
return true;
|
||||
|
||||
if (unchecked == "false")
|
||||
return false;
|
||||
|
||||
throw new MalformedException("boolean.error", unchecked);
|
||||
},
|
||||
|
||||
string : function(unchecked)
|
||||
{
|
||||
return String(unchecked);
|
||||
},
|
||||
|
||||
nonemptystring : function(unchecked)
|
||||
{
|
||||
if (!unchecked)
|
||||
throw new MalformedException("string_empty.error", unchecked);
|
||||
|
||||
return this.string(unchecked);
|
||||
},
|
||||
|
||||
/**
|
||||
* Allow only letters, numbers, "-" and "_".
|
||||
*
|
||||
* Empty strings not allowed (good idea?).
|
||||
*/
|
||||
alphanumdash : function(unchecked)
|
||||
{
|
||||
var str = this.nonemptystring(unchecked);
|
||||
if (!/^[a-zA-Z0-9\-\_]*$/.test(str))
|
||||
throw new MalformedException("alphanumdash.error", unchecked);
|
||||
|
||||
return str;
|
||||
},
|
||||
|
||||
/**
|
||||
* DNS hostnames like foo.bar.example.com
|
||||
* Allow only letters, numbers, "-" and "."
|
||||
* Empty strings not allowed.
|
||||
* Currently does not support IDN (international domain names).
|
||||
*/
|
||||
hostname : function(unchecked)
|
||||
{
|
||||
let str = cleanUpHostName(this.nonemptystring(unchecked));
|
||||
|
||||
// Allow placeholders. TODO move to a new hostnameOrPlaceholder()
|
||||
// The regex is "anything, followed by one or more (placeholders than
|
||||
// anything)". This doesn't catch the non-placeholder case, but that's
|
||||
// handled down below.
|
||||
if (/^[a-zA-Z0-9\-\.]*(%[A-Z0-9]+%[a-zA-Z0-9\-\.]*)+$/.test(str))
|
||||
return str;
|
||||
|
||||
if (!isLegalHostNameOrIP(str))
|
||||
throw new MalformedException("hostname_syntax.error", unchecked);
|
||||
|
||||
return str.toLowerCase();
|
||||
},
|
||||
/**
|
||||
* A non-chrome URL that's safe to request.
|
||||
*/
|
||||
url : function (unchecked)
|
||||
{
|
||||
var str = this.string(unchecked);
|
||||
if (!str.startsWith("http") && !str.startsWith("https"))
|
||||
throw new MalformedException("url_scheme.error", unchecked);
|
||||
|
||||
var uri;
|
||||
try {
|
||||
uri = Services.io.newURI(str, null, null);
|
||||
uri = uri.QueryInterface(Ci.nsIURL);
|
||||
} catch (e) {
|
||||
throw new MalformedException("url_parsing.error", unchecked);
|
||||
}
|
||||
|
||||
if (uri.scheme != "http" && uri.scheme != "https")
|
||||
throw new MalformedException("url_scheme.error", unchecked);
|
||||
|
||||
return uri.spec;
|
||||
},
|
||||
|
||||
/**
|
||||
* A value which should be shown to the user in the UI as label
|
||||
*/
|
||||
label : function(unchecked)
|
||||
{
|
||||
return this.string(unchecked);
|
||||
},
|
||||
|
||||
/**
|
||||
* Allows only certain values as input, otherwise throw.
|
||||
*
|
||||
* @param unchecked {Any} The value to check
|
||||
* @param allowedValues {Array} List of values that |unchecked| may have.
|
||||
* @param defaultValue {Any} (Optional) If |unchecked| does not match
|
||||
* anything in |mapping|, a |defaultValue| can be returned instead of
|
||||
* throwing an exception. The latter is the default and happens when
|
||||
* no |defaultValue| is passed.
|
||||
* @throws MalformedException
|
||||
*/
|
||||
enum : function(unchecked, allowedValues, defaultValue)
|
||||
{
|
||||
for (let allowedValue of allowedValues)
|
||||
{
|
||||
if (allowedValue == unchecked)
|
||||
return allowedValue;
|
||||
}
|
||||
// value is bad
|
||||
if (typeof(defaultValue) == "undefined")
|
||||
throw new MalformedException("allowed_value.error", unchecked);
|
||||
return defaultValue;
|
||||
},
|
||||
|
||||
/**
|
||||
* Like enum, allows only certain (string) values as input, but allows the
|
||||
* caller to specify another value to return instead of the input value. E.g.,
|
||||
* if unchecked == "foo", return 1, if unchecked == "bar", return 2,
|
||||
* otherwise throw. This allows to translate string enums into integer enums.
|
||||
*
|
||||
* @param unchecked {Any} The value to check
|
||||
* @param mapping {Object} Associative array. property name is the input
|
||||
* value, property value is the output value. E.g. the example above
|
||||
* would be: { foo: 1, bar : 2 }.
|
||||
* Use quotes when you need freaky characters: "baz-" : 3.
|
||||
* @param defaultValue {Any} (Optional) If |unchecked| does not match
|
||||
* anything in |mapping|, a |defaultValue| can be returned instead of
|
||||
* throwing an exception. The latter is the default and happens when
|
||||
* no |defaultValue| is passed.
|
||||
* @throws MalformedException
|
||||
*/
|
||||
translate : function(unchecked, mapping, defaultValue)
|
||||
{
|
||||
for (var inputValue in mapping)
|
||||
{
|
||||
if (inputValue == unchecked)
|
||||
return mapping[inputValue];
|
||||
}
|
||||
// value is bad
|
||||
if (typeof(defaultValue) == "undefined")
|
||||
throw new MalformedException("allowed_value.error", unchecked);
|
||||
return defaultValue;
|
||||
}
|
||||
};
|
||||
|
||||
function MalformedException(msgID, uncheckedBadValue)
|
||||
{
|
||||
var stringBundle = getStringBundle(
|
||||
"chrome://messenger/locale/accountCreationUtil.properties");
|
||||
var msg = stringBundle.GetStringFromName(msgID);
|
||||
if (kDebug)
|
||||
msg += " (bad value: " + new String(uncheckedBadValue) + ")";
|
||||
Exception.call(this, msg);
|
||||
}
|
||||
MalformedException.prototype = Object.create(Exception.prototype);
|
||||
MalformedException.prototype.constructor = MalformedException;
|
||||
|
||||
304
mailnews/base/prefs/content/accountcreation/util.js
Normal file
304
mailnews/base/prefs/content/accountcreation/util.js
Normal file
|
|
@ -0,0 +1,304 @@
|
|||
/* -*- Mode: Java; tab-width: 2; 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/. */
|
||||
/**
|
||||
* Some common, generic functions
|
||||
*/
|
||||
|
||||
try {
|
||||
var Cc = Components.classes;
|
||||
var Ci = Components.interfaces;
|
||||
} catch (e) { ddump(e); } // if already declared, as in xpcshell-tests
|
||||
try {
|
||||
var Cu = Components.utils;
|
||||
} catch (e) { ddump(e); }
|
||||
|
||||
Cu.import("resource:///modules/errUtils.js");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
function assert(test, errorMsg)
|
||||
{
|
||||
if (!test)
|
||||
throw new NotReached(errorMsg ? errorMsg :
|
||||
"Programming bug. Assertion failed, see log.");
|
||||
}
|
||||
|
||||
function makeCallback(obj, func)
|
||||
{
|
||||
return function()
|
||||
{
|
||||
return func.apply(obj, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Runs the given function sometime later
|
||||
*
|
||||
* Currently implemented using setTimeout(), but
|
||||
* can later be replaced with an nsITimer impl,
|
||||
* when code wants to use it in a module.
|
||||
*/
|
||||
function runAsync(func)
|
||||
{
|
||||
setTimeout(func, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param uriStr {String}
|
||||
* @result {nsIURI}
|
||||
*/
|
||||
function makeNSIURI(uriStr)
|
||||
{
|
||||
return Services.io.newURI(uriStr, null, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads UTF8 data from a URL.
|
||||
*
|
||||
* @param uri {nsIURI} what you want to read
|
||||
* @return {Array of String} the contents of the file, one string per line
|
||||
*/
|
||||
function readURLasUTF8(uri)
|
||||
{
|
||||
assert(uri instanceof Ci.nsIURI, "uri must be an nsIURI");
|
||||
try {
|
||||
let chan = Services.io.newChannelFromURI2(uri,
|
||||
null,
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
null,
|
||||
Ci.nsILoadInfo.SEC_NORMAL,
|
||||
Ci.nsIContentPolicy.TYPE_OTHER);
|
||||
let is = Cc["@mozilla.org/intl/converter-input-stream;1"]
|
||||
.createInstance(Ci.nsIConverterInputStream);
|
||||
is.init(chan.open(), "UTF-8", 1024,
|
||||
Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
|
||||
|
||||
let content = "";
|
||||
let strOut = new Object();
|
||||
try {
|
||||
while (is.readString(1024, strOut) != 0)
|
||||
content += strOut.value;
|
||||
// catch in outer try/catch
|
||||
} finally {
|
||||
is.close();
|
||||
}
|
||||
|
||||
return content;
|
||||
} catch (e) {
|
||||
// TODO this has a numeric error message. We need to ship translations
|
||||
// into human language.
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a string (which is typically the content of a file,
|
||||
* e.g. the result returned from readURLUTF8() ), and splits
|
||||
* it into lines, and returns an array with one string per line
|
||||
*
|
||||
* Linebreaks are not contained in the result,,
|
||||
* and all of \r\n, (Windows) \r (Mac) and \n (Unix) counts as linebreak.
|
||||
*
|
||||
* @param content {String} one long string with the whole file
|
||||
* @return {Array of String} one string per line (no linebreaks)
|
||||
*/
|
||||
function splitLines(content)
|
||||
{
|
||||
content = content.replace("\r\n", "\n");
|
||||
content = content.replace("\r", "\n");
|
||||
return content.split("\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bundleURI {String} chrome URL to properties file
|
||||
* @return nsIStringBundle
|
||||
*/
|
||||
function getStringBundle(bundleURI)
|
||||
{
|
||||
try {
|
||||
return Services.strings.createBundle(bundleURI);
|
||||
} catch (e) {
|
||||
throw new Exception("Failed to get stringbundle URI <" + bundleURI +
|
||||
">. Error: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function Exception(msg)
|
||||
{
|
||||
this._message = msg;
|
||||
|
||||
// get stack
|
||||
try {
|
||||
not.found.here += 1; // force a native exception ...
|
||||
} catch (e) {
|
||||
this.stack = e.stack; // ... to get the current stack
|
||||
}
|
||||
}
|
||||
Exception.prototype =
|
||||
{
|
||||
get message()
|
||||
{
|
||||
return this._message;
|
||||
},
|
||||
toString : function()
|
||||
{
|
||||
return this._message;
|
||||
}
|
||||
}
|
||||
|
||||
function NotReached(msg)
|
||||
{
|
||||
Exception.call(this, msg); // call super constructor
|
||||
logException(this);
|
||||
}
|
||||
// Make NotReached extend Exception.
|
||||
NotReached.prototype = Object.create(Exception.prototype);
|
||||
NotReached.prototype.constructor = NotReached;
|
||||
|
||||
/**
|
||||
* A handle for an async function which you can cancel.
|
||||
* The async function will return an object of this type (a subtype)
|
||||
* and you can call cancel() when you feel like killing the function.
|
||||
*/
|
||||
function Abortable()
|
||||
{
|
||||
}
|
||||
Abortable.prototype =
|
||||
{
|
||||
cancel : function()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility implementation, for allowing to abort a setTimeout.
|
||||
* Use like: return new TimeoutAbortable(setTimeout(function(){ ... }, 0));
|
||||
* @param setTimeoutID {Integer} Return value of setTimeout()
|
||||
*/
|
||||
function TimeoutAbortable(setTimeoutID)
|
||||
{
|
||||
Abortable.call(this, setTimeoutID); // call super constructor
|
||||
this._id = setTimeoutID;
|
||||
}
|
||||
TimeoutAbortable.prototype = Object.create(Abortable.prototype);
|
||||
TimeoutAbortable.prototype.constructor = TimeoutAbortable;
|
||||
TimeoutAbortable.prototype.cancel = function() { clearTimeout(this._id); }
|
||||
|
||||
/**
|
||||
* Utility implementation, for allowing to abort a setTimeout.
|
||||
* Use like: return new TimeoutAbortable(setTimeout(function(){ ... }, 0));
|
||||
* @param setIntervalID {Integer} Return value of setInterval()
|
||||
*/
|
||||
function IntervalAbortable(setIntervalID)
|
||||
{
|
||||
Abortable.call(this, setIntervalID); // call super constructor
|
||||
this._id = setIntervalID;
|
||||
}
|
||||
IntervalAbortable.prototype = Object.create(Abortable.prototype);
|
||||
IntervalAbortable.prototype.constructor = IntervalAbortable;
|
||||
IntervalAbortable.prototype.cancel = function() { clearInterval(this._id); }
|
||||
|
||||
// Allows you to make several network calls, but return
|
||||
// only one Abortable object.
|
||||
function SuccessiveAbortable()
|
||||
{
|
||||
Abortable.call(this); // call super constructor
|
||||
this._current = null;
|
||||
}
|
||||
SuccessiveAbortable.prototype = {
|
||||
__proto__: Abortable.prototype,
|
||||
get current() { return this._current; },
|
||||
set current(abortable)
|
||||
{
|
||||
assert(abortable instanceof Abortable || abortable == null,
|
||||
"need an Abortable object (or null)");
|
||||
this._current = abortable;
|
||||
},
|
||||
cancel: function()
|
||||
{
|
||||
if (this._current)
|
||||
this._current.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
function deepCopy(org)
|
||||
{
|
||||
if (typeof(org) == "undefined")
|
||||
return undefined;
|
||||
if (org == null)
|
||||
return null;
|
||||
if (typeof(org) == "string")
|
||||
return org;
|
||||
if (typeof(org) == "number")
|
||||
return org;
|
||||
if (typeof(org) == "boolean")
|
||||
return org == true;
|
||||
if (typeof(org) == "function")
|
||||
return org;
|
||||
if (typeof(org) != "object")
|
||||
throw "can't copy objects of type " + typeof(org) + " yet";
|
||||
|
||||
//TODO still instanceof org != instanceof copy
|
||||
//var result = new org.constructor();
|
||||
var result = new Object();
|
||||
if (typeof(org.length) != "undefined")
|
||||
var result = new Array();
|
||||
for (var prop in org)
|
||||
result[prop] = deepCopy(org[prop]);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (typeof gEmailWizardLogger == "undefined") {
|
||||
Cu.import("resource:///modules/gloda/log4moz.js");
|
||||
var gEmailWizardLogger = Log4Moz.getConfiguredLogger("mail.wizard");
|
||||
}
|
||||
function ddump(text)
|
||||
{
|
||||
gEmailWizardLogger.info(text);
|
||||
}
|
||||
|
||||
function debugObject(obj, name, maxDepth, curDepth)
|
||||
{
|
||||
if (curDepth == undefined)
|
||||
curDepth = 0;
|
||||
if (maxDepth != undefined && curDepth > maxDepth)
|
||||
return "";
|
||||
|
||||
var result = "";
|
||||
var i = 0;
|
||||
for (let prop in obj)
|
||||
{
|
||||
i++;
|
||||
try {
|
||||
if (typeof(obj[prop]) == "object")
|
||||
{
|
||||
if (obj[prop] && obj[prop].length != undefined)
|
||||
result += name + "." + prop + "=[probably array, length " +
|
||||
obj[prop].length + "]\n";
|
||||
else
|
||||
result += name + "." + prop + "=[" + typeof(obj[prop]) + "]\n";
|
||||
result += debugObject(obj[prop], name + "." + prop,
|
||||
maxDepth, curDepth + 1);
|
||||
}
|
||||
else if (typeof(obj[prop]) == "function")
|
||||
result += name + "." + prop + "=[function]\n";
|
||||
else
|
||||
result += name + "." + prop + "=" + obj[prop] + "\n";
|
||||
} catch (e) {
|
||||
result += name + "." + prop + "-> Exception(" + e + ")\n";
|
||||
}
|
||||
}
|
||||
if (!i)
|
||||
result += name + " is empty\n";
|
||||
return result;
|
||||
}
|
||||
|
||||
function alertPrompt(alertTitle, alertMsg)
|
||||
{
|
||||
Services.prompt.alert(window, alertTitle, alertMsg);
|
||||
}
|
||||
347
mailnews/base/prefs/content/accountcreation/verifyConfig.js
Normal file
347
mailnews/base/prefs/content/accountcreation/verifyConfig.js
Normal file
|
|
@ -0,0 +1,347 @@
|
|||
/* -*- Mode: Java; tab-width: 2; 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/. */
|
||||
|
||||
/**
|
||||
* This checks a given config, by trying a real connection and login,
|
||||
* with username and password.
|
||||
*
|
||||
* TODO
|
||||
* - give specific errors, bug 555448
|
||||
* - return a working |Abortable| to allow cancel
|
||||
*
|
||||
* @param accountConfig {AccountConfig} The guessed account config.
|
||||
* username, password, realname, emailaddress etc. are not filled out,
|
||||
* but placeholders to be filled out via replaceVariables().
|
||||
* @param alter {boolean}
|
||||
* Try other usernames and login schemes, until login works.
|
||||
* Warning: Modifies |accountConfig|.
|
||||
*
|
||||
* This function is async.
|
||||
* @param successCallback function(accountConfig)
|
||||
* Called when we could guess the config.
|
||||
* For accountConfig, see below.
|
||||
* @param errorCallback function(ex)
|
||||
* Called when we could guess not the config, either
|
||||
* because we have not found anything or
|
||||
* because there was an error (e.g. no network connection).
|
||||
* The ex.message will contain a user-presentable message.
|
||||
*/
|
||||
|
||||
Components.utils.import("resource:///modules/mailServices.js");
|
||||
Components.utils.import("resource://gre/modules/OAuth2Providers.jsm");
|
||||
|
||||
if (typeof gEmailWizardLogger == "undefined") {
|
||||
Cu.import("resource:///modules/gloda/log4moz.js");
|
||||
var gEmailWizardLogger = Log4Moz.getConfiguredLogger("mail.wizard");
|
||||
}
|
||||
|
||||
function verifyConfig(config, alter, msgWindow, successCallback, errorCallback)
|
||||
{
|
||||
ddump(debugObject(config, "config", 3));
|
||||
assert(config instanceof AccountConfig,
|
||||
"BUG: Arg 'config' needs to be an AccountConfig object");
|
||||
assert(typeof(alter) == "boolean");
|
||||
assert(typeof(successCallback) == "function");
|
||||
assert(typeof(errorCallback) == "function");
|
||||
|
||||
if (MailServices.accounts.findRealServer(config.incoming.username,
|
||||
config.incoming.hostname,
|
||||
sanitize.enum(config.incoming.type,
|
||||
["pop3", "imap", "nntp"]),
|
||||
config.incoming.port)) {
|
||||
errorCallback("Incoming server exists");
|
||||
return;
|
||||
}
|
||||
|
||||
// incoming server
|
||||
let inServer =
|
||||
MailServices.accounts.createIncomingServer(config.incoming.username,
|
||||
config.incoming.hostname,
|
||||
sanitize.enum(config.incoming.type,
|
||||
["pop3", "imap", "nntp"]));
|
||||
inServer.port = config.incoming.port;
|
||||
inServer.password = config.incoming.password;
|
||||
if (config.incoming.socketType == 1) // plain
|
||||
inServer.socketType = Ci.nsMsgSocketType.plain;
|
||||
else if (config.incoming.socketType == 2) // SSL
|
||||
inServer.socketType = Ci.nsMsgSocketType.SSL;
|
||||
else if (config.incoming.socketType == 3) // STARTTLS
|
||||
inServer.socketType = Ci.nsMsgSocketType.alwaysSTARTTLS;
|
||||
|
||||
gEmailWizardLogger.info("Setting incoming server authMethod to " +
|
||||
config.incoming.auth);
|
||||
inServer.authMethod = config.incoming.auth;
|
||||
|
||||
try {
|
||||
// Lookup issuer if needed.
|
||||
if (config.incoming.auth == Ci.nsMsgAuthMethod.OAuth2 ||
|
||||
config.outgoing.auth == Ci.nsMsgAuthMethod.OAuth2) {
|
||||
if (!config.oauthSettings)
|
||||
config.oauthSettings = {};
|
||||
if (!config.oauthSettings.issuer || !config.oauthSettings.scope) {
|
||||
// lookup issuer or scope from hostname
|
||||
let hostname = (config.incoming.auth == Ci.nsMsgAuthMethod.OAuth2) ?
|
||||
config.incoming.hostname : config.outgoing.hostname;
|
||||
let hostDetails = OAuth2Providers.getHostnameDetails(hostname);
|
||||
if (hostDetails)
|
||||
[config.oauthSettings.issuer, config.oauthSettings.scope] = hostDetails;
|
||||
if (!config.oauthSettings.issuer || !config.oauthSettings.scope)
|
||||
throw "Could not get issuer for oauth2 authentication";
|
||||
}
|
||||
gEmailWizardLogger.info("Saving oauth parameters for issuer " +
|
||||
config.oauthSettings.issuer);
|
||||
inServer.setCharValue("oauth2.scope", config.oauthSettings.scope);
|
||||
inServer.setCharValue("oauth2.issuer", config.oauthSettings.issuer);
|
||||
gEmailWizardLogger.info("OAuth2 issuer, scope is " +
|
||||
config.oauthSettings.issuer + ", " + config.oauthSettings.scope);
|
||||
}
|
||||
|
||||
if (inServer.password ||
|
||||
inServer.authMethod == Ci.nsMsgAuthMethod.OAuth2)
|
||||
verifyLogon(config, inServer, alter, msgWindow,
|
||||
successCallback, errorCallback);
|
||||
else {
|
||||
// Avoid pref pollution, clear out server prefs.
|
||||
MailServices.accounts.removeIncomingServer(inServer, true);
|
||||
successCallback(config);
|
||||
}
|
||||
return;
|
||||
}
|
||||
catch (e) {
|
||||
gEmailWizardLogger.error("ERROR: verify logon shouldn't have failed");
|
||||
}
|
||||
// Avoid pref pollution, clear out server prefs.
|
||||
MailServices.accounts.removeIncomingServer(inServer, true);
|
||||
errorCallback(e);
|
||||
}
|
||||
|
||||
function verifyLogon(config, inServer, alter, msgWindow, successCallback,
|
||||
errorCallback)
|
||||
{
|
||||
gEmailWizardLogger.info("verifyLogon for server at " + inServer.hostName);
|
||||
// hack - save away the old callbacks.
|
||||
let saveCallbacks = msgWindow.notificationCallbacks;
|
||||
// set our own callbacks - this works because verifyLogon will
|
||||
// synchronously create the transport and use the notification callbacks.
|
||||
let listener = new urlListener(config, inServer, alter, msgWindow,
|
||||
successCallback, errorCallback);
|
||||
// our listener listens both for the url and cert errors.
|
||||
msgWindow.notificationCallbacks = listener;
|
||||
// try to work around bug where backend is clearing password.
|
||||
try {
|
||||
inServer.password = config.incoming.password;
|
||||
let uri = inServer.verifyLogon(listener, msgWindow);
|
||||
// clear msgWindow so url won't prompt for passwords.
|
||||
uri.QueryInterface(Ci.nsIMsgMailNewsUrl).msgWindow = null;
|
||||
}
|
||||
catch (e) { gEmailWizardLogger.error("verifyLogon failed: " + e); throw e;}
|
||||
finally {
|
||||
// restore them
|
||||
msgWindow.notificationCallbacks = saveCallbacks;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The url listener also implements nsIBadCertListener2. Its job is to prevent
|
||||
* "bad cert" security dialogs from being shown to the user. Currently it puts
|
||||
* up the cert override dialog, though we'd like to give the user more detailed
|
||||
* information in the future.
|
||||
*/
|
||||
|
||||
function urlListener(config, server, alter, msgWindow, successCallback,
|
||||
errorCallback)
|
||||
{
|
||||
this.mConfig = config;
|
||||
this.mServer = server;
|
||||
this.mAlter = alter;
|
||||
this.mSuccessCallback = successCallback;
|
||||
this.mErrorCallback = errorCallback;
|
||||
this.mMsgWindow = msgWindow;
|
||||
this.mCertError = false;
|
||||
this._log = Log4Moz.getConfiguredLogger("mail.wizard");
|
||||
}
|
||||
urlListener.prototype =
|
||||
{
|
||||
OnStartRunningUrl: function(aUrl)
|
||||
{
|
||||
this._log.info("Starting to test username");
|
||||
this._log.info(" username=" + (this.mConfig.incoming.username !=
|
||||
this.mConfig.identity.emailAddress) +
|
||||
", have savedUsername=" +
|
||||
(this.mConfig.usernameSaved ? "true" : "false"));
|
||||
this._log.info(" authMethod=" + this.mServer.authMethod);
|
||||
},
|
||||
|
||||
OnStopRunningUrl: function(aUrl, aExitCode)
|
||||
{
|
||||
this._log.info("Finished verifyConfig resulted in " + aExitCode);
|
||||
if (Components.isSuccessCode(aExitCode))
|
||||
{
|
||||
this._cleanup();
|
||||
this.mSuccessCallback(this.mConfig);
|
||||
}
|
||||
// Logon failed, and we aren't supposed to try other variations.
|
||||
else if (!this.mAlter)
|
||||
{
|
||||
this._cleanup();
|
||||
var errorMsg = getStringBundle(
|
||||
"chrome://messenger/locale/accountCreationModel.properties")
|
||||
.GetStringFromName("cannot_login.error");
|
||||
this.mErrorCallback(new Exception(errorMsg));
|
||||
}
|
||||
// Try other variations, unless there's a cert error, in which
|
||||
// case we'll see what the user chooses.
|
||||
else if (!this.mCertError)
|
||||
{
|
||||
this.tryNextLogon()
|
||||
}
|
||||
},
|
||||
|
||||
tryNextLogon: function()
|
||||
{
|
||||
this._log.info("tryNextLogon()");
|
||||
this._log.info(" username=" + (this.mConfig.incoming.username !=
|
||||
this.mConfig.identity.emailAddress) +
|
||||
", have savedUsername=" +
|
||||
(this.mConfig.usernameSaved ? "true" : "false"));
|
||||
this._log.info(" authMethod=" + this.mServer.authMethod);
|
||||
// check if we tried full email address as username
|
||||
if (this.mConfig.incoming.username != this.mConfig.identity.emailAddress)
|
||||
{
|
||||
this._log.info(" Changing username to email address.");
|
||||
this.mConfig.usernameSaved = this.mConfig.incoming.username;
|
||||
this.mConfig.incoming.username = this.mConfig.identity.emailAddress;
|
||||
this.mConfig.outgoing.username = this.mConfig.identity.emailAddress;
|
||||
this.mServer.username = this.mConfig.incoming.username;
|
||||
this.mServer.password = this.mConfig.incoming.password;
|
||||
verifyLogon(this.mConfig, this.mServer, this.mAlter, this.mMsgWindow,
|
||||
this.mSuccessCallback, this.mErrorCallback);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.mConfig.usernameSaved)
|
||||
{
|
||||
this._log.info(" Re-setting username.");
|
||||
// If we tried the full email address as the username, then let's go
|
||||
// back to trying just the username before trying the other cases.
|
||||
this.mConfig.incoming.username = this.mConfig.usernameSaved;
|
||||
this.mConfig.outgoing.username = this.mConfig.usernameSaved;
|
||||
this.mConfig.usernameSaved = null;
|
||||
this.mServer.username = this.mConfig.incoming.username;
|
||||
this.mServer.password = this.mConfig.incoming.password;
|
||||
}
|
||||
|
||||
// sec auth seems to have failed, and we've tried both
|
||||
// varieties of user name, sadly.
|
||||
// So fall back to non-secure auth, and
|
||||
// again try the user name and email address as username
|
||||
assert(this.mConfig.incoming.auth == this.mServer.authMethod);
|
||||
this._log.info(" Using SSL: " +
|
||||
(this.mServer.socketType == Ci.nsMsgSocketType.SSL ||
|
||||
this.mServer.socketType == Ci.nsMsgSocketType.alwaysSTARTTLS));
|
||||
if (this.mConfig.incoming.authAlternatives &&
|
||||
this.mConfig.incoming.authAlternatives.length)
|
||||
// We may be dropping back to insecure auth methods here,
|
||||
// which is not good. But then again, we already warned the user,
|
||||
// if it is a config without SSL.
|
||||
{
|
||||
this._log.info(" auth alternatives = " +
|
||||
this.mConfig.incoming.authAlternatives.join(","));
|
||||
this._log.info(" Decreasing auth.");
|
||||
this._log.info(" Have password: " +
|
||||
(this.mServer.password ? "true" : "false"));
|
||||
let brokenAuth = this.mConfig.incoming.auth;
|
||||
// take the next best method (compare chooseBestAuthMethod() in guess)
|
||||
this.mConfig.incoming.auth =
|
||||
this.mConfig.incoming.authAlternatives.shift();
|
||||
this.mServer.authMethod = this.mConfig.incoming.auth;
|
||||
// Assume that SMTP server has same methods working as incoming.
|
||||
// Broken assumption, but we currently have no SMTP verification.
|
||||
// TODO implement real SMTP verification
|
||||
if (this.mConfig.outgoing.auth == brokenAuth &&
|
||||
this.mConfig.outgoing.authAlternatives.indexOf(
|
||||
this.mConfig.incoming.auth) != -1)
|
||||
this.mConfig.outgoing.auth = this.mConfig.incoming.auth;
|
||||
this._log.info(" outgoing auth: " + this.mConfig.outgoing.auth);
|
||||
verifyLogon(this.mConfig, this.mServer, this.mAlter, this.mMsgWindow,
|
||||
this.mSuccessCallback, this.mErrorCallback);
|
||||
return;
|
||||
}
|
||||
|
||||
// Tried all variations we can. Give up.
|
||||
this._log.info("Giving up.");
|
||||
this._cleanup();
|
||||
let errorMsg = getStringBundle(
|
||||
"chrome://messenger/locale/accountCreationModel.properties")
|
||||
.GetStringFromName("cannot_login.error");
|
||||
this.mErrorCallback(new Exception(errorMsg));
|
||||
return;
|
||||
},
|
||||
|
||||
_cleanup : function()
|
||||
{
|
||||
try {
|
||||
// Avoid pref pollution, clear out server prefs.
|
||||
if (this.mServer) {
|
||||
MailServices.accounts.removeIncomingServer(this.mServer, true);
|
||||
this.mServer = null;
|
||||
}
|
||||
} catch (e) { this._log.error(e); }
|
||||
},
|
||||
|
||||
// Suppress any certificate errors
|
||||
notifyCertProblem: function(socketInfo, status, targetSite) {
|
||||
this.mCertError = true;
|
||||
this._log.error("cert error");
|
||||
let self = this;
|
||||
setTimeout(function () {
|
||||
try {
|
||||
self.informUserOfCertError(socketInfo, status, targetSite);
|
||||
} catch (e) { logException(e); }
|
||||
}, 0);
|
||||
return true;
|
||||
},
|
||||
|
||||
informUserOfCertError : function(socketInfo, status, targetSite) {
|
||||
var params = {
|
||||
exceptionAdded : false,
|
||||
sslStatus : status,
|
||||
prefetchCert : true,
|
||||
location : targetSite,
|
||||
};
|
||||
window.openDialog("chrome://pippki/content/exceptionDialog.xul",
|
||||
"","chrome,centerscreen,modal", params);
|
||||
this._log.info("cert exception dialog closed");
|
||||
this._log.info("cert exceptionAdded = " + params.exceptionAdded);
|
||||
if (!params.exceptionAdded) {
|
||||
this._cleanup();
|
||||
let errorMsg = getStringBundle(
|
||||
"chrome://messenger/locale/accountCreationModel.properties")
|
||||
.GetStringFromName("cannot_login.error");
|
||||
this.mErrorCallback(new Exception(errorMsg));
|
||||
}
|
||||
else {
|
||||
// Retry the logon now that we've added the cert exception.
|
||||
verifyLogon(this.mConfig, this.mServer, this.mAlter, this.mMsgWindow,
|
||||
this.mSuccessCallback, this.mErrorCallback);
|
||||
}
|
||||
},
|
||||
|
||||
// nsIInterfaceRequestor
|
||||
getInterface: function(iid) {
|
||||
return this.QueryInterface(iid);
|
||||
},
|
||||
|
||||
// nsISupports
|
||||
QueryInterface: function(iid) {
|
||||
if (!iid.equals(Components.interfaces.nsIBadCertListener2) &&
|
||||
!iid.equals(Components.interfaces.nsIInterfaceRequestor) &&
|
||||
!iid.equals(Components.interfaces.nsIUrlListener) &&
|
||||
!iid.equals(Components.interfaces.nsISupports))
|
||||
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue