2014-11-17 12:58:03 -05:00
|
|
|
'use strict';
|
|
|
|
|
2014-11-20 16:53:30 -05:00
|
|
|
var ngModule = angular.module('woEmail');
|
2014-11-17 12:58:03 -05:00
|
|
|
ngModule.service('account', Account);
|
2014-11-19 09:15:57 -05:00
|
|
|
module.exports = Account;
|
2014-11-17 12:58:03 -05:00
|
|
|
|
2014-11-19 14:54:59 -05:00
|
|
|
var axe = require('axe-logger'),
|
|
|
|
util = require('crypto-lib').util,
|
|
|
|
PgpMailer = require('pgpmailer'),
|
|
|
|
ImapClient = require('imap-client');
|
|
|
|
|
2014-11-21 11:25:55 -05:00
|
|
|
function Account(appConfig, auth, accountStore, email, outbox, keychain, updateHandler, pgpbuilder, dialog) {
|
2014-11-19 14:54:59 -05:00
|
|
|
this._appConfig = appConfig;
|
|
|
|
this._auth = auth;
|
2014-11-21 11:25:55 -05:00
|
|
|
this._accountStore = accountStore;
|
2014-11-19 14:54:59 -05:00
|
|
|
this._emailDao = email;
|
|
|
|
this._outbox = outbox;
|
2014-11-21 11:25:55 -05:00
|
|
|
this._keychain = keychain;
|
2014-11-19 14:54:59 -05:00
|
|
|
this._updateHandler = updateHandler;
|
2014-11-21 11:25:55 -05:00
|
|
|
this._pgpbuilder = pgpbuilder;
|
|
|
|
this._dialog = dialog;
|
2014-11-19 14:54:59 -05:00
|
|
|
this._accounts = []; // init accounts list
|
2014-11-17 12:58:03 -05:00
|
|
|
}
|
|
|
|
|
2014-11-19 14:54:59 -05:00
|
|
|
/**
|
|
|
|
* Check if the account is already logged in.
|
|
|
|
* @return {Boolean} if the account is logged in
|
|
|
|
*/
|
|
|
|
Account.prototype.isLoggedIn = function() {
|
|
|
|
return (this._accounts.length > 0);
|
|
|
|
};
|
|
|
|
|
2014-11-17 12:58:03 -05:00
|
|
|
/**
|
|
|
|
* Lists all of the current accounts connected to the app
|
|
|
|
* @return {Array<Object>} The account objects containing folder and message objects
|
|
|
|
*/
|
2014-11-18 14:19:29 -05:00
|
|
|
Account.prototype.list = function() {
|
2014-11-19 14:54:59 -05:00
|
|
|
return this._accounts;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fire up the database, retrieve the available keys for the user and initialize the email data access object
|
|
|
|
*/
|
2014-12-15 13:31:34 -05:00
|
|
|
Account.prototype.init = function(options) {
|
2014-11-19 14:54:59 -05:00
|
|
|
var self = this;
|
|
|
|
|
|
|
|
// account information for the email dao
|
|
|
|
var account = {
|
|
|
|
realname: options.realname,
|
|
|
|
emailAddress: options.emailAddress,
|
2014-12-01 10:42:30 -05:00
|
|
|
asymKeySize: this._appConfig.config.asymKeySize
|
2014-11-19 14:54:59 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
// Pre-Flight check: don't even start to initialize stuff if the email address is not valid
|
|
|
|
if (!util.validateEmailAddress(options.emailAddress)) {
|
2014-12-15 13:31:34 -05:00
|
|
|
return new Promise(function() {
|
|
|
|
throw new Error('The user email address is invalid!');
|
|
|
|
});
|
2014-11-19 14:54:59 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Pre-Flight check: initialize and prepare user's local database
|
2014-12-15 13:31:34 -05:00
|
|
|
return self._accountStore.init(options.emailAddress).then(function() {
|
|
|
|
// Migrate the databases if necessary
|
|
|
|
return self._updateHandler.update().catch(function(err) {
|
|
|
|
throw new Error('Updating the internal database failed. Please reinstall the app! Reason: ' + err.message);
|
2014-11-19 14:54:59 -05:00
|
|
|
});
|
|
|
|
|
2014-12-15 13:31:34 -05:00
|
|
|
}).then(function() {
|
|
|
|
// retrieve keypair fom devicestorage/cloud, refresh public key if signup was incomplete before
|
|
|
|
return self._keychain.getUserKeyPair(options.emailAddress);
|
|
|
|
|
|
|
|
}).then(function(keys) {
|
|
|
|
// this is either a first start on a new device, OR a subsequent start without completing the signup,
|
|
|
|
// since we can't differenciate those cases here, do a public key refresh because it might be outdated
|
|
|
|
if (keys && keys.publicKey && !keys.privateKey) {
|
|
|
|
return self._keychain.refreshKeyForUserId({
|
|
|
|
userId: options.emailAddress,
|
|
|
|
overridePermission: true
|
|
|
|
}).then(function(publicKey) {
|
|
|
|
return {
|
|
|
|
publicKey: publicKey
|
|
|
|
};
|
|
|
|
});
|
|
|
|
}
|
|
|
|
// either signup was complete or no pubkey is available, so we're good here.
|
|
|
|
return keys;
|
2014-11-19 14:54:59 -05:00
|
|
|
|
2014-12-15 13:31:34 -05:00
|
|
|
}).then(function(keys) {
|
|
|
|
// init the email data access object
|
|
|
|
return self._emailDao.init({
|
2014-11-19 14:54:59 -05:00
|
|
|
account: account
|
2014-12-15 13:31:34 -05:00
|
|
|
}).then(function() {
|
2014-11-19 14:54:59 -05:00
|
|
|
// Handle offline and online gracefully ... arm dom event
|
|
|
|
window.addEventListener('online', self.onConnect.bind(self));
|
|
|
|
window.addEventListener('offline', self.onDisconnect.bind(self));
|
|
|
|
|
|
|
|
// add account object to the accounts array for the ng controllers
|
|
|
|
self._accounts.push(account);
|
|
|
|
|
2014-12-15 13:31:34 -05:00
|
|
|
return keys;
|
2014-11-19 14:54:59 -05:00
|
|
|
});
|
2014-12-15 13:31:34 -05:00
|
|
|
});
|
2014-11-19 14:54:59 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the user agent is online.
|
|
|
|
*/
|
|
|
|
Account.prototype.isOnline = function() {
|
|
|
|
return navigator.onLine;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Event that is called when the user agent goes online. This create new instances of the imap-client and pgp-mailer and connects to the mail server.
|
|
|
|
*/
|
2014-12-11 09:02:08 -05:00
|
|
|
Account.prototype.onConnect = function(callback) {
|
2014-11-19 14:54:59 -05:00
|
|
|
var self = this;
|
|
|
|
var config = self._appConfig.config;
|
2014-12-15 13:31:34 -05:00
|
|
|
|
2014-12-11 09:02:08 -05:00
|
|
|
callback = callback || self._dialog.error;
|
2014-11-19 14:54:59 -05:00
|
|
|
|
|
|
|
if (!self.isOnline() || !self._emailDao || !self._emailDao._account) {
|
|
|
|
// prevent connection infinite loop
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-12-15 13:31:34 -05:00
|
|
|
// init imap/smtp clients
|
|
|
|
self._auth.getCredentials().then(function(credentials) {
|
2014-11-19 14:54:59 -05:00
|
|
|
// add the maximum update batch size for imap folders to the imap configuration
|
|
|
|
credentials.imap.maxUpdateSize = config.imapUpdateBatchSize;
|
|
|
|
|
|
|
|
// tls socket worker path for multithreaded tls in non-native tls environments
|
|
|
|
credentials.imap.tlsWorkerPath = credentials.smtp.tlsWorkerPath = config.workerPath + '/tcp-socket-tls-worker.min.js';
|
|
|
|
|
|
|
|
var pgpMailer = new PgpMailer(credentials.smtp, self._pgpbuilder);
|
|
|
|
var imapClient = new ImapClient(credentials.imap);
|
|
|
|
imapClient.onError = onConnectionError;
|
|
|
|
pgpMailer.onError = onConnectionError;
|
|
|
|
|
|
|
|
// certificate update handling
|
2014-12-15 13:31:34 -05:00
|
|
|
imapClient.onCert = self._auth.handleCertificateUpdate.bind(self._auth, 'imap', self.onConnect.bind(self), self._dialog.error);
|
|
|
|
pgpMailer.onCert = self._auth.handleCertificateUpdate.bind(self._auth, 'smtp', self.onConnect.bind(self), self._dialog.error);
|
2014-11-19 14:54:59 -05:00
|
|
|
|
|
|
|
// connect to clients
|
2014-12-15 13:31:34 -05:00
|
|
|
return self._emailDao.onConnect({
|
2014-11-19 14:54:59 -05:00
|
|
|
imapClient: imapClient,
|
|
|
|
pgpMailer: pgpMailer,
|
|
|
|
ignoreUploadOnSent: self._emailDao.checkIgnoreUploadOnSent(credentials.imap.host)
|
2014-12-15 13:31:34 -05:00
|
|
|
});
|
|
|
|
}).then(callback).catch(callback);
|
2014-11-19 14:54:59 -05:00
|
|
|
|
|
|
|
function onConnectionError(error) {
|
|
|
|
axe.debug('Connection error. Attempting reconnect in ' + config.reconnectInterval + ' ms. Error: ' + (error.errMsg || error.message) + (error.stack ? ('\n' + error.stack) : ''));
|
|
|
|
|
|
|
|
setTimeout(function() {
|
|
|
|
axe.debug('Reconnecting...');
|
|
|
|
// re-init client modules on error
|
|
|
|
self.onConnect(function(err) {
|
|
|
|
if (err) {
|
|
|
|
axe.error('Reconnect attempt failed! ' + (err.errMsg || err.message) + (err.stack ? ('\n' + err.stack) : ''));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
axe.debug('Reconnect attempt complete.');
|
|
|
|
});
|
|
|
|
}, config.reconnectInterval);
|
|
|
|
}
|
2014-11-17 12:58:03 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2014-11-19 14:54:59 -05:00
|
|
|
* Event handler that is called when the user agent goes offline.
|
2014-11-17 12:58:03 -05:00
|
|
|
*/
|
2014-11-19 14:54:59 -05:00
|
|
|
Account.prototype.onDisconnect = function() {
|
2014-12-15 13:31:34 -05:00
|
|
|
return this._emailDao.onDisconnect();
|
2014-11-17 12:58:03 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2014-11-19 14:54:59 -05:00
|
|
|
* Logout of an email account. Log the current user out by clear the app config store and deleting instances of imap-client and pgp-mailer.
|
2014-11-17 12:58:03 -05:00
|
|
|
*/
|
2014-11-19 14:54:59 -05:00
|
|
|
Account.prototype.logout = function() {
|
|
|
|
var self = this;
|
|
|
|
// clear app config store
|
2014-12-15 13:31:34 -05:00
|
|
|
return self._auth.logout().then(function() {
|
2014-11-19 14:54:59 -05:00
|
|
|
// delete instance of imap-client and pgp-mailer
|
2014-12-15 13:31:34 -05:00
|
|
|
return self._emailDao.onDisconnect();
|
|
|
|
|
|
|
|
}).then(function() {
|
|
|
|
if (typeof window.chrome !== 'undefined' && chrome.runtime && chrome.runtime.reload) {
|
|
|
|
// reload chrome app
|
|
|
|
chrome.runtime.reload();
|
|
|
|
} else {
|
|
|
|
// navigate to login
|
|
|
|
window.location.href = '/';
|
|
|
|
}
|
2014-11-19 14:54:59 -05:00
|
|
|
});
|
2014-11-21 11:25:55 -05:00
|
|
|
};
|