Publish Vulpes Preview 15 sources for desktop, Android and Tundra

Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
OverSu 2026-09-30 23:13:03 +02:00
commit b38dea0597
25901 changed files with 9982925 additions and 369 deletions

View file

@ -0,0 +1,867 @@
/**
* Common code for creating and working with various account types.
**/
define(
[
'./a64',
'logic',
'./allback',
'require',
'module',
'exports'
],
function(
$a64,
logic,
allback,
require,
$module,
exports
) {
var latchedWithRejections = allback.latchedWithRejections;
// The number of milliseconds to wait for various (non-ActiveSync) XHRs to
// complete during the autoconfiguration process. This value is intentionally
// fairly large so that we don't abort an XHR just because the network is
// spotty.
var AUTOCONFIG_TIMEOUT_MS = 30 * 1000;
var ISPDB_AUTOCONFIG_ROOT =
'https://live.mozillamessaging.com/autoconfig/v1.1/';
function requireConfigurator(type, fn) {
if (type === 'activesync') {
require(['activesync/configurator'], fn);
} else if (type === 'pop3+smtp' || type === 'imap+smtp') {
require(['composite/configurator'], fn);
}
}
function accountTypeToClass(type, callback) {
requireConfigurator(type, function(mod) {
callback(mod.account.Account);
});
}
exports.accountTypeToClass = accountTypeToClass;
// Simple hard-coded autoconfiguration by domain...
var autoconfigByDomain = exports._autoconfigByDomain = {
'localhost': {
type: 'imap+smtp',
incoming: {
hostname: 'localhost',
port: 143,
socketType: 'plain',
username: '%EMAILLOCALPART%',
},
outgoing: {
hostname: 'localhost',
port: 25,
socketType: 'plain',
username: '%EMAILLOCALPART%',
},
},
'fakeimaphost': {
type: 'imap+smtp',
incoming: {
hostname: 'localhost',
port: 0,
socketType: 'plain',
username: '%EMAILLOCALPART%',
},
outgoing: {
hostname: 'localhost',
port: 0,
socketType: 'plain',
username: '%EMAILLOCALPART%',
},
},
'fakepop3host': {
type: 'pop3+smtp',
incoming: {
hostname: 'localhost',
port: 0,
socketType: 'plain',
username: '%EMAILLOCALPART%',
},
outgoing: {
hostname: 'localhost',
port: 0,
socketType: 'plain',
username: '%EMAILLOCALPART%',
},
},
'slocalhost': {
type: 'imap+smtp',
incoming: {
hostname: 'localhost',
port: 993,
socketType: 'SSL',
username: '%EMAILLOCALPART%',
},
outgoing: {
hostname: 'localhost',
port: 465,
socketType: 'SSL',
username: '%EMAILLOCALPART%',
},
},
'fakeashost': {
type: 'activesync',
displayName: 'Test',
incoming: {
// This string will be clobbered with the correct port number when running
// as a unit test.
server: 'http://localhost:8880',
username: '%EMAILADDRESS%',
},
},
// like slocalhost, really just exists to generate a test failure
'saslocalhost': {
type: 'activesync',
displayName: 'Test',
incoming: {
server: 'https://localhost:443',
username: '%EMAILADDRESS%',
},
},
// Mapping for a nonexistent domain for testing a bad domain without it being
// detected ahead of time by the autoconfiguration logic or otherwise.
'nonesuch.nonesuch': {
type: 'imap+smtp',
imapHost: 'nonesuch.nonesuch',
imapPort: 993,
imapCrypto: true,
smtpHost: 'nonesuch.nonesuch',
smtpPort: 465,
smtpCrypto: true,
usernameIsFullEmail: false,
},
};
/**
* Recreate the array of identities for a given account.
*
* @param universe the MailUniverse
* @param accountId the ID for this account
* @param oldIdentities an array of the old identities
* @return the new identities
*/
function recreateIdentities(universe, accountId, oldIdentities) {
var identities = [];
for (var iter in Iterator(oldIdentities)) {
var oldIdentity = iter[1];
identities.push({
id: accountId + '/' + $a64.encodeInt(universe.config.nextIdentityNum++),
name: oldIdentity.name,
address: oldIdentity.address,
replyTo: oldIdentity.replyTo,
signature: oldIdentity.signature,
signatureEnabled: oldIdentity.signatureEnabled
});
}
return identities;
}
exports.recreateIdentities = recreateIdentities;
function fillConfigPlaceholders(userDetails, sourceConfigInfo) {
// Return a mutated copy, don't mutate the original.
var configInfo = JSON.parse(JSON.stringify(sourceConfigInfo));
var details = userDetails.emailAddress.split('@');
var emailLocalPart = details[0], emailDomainPart = details[1];
var domain = emailDomainPart.toLowerCase();
var placeholderFields = {
incoming: ['username', 'hostname', 'server'],
outgoing: ['username', 'hostname'],
};
function fillPlaceholder(value) {
return value.replace('%EMAILADDRESS%', userDetails.emailAddress)
.replace('%EMAILLOCALPART%', emailLocalPart)
.replace('%EMAILDOMAIN%', emailDomainPart)
.replace('%REALNAME%', userDetails.displayName);
}
for (var serverType in placeholderFields) {
var fields = placeholderFields[serverType];
var server = configInfo[serverType];
if (!server) {
continue;
}
for (var iField = 0; iField < fields.length; iField++) {
var field = fields[iField];
if (server.hasOwnProperty(field)) {
server[field] = fillPlaceholder(server[field]);
}
}
}
return configInfo;
}
exports.fillConfigPlaceholders = fillConfigPlaceholders;
/**
* The Autoconfigurator tries to automatically determine account settings, in
* large part by taking advantage of Thunderbird's prior work on autoconfig:
* <https://developer.mozilla.org/en-US/docs/Thunderbird/Autoconfiguration>.
* There are some important differences, however, since we support ActiveSync
* whereas Thunderbird does not.
*
* The v2 process is as follows. All of this is done without a password (since
* it might turn out we don't need a password in the case of OAuth2-based auth.)
*
* 1) Get the domain from the user's email address
* 2) Check hardcoded-into-GELAM account settings for the domain (useful for
* unit tests)
* 3) Check locally stored XML config files in Gaia for the domain at
* `/autoconfig/<domain>`
* 4) In parallel:
* - Do server-hosted autoconfig checks at URLs, passing the user's email
* address in the query string (as `emailaddress`)
* - `https://autoconfig.<domain>/mail/config-v1.1.xml` and
* - `https://<domain>/.well-known/autoconfig/mail/config-v1.1.xml`,
* - Check the Mozilla ISPDB for the domain:
* - `https://live.mozillamessaging.com/autoconfig/v1.1/<domain>`
* - Having the Mozilla ISPDB do an MX lookup.
* 5) If we didn't reach a conclusion in step 4, check the MX lookup result.
* If it differed from the domain, then re-lookup the locally stored XML
* config and failing that, check the ISPDB for that domain.
* 6) If that didn't net us anything, look for evidence of ActiveSync
* AutoDiscover endpoints at the following locations in parallel:
* `https://<domain>/autodiscover/autodiscover.xml`
* `https://autodiscover.<domain>/autodiscover/autodiscover.xml`
*
* This differs from the v1 process in that:
* - v1 did everything in serial not parallel
* - v1 used http, not httpS, to look for self-hosted autoconfig servers.
* - v1 ran autodiscover before checking with the ISPDB but after local and
* server-hosted ISPDB autoconfig files.
* - v1 actually ran autodiscover. Now we just look for evidence of
* autodiscover. AutoDiscover requires being able to authenticate the user
* which implies having the password. Since we're not sure if we need a
* password or not, we can't do that yet. We leave AutoDiscover up to the
* ActiveSync configurator to perform.
* - v1 wanted the user's password
*
* These changes were informed by the following needs and observations:
* - http was a bad idea security-wise, but was done for consistency with
* Thunderbird. The Thunderbird rationale involved DNS also being insecure,
* so an attacker could already win with local network control. However,
* Thunderbird also allowed autoconfig to return settings that didn't
* use SSL/TLS, whereas we do not. (We ignore them).
*
* Thunderbird has recently come around to the use of https instead of http
* for this purpose. It's also worth noting that as far as we know, almost
* no one actually hosts their own autoconfig servers.
*
* - AutoDiscover can be very slow, especially if we're waiting for our requests
* to timeout.
*
* - It's become common to see servers that implement ActiveSync (which we
* strongly dislike) as well as IMAP (which we strongly prefer). By letting
* ActiveSync derail the decision-making process we rob ourselves of the
* ability to have the ISPDB indicate the IMAP is an option.
*
* - If the user's server supports OAuth2, there's no need to make them type in
* their password; it might even be confusing to them.
*
* Our ConfigInfo structures look like the following:
*
* {
* type: 'imap+smtp',
* incoming: {
* hostname: <imap hostname>,
* port: <imap port number>,
* socketType: <one of 'plain', 'SSL', 'STARTTLS'>,
* username: <imap username>,
* authentication: <one of 'password-cleartext', 'xoauth2'>
* },
* outgoing: {
* hostname: <smtp hostname>,
* port: <smtp port>,
* socketType: <one of 'plain', 'SSL', 'STARTTLS'>,
* username: <smtp username>,
* authentication: <one of 'password-cleartext', 'xoauth2'>
* },
* oauth2Settings: null or {
* secretGroup: <group identify which app secrets should be used>,
* authEndpoint: <auth url of the page to show to the user>,
* tokenEndpoint: <url for getting/refreshing tokens (no user ui)>,
* scope: <space-delimited scopes to request>
* }
* }
*
* POP3 is similar to IMAP/SMTP but it's 'pop3+smtp'. ActiveSync looks
* like:
*
* {
* type: 'activesync',
* displayName: <display name>, (optional)
* incoming: {
* server: 'https://<activesync hostname>'
* },
* }
*/
function Autoconfigurator() {
this.timeout = AUTOCONFIG_TIMEOUT_MS;
logic.defineScope(this, 'Autoconfigurator');
}
exports.Autoconfigurator = Autoconfigurator;
Autoconfigurator.prototype = {
/**
* The list of fatal error codes.
*
* What's fatal and why:
* - bad-user-or-pass: We found a server, it told us the credentials were
* bogus. There is no point going on.
* - not-authorized: We found a server, it told us the credentials are fine
* but the access rights are insufficient. There is no point going on.
*
* Non-fatal and why:
* - unknown: If something failed we should keep checking other info sources.
* - no-config-info: The specific source had no details; we should keep
* checking other sources.
*/
_fatalErrors: ['bad-user-or-pass', 'not-authorized'],
/**
* Check the supplied error and return true if it's really a "success" or if
* it's a fatal error we can't recover from.
*
* @param error the error code
* @return true if the error is a "success" or if it's a fatal error
*/
_isSuccessOrFatal: function(error) {
return !error || this._fatalErrors.indexOf(error) !== -1;
},
/**
* Get an XML config file from the supplied url. The format is defined at
* <https://wiki.mozilla.org/Thunderbird:Autoconfiguration:ConfigFileFormat>.
*
* @param url the URL to fetch the config file from
* @param callback a callback taking an error string (if any) and the config
* info, formatted as JSON
*/
_getXmlConfig: function getXmlConfig(url) {
return new Promise(function(resolve, reject) {
var scope = logic.subscope(this, { method: 'GET', url: url });
logic(scope, 'xhr:start');
var xhr = new XMLHttpRequest({mozSystem: true});
xhr.open('GET', url, true);
xhr.timeout = this.timeout;
xhr.onload = function() {
logic(scope, 'xhr:end', { status: xhr.status });
if (xhr.status < 200 || xhr.status >= 300) {
reject('status' + xhr.status);
return;
}
// XXX: For reasons which are currently unclear (possibly a platform
// issue), trying to use responseXML results in a SecurityError when
// running XPath queries. So let's just do an end-run around the
// "security".
self.postMessage({
uid: 0,
type: 'configparser',
cmd: 'accountcommon',
args: [xhr.responseText]
});
self.addEventListener('message', function onworkerresponse(evt) {
var data = evt.data;
if (data.type != 'configparser' || data.cmd != 'accountcommon') {
return;
}
self.removeEventListener(evt.type, onworkerresponse);
var args = data.args;
var config = args[0], status = args[1];
resolve(config);
});
};
// Caution: don't overwrite ".onerror" twice here. Just be careful
// to only assign that once until <http://bugzil.la/949722> is fixed.
xhr.ontimeout = function() {
logic(scope, 'xhr:end', { status: 'timeout' });
reject('timeout');
};
xhr.onerror = function() {
logic(scope, 'xhr:end', { status: 'error' });
reject('error');
};
// At least in the past, Gecko might synchronously throw when we call
// send for a locally-hosted file, so we're sticking with this until the
// end of time.
try {
xhr.send();
}
catch(e) {
logic(scope, 'xhr:end', { status: 'sync-error' });
reject('status404');
}
}.bind(this));
},
/**
* Attempt to get an XML config file locally.
*
* @param domain the domain part of the user's email address
* @param callback a callback taking an error string (if any) and the config
* info, formatted as JSON
*/
_getConfigFromLocalFile: function getConfigFromLocalFile(domain) {
return this._getXmlConfig('/autoconfig/' + encodeURIComponent(domain));
},
/**
* Check whether it looks like there's an AutoDiscover endpoint at the given
* URL. AutoDiscover wants us to be authenticated, so we treat a 401 status
* as success and anything else as failure.
*
* For maximum realism we perform a POST.
*/
_checkAutodiscoverUrl: function(url) {
return new Promise(function(resolve, reject) {
var scope = logic.subscope(this, { method: 'POST', url: url });
logic(scope, 'autodiscoverProbe:start');
var xhr = new XMLHttpRequest({mozSystem: true});
xhr.open('POST', url, true);
xhr.timeout = this.timeout;
var victory = function() {
resolve({
type: 'activesync',
incoming: {
autodiscoverEndpoint: url
}
});
}.bind(this);
xhr.onload = function() {
logic(scope, 'autodiscoverProbe:end', { status: xhr.status });
if (xhr.status === 401) {
victory();
return;
}
reject('status' + xhr.status);
};
xhr.ontimeout = function() {
logic(scope, 'autodiscoverProbe:end', { status: 'timeout' });
reject('timeout');
};
xhr.onerror = function() {
logic(scope, 'autodiscoverProbe:end', { status: 'error' });
reject('error');
};
try {
xhr.send(null);
}
catch(e) {
logic(scope, 'autodiscoverProbe:end', { status: 'sync-error' });
reject('status404');
}
}.bind(this));
},
/**
* Look for AutoDiscover endpoints for the given domain. If we find one, we
* return what amounts to pseudo-config information. We don't actually know
* enough information to do a full autodiscover at this point, so we need to
* return enough for our ActiveSync account's tryToCreateAccount method to
* handle things from there.
*/
_probeForAutodiscover: function(domain) {
var subdirUrl = 'https://' + domain + '/autodiscover/autodiscover.xml';
var domainUrl = 'https://autodiscover.' + domain +
'/autodiscover/autodiscover.xml';
return latchedWithRejections({
subdir: this._checkAutodiscoverUrl(subdirUrl),
domain: this._checkAutodiscoverUrl(domainUrl)
}).then(function(results) {
// Favor the subdirectory discovery point.
if (results.subdir.resolved && results.subdir.value) {
return results.subdir.value;
}
if (results.domain.resolved && results.domain.value) {
return results.domain.value;
}
// Yeah, no AutoDiscover possible.
return null;
}.bind(this));
},
/**
* Attempt to get an XML config file from the Mozilla ISPDB.
*
* @param domain the domain part of the user's email address
*/
_getConfigFromISPDB: function(domain) {
return this._getXmlConfig(ISPDB_AUTOCONFIG_ROOT +
encodeURIComponent(domain));
},
/**
* Look up the DNS MX record for a domain. This currently uses a web service
* instead of querying it directly.
*
* @param domain the domain part of the user's email address
*
* @return {Promise}
* If we locate a MX domain and that domain differs from our own domain, we
* will resolve the promise with that String. If there is no MX domain or
* it is the same as our existing domain, we will resolve with a null value.
* In the event of a problem contacting the ISPDB server, we will reject
* the promise.
*/
_getMX: function getMX(domain) {
return new Promise(function(resolve, reject) {
var scope = logic.subscope(this, { domain: domain });
logic(scope, 'mxLookup:begin');
var xhr = new XMLHttpRequest({mozSystem: true});
xhr.open('GET', 'https://live.mozillamessaging.com/dns/mx/' +
encodeURIComponent(domain), true);
xhr.timeout = this.timeout;
xhr.onload = function() {
var reportDomain = null;
if (xhr.status === 200) {
var normStr = xhr.responseText.split('\n')[0];
if (normStr) {
normStr = normStr.toLowerCase();
// XXX: We need to normalize the domain here to get the base domain,
// but that's complicated because people like putting dots in
// TLDs. For now, let's just pretend no one would do such a horrible
// thing.
var mxDomain = normStr.split('.').slice(-2).join('.');
if (mxDomain !== domain) {
reportDomain = mxDomain;
}
}
}
logic(scope, 'mxLookup:end',
{ 'raw': normStr, normalized: mxDomain, reporting: reportDomain });
resolve(reportDomain);
};
xhr.ontimeout = function() {
logic(scope, 'mxLookup:end', { status: 'timeout' });
reject('timeout');
};
xhr.onerror = function() {
logic(scope, 'mxLookup:end', { status: 'error' });
reject('error');
};
xhr.send();
}.bind(this));
},
_getHostedAndISPDBConfigs: function(domain, emailAddress) {
var commonAutoconfigSuffix = '/mail/config-v1.1.xml?emailaddress=' +
encodeURIComponent(emailAddress);
// subdomain autoconfig URL
var subdomainAutoconfigUrl =
'https://autoconfig.' + domain + commonAutoconfigSuffix;
// .well-known autoconfig URL
var wellKnownAutoconfigUrl =
'https://' + domain + '/.well-known/autoconfig' +
commonAutoconfigSuffix;
return latchedWithRejections({
autoconfigSubdomain: this._getXmlConfig(subdomainAutoconfigUrl),
autoconfigWellKnown: this._getXmlConfig(wellKnownAutoconfigUrl),
ispdb: this._getConfigFromISPDB(domain),
mxDomain: this._getMX(domain)
}).then(function(results) {
// Favor the autoconfig subdomain for historical reasons
if (results.autoconfigSubdomain.resolved &&
results.autoconfigSubdomain.value) {
return { type: 'config', source: 'autoconfig-subdomain',
config: results.autoconfigSubdomain.value };
}
// Then the well-known
if (results.autoconfigWellKnown.resolved &&
results.autoconfigWellKnown.value) {
return { type: 'config', source: 'autoconfig-wellknown',
config: results.autoconfigWellKnown.value };
}
// Then the ISPDB
if (results.ispdb.resolved &&
results.ispdb.value) {
return { type: 'config', source: 'ispdb',
config: results.ispdb.value };
}
if (results.mxDomain.resolved &&
results.mxDomain.value &&
results.mxDomain.value !== domain) {
return { type: 'mx', domain: results.mxDomain.value };
}
return { type: null };
}.bind(this));
},
/**
* Attempt to get an XML config file by checking the DNS MX record and
* querying the Mozilla ISPDB.
*
* @param domain the domain part of the user's email address
* @param callback a callback taking an error string (if any) and the config
* info, formatted as JSON
*/
_getConfigFromMX: function getConfigFromMX(domain, callback) {
var self = this;
this._getMX(domain, function(error, mxDomain, errorDetails) {
if (error)
return callback(error, null, errorDetails);
console.log(' Found MX for', mxDomain);
if (domain === mxDomain)
return callback('no-config-info', null, { status: 'mxsame' });
// If we found a different domain after MX lookup, we should look in our
// local file store (mostly to support Google Apps domains) and, if that
// doesn't work, the Mozilla ISPDB.
console.log(' Looking in local file store');
self._getConfigFromLocalFile(mxDomain, function(error, config,
errorDetails) {
// (Local XML lookup should not have any fatal errors)
if (!error) {
callback(error, config, errorDetails);
return;
}
console.log(' Looking in the Mozilla ISPDB');
self._getConfigFromDB(mxDomain, callback);
});
});
},
_checkGelamConfig: function(domain) {
if (autoconfigByDomain.hasOwnProperty(domain)) {
return autoconfigByDomain[domain];
}
return null;
},
/**
* See the MailAPI.learnAboutAccount documentation for usage information.
*
* Internals:
*
*
*
* @return {Promise}
*/
learnAboutAccount: function(details) {
return new Promise(function(resolve, reject) {
var emailAddress = details.emailAddress;
var emailParts = emailAddress.split('@');
var emailLocalPart = emailParts[0], emailDomainPart = emailParts[1];
var domain = emailDomainPart.toLowerCase();
var scope = logic.subscope(this, { domain: domain });
logic(scope, 'autoconfig:begin');
// Call this when we find a usable config setting to perform appropriate
// normalization, logging, and promise resolution.
var victory = function(sourceConfigInfo, source) {
var configInfo = null, result;
if (sourceConfigInfo) {
configInfo = fillConfigPlaceholders(details, sourceConfigInfo);
if (configInfo.incoming &&
configInfo.incoming.authentication === 'xoauth2') {
result = 'need-oauth2';
} else {
result = 'need-password';
}
} else {
result = 'no-config-info';
}
logic(scope, 'autoconfig:end', {
result: result,
source: source,
configInfo: configInfo
});
resolve({ result: result, source: source, configInfo: configInfo });
}.bind(this);
// Call this if we can't find a configuration.
var failsafeFailure = function(err) {
logic(this, 'autoconfig:end', { err: {
message: err && err.message,
stack: err && err.stack
}});
resolve({ result: 'no-config-info', configInfo: null });
}.bind(this);
// Helper that turns a rejection into a null and outputs a log entry.
var coerceRejectionToNull = function(err) {
logic(scope, 'autoconfig:coerceRejection', { err: err });
return null;
}.bind(this);
// -- Synchronous logic
// - Group 0: hardcoded in GELAM (testing only)
var hardcodedConfig = this._checkGelamConfig(domain);
if (hardcodedConfig) {
victory(hardcodedConfig, 'hardcoded');
return;
}
// -- Asynchronous setup
// This all wants to be a generator. It doesn't make a lot of sense to
// structure this as a chain of then's since we do want an early return.
// This is a good candidate for 'koa' or something like it in the near
// future.
// - Group 1: local config
var localConfigHandler = function(info) {
if (info) {
victory(info, 'local');
return null;
}
// We don't need to coerce because there will be no rejections.
return this._getHostedAndISPDBConfigs(domain, emailAddress)
.then(selfHostedAndISPDBHandler);
}.bind(this);
// - Group 2: self-hosted autoconfig, ISPDB first checks
var mxDomain;
var selfHostedAndISPDBHandler = function(typedResult) {
if (typedResult.type === 'config') {
victory(typedResult.config, typedResult.source);
return null;
}
// Did we get a different MX result?
if (typedResult.type === 'mx') {
mxDomain = typedResult.domain;
return this._getConfigFromLocalFile(mxDomain)
.catch(coerceRejectionToNull)
.then(mxLocalHandler);
}
// No MX result, probe autodiscover.
return this._probeForAutodiscover(domain)
.then(autodiscoverHandler);
}.bind(this);
// - Group 3: MX-derived lookups
var mxLocalHandler = function(info) {
if (info) {
victory(info, 'mx local');
return null;
}
// We didn't know about it locally, ask the ISPDB
return this._getConfigFromISPDB(mxDomain)
.catch(coerceRejectionToNull)
.then(mxISPDBHandler);
}.bind(this);
var mxISPDBHandler = function(info) {
if (info) {
victory(info, 'mx ispdb');
return null;
}
// The ISPDB didn't know, probe for autodiscovery. No coercion needed.
return this._probeForAutodiscover(domain)
.then(autodiscoverHandler);
}.bind(this);
// - Group 4: Autodiscover probing
var autodiscoverHandler = function(info) {
// This is either success or we're simply done.
victory(info, info ? 'autodiscover' : null);
return null;
}.bind(this);
// -- Kick it off.
this._getConfigFromLocalFile(domain)
// Coerce the rejection for our then handler's purpose.
.catch(coerceRejectionToNull)
.then(localConfigHandler)
// Register a catch-handler against localConfigHandler and all of the
// follow-on handlers we associate. Technically we should never call
// this, but better safe than sorry.
.catch(failsafeFailure);
}.bind(this));
},
/**
* Try to create an account for the user's email address by running through
* autoconfigure and, if successful, delegating to the appropriate account
* type.
*
* @param universe the MailUniverse object
* @param userDetails an object containing `emailAddress` and `password`
* attributes
* @param callback a callback taking an error string (if any) and the config
* info, formatted as JSON
*/
tryToCreateAccount: function(universe, userDetails, callback) {
this.learnAboutAccount(userDetails).then(
function success(results) {
// If we found a config and we just need a password, then we're good
// to go.
if (results.result === 'need-password') {
var config = results.configInfo;
requireConfigurator(config.type, function (mod) {
mod.configurator.tryToCreateAccount(universe, userDetails, config,
callback);
});
return;
}
logic(this, 'legacyCreateFail', { result: results.result });
// need-oauth2 is not supported via this code-path; coerce to a config
// failure...
callback('no-config-info');
}.bind(this),
function failure(err) {
callback(err, null, null);
}.bind(this));
},
};
/**
* Recreate an existing account, e.g. after a database upgrade.
*
* @param universe the MailUniverse
* @param oldVersion the old database version, to help with migration
* @param accountInfo the old account info
* @param callback a callback to fire when we've completed recreating the
* account
*/
function recreateAccount(universe, oldVersion, accountInfo, callback) {
requireConfigurator(accountInfo.def.type, function (mod) {
mod.configurator.recreateAccount(universe, oldVersion,
accountInfo, callback);
});
}
exports.recreateAccount = recreateAccount;
function tryToManuallyCreateAccount(universe, userDetails, domainInfo,
callback) {
requireConfigurator(domainInfo.type, function (mod) {
mod.configurator.tryToCreateAccount(universe, userDetails, domainInfo,
callback);
});
}
exports.tryToManuallyCreateAccount = tryToManuallyCreateAccount;
}); // end define