mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-25 09:57:32 +09:00
353 lines
14 KiB
JavaScript
353 lines
14 KiB
JavaScript
/* -*- 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");
|
|
#ifdef MOZ_MAILNEWS_OAUTH2
|
|
Components.utils.import("resource://gre/modules/OAuth2Providers.jsm");
|
|
#endif
|
|
|
|
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 {
|
|
#ifdef MOZ_MAILNEWS_OAUTH2
|
|
// 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)
|
|
#else
|
|
if (inServer.password)
|
|
#endif
|
|
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;
|
|
}
|
|
}
|