/** * The main application controller */ define(function(require) { 'use strict'; var Auth = require('js/bo/auth'), PGP = require('js/crypto/pgp'), PgpMailer = require('pgpmailer'), OAuth = require('js/util/oauth'), PgpBuilder = require('pgpbuilder'), OutboxBO = require('js/bo/outbox'), mailreader = require('mailreader'), ImapClient = require('imap-client'), Crypto = require('js/crypto/crypto'), RestDAO = require('js/dao/rest-dao'), EmailDAO = require('js/dao/email-dao'), appConfig = require('js/app-config'), config = appConfig.config, str = appConfig.string, KeychainDAO = require('js/dao/keychain-dao'), PublicKeyDAO = require('js/dao/publickey-dao'), LawnchairDAO = require('js/dao/lawnchair-dao'), PrivateKeyDAO = require('js/dao/privatekey-dao'), InvitationDAO = require('js/dao/invitation-dao'), DeviceStorageDAO = require('js/dao/devicestorage-dao'), UpdateHandler = require('js/util/update/update-handler'); var self = {}; /** * Start the application */ self.start = function(options, callback) { // are we running in a cordova app or in a browser environment? if (window.cordova) { // wait for 'deviceready' event to make sure plugins are loaded console.log('Assuming Cordova environment...'); document.addEventListener("deviceready", onDeviceReady, false); } else { // No need to wait on events... just start the app console.log('Assuming Browser environment...'); onDeviceReady(); } function onDeviceReady() { console.log('Starting app.'); self.buildModules(options); // Handle offline and online gracefully window.addEventListener('online', self.onConnect.bind(self, options.onError)); window.addEventListener('offline', self.onDisconnect.bind(self)); self._appConfigStore.init('app-config', callback); } }; self.buildModules = function(options) { var lawnchairDao, restDao, pubkeyDao, privkeyDao, crypto, emailDao, keychain, pgp, userStorage, pgpbuilder, oauth, appConfigStore; // start the mailreader's worker thread mailreader.startWorker(config.workerPath + '/../lib/mailreader-parser-worker.js'); // init objects and inject dependencies restDao = new RestDAO(); lawnchairDao = new LawnchairDAO(); pubkeyDao = new PublicKeyDAO(restDao); privkeyDao = new PrivateKeyDAO(new RestDAO(config.privkeyServerUrl)); oauth = new OAuth(new RestDAO('https://www.googleapis.com')); crypto = new Crypto(); self._pgp = pgp = new PGP(); self._keychain = keychain = new KeychainDAO(lawnchairDao, pubkeyDao, privkeyDao, crypto, pgp); keychain.requestPermissionForKeyUpdate = function(params, callback) { var message = params.newKey ? str.updatePublicKeyMsgNewKey : str.updatePublicKeyMsgRemovedKey; message = message.replace('{0}', params.userId); options.onError({ title: str.updatePublicKeyTitle, message: message, positiveBtnStr: str.updatePublicKeyPosBtn, negativeBtnStr: str.updatePublicKeyNegBtn, showNegativeBtn: true, callback: callback }); }; self._appConfigStore = appConfigStore = new DeviceStorageDAO(new LawnchairDAO()); self._auth = new Auth(appConfigStore, oauth, new RestDAO('/ca')); self._userStorage = userStorage = new DeviceStorageDAO(lawnchairDao); self._invitationDao = new InvitationDAO(restDao); self._pgpbuilder = pgpbuilder = new PgpBuilder(); self._emailDao = emailDao = new EmailDAO(keychain, pgp, userStorage, pgpbuilder, mailreader); self._outboxBo = new OutboxBO(emailDao, keychain, userStorage); self._updateHandler = new UpdateHandler(appConfigStore, userStorage); emailDao.onError = options.onError; }; self.isOnline = function() { return navigator.onLine; }; self.onDisconnect = function() { self._emailDao.onDisconnect(); }; self.onConnect = function(callback) { if (!self.isOnline() || !self._emailDao || !self._emailDao._account) { // prevent connection infinite loop callback(); return; } // fetch pinned local ssl certificate self._auth.getCredentials({}, function(err, credentials) { if (err) { callback(err); return; } initClients(credentials); }); function initClients(credentials) { var auth, imapOptions, imapClient, smtpOptions, pgpMailer; auth = { user: credentials.emailAddress, xoauth2: credentials.oauthToken }; imapOptions = { secure: config.gmail.imap.secure, port: config.gmail.imap.port, host: config.gmail.imap.host, auth: auth, ca: [credentials.sslCert] }; smtpOptions = { secureConnection: config.gmail.smtp.secure, port: config.gmail.smtp.port, host: config.gmail.smtp.host, auth: auth, tls: { ca: [credentials.sslCert] }, onError: console.error }; pgpMailer = new PgpMailer(smtpOptions, self._pgpbuilder); imapClient = new ImapClient(imapOptions); imapClient.onError = onImapError; // connect to clients self._emailDao.onConnect({ imapClient: imapClient, pgpMailer: pgpMailer }, callback); } function onImapError(err) { console.log('IMAP error.', err); console.log('IMAP reconnecting...'); // re-init client modules on error self.onConnect(function(err) { if (err) { console.error('IMAP reconnect failed!', err); return; } console.log('IMAP reconnect attempt complete.'); }); } }; self.checkForUpdate = function() { if (!window.chrome || !chrome.runtime || !chrome.runtime.onUpdateAvailable) { return; } // check for update and restart chrome.runtime.onUpdateAvailable.addListener(function(details) { console.log("Updating to version " + details.version); chrome.runtime.reload(); }); chrome.runtime.requestUpdateCheck(function(status) { if (status === "update_found") { console.log("Update pending..."); } else if (status === "no_update") { console.log("No update found."); } else if (status === "throttled") { console.log("Checking updates too frequently."); } }); }; /** * Instanciate the mail email data access object and its dependencies. Login to imap on init. */ self.init = function(options, callback) { // init user's local database self._userStorage.init(options.emailAddress, function(err) { if (err) { callback(err); return; } // Migrate the databases if necessary self._updateHandler.update(onUpdate); }); function onUpdate(err) { if (err) { callback({ errMsg: 'Update failed, please reinstall the app.', err: err }); return; } // account information for the email dao var account = { emailAddress: options.emailAddress, asymKeySize: config.asymKeySize }; // init email dao self._emailDao.init({ account: account }, function(err, keypair) { if (err) { callback(err); return; } callback(null, keypair); }); } }; return self; });