Skip to content
This repository has been archived by the owner on Nov 3, 2021. It is now read-only.

Commit

Permalink
Bug 948580 - [B2G][Email] Send "MAIL FROM" in SMTP probe to validate …
Browse files Browse the repository at this point in the history
…e-mail address. r=asuth
  • Loading branch information
mcav committed Feb 13, 2014
1 parent f21420c commit e947bf0
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 28 deletions.
1 change: 1 addition & 0 deletions apps/email/js/cards/setup_l10n_map.js
Expand Up @@ -7,6 +7,7 @@
define({
'offline': 'setup-error-offline',
'bad-user-or-pass': 'setup-error-bad-user-or-pass2',
'bad-address': 'setup-error-bad-address',
'not-authorized': 'setup-error-not-authorized',
'unknown': 'setup-error-unknown2',
'needs-app-pass': 'setup-error-needs-app-pass',
Expand Down
1 change: 1 addition & 0 deletions apps/email/js/ext/mailapi/composite/configurator.js
Expand Up @@ -9402,6 +9402,7 @@ exports.configurator = {
incomingInfo.preferredAuthMethod = null;
}
smtpConnInfo = {
emailAddress: userDetails.emailAddress, // used for probing
hostname: domainInfo.outgoing.hostname,
port: domainInfo.outgoing.port,
crypto: (typeof domainInfo.outgoing.socketType === 'string' ?
Expand Down
4 changes: 4 additions & 0 deletions apps/email/js/ext/mailapi/main-frame-setup.js
Expand Up @@ -360,6 +360,7 @@ function MailAccount(api, wireRep, acctsSlice) {
/**
* @listof[@oneof[
* @case['bad-user-or-pass']
* @case['bad-address']
* @case['needs-app-pass']
* @case['imap-disabled']
* @case['pop-server-not-great']{
Expand Down Expand Up @@ -2945,6 +2946,9 @@ MailAPI.prototype = {
* The username and password didn't check out. We don't know which one
* is wrong, just that one of them is wrong.
* }
* @case['bad-address']{
* The e-mail address provided was rejected by the SMTP probe.
* }
* @case['pop-server-not-great']{
* The POP3 server doesn't support IDLE and TOP, so we can't use it.
* }
Expand Down
134 changes: 106 additions & 28 deletions apps/email/js/ext/mailapi/smtp/probe.js
Expand Up @@ -1112,9 +1112,20 @@ exports.CONNECT_TIMEOUT_MS = 30000;
* Validate that we find an SMTP server using the connection info and that it
* seems to like our credentials.
*
* Because the SMTP client has no connection timeout support, use our own timer
* to decide when to give up on the SMTP connection. We use the timer for the
* whole process, including even after the connection is established.
* Because the SMTP client has no connection timeout support, use our
* own timer to decide when to give up on the SMTP connection. We use
* the timer for the whole process, including even after the
* connection is established and we probe for a valid address.
*
* The process here is in two steps: First, connect to the server and
* make sure that we can authenticate properly. Then, if that
* succeeds, we send a "MAIL FROM:<our address>" line to see if the
* server will reject the e-mail address, followed by "RCPT TO" for
* the same purpose. This could fail if the user uses manual setup and
* gets everything right except for their e-mail address. We want to
* catch this error before they complete account setup; if we don't,
* they'll be left with an account that can't send e-mail, and we
* currently don't allow them to change their address after setup.
*/
function SmtpProber(credentials, connInfo) {
console.log("PROBE:SMTP attempting to connect to", connInfo.hostname);
Expand All @@ -1125,56 +1136,123 @@ function SmtpProber(credentials, connInfo) {
auth: { user: credentials.username, pass: credentials.password },
debug: exports.TEST_USE_DEBUG_MODE,
});
// onIdle happens after successful login, and so is what our probing uses.
this._conn.on('idle', this.onResult.bind(this, null));
this._conn.on('error', this.onResult.bind(this));
this._conn.on('end', this.onResult.bind(this, 'unknown'));

this.timeoutId = setTimeoutFunc(
this.onResult.bind(this, 'unresponsive-server'),
exports.CONNECT_TIMEOUT_MS);
// For the first step (connection/authentication), handle callbacks
// in this.onConnectionResult.
this.setConnectionListenerCallback(this.onConnectionResult);

this.timeoutId = setTimeoutFunc(function() {
// Emit a fake error from the connection so that we can send the
// error to the proper callback handler depending on what state
// the connection is in.
this._conn.emit('error', 'unresponsive-server');
}.bind(this), exports.CONNECT_TIMEOUT_MS);

this.emailAddress = connInfo.emailAddress;
this.onresult = null;
this.error = null;
this.errorDetails = { server: connInfo.hostname };
}
exports.SmtpProber = SmtpProber;
SmtpProber.prototype = {
onResult: function(err) {

/**
* Unsubscribe any existing listeners, and resubscribe to all
* relevant events for the given fn handler.
*/
setConnectionListenerCallback: function(fn) {
this._conn.removeAllListeners();
// onIdle happens after successful login, and so is what our probing uses.
this._conn.on('idle', fn.bind(this, null));
this._conn.on('error', fn.bind(this));
this._conn.on('end', fn.bind(this, 'unknown'));
},

/**
* Callback for initial connection, before we check for address
* validity. Connection and security errors will happen here.
*/
onConnectionResult: function(err) {
if (!this.onresult)
return;
return; // We already handled the result.

// XXX just map all security errors as indicated by name
if (err && typeof(err) === 'object') {
// XXX just map all security errors as indicated by name
if (err.name && /^Security/.test(err.name)) {
err = 'bad-security';
}
else {
} else {
switch (err.name) {
case 'AuthError':
err = 'bad-user-or-pass';
break;
case 'UnknownAuthError':
default:
err = 'server-problem';
break;
case 'AuthError':
err = 'bad-user-or-pass';
break;
case 'UnknownAuthError':
default:
err = 'server-problem';
break;
}
}
}

this.error = err;
if (err)
if (err) {
this.cleanup(err);
} else {
console.log('PROBE:SMTP connected, checking address validity');
// For clarity, send callbacks to onAddressValidityResult.
this.setConnectionListenerCallback(this.onAddressValidityResult);
this._conn.useEnvelope({
from: this.emailAddress,
to: [this.emailAddress]
});
this._conn.on('message', function() {
// Success! Our recipient was valid.
this.onAddressValidityResult(null);
}.bind(this));
}
},

/**
* The server will respond to a "MAIL FROM" probe, indicating
* whether or not the e-mail address is invalid. We try to succeed
* unless we're positive that the server actually rejected the
* address (in other words, any error other than "SenderError" is
* ignored).
*/
onAddressValidityResult: function(err) {
if (!this.onresult)
return; // We already handled the result.

if (err && (err.name === 'SenderError' ||
err.name === 'RecipientError')) {
err = 'bad-address';
} else if (err && err.name) {
// This error wasn't normalized (so it's not
// "unresponsive-server"); we don't expect any auth or
// connection failures here, so treat it as an unknown error.
err = 'server-problem';
}
this.cleanup(err);
},

/**
* Send the final probe result (with error or not) and close the
* SMTP connection.
*/
cleanup: function(err) {
clearTimeoutFunc(this.timeoutId);

if (err) {
console.warn('PROBE:SMTP sad. error: | ' + (err && err.name || err) +
' | ' + (err && err.message || '') + ' |');
else
} else {
console.log('PROBE:SMTP happy');
}

clearTimeoutFunc(this.timeoutId);

this.error = err;
this.onresult(this.error, this.errorDetails);
this.onresult = null;

this._conn.close();
},
}
};

}); // end define
Expand Down
2 changes: 2 additions & 0 deletions apps/email/js/ext/mailapi/worker-bootstrap.js
Expand Up @@ -13578,6 +13578,7 @@ MailBridge.prototype = {
// we're offline.
if (!err || (
err !== 'bad-user-or-pass' &&
err !== 'bad-address' &&
err !== 'needs-app-pass' &&
err !== 'imap-disabled'
)) {
Expand Down Expand Up @@ -16753,6 +16754,7 @@ MailUniverse.prototype = {

switch (problem) {
case 'bad-user-or-pass':
case 'bad-address':
case 'imap-disabled':
case 'needs-app-pass':
this.__notifyBadLogin(account, problem);
Expand Down
3 changes: 3 additions & 0 deletions apps/email/locales/email.en-US.properties
Expand Up @@ -40,6 +40,9 @@ setup-error-offline=This device is currently offline. Connect to a network and t
# tell them apart either. Also, if the user made a typo, they could have made
# it in either place.
setup-error-bad-user-or-pass2=Invalid username or password. Check for typos.
# The error for a bad e-mail address. We tried to send email but
# couldn't because the server said it was a bad address.
setup-error-bad-address=Invalid e-mail address. Check for typos.
# The error for a user having the correct username and password, but the mail
# server explicitly indicates they don't have permission to talk to the mail
# server. This is currently only expected from ActiveSync servers; situations
Expand Down

0 comments on commit e947bf0

Please sign in to comment.