mail/src/js/email/account.js

236 lines
7.7 KiB
JavaScript

'use strict';
var ngModule = angular.module('woEmail');
ngModule.service('account', Account);
module.exports = Account;
var axe = require('axe-logger'),
util = require('crypto-lib').util,
PgpMailer = require('pgpmailer'),
ImapClient = require('imap-client');
function Account(appConfig, auth, accountStore, email, outbox, keychain, updateHandler, pgpbuilder, dialog) {
this._appConfig = appConfig;
this._auth = auth;
this._accountStore = accountStore;
this._emailDao = email;
this._outbox = outbox;
this._keychain = keychain;
this._updateHandler = updateHandler;
this._pgpbuilder = pgpbuilder;
this._dialog = dialog;
this._accounts = []; // init accounts list
}
/**
* 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);
};
/**
* Lists all of the current accounts connected to the app
* @return {Array<Object>} The account objects containing folder and message objects
*/
Account.prototype.list = function() {
return this._accounts;
};
/**
* Fire up the database, retrieve the available keys for the user and initialize the email data access object
*/
Account.prototype.init = function(options, callback) {
var self = this;
// account information for the email dao
var account = {
realname: options.realname,
emailAddress: options.emailAddress,
asymKeySize: this._appConfig.config.asymKeySize
};
// Pre-Flight check: don't even start to initialize stuff if the email address is not valid
if (!util.validateEmailAddress(options.emailAddress)) {
return callback(new Error('The user email address is invalid!'));
}
prepareDatabase();
// Pre-Flight check: initialize and prepare user's local database
function prepareDatabase() {
self._accountStore.init(options.emailAddress, function(err) {
if (err) {
return callback(err);
}
// Migrate the databases if necessary
self._updateHandler.update(function(err) {
if (err) {
return callback(new Error('Updating the internal database failed. Please reinstall the app! Reason: ' + err.message));
}
prepareKeys();
});
});
}
// retrieve keypair fom devicestorage/cloud, refresh public key if signup was incomplete before
function prepareKeys() {
self._keychain.getUserKeyPair(options.emailAddress, function(err, keys) {
if (err) {
return callback(err);
}
// 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) {
self._keychain.refreshKeyForUserId({
userId: options.emailAddress,
overridePermission: true
}, function(err, publicKey) {
if (err) {
return callback(err);
}
initEmailDao({
publicKey: publicKey
});
});
return;
}
// either signup was complete or no pubkey is available, so we're good here.
initEmailDao(keys);
});
}
function initEmailDao(keys) {
self._emailDao.init({
account: account
}, function(err) {
if (err) {
return callback(err);
}
// 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);
callback(null, keys);
});
}
};
/**
* 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.
*/
Account.prototype.onConnect = function() {
var self = this;
var config = self._appConfig.config;
if (!self.isOnline() || !self._emailDao || !self._emailDao._account) {
// prevent connection infinite loop
return;
}
self._auth.getCredentials(function(err, credentials) {
if (err) {
self._dialog.error(err);
return;
}
initClients(credentials);
});
function initClients(credentials) {
// 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
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);
// connect to clients
self._emailDao.onConnect({
imapClient: imapClient,
pgpMailer: pgpMailer,
ignoreUploadOnSent: self._emailDao.checkIgnoreUploadOnSent(credentials.imap.host)
}, self._dialog.error);
}
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);
}
};
/**
* Event handler that is called when the user agent goes offline.
*/
Account.prototype.onDisconnect = function() {
this._emailDao.onDisconnect();
};
/**
* 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.
*/
Account.prototype.logout = function() {
var self = this;
// clear app config store
self._auth.logout(function(err) {
if (err) {
self._dialog.error(err);
return;
}
// delete instance of imap-client and pgp-mailer
self._emailDao.onDisconnect(function(err) {
if (err) {
self._dialog.error(err);
return;
}
if (typeof window.chrome !== 'undefined' && chrome.runtime && chrome.runtime.reload) {
// reload chrome app
chrome.runtime.reload();
} else {
// navigate to login
window.location.href = '/';
}
});
});
};