Bug 1788086 - Remove temporary server when account setup is finished. r=leftmostcat

Modernize verifyConfig.
Hook up unload (cancel) to remove the temporary server if we got that far.

Differential Revision: https://phabricator.services.mozilla.com/D174546

--HG--
rename : mail/components/accountcreation/verifyConfig.jsm => mail/components/accountcreation/ConfigVerifier.jsm
This commit is contained in:
Magnus Melin 2023-04-17 00:15:43 +00:00
Родитель f4af19242e
Коммит de4f22e8fe
5 изменённых файлов: 399 добавлений и 482 удалений

Просмотреть файл

@ -0,0 +1,374 @@
/* 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/. */
const EXPORTED_SYMBOLS = ["ConfigVerifier"];
const { MailServices } = ChromeUtils.import(
"resource:///modules/MailServices.jsm"
);
const { OAuth2Providers } = ChromeUtils.import(
"resource:///modules/OAuth2Providers.jsm"
);
const { AccountCreationUtils } = ChromeUtils.import(
"resource:///modules/accountcreation/AccountCreationUtils.jsm"
);
/**
* @implements {nsIUrlListener}
* @implements {nsIInterfaceRequestor}
*/
class ConfigVerifier {
QueryInterface = ChromeUtils.generateQI([
"nsIInterfaceRequestor",
"nsIUrlListener",
]);
// @see {nsIInterfaceRequestor}
getInterface(iid) {
return this.QueryInterface(iid);
}
constructor(msgWindow) {
this.msgWindow = msgWindow;
this._log = console.createInstance({
prefix: "mail.setup",
maxLogLevel: "Warn",
maxLogLevelPref: "mail.setup.loglevel",
});
}
// @see {nsIUrlListener}
OnStartRunningUrl(url) {
this._log.debug(`Starting to verify configuration;
email as username=${this.config.incoming.username !=
this.config.identity.emailAddress}
savedUsername=${this.config.usernameSaved ? "true" : "false"},
authMethod=${this.server.authMethod}`);
}
// @see {nsIUrlListener}
OnStopRunningUrl(url, status) {
if (Components.isSuccessCode(status)) {
this._log.debug(`Configuration verified successfully!`);
this.cleanup();
this.successCallback(this.config);
return;
}
this._log.debug(`Verifying configuration failed; status=${status}`);
let certError = false;
try {
let nssErrorsService = Cc["@mozilla.org/nss_errors_service;1"].getService(
Ci.nsINSSErrorsService
);
let errorClass = nssErrorsService.getErrorClass(status);
if (errorClass == Ci.nsINSSErrorsService.ERROR_CLASS_BAD_CERT) {
certError = true;
}
} catch (e) {
// It's not an NSS error.
}
if (certError) {
let mailNewsUrl = url.QueryInterface(Ci.nsIMsgMailNewsUrl);
let secInfo = mailNewsUrl.failedSecInfo;
this.informUserOfCertError(secInfo, url.asciiHostPort);
} else if (this.alter) {
// Try other variations.
this.server.closeCachedConnections();
this.tryNextLogon(url);
} else {
// Logon failed, and we aren't supposed to try other variations.
this._failed(url);
}
}
tryNextLogon(aPreviousUrl) {
this._log.debug("Trying next logon variation");
// check if we tried full email address as username
if (this.config.incoming.username != this.config.identity.emailAddress) {
this._log.debug("Changing username to email address.");
this.config.usernameSaved = this.config.incoming.username;
this.config.incoming.username = this.config.identity.emailAddress;
this.config.outgoing.username = this.config.identity.emailAddress;
this.server.username = this.config.incoming.username;
this.server.password = this.config.incoming.password;
this.verifyLogon();
return;
}
if (this.config.usernameSaved) {
this._log.debug("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.config.incoming.username = this.config.usernameSaved;
this.config.outgoing.username = this.config.usernameSaved;
this.config.usernameSaved = null;
this.server.username = this.config.incoming.username;
this.server.password = this.config.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
if (this.server.socketType == Ci.nsMsgSocketType.SSL) {
this._log.debug("Using SSL");
} else if (this.server.socketType == Ci.nsMsgSocketType.alwaysSTARTTLS) {
this._log.debug("Using STARTTLS");
}
if (
this.config.incoming.authAlternatives &&
this.config.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.
let brokenAuth = this.config.incoming.auth;
// take the next best method (compare chooseBestAuthMethod() in guess)
this.config.incoming.auth = this.config.incoming.authAlternatives.shift();
this.server.authMethod = this.config.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.config.outgoing.auth == brokenAuth &&
this.config.outgoing.authAlternatives.includes(
this.config.incoming.auth
)
) {
this.config.outgoing.auth = this.config.incoming.auth;
}
this._log.debug(`Trying next auth method: ${this.server.authMethod}`);
this.verifyLogon();
return;
}
// Tried all variations we can. Give up.
this._log.debug("Have tried all variations. Giving up.");
this._failed(aPreviousUrl);
}
cleanup() {
try {
// Avoid pref pollution, clear out server prefs.
if (this.server) {
MailServices.accounts.removeIncomingServer(this.server, true);
this.server = null;
}
} catch (e) {
this._log.error(e);
}
}
_failed(url) {
this.cleanup();
let code = url.errorCode || "login-error-unknown";
let msg = url.errorMessage;
// *Only* for known (!) username/password errors, show our message.
// But there are 1000 other reasons why it could have failed, e.g.
// server not reachable, bad auth method, server hiccups, or even
// custom server messages that tell the user to do something,
// so show the backend error message, unless we are certain
// that it's a wrong username or password.
if (
!msg || // Normal IMAP login error sets no error msg
code == "pop3UsernameFailure" ||
code == "pop3PasswordFailed" ||
code == "imapOAuth2Error"
) {
msg = AccountCreationUtils.getStringBundle(
"chrome://messenger/locale/accountCreationModel.properties"
).GetStringFromName("cannot_login.error");
}
this.errorCallback(new Error(msg));
}
/**
* Inform users that we got a certificate error for the specified location.
* Allow them to add an exception for it.
*
* @param {nsITransportSecurityInfo} secInfo
* @param {string} location - "host:port" that had the problem.
*/
informUserOfCertError(secInfo, location) {
this._log.debug(`Informing user about cert error for ${location}`);
let params = {
exceptionAdded: false,
securityInfo: secInfo,
prefetchCert: true,
location,
};
Services.wm
.getMostRecentWindow("mail:3pane")
.browsingContext.topChromeWindow.openDialog(
"chrome://pippki/content/exceptionDialog.xhtml",
"exceptionDialog",
"chrome,centerscreen,modal",
params
);
if (!params.exceptionAdded) {
this._log.debug(`Did not accept exception for ${location}`);
this.cleanup();
let errorMsg = AccountCreationUtils.getStringBundle(
"chrome://messenger/locale/accountCreationModel.properties"
).GetStringFromName("cannot_login.error");
this.errorCallback(new Error(errorMsg));
} else {
this._log.debug(`Accept exception for ${location} - will retry logon.`);
// Retry the logon now that we've added the cert exception.
this.verifyLogon();
}
}
/**
* This checks a given config, by trying a real connection and login,
* with username and password.
*
* @param {AccountConfig} config - 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 |config|.
* @returns {Promise<AccountConfig>} the successful configuration.
* @throws {Error} 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.
*/
async verifyConfig(config, alter) {
this.alter = alter;
return new Promise((resolve, reject) => {
this.config = config;
this.successCallback = resolve;
this.errorCallback = reject;
if (
MailServices.accounts.findServer(
config.incoming.username,
config.incoming.hostname,
config.incoming.type,
config.incoming.port
)
) {
reject(new Error("Incoming server exists"));
return;
}
// incoming server
if (!this.server) {
this.server = MailServices.accounts.createIncomingServer(
config.incoming.username,
config.incoming.hostname,
config.incoming.type
);
}
this.server.port = config.incoming.port;
this.server.password = config.incoming.password;
this.server.socketType = config.incoming.socketType;
this._log.info(
"Setting incoming server authMethod to " + config.incoming.auth
);
this.server.authMethod = config.incoming.auth;
try {
// Lookup OAuth2 issuer if needed.
// -- Incoming.
if (
config.incoming.auth == Ci.nsMsgAuthMethod.OAuth2 &&
(!config.incoming.oauthSettings ||
!config.incoming.oauthSettings.issuer ||
!config.incoming.oauthSettings.scope)
) {
let details = OAuth2Providers.getHostnameDetails(
config.incoming.hostname
);
if (!details) {
reject(
new Error(
`Could not get OAuth2 details for hostname=${config.incoming.hostname}.`
)
);
}
config.incoming.oauthSettings = {
issuer: details[0],
scope: details[1],
};
}
// -- Outgoing.
if (
config.outgoing.auth == Ci.nsMsgAuthMethod.OAuth2 &&
(!config.outgoing.oauthSettings ||
!config.outgoing.oauthSettings.issuer ||
!config.outgoing.oauthSettings.scope)
) {
let details = OAuth2Providers.getHostnameDetails(
config.outgoing.hostname
);
if (!details) {
reject(
new Error(
`Could not get OAuth2 details for hostname=${config.outgoing.hostname}.`
)
);
}
config.outgoing.oauthSettings = {
issuer: details[0],
scope: details[1],
};
}
if (config.incoming.owaURL) {
this.server.setUnicharValue("owa_url", config.incoming.owaURL);
}
if (config.incoming.ewsURL) {
this.server.setUnicharValue("ews_url", config.incoming.ewsURL);
}
if (config.incoming.easURL) {
this.server.setUnicharValue("eas_url", config.incoming.easURL);
}
if (
this.server.password ||
this.server.authMethod == Ci.nsMsgAuthMethod.OAuth2
) {
this.verifyLogon();
} else {
// Avoid pref pollution, clear out server prefs.
MailServices.accounts.removeIncomingServer(this.server, true);
resolve(config);
}
} catch (e) {
this._log.info("verifyConfig failed: " + e);
// Avoid pref pollution, clear out server prefs.
MailServices.accounts.removeIncomingServer(this.server, true);
reject(e);
}
});
}
/**
* Verify that the provided credentials can log in to the incoming server.
*/
verifyLogon() {
this._log.info("verifyLogon for server at " + this.server.hostName);
// Save away the old callbacks.
let saveCallbacks = this.msgWindow.notificationCallbacks;
// Set our own callbacks - this works because verifyLogon will
// synchronously create the transport and use the notification callbacks.
// Our listener listens both for the url and cert errors.
this.msgWindow.notificationCallbacks = this;
// try to work around bug where backend is clearing password.
try {
this.server.password = this.config.incoming.password;
let uri = this.server.verifyLogon(this, this.msgWindow);
// clear msgWindow so url won't prompt for passwords.
uri.QueryInterface(Ci.nsIMsgMailNewsUrl).msgWindow = null;
} finally {
// restore them
this.msgWindow.notificationCallbacks = saveCallbacks;
}
}
}

Просмотреть файл

@ -20,12 +20,12 @@ XPCOMUtils.defineLazyModuleGetters(this, {
AddonManager: "resource://gre/modules/AddonManager.jsm",
cal: "resource:///modules/calendar/calUtils.jsm",
CardDAVUtils: "resource:///modules/CardDAVUtils.jsm",
ConfigVerifier: "resource:///modules/accountcreation/ConfigVerifier.jsm",
CreateInBackend: "resource:///modules/accountcreation/CreateInBackend.jsm",
FetchConfig: "resource:///modules/accountcreation/FetchConfig.jsm",
GuessConfig: "resource:///modules/accountcreation/GuessConfig.jsm",
OAuth2Providers: "resource:///modules/OAuth2Providers.jsm",
Sanitizer: "resource:///modules/accountcreation/Sanitizer.jsm",
verifyConfig: "resource:///modules/accountcreation/verifyConfig.jsm",
UIDensity: "resource:///modules/UIDensity.jsm",
UIFontSize: "resource:///modules/UIFontSize.jsm",
});
@ -66,29 +66,6 @@ var {
* - If user clicks OK, create the account
*/
/**
TODO for bug 549045:
- autodetect protocol
Bugs
- SSL cert errors
- invalid cert (hostname mismatch) doesn't trigger warning dialog as it should
- accept self-signed cert (e.g. imap.mail.ru) doesn't work
(works without my patch),
verifyConfig.js line 124 has no inServer, for whatever reason,
although I didn't change verifyConfig.js at all
(the change you see in that file is irrelevant: that was an attempt to fix
the bug and clean up the code).
Things to test (works for me):
- state transitions, buttons enable, status msgs
- stop button
- showes up again after stopping detection and restarting it
- when stopping [retest]: buttons proper?
- enter nonsense domain. guess fails, (so automatically) manual,
change domain to real one (not in DB), guess succeeds.
former bug: goes to manual first shortly, then to result
*/
// Keep track of the prefers-reduce-motion media query for JS based animations.
var gReducedMotion;
@ -2257,17 +2234,21 @@ var gAccountSetup = {
: this._currentConfig.source;
let self = this;
// logic function defined in verifyConfig.js
verifyConfig(
configFilledIn,
// guess login config?
configFilledIn.source != AccountConfig.kSourceXML,
// TODO Instead, the following line would be correct, but I cannot use it,
// because some other code doesn't adhere to the expectations/specs.
// Find out what it was and fix it.
// concreteConfig.source == AccountConfig.kSourceGuess,
this._msgWindow,
function(successfulConfig) {
let verifier = new ConfigVerifier(this._msgWindow);
window.addEventListener("unload", event => {
verifier.cleanup();
});
verifier
.verifyConfig(
configFilledIn,
// guess login config?
configFilledIn.source != AccountConfig.kSourceXML
// TODO Instead, the following line would be correct, but I cannot use it,
// because some other code doesn't adhere to the expectations/specs.
// Find out what it was and fix it.
// concreteConfig.source == AccountConfig.kSourceGuess,
)
.then(successfulConfig => {
// success
self.stopLoadingState(
successfulConfig.incoming.password
@ -2275,8 +2256,7 @@ var gAccountSetup = {
: null
);
// the auth might have changed, so we
// should back-port it to the current config.
// The auth might have changed, so we should update the current config.
self._currentConfig.incoming.auth = successfulConfig.incoming.auth;
self._currentConfig.outgoing.auth = successfulConfig.outgoing.auth;
self._currentConfig.incoming.username =
@ -2301,8 +2281,8 @@ var gAccountSetup = {
telemetryKey,
1
);
},
function(e) {
})
.catch(e => {
// failed
// Could be a wrong password, but there are 1000 other
// reasons why this failed. Only the backend knows.
@ -2333,10 +2313,12 @@ var gAccountSetup = {
telemetryKey,
1
);
}
);
});
},
/**
* @param {AccountConfig} concreteConfig - The config to use.
*/
finish(concreteConfig) {
gAccountSetupLogger.debug("creating account in backend");
let newAccount = CreateInBackend.createAccountInBackend(concreteConfig);

Просмотреть файл

@ -8,6 +8,7 @@ JAR_MANIFESTS += ["jar.mn"]
EXTRA_JS_MODULES.accountcreation += [
"AccountConfig.jsm",
"AccountCreationUtils.jsm",
"ConfigVerifier.jsm",
"CreateInBackend.jsm",
"ExchangeAutoDiscover.jsm",
"FetchConfig.jsm",
@ -15,7 +16,6 @@ EXTRA_JS_MODULES.accountcreation += [
"GuessConfig.jsm",
"readFromXML.jsm",
"Sanitizer.jsm",
"verifyConfig.jsm",
]
XPCSHELL_TESTS_MANIFESTS += [

Просмотреть файл

@ -1,439 +0,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/. */
const EXPORTED_SYMBOLS = ["verifyConfig"];
const lazy = {};
ChromeUtils.defineModuleGetter(
lazy,
"AccountConfig",
"resource:///modules/accountcreation/AccountConfig.jsm"
);
const { AccountCreationUtils } = ChromeUtils.import(
"resource:///modules/accountcreation/AccountCreationUtils.jsm"
);
const { MailServices } = ChromeUtils.import(
"resource:///modules/MailServices.jsm"
);
const { OAuth2Providers } = ChromeUtils.import(
"resource:///modules/OAuth2Providers.jsm"
);
const {
assert,
ddump,
Exception,
gAccountSetupLogger,
getStringBundle,
} = AccountCreationUtils;
/**
* 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.
*/
function verifyConfig(
config,
alter,
msgWindow,
successCallback,
errorCallback
) {
ddump("verify config:\n" + config);
assert(
config instanceof lazy.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.findServer(
config.incoming.username,
config.incoming.hostname,
config.incoming.type,
config.incoming.port
)
) {
errorCallback("Incoming server exists");
return;
}
// incoming server
let inServer = MailServices.accounts.createIncomingServer(
config.incoming.username,
config.incoming.hostname,
config.incoming.type
);
inServer.port = config.incoming.port;
inServer.password = config.incoming.password;
inServer.socketType = config.incoming.socketType;
gAccountSetupLogger.info(
"Setting incoming server authMethod to " + config.incoming.auth
);
inServer.authMethod = config.incoming.auth;
try {
// Lookup OAuth2 issuer if needed.
// -- Incoming.
if (
config.incoming.auth == Ci.nsMsgAuthMethod.OAuth2 &&
(!config.incoming.oauthSettings ||
!config.incoming.oauthSettings.issuer ||
!config.incoming.oauthSettings.scope)
) {
let details = OAuth2Providers.getHostnameDetails(
config.incoming.hostname
);
if (!details) {
throw new Error(
`Could not get OAuth2 details for hostname=${config.incoming.hostname}.`
);
}
config.incoming.oauthSettings = { issuer: details[0], scope: details[1] };
}
// -- Outgoing.
if (
config.outgoing.auth == Ci.nsMsgAuthMethod.OAuth2 &&
(!config.outgoing.oauthSettings ||
!config.outgoing.oauthSettings.issuer ||
!config.outgoing.oauthSettings.scope)
) {
let details = OAuth2Providers.getHostnameDetails(
config.outgoing.hostname
);
if (!details) {
throw new Error(
`Could not get OAuth2 details for hostname=${config.outgoing.hostname}.`
);
}
config.outgoing.oauthSettings = { issuer: details[0], scope: details[1] };
}
if (config.incoming.owaURL) {
inServer.setUnicharValue("owa_url", config.incoming.owaURL);
}
if (config.incoming.ewsURL) {
inServer.setUnicharValue("ews_url", config.incoming.ewsURL);
}
if (config.incoming.easURL) {
inServer.setUnicharValue("eas_url", config.incoming.easURL);
}
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);
}
} catch (e) {
gAccountSetupLogger.info("verifyConfig failed: " + e);
// Avoid pref pollution, clear out server prefs.
MailServices.accounts.removeIncomingServer(inServer, true);
errorCallback(e);
}
}
function verifyLogon(
config,
inServer,
alter,
msgWindow,
successCallback,
errorCallback
) {
gAccountSetupLogger.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;
} finally {
// restore them
msgWindow.notificationCallbacks = saveCallbacks;
}
}
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 = gAccountSetupLogger;
}
urlListener.prototype = {
OnStartRunningUrl(aUrl) {
this._log.debug(`Starting to verify configuration;
email as username=${this.mConfig.incoming.username !=
this.mConfig.identity.emailAddress}
savedUsername=${this.mConfig.usernameSaved ? "true" : "false"},
authMethod=${this.mServer.authMethod}`);
},
OnStopRunningUrl(aUrl, aExitCode) {
if (Components.isSuccessCode(aExitCode)) {
this._log.debug(`Configuration verified successfully!`);
this._cleanup();
this.mSuccessCallback(this.mConfig);
return;
}
this._log.debug(`Verifying configuration failed; status=${aExitCode}`);
try {
let nssErrorsService = Cc["@mozilla.org/nss_errors_service;1"].getService(
Ci.nsINSSErrorsService
);
let errorClass = nssErrorsService.getErrorClass(aExitCode);
if (errorClass == Ci.nsINSSErrorsService.ERROR_CLASS_BAD_CERT) {
this.mCertError = true;
}
} catch (e) {
// It's not an NSS error.
}
if (this.mCertError) {
let mailNewsUrl = aUrl.QueryInterface(Ci.nsIMsgMailNewsUrl);
let secInfo = mailNewsUrl.failedSecInfo;
this.informUserOfCertError(secInfo, aUrl.asciiHostPort);
} else if (this.mAlter) {
// Try other variations.
this.mServer.closeCachedConnections();
this.tryNextLogon(aUrl);
} else {
// Logon failed, and we aren't supposed to try other variations.
this._failed(aUrl);
}
},
tryNextLogon(aPreviousUrl) {
this._log.debug("Trying next logon variation");
// check if we tried full email address as username
if (this.mConfig.incoming.username != this.mConfig.identity.emailAddress) {
this._log.debug("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.debug("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);
if (this.mServer.socketType == Ci.nsMsgSocketType.SSL) {
this._log.debug("Using SSL");
} else if (this.mServer.socketType == Ci.nsMsgSocketType.alwaysSTARTTLS) {
this._log.debug("Using STARTTLS");
}
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.
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.includes(
this.mConfig.incoming.auth
)
) {
this.mConfig.outgoing.auth = this.mConfig.incoming.auth;
}
this._log.debug(`Trying next auth method: ${this.mServer.authMethod}`);
verifyLogon(
this.mConfig,
this.mServer,
this.mAlter,
this.mMsgWindow,
this.mSuccessCallback,
this.mErrorCallback
);
return;
}
// Tried all variations we can. Give up.
this._log.debug("Have tried all variations. Giving up.");
this._failed(aPreviousUrl);
},
_cleanup() {
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);
}
},
_failed(aUrl) {
this._cleanup();
var code = aUrl.errorCode || "login-error-unknown";
var msg = aUrl.errorMessage;
// *Only* for known (!) username/password errors, show our message.
// But there are 1000 other reasons why it could have failed, e.g.
// server not reachable, bad auth method, server hiccups, or even
// custom server messages that tell the user to do something,
// so show the backend error message, unless we are certain
// that it's a wrong username or password.
if (
!msg || // Normal IMAP login error sets no error msg
code == "pop3UsernameFailure" ||
code == "pop3PasswordFailed" ||
code == "imapOAuth2Error"
) {
msg = getStringBundle(
"chrome://messenger/locale/accountCreationModel.properties"
).GetStringFromName("cannot_login.error");
}
var ex = new Exception(msg);
ex.code = code;
this.mErrorCallback(ex);
},
/**
* Inform users that we got a certificate error for the specified location.
* Allow them to add an exception for it.
*
* @param {nsITransportSecurityInfo} secInfo
* @param {string} location - "host:port" that had the problem.
*/
informUserOfCertError(secInfo, location) {
this._log.debug(`Informing user about cert error for ${location}`);
let params = {
exceptionAdded: false,
securityInfo: secInfo,
prefetchCert: true,
location,
};
Services.wm
.getMostRecentWindow("mail:3pane")
.browsingContext.topChromeWindow.openDialog(
"chrome://pippki/content/exceptionDialog.xhtml",
"exceptionDialog",
"chrome,centerscreen,modal",
params
);
if (!params.exceptionAdded) {
this._log.debug(`Did not accept exception for ${location}`);
this._cleanup();
let errorMsg = getStringBundle(
"chrome://messenger/locale/accountCreationModel.properties"
).GetStringFromName("cannot_login.error");
this.mErrorCallback(new Exception(errorMsg));
} else {
this._log.debug(`Accept exception for ${location} - will retry logon.`);
// 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(iid) {
return this.QueryInterface(iid);
},
// nsISupports
QueryInterface: ChromeUtils.generateQI([
"nsIInterfaceRequestor",
"nsIUrlListener",
]),
};

Просмотреть файл

@ -280,13 +280,13 @@
"resource:///modules/WindowsJumpLists.jsm": "comm/mail/modules/WindowsJumpLists.jsm",
"resource:///modules/accountcreation/AccountConfig.jsm": "comm/mail/components/accountcreation/AccountConfig.jsm",
"resource:///modules/accountcreation/AccountCreationUtils.jsm": "comm/mail/components/accountcreation/AccountCreationUtils.jsm",
"resource:///modules/accountcreation/ConfigVerifier.jsm": "comm/mail/components/accountcreation/ConfigVerifier.jsm",
"resource:///modules/accountcreation/CreateInBackend.jsm": "comm/mail/components/accountcreation/CreateInBackend.jsm",
"resource:///modules/accountcreation/ExchangeAutoDiscover.jsm": "comm/mail/components/accountcreation/ExchangeAutoDiscover.jsm",
"resource:///modules/accountcreation/FetchConfig.jsm": "comm/mail/components/accountcreation/FetchConfig.jsm",
"resource:///modules/accountcreation/FetchHTTP.jsm": "comm/mail/components/accountcreation/FetchHTTP.jsm",
"resource:///modules/accountcreation/GuessConfig.jsm": "comm/mail/components/accountcreation/GuessConfig.jsm",
"resource:///modules/accountcreation/readFromXML.jsm": "comm/mail/components/accountcreation/readFromXML.jsm",
"resource:///modules/accountcreation/verifyConfig.jsm": "comm/mail/components/accountcreation/verifyConfig.jsm",
"resource:///modules/activity/activityModules.jsm": "comm/mail/components/activity/modules/activityModules.jsm",
"resource:///modules/activity/alertHook.jsm": "comm/mail/components/activity/modules/alertHook.jsm",
"resource:///modules/activity/autosync.jsm": "comm/mail/components/activity/modules/autosync.jsm",