From 4c04ba4e74295499635a92e6aec8f11252acb054 Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Wed, 19 Nov 2014 20:54:59 +0100 Subject: [PATCH] Refactor login controllers and delete AppController --- src/index.html | 2 +- src/js/app-config.js | 2 +- src/js/app-controller.js | 319 ------------------ src/js/app.js | 46 ++- src/js/controller/app/navigation.js | 32 +- src/js/controller/login/add-account.js | 13 +- src/js/controller/login/create-account.js | 20 +- src/js/controller/login/login-existing.js | 19 +- src/js/controller/login/login-initial.js | 18 +- src/js/controller/login/login-new-device.js | 22 +- .../login/login-privatekey-download.js | 20 +- .../controller/login/login-set-credentials.js | 16 +- src/js/controller/login/login.js | 46 +-- src/js/controller/login/validate-phone.js | 25 +- src/js/crypto/index.js | 2 +- src/js/email/account.js | 239 +++++++++++-- src/js/email/email.js | 6 +- src/js/email/index.js | 5 +- src/js/email/outbox.js | 6 +- src/js/service/admin.js | 3 +- src/js/service/app-config-store.js | 15 + src/js/service/auth.js | 18 +- src/js/service/devicestorage.js | 12 +- src/js/service/index.js | 17 +- src/js/service/invitation.js | 6 +- src/js/service/keychain.js | 54 ++- src/js/service/lawnchair.js | 16 +- src/js/service/privatekey.js | 6 +- src/js/service/publickey.js | 6 +- src/js/util/update/update-handler.js | 15 +- 30 files changed, 455 insertions(+), 571 deletions(-) delete mode 100644 src/js/app-controller.js create mode 100644 src/js/service/app-config-store.js diff --git a/src/index.html b/src/index.html index 8f12cd6..dc456e1 100644 --- a/src/index.html +++ b/src/index.html @@ -1,5 +1,5 @@ - + Whiteout Mail diff --git a/src/js/app-config.js b/src/js/app-config.js index 4f06416..2678a45 100644 --- a/src/js/app-config.js +++ b/src/js/app-config.js @@ -2,7 +2,7 @@ var appCfg = {}; -var ngModule = angular.module('mail'); +var ngModule = angular.module('woAppConfig'); ngModule.factory('appConfig', function() { return appCfg; }); diff --git a/src/js/app-controller.js b/src/js/app-controller.js deleted file mode 100644 index b28e9f5..0000000 --- a/src/js/app-controller.js +++ /dev/null @@ -1,319 +0,0 @@ -/** - * The main application controller - */ - -'use strict'; - -var axe = require('axe-logger'), - Auth = require('./bo/auth'), - PGP = require('./crypto/pgp'), - OAuth = require('./util/oauth'), - PgpMailer = require('pgpmailer'), - util = require('crypto-lib').util, - PgpBuilder = require('pgpbuilder'), - OutboxBO = require('./bo/outbox'), - mailreader = require('mailreader'), - ImapClient = require('imap-client'), - Crypto = require('./crypto/crypto'), - RestDAO = require('./dao/rest-dao'), - appConfig = require('./app-config'), - EmailDAO = require('./dao/email-dao'), - AdminDao = require('./dao/admin-dao'), - KeychainDAO = require('./dao/keychain-dao'), - PublicKeyDAO = require('./dao/publickey-dao'), - LawnchairDAO = require('./dao/lawnchair-dao'), - PrivateKeyDAO = require('./dao/privatekey-dao'), - InvitationDAO = require('./dao/invitation-dao'), - DeviceStorageDAO = require('./dao/devicestorage-dao'), - ConnectionDoctor = require('./util/connection-doctor'), - UpdateHandler = require('./util/update/update-handler'), - config = appConfig.config, - str = appConfig.string; - -var ctrl = {}; - -/** - * Start the application. - */ -ctrl.start = function(options, callback) { - if (ctrl.started) { - return callback(); - } - - ctrl.started = true; - ctrl.onError = options.onError; // TODO: replace by errorService - - // 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 - axe.debug('Assuming Cordova environment...'); - document.addEventListener("deviceready", onDeviceReady, false); - } else { - // No need to wait on events... just start the app - axe.debug('Assuming Browser environment...'); - onDeviceReady(); - } - - function onDeviceReady() { - axe.debug('Starting app.'); - - // TODO: will be replaced by angular dependency management - ctrl.buildModules(); - - // TODO: move self-contained connection management in emailDao - // Handle offline and online gracefully - window.addEventListener('online', ctrl.onConnect.bind(ctrl, ctrl.onError)); - window.addEventListener('offline', ctrl.onDisconnect.bind(ctrl)); - - // TODO: move appConfigService shared singleton - ctrl._appConfigStore.init('app-config', callback); - } -}; - -// TODO: will be replaced by angular dependency management - -/** - * Initialize the dependency tree. - */ -ctrl.buildModules = function() { - var lawnchairDao, restDao, pubkeyDao, privkeyDao, crypto, emailDao, keychain, pgp, userStorage, pgpbuilder, oauth, appConfigStore, auth; - - // 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(); - ctrl._pgp = pgp = new PGP(); - ctrl._keychain = keychain = new KeychainDAO(lawnchairDao, pubkeyDao, privkeyDao, crypto, pgp); - - // TODO: inject dialog service directly into keychain service - keychain.requestPermissionForKeyUpdate = function(params, callback) { - var message = params.newKey ? str.updatePublicKeyMsgNewKey : str.updatePublicKeyMsgRemovedKey; - message = message.replace('{0}', params.userId); - - ctrl.onError({ - title: str.updatePublicKeyTitle, - message: message, - positiveBtnStr: str.updatePublicKeyPosBtn, - negativeBtnStr: str.updatePublicKeyNegBtn, - showNegativeBtn: true, - callback: callback - }); - }; - - ctrl._appConfigStore = appConfigStore = new DeviceStorageDAO(new LawnchairDAO()); - ctrl._auth = auth = new Auth(appConfigStore, oauth, pgp); - ctrl._userStorage = userStorage = new DeviceStorageDAO(lawnchairDao); - ctrl._invitationDao = new InvitationDAO(restDao); - ctrl._pgpbuilder = pgpbuilder = new PgpBuilder(); - ctrl._emailDao = emailDao = new EmailDAO(keychain, pgp, userStorage, pgpbuilder, mailreader); - ctrl._outboxBo = new OutboxBO(emailDao, keychain, userStorage); - ctrl._updateHandler = new UpdateHandler(appConfigStore, userStorage, auth); - ctrl._adminDao = new AdminDao(new RestDAO(config.adminUrl)); - ctrl._doctor = new ConnectionDoctor(); - - emailDao.onError = ctrl.onError; -}; - -/** - * Calls runtime hooks to check if an app update is available. - */ -ctrl.checkForUpdate = function() { - ctrl._updateHandler.checkForUpdate(ctrl.onError); -}; - -// TODO: move to AccountService - -/** - * Fire up the database, retrieve the available keys for the user and initialize the email data access object - */ -ctrl.init = function(options, callback) { - // account information for the email dao - var account = { - realname: options.realname, - emailAddress: options.emailAddress, - asymKeySize: 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() { - ctrl._userStorage.init(options.emailAddress, function(err) { - if (err) { - return callback(err); - } - - // Migrate the databases if necessary - ctrl._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() { - ctrl._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) { - ctrl._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) { - ctrl._emailDao.init({ - account: account - }, function(err) { - if (err) { - return callback(err); - } - - callback(null, keys); - }); - } -}; - -/** - * Check if the user agent is online. - */ -ctrl.isOnline = function() { - return navigator.onLine; -}; - -// TODO: move to AccountService - -/** - * Event handler that is called when the user agent goes offline. - */ -ctrl.onDisconnect = function() { - ctrl._emailDao.onDisconnect(); -}; - -// TODO: move to AccountService - -/** - * Log the current user out by clear the app config store and deleting instances of imap-client and pgp-mailer. - */ -ctrl.logout = function() { - // clear app config store - ctrl._auth.logout(function(err) { - if (err) { - ctrl.onError(err); - return; - } - - // delete instance of imap-client and pgp-mailer - ctrl._emailDao.onDisconnect(function(err) { - if (err) { - ctrl.onError(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 = '/'; - } - }); - }); -}; - -// TODO: move onConnect to Account service - -/** - * 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. - */ -ctrl.onConnect = function(callback) { - if (!ctrl.isOnline() || !ctrl._emailDao || !ctrl._emailDao._account) { - // prevent connection infinite loop - callback(); - return; - } - - ctrl._auth.getCredentials(function(err, credentials) { - if (err) { - callback(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, ctrl._pgpbuilder); - var imapClient = new ImapClient(credentials.imap); - imapClient.onError = onConnectionError; - pgpMailer.onError = onConnectionError; - - // certificate update handling - imapClient.onCert = ctrl._auth.handleCertificateUpdate.bind(ctrl._auth, 'imap', ctrl.onConnect, ctrl.onError); - pgpMailer.onCert = ctrl._auth.handleCertificateUpdate.bind(ctrl._auth, 'smtp', ctrl.onConnect, ctrl.onError); - - // connect to clients - ctrl._emailDao.onConnect({ - imapClient: imapClient, - pgpMailer: pgpMailer, - ignoreUploadOnSent: ctrl._emailDao.checkIgnoreUploadOnSent(credentials.imap.host) - }, callback); - } - - 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 - ctrl.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); - } -}; - -module.exports = ctrl; \ No newline at end of file diff --git a/src/js/app.js b/src/js/app.js index 0b52818..738cefe 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -14,7 +14,8 @@ if (typeof window.applicationCache !== 'undefined') { }; } -var DialogCtrl = require('./controller/dialog'), +var axe = require('axe-logger'), + DialogCtrl = require('./controller/dialog'), AddAccountCtrl = require('./controller/add-account'), CreateAccountCtrl = require('./controller/create-account'), ValidatePhoneCtrl = require('./controller/validate-phone'), @@ -36,13 +37,26 @@ var DialogCtrl = require('./controller/dialog'), ActionBarCtrl = require('./controller/action-bar'), errorUtil = require('./util/error'), backButtonUtil = require('./util/backbutton-handler'); -require('./directive/common'), + +// include angular modules +require('./app-config'); +require('./directive/common'); +require('./util'); +require('./crypto'); require('./service'); +require('./email'); // init main angular module including dependencies var app = angular.module('mail', [ 'ngRoute', 'ngAnimate', + 'ngTagsInput', + 'woAppConfig', + 'woDirectives', + 'woUtil', + 'woCrypto,', + 'woServices', + 'woEmail', 'navigation', 'mail-list', 'write', @@ -50,10 +64,7 @@ var app = angular.module('mail', [ 'contacts', 'login-new-device', 'privatekey-upload', - 'infinite-scroll', - 'ngTagsInput', - 'woDirectives', - 'woServices' + 'infinite-scroll' ]); // set router paths @@ -130,4 +141,25 @@ app.controller('PrivateKeyUploadCtrl', PrivateKeyUploadCtrl); app.controller('ContactsCtrl', ContactsCtrl); app.controller('AboutCtrl', AboutCtrl); app.controller('DialogCtrl', DialogCtrl); -app.controller('ActionBarCtrl', ActionBarCtrl); \ No newline at end of file +app.controller('ActionBarCtrl', ActionBarCtrl); + +// +// Manual angular bootstraping +// + +// 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 + axe.debug('Assuming Cordova environment...'); + document.addEventListener('deviceready', bootstrap, false); +} else { + // No need to wait on events... just start the app + axe.debug('Assuming Browser environment...'); + bootstrap(); +} + +function bootstrap() { + angular.element(document).ready(function() { + angular.bootstrap(document, ['mail']); + }); +} \ No newline at end of file diff --git a/src/js/controller/app/navigation.js b/src/js/controller/app/navigation.js index 1ec4080..5fcc458 100644 --- a/src/js/controller/app/navigation.js +++ b/src/js/controller/app/navigation.js @@ -5,8 +5,7 @@ var appController = require('../app-controller'), backBtnHandler = require('../util/backbutton-handler'), appCfg = require('../app-config'), config = appCfg.config, - str = appCfg.string, - emailDao, outboxBo; + str = appCfg.string; // // Constants @@ -19,14 +18,8 @@ var NOTIFICATION_SENT_TIMEOUT = 2000; // Controller // -var NavigationCtrl = function($scope, $routeParams, $location) { - if (!appController._emailDao && !$routeParams.dev) { - $location.path('/'); // init app - return; - } - - emailDao = appController._emailDao; - outboxBo = appController._outboxBo; +var NavigationCtrl = function($scope, $routeParams, $location, account, email, outbox) { + !$routeParams.dev && !account.isLoggedIn() && $location.path('/'); // init app // // scope functions @@ -51,14 +44,14 @@ var NavigationCtrl = function($scope, $routeParams, $location) { } // update the outbox mail count - var outbox = _.findWhere($scope.account.folders, { + var ob = _.findWhere($scope.account.folders, { type: config.outboxMailboxType }); - outbox.count = count; + ob.count = count; $scope.$apply(); - emailDao.refreshFolder({ - folder: outbox + email.refreshFolder({ + folder: ob }, $scope.onError); }; @@ -114,18 +107,19 @@ var NavigationCtrl = function($scope, $routeParams, $location) { } // get pointer to account/folder/message tree on root scope - $scope.$root.account = emailDao._account; + $scope.$root.account = email._account; + // TODO: $scope.accounts = account.list(); // set notificatio handler for sent messages - outboxBo.onSent = sentNotification; + outbox.onSent = sentNotification; // start checking outbox periodically - outboxBo.startChecking($scope.onOutboxUpdate); + outbox.startChecking($scope.onOutboxUpdate); } - function sentNotification(email) { + function sentNotification(message) { notification.create({ title: 'Message sent', - message: email.subject, + message: message.subject, timeout: NOTIFICATION_SENT_TIMEOUT }, function() {}); } diff --git a/src/js/controller/login/add-account.js b/src/js/controller/login/add-account.js index 28276da..82e2184 100644 --- a/src/js/controller/login/add-account.js +++ b/src/js/controller/login/add-account.js @@ -1,12 +1,7 @@ 'use strict'; -var appCtrl = require('../app-controller'); - -var AddAccountCtrl = function($scope, $location, $routeParams, mailConfig) { - if (!appCtrl._auth && !$routeParams.dev) { - $location.path('/'); // init app - return; - } +var AddAccountCtrl = function($scope, $location, $routeParams, mailConfig, auth) { + !$routeParams.dev && !auth.isInitialized() && $location.path('/'); // init app $scope.getAccountSettings = function() { if ($scope.form.$invalid) { @@ -25,7 +20,7 @@ var AddAccountCtrl = function($scope, $location, $routeParams, mailConfig) { }; var hostname = config.imap.hostname; - if (appCtrl._auth.useOAuth(hostname)) { + if (auth.useOAuth(hostname)) { // check for oauth support $scope.oauthPossible(); } else { @@ -62,7 +57,7 @@ var AddAccountCtrl = function($scope, $location, $routeParams, mailConfig) { function getOAuthToken() { // fetches the email address from the chrome identity api - appCtrl._auth.getOAuthToken(function(err) { + auth.getOAuthToken(function(err) { if (err) { return $scope.onError(err); } diff --git a/src/js/controller/login/create-account.js b/src/js/controller/login/create-account.js index 7222d3d..a9b0c8e 100644 --- a/src/js/controller/login/create-account.js +++ b/src/js/controller/login/create-account.js @@ -1,13 +1,7 @@ 'use strict'; -var appCtrl = require('../app-controller'), - cfg = require('../app-config').config; - -var CreateAccountCtrl = function($scope, $location, $routeParams) { - if (!appCtrl._auth && !$routeParams.dev) { - $location.path('/'); // init app - return; - } +var CreateAccountCtrl = function($scope, $location, $routeParams, auth, admin, appConfig) { + !$routeParams.dev && !auth.isInitialized() && $location.path('/'); // init app $scope.createWhiteoutAccount = function() { if ($scope.form.$invalid) { @@ -17,17 +11,17 @@ var CreateAccountCtrl = function($scope, $location, $routeParams) { $scope.busy = true; $scope.errMsg = undefined; // reset error msg - var emailAddress = $scope.user + '@' + cfg.wmailDomain; + var emailAddress = $scope.user + '@' + appConfig.config.wmailDomain; // set to state for next view - $scope.state.createAccount = { + auth.setCredentials({ emailAddress: emailAddress, - pass: $scope.pass, + password: $scope.pass, realname: $scope.realname - }; + }); // call REST api - appCtrl._adminDao.createUser({ + admin.createUser({ emailAddress: emailAddress, password: $scope.pass, phone: $scope.phone.replace(/\s+/g, ''), // remove spaces from the phone number diff --git a/src/js/controller/login/login-existing.js b/src/js/controller/login/login-existing.js index 24cf496..505f1d2 100644 --- a/src/js/controller/login/login-existing.js +++ b/src/js/controller/login/login-existing.js @@ -1,14 +1,7 @@ 'use strict'; -var appController = require('../app-controller'); - -var LoginExistingCtrl = function($scope, $location, $routeParams) { - if (!appController._emailDao && !$routeParams.dev) { - $location.path('/'); // init app - return; - } - - var emailDao = appController._emailDao; +var LoginExistingCtrl = function($scope, $location, $routeParams, email, auth, keychain) { + !$routeParams.dev && !auth.isInitialized() && $location.path('/'); // init app $scope.confirmPassphrase = function() { if ($scope.form.$invalid) { @@ -24,14 +17,14 @@ var LoginExistingCtrl = function($scope, $location, $routeParams) { }; function unlockCrypto() { - var userId = emailDao._account.emailAddress; - emailDao._keychain.getUserKeyPair(userId, function(err, keypair) { + var userId = auth.emailAddress; + keychain.getUserKeyPair(userId, function(err, keypair) { if (err) { displayError(err); return; } - emailDao.unlock({ + email.unlock({ keypair: keypair, passphrase: $scope.passphrase }, onUnlock); @@ -44,7 +37,7 @@ var LoginExistingCtrl = function($scope, $location, $routeParams) { return; } - appController._auth.storeCredentials(function(err) { + auth.storeCredentials(function(err) { if (err) { displayError(err); return; diff --git a/src/js/controller/login/login-initial.js b/src/js/controller/login/login-initial.js index ced1bd1..08408d6 100644 --- a/src/js/controller/login/login-initial.js +++ b/src/js/controller/login/login-initial.js @@ -1,17 +1,9 @@ 'use strict'; -var appController = require('../app-controller'); +var LoginInitialCtrl = function($scope, $location, $routeParams, newsletter, email, auth) { + !$routeParams.dev && !auth.isInitialized() && $location.path('/'); // init app -var LoginInitialCtrl = function($scope, $location, $routeParams, newsletter) { - if (!appController._emailDao && !$routeParams.dev) { - $location.path('/'); // init app - return; - } - - if (appController._emailDao) { - var emailDao = appController._emailDao, - emailAddress = emailDao._account.emailAddress; - } + var emailAddress = auth.emailAddress; var termsMsg = 'You must accept the Terms of Service to continue.', states = { @@ -59,7 +51,7 @@ var LoginInitialCtrl = function($scope, $location, $routeParams, newsletter) { // go to set keygen screen $scope.setState(states.PROCESSING); - emailDao.unlock({ + email.unlock({ passphrase: undefined // generate key without passphrase }, function(err) { if (err) { @@ -67,7 +59,7 @@ var LoginInitialCtrl = function($scope, $location, $routeParams, newsletter) { return; } - appController._auth.storeCredentials(function(err) { + auth.storeCredentials(function(err) { if (err) { displayError(err); return; diff --git a/src/js/controller/login/login-new-device.js b/src/js/controller/login/login-new-device.js index fc24b17..44a3292 100644 --- a/src/js/controller/login/login-new-device.js +++ b/src/js/controller/login/login-new-device.js @@ -1,15 +1,7 @@ 'use strict'; -var appController = require('../app-controller'); - -var LoginExistingCtrl = function($scope, $location, $routeParams) { - if (!appController._emailDao && !$routeParams.dev) { - $location.path('/'); // init app - return; - } - - var emailDao = appController._emailDao, - pgp = appController._pgp; +var LoginExistingCtrl = function($scope, $location, $routeParams, email, auth, pgp, keychain) { + !$routeParams.dev && !auth.isInitialized() && $location.path('/'); // init app $scope.incorrect = false; @@ -27,9 +19,9 @@ var LoginExistingCtrl = function($scope, $location, $routeParams) { }; function unlockCrypto() { - var userId = emailDao._account.emailAddress; + var userId = auth.emailAddress; // check if user already has a public key on the key server - emailDao._keychain.getUserKeyPair(userId, function(err, keypair) { + keychain.getUserKeyPair(userId, function(err, keypair) { if (err) { $scope.displayError(err); return; @@ -76,7 +68,7 @@ var LoginExistingCtrl = function($scope, $location, $routeParams) { } // import and validate keypair - emailDao.unlock({ + email.unlock({ keypair: keypair, passphrase: $scope.passphrase }, function(err) { @@ -86,7 +78,7 @@ var LoginExistingCtrl = function($scope, $location, $routeParams) { return; } - emailDao._keychain.putUserKeyPair(keypair, onUnlock); + keychain.putUserKeyPair(keypair, onUnlock); }); }); } @@ -97,7 +89,7 @@ var LoginExistingCtrl = function($scope, $location, $routeParams) { return; } - appController._auth.storeCredentials(function(err) { + auth.storeCredentials(function(err) { if (err) { $scope.displayError(err); return; diff --git a/src/js/controller/login/login-privatekey-download.js b/src/js/controller/login/login-privatekey-download.js index e45106c..2d9715b 100644 --- a/src/js/controller/login/login-privatekey-download.js +++ b/src/js/controller/login/login-privatekey-download.js @@ -1,18 +1,7 @@ 'use strict'; -var appController = require('../app-controller'); - -var LoginPrivateKeyDownloadCtrl = function($scope, $location, $routeParams) { - if (!appController._emailDao && !$routeParams.dev) { - $location.path('/'); // init app - return; - } - - if (appController._emailDao) { - var keychain = appController._keychain, - emailDao = appController._emailDao, - userId = emailDao._account.emailAddress; - } +var LoginPrivateKeyDownloadCtrl = function($scope, $location, $routeParams, auth, email, keychain) { + !$routeParams.dev && !auth.isInitialized() && $location.path('/'); // init app $scope.step = 1; @@ -38,6 +27,7 @@ var LoginPrivateKeyDownloadCtrl = function($scope, $location, $routeParams) { }; $scope.verifyRecoveryToken = function(callback) { + var userId = auth.emailAddress; keychain.getUserKeyPair(userId, function(err, keypair) { if (err) { displayError(err); @@ -95,7 +85,7 @@ var LoginPrivateKeyDownloadCtrl = function($scope, $location, $routeParams) { $scope.cachedKeypair.privateKey = privateKey; // try empty passphrase - emailDao.unlock({ + email.unlock({ keypair: $scope.cachedKeypair, passphrase: undefined }, function(err) { @@ -106,7 +96,7 @@ var LoginPrivateKeyDownloadCtrl = function($scope, $location, $routeParams) { } // passphrase is corrent ... go to main app - appController._auth.storeCredentials(function(err) { + auth.storeCredentials(function(err) { if (err) { displayError(err); return; diff --git a/src/js/controller/login/login-set-credentials.js b/src/js/controller/login/login-set-credentials.js index 795ec37..c5bfbd2 100644 --- a/src/js/controller/login/login-set-credentials.js +++ b/src/js/controller/login/login-set-credentials.js @@ -4,16 +4,8 @@ var ENCRYPTION_METHOD_NONE = 0; var ENCRYPTION_METHOD_STARTTLS = 1; var ENCRYPTION_METHOD_TLS = 2; -var appCtrl = require('../app-controller'); - -var SetCredentialsCtrl = function($scope, $location, $routeParams) { - if (!appCtrl._auth && !$routeParams.dev) { - $location.path('/'); // init app - return; - } - - var auth = appCtrl._auth; - var doctor = appCtrl._doctor; +var SetCredentialsCtrl = function($scope, $location, $routeParams, auth, connectionDoctor) { + !$routeParams.dev && !auth.isInitialized() && $location.path('/'); // init app // // Presets and Settings @@ -87,11 +79,11 @@ var SetCredentialsCtrl = function($scope, $location, $routeParams) { }; // use the credentials in the connection doctor - doctor.configure(credentials); + connectionDoctor.configure(credentials); // run connection doctor test suite $scope.busy = true; - doctor.check(function(err) { + connectionDoctor.check(function(err) { if (err) { // display the error in the settings UI $scope.connectionError = err; diff --git a/src/js/controller/login/login.js b/src/js/controller/login/login.js index 7087cbc..ed163f9 100644 --- a/src/js/controller/login/login.js +++ b/src/js/controller/login/login.js @@ -1,31 +1,19 @@ 'use strict'; -var appController = require('../app-controller'); +var LoginCtrl = function($scope, $location, updateHandler, account, auth, email, keychain, dialog) { -var LoginCtrl = function($scope, $location) { - - // start main application controller - appController.start({ - onError: $scope.onError - }, function(err) { - if (err) { - $scope.onError(err); - return; - } - - // check for app update - appController.checkForUpdate(); - - initializeUser(); - }); - - // TODO: move to Account service login function + // check for app update + updateHandler.checkForUpdate(); + // initialize the user account + initializeUser(); function initializeUser() { + // init the auth modules + auth.init(); // get OAuth token from chrome - appController._auth.getEmailAddress(function(err, info) { + auth.getEmailAddress(function(err, info) { if (err) { - $scope.onError(err); + dialog.error(err); return; } @@ -35,13 +23,13 @@ var LoginCtrl = function($scope, $location) { return; } - // initiate controller by creating email dao - appController.init({ + // initiate the account by initializing the email dao and user storage + account.init({ emailAddress: info.emailAddress, realname: info.realname }, function(err, availableKeys) { if (err) { - $scope.onError(err); + dialog.error(err); return; } @@ -53,7 +41,7 @@ var LoginCtrl = function($scope, $location) { function redirect(availableKeys) { if (availableKeys && availableKeys.publicKey && availableKeys.privateKey) { // public and private key available, try empty passphrase - appController._emailDao.unlock({ + email.unlock({ keypair: availableKeys, passphrase: undefined }, function(err) { @@ -62,9 +50,9 @@ var LoginCtrl = function($scope, $location) { return; } - appController._auth.storeCredentials(function(err) { + auth.storeCredentials(function(err) { if (err) { - return $scope.onError(err); + return dialog.error(err); } goTo('/desktop'); @@ -72,12 +60,12 @@ var LoginCtrl = function($scope, $location) { }); } else if (availableKeys && availableKeys.publicKey && !availableKeys.privateKey) { // check if private key is synced - appController._keychain.requestPrivateKeyDownload({ + keychain.requestPrivateKeyDownload({ userId: availableKeys.publicKey.userId, keyId: availableKeys.publicKey._id, }, function(err, privateKeySynced) { if (err) { - $scope.onError(err); + dialog.error(err); return; } diff --git a/src/js/controller/login/validate-phone.js b/src/js/controller/login/validate-phone.js index 05c1221..a673416 100644 --- a/src/js/controller/login/validate-phone.js +++ b/src/js/controller/login/validate-phone.js @@ -1,12 +1,7 @@ 'use strict'; -var appCtrl = require('../app-controller'); - -var ValidatePhoneCtrl = function($scope, $location, $routeParams, mailConfig) { - if (!appCtrl._auth && !$routeParams.dev) { - $location.path('/'); // init app - return; - } +var ValidatePhoneCtrl = function($scope, $location, $routeParams, mailConfig, auth, admin) { + !$routeParams.dev && !auth.isInitialized() && $location.path('/'); // init app // TODO: move to Account service create function @@ -20,8 +15,8 @@ var ValidatePhoneCtrl = function($scope, $location, $routeParams, mailConfig) { $scope.errMsg = undefined; // reset error msg // verify user to REST api - appCtrl._adminDao.validateUser({ - emailAddress: $scope.state.createAccount.emailAddress, + admin.validateUser({ + emailAddress: auth.emailAddress, token: $scope.token.toUpperCase() }, function(err) { if (err) { @@ -37,14 +32,14 @@ var ValidatePhoneCtrl = function($scope, $location, $routeParams, mailConfig) { }; $scope.login = function() { - var address = $scope.state.createAccount.emailAddress; + var address = auth.emailAddress; return mailConfig.get(address).then(function(config) { // store credentials in memory - appCtrl._auth.setCredentials({ - emailAddress: $scope.state.createAccount.emailAddress, - username: $scope.state.createAccount.emailAddress, - realname: $scope.state.createAccount.realname, - password: $scope.state.createAccount.pass, + auth.setCredentials({ + emailAddress: auth.emailAddress, + username: auth.emailAddress, + realname: auth.realname, + password: auth.password, imap: { host: config.imap.hostname, port: parseInt(config.imap.port, 10), diff --git a/src/js/crypto/index.js b/src/js/crypto/index.js index f471282..6a77bcc 100644 --- a/src/js/crypto/index.js +++ b/src/js/crypto/index.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('woCrypto', []); +angular.module('woCrypto', ['woAppConfig', 'woUtil']); require('./pgp'); require('./crypto'); \ No newline at end of file diff --git a/src/js/email/account.js b/src/js/email/account.js index 6b87b47..debca95 100644 --- a/src/js/email/account.js +++ b/src/js/email/account.js @@ -4,41 +4,240 @@ var ngModule = angular.module('woServices'); ngModule.service('account', Account); module.exports = Account; -function Account(email, outbox) { - this._emailDAOs = [email]; - this._outboxes = [outbox]; - this._accounts = undefined; +var axe = require('axe-logger'), + util = require('crypto-lib').util, + PgpMailer = require('pgpmailer'), + ImapClient = require('imap-client'); + +function Account(appConfig, auth, admin, mailConfig, keychain, pgpbuilder, email, outbox, deviceStorage, updateHandler) { + this._appConfig = appConfig; + this._auth = auth; + this._admin = admin; + this._mailConfig = mailConfig; + this._keychain = keychain; + this._emailDao = email; + this._pgpbuilder = pgpbuilder; + this._outbox = outbox; + this._deviceStorage = deviceStorage; + this._updateHandler = updateHandler; + 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} The account objects containing folder and message objects */ Account.prototype.list = function() { - this._accounts = this._emailDAOs.map(function(emailDao) { - return emailDao._account; - }); - return this._accounts; }; /** - * Login to an existing email account. This creates a new email data access object instance for that account and logs in via IMAP. - * @param {String} options.emailAddress The account's email address + * Fire up the database, retrieve the available keys for the user and initialize the email data access object */ -Account.prototype.login = function(options) { - var emailDao = new Email(); - this._emailDAOs.push(emailDao); +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.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._deviceStorage.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(callback) { + var self = this; + var config = self._appConfig.config; + + if (!self.isOnline() || !self._emailDao || !self._emailDao._account) { + // prevent connection infinite loop + callback(); + 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, self._dialog.error); + pgpMailer.onCert = self._auth.handleCertificateUpdate.bind(self._auth, 'smtp', self.onConnect, 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 = '/'; + } + }); + }); }; /** * Create a new whiteout account. This creates a new email data access object instance for that account and logs in via IMAP. * @param {String} options.emailAddress The account's email address */ -Account.prototype.create = function(options) {}; - -/** - * Logout of an email account. This creates a new email data access object instance for that account and logs in via IMAP. - * @param {String} options.emailAddress The account's email address - */ -Account.prototype.logout = function(options) {}; \ No newline at end of file +Account.prototype.create = function() {}; \ No newline at end of file diff --git a/src/js/email/email.js b/src/js/email/email.js index 85b07d8..9bc1631 100644 --- a/src/js/email/email.js +++ b/src/js/email/email.js @@ -50,12 +50,14 @@ var MSG_PART_TYPE_HTML = 'html'; * @param {Object} pgpbuilder Generates and encrypts MIME and SMTP messages * @param {Object} mailreader Parses MIME messages received from IMAP */ -function Email(keychain, pgp, devicestorage, pgpbuilder, mailreader) { +function Email(keychain, pgp, deviceStorage, pgpbuilder, mailreader, dialog) { this._keychain = keychain; this._pgp = pgp; - this._devicestorage = devicestorage; + this._devicestorage = deviceStorage; this._pgpbuilder = pgpbuilder; this._mailreader = mailreader; + + this.onError = dialog.error; } diff --git a/src/js/email/index.js b/src/js/email/index.js index e8c6d5a..1860dff 100644 --- a/src/js/email/index.js +++ b/src/js/email/index.js @@ -1,6 +1,9 @@ 'use strict'; -angular.module('woEmail', []); +angular.module('woEmail', ['woAppConfig', 'woUtil', 'woServices']); +require('./mailreader'); +require('./pgpbuilder'); require('./email'); +require('./outbox'); require('./account'); \ No newline at end of file diff --git a/src/js/email/outbox.js b/src/js/email/outbox.js index a81d06a..b4350d9 100644 --- a/src/js/email/outbox.js +++ b/src/js/email/outbox.js @@ -13,15 +13,15 @@ var util = require('crypto-lib').util, * The local outbox takes care of the emails before they are being sent. * It also checks periodically if there are any mails in the local device storage to be sent. */ -function Outbox(emailDao, keychain, devicestorage) { +function Outbox(email, keychain, deviceStorage) { /** @private */ - this._emailDao = emailDao; + this._emailDao = email; /** @private */ this._keychain = keychain; /** @private */ - this._devicestorage = devicestorage; + this._devicestorage = deviceStorage; /** * Semaphore-esque flag to avoid 'concurrent' calls to _processOutbox when the timeout fires, but a call is still in process. diff --git a/src/js/service/admin.js b/src/js/service/admin.js index 8c3fc13..5a3549b 100644 --- a/src/js/service/admin.js +++ b/src/js/service/admin.js @@ -4,8 +4,9 @@ var ngModule = angular.module('woServices'); ngModule.service('admin', Admin); module.exports = Admin; -function Admin(restDao) { +function Admin(restDao, appConfig) { this._restDao = restDao; + this._restDao.setBaseUri(appConfig.config.adminUrl); } /** diff --git a/src/js/service/app-config-store.js b/src/js/service/app-config-store.js new file mode 100644 index 0000000..a51dc67 --- /dev/null +++ b/src/js/service/app-config-store.js @@ -0,0 +1,15 @@ +'use strict'; + +var ngModule = angular.module('woServices'); +ngModule.service('appConfigStore', AppConfigStore); +module.exports = AppConfigStore; + +/** + * A service for storing app configuration and user credential data locally + */ +function AppConfigStore(lawnchairDAO) { + this._localDbDao = lawnchairDAO; + this._localDbDao.init('app-config'); +} + +// TODO: inherit DeviceStorage service api \ No newline at end of file diff --git a/src/js/service/auth.js b/src/js/service/auth.js index 6cb1863..4150d17 100644 --- a/src/js/service/auth.js +++ b/src/js/service/auth.js @@ -25,12 +25,26 @@ var SMTP_DB_KEY = 'smtp'; * auth.getCredentials(...); // called to gather all the information to connect to IMAP/SMTP, * username, password / oauth token, IMAP/SMTP server host names, ... */ -function Auth(deviceStorage, oauth, pgp) { - this._appConfigStore = deviceStorage; +function Auth(appConfigStore, oauth, pgp) { + this._appConfigStore = appConfigStore; this._oauth = oauth; this._pgp = pgp; } +/** + * Initialize the service + */ +Auth.prototype.init = function() { + this._initialized = true; +}; + +/** + * Check if the service has been initialized. + */ +Auth.prototype.isInitialized = function() { + return this._initialized; +}; + /** * Retrieves credentials and IMAP/SMTP settings: * 1) Fetches the credentials from disk, then... diff --git a/src/js/service/devicestorage.js b/src/js/service/devicestorage.js index b457e15..2296e10 100644 --- a/src/js/service/devicestorage.js +++ b/src/js/service/devicestorage.js @@ -1,22 +1,18 @@ 'use strict'; var ngModule = angular.module('woServices'); -ngModule.factory('deviceStorage', ['lawnchairDAO', - function(lawnchairDAO) { - return new DeviceStorage(lawnchairDAO); - } -]); +ngModule.service('deviceStorage', DeviceStorage); module.exports = DeviceStorage; /** - * High level storage api that handles all persistence on the device. + * High level storage api that handles all persistence of a user's data on the device. */ function DeviceStorage(lawnchairDAO) { this._localDbDao = lawnchairDAO; } -DeviceStorage.prototype.init = function(dbName, callback) { - this._localDbDao.init(dbName, callback); +DeviceStorage.prototype.init = function(dbName) { + this._localDbDao.init(dbName); }; /** diff --git a/src/js/service/index.js b/src/js/service/index.js index bbe35ab..7e18939 100644 --- a/src/js/service/index.js +++ b/src/js/service/index.js @@ -1,8 +1,17 @@ 'use strict'; -angular.module('woServices', []); +angular.module('woServices', ['woAppConfig', 'woUtil', 'woCrypto']); -require('./newsletter'); +require('./rest'); +require('./invitation'); require('./mail-config'); -require('./account'); -require('.pgpbuilder'); \ No newline at end of file +require('./newsletter'); +require('./oauth'); +require('./privatekey'); +require('./publickey'); +require('./admin'); +require('./lawnchair'); +require('./devicestorage'); +require('./app-config-store'); +require('./auth'); +require('./keychain'); \ No newline at end of file diff --git a/src/js/service/invitation.js b/src/js/service/invitation.js index 9a34211..75916cd 100644 --- a/src/js/service/invitation.js +++ b/src/js/service/invitation.js @@ -4,15 +4,13 @@ var ngModule = angular.module('woServices'); ngModule.service('invitation', Invitation); module.exports = Invitation; -var config = require('../app-config').config; - /** * The Invitation is a high level Data Access Object that access the invitation service REST endpoint. * @param {Object} restDao The REST Data Access Object abstraction */ -function Invitation(restDao) { +function Invitation(restDao, appConfig) { this._restDao = restDao; - this._restDao.setBaseUri(config.cloudUrl); + this._restDao.setBaseUri(appConfig.config.cloudUrl); } // diff --git a/src/js/service/keychain.js b/src/js/service/keychain.js index e284893..28b9405 100644 --- a/src/js/service/keychain.js +++ b/src/js/service/keychain.js @@ -16,18 +16,40 @@ var DB_PUBLICKEY = 'publickey', * A high-level Data-Access Api for handling Keypair synchronization * between the cloud service and the device's local storage */ -function Keychain(localDbDao, publicKeyDao, privateKeyDao, crypto, pgp) { - this._localDbDao = localDbDao; - this._publicKeyDao = publicKeyDao; - this._privateKeyDao = privateKeyDao; +function Keychain(lawnchairDAO, publicKey, privateKey, crypto, pgp, dialog, appConfig) { + this._lawnchairDAO = lawnchairDAO; + this._publicKeyDao = publicKey; + this._privateKeyDao = privateKey; this._crypto = crypto; this._pgp = pgp; + this._dialog = dialog; + this._appConfig = appConfig; } // // Public key functions // +/** + * Display confirmation dialog to request a public key update + * @param {Object} params.newKey The user's updated public key object + * @param {String} params.userId The user's email address + */ +Keychain.prototype.requestPermissionForKeyUpdate = function(params, callback) { + var str = this._appConfig.string; + var message = params.newKey ? str.updatePublicKeyMsgNewKey : str.updatePublicKeyMsgRemovedKey; + message = message.replace('{0}', params.userId); + + this._dialog.confirm({ + title: str.updatePublicKeyTitle, + message: message, + positiveBtnStr: str.updatePublicKeyPosBtn, + negativeBtnStr: str.updatePublicKeyNegBtn, + showNegativeBtn: true, + callback: callback + }); +}; + /** * Verifies the public key of a user o nthe public key store * @param {String} uuid The uuid to verify the key @@ -196,7 +218,7 @@ Keychain.prototype.getReceiverPublicKey = function(userId, callback) { var self = this; // search local keyring for public key - self._localDbDao.list(DB_PUBLICKEY, 0, null, function(err, allPubkeys) { + self._lawnchairDAO.list(DB_PUBLICKEY, 0, null, function(err, allPubkeys) { if (err) { callback(err); return; @@ -275,7 +297,7 @@ Keychain.prototype.setDeviceName = function(deviceName, callback) { return; } - this._localDbDao.persist(DB_DEVICENAME, deviceName, callback); + this._lawnchairDAO.persist(DB_DEVICENAME, deviceName, callback); }; /** @@ -285,7 +307,7 @@ Keychain.prototype.setDeviceName = function(deviceName, callback) { */ Keychain.prototype.getDeviceName = function(callback) { // check if deviceName is already persisted in storage - this._localDbDao.read(DB_DEVICENAME, function(err, deviceName) { + this._lawnchairDAO.read(DB_DEVICENAME, function(err, deviceName) { if (err) { callback(err); return; @@ -308,7 +330,7 @@ Keychain.prototype.getDeviceSecret = function(callback) { var self = this; // generate random deviceSecret or get from storage - self._localDbDao.read(DB_DEVICE_SECRET, function(err, storedDevSecret) { + self._lawnchairDAO.read(DB_DEVICE_SECRET, function(err, storedDevSecret) { if (err) { callback(err); return; @@ -323,7 +345,7 @@ Keychain.prototype.getDeviceSecret = function(callback) { // generate random deviceSecret var deviceSecret = util.random(config.symKeySize); // persist deviceSecret to local storage (in plaintext) - self._localDbDao.persist(DB_DEVICE_SECRET, deviceSecret, function(err) { + self._lawnchairDAO.persist(DB_DEVICE_SECRET, deviceSecret, function(err) { if (err) { callback(err); return; @@ -763,7 +785,7 @@ Keychain.prototype.getUserKeyPair = function(userId, callback) { var self = this; // search for user's public key locally - self._localDbDao.list(DB_PUBLICKEY, 0, null, function(err, allPubkeys) { + self._lawnchairDAO.list(DB_PUBLICKEY, 0, null, function(err, allPubkeys) { if (err) { callback(err); return; @@ -886,7 +908,7 @@ Keychain.prototype.lookupPublicKey = function(id, callback) { } // lookup in local storage - self._localDbDao.read(DB_PUBLICKEY + '_' + id, function(err, pubkey) { + self._lawnchairDAO.read(DB_PUBLICKEY + '_' + id, function(err, pubkey) { if (err) { callback(err); return; @@ -922,26 +944,26 @@ Keychain.prototype.lookupPublicKey = function(id, callback) { */ Keychain.prototype.listLocalPublicKeys = function(callback) { // search local keyring for public key - this._localDbDao.list(DB_PUBLICKEY, 0, null, callback); + this._lawnchairDAO.list(DB_PUBLICKEY, 0, null, callback); }; Keychain.prototype.removeLocalPublicKey = function(id, callback) { - this._localDbDao.remove(DB_PUBLICKEY + '_' + id, callback); + this._lawnchairDAO.remove(DB_PUBLICKEY + '_' + id, callback); }; Keychain.prototype.lookupPrivateKey = function(id, callback) { // lookup in local storage - this._localDbDao.read(DB_PRIVATEKEY + '_' + id, callback); + this._lawnchairDAO.read(DB_PRIVATEKEY + '_' + id, callback); }; Keychain.prototype.saveLocalPublicKey = function(pubkey, callback) { // persist public key (email, _id) var pkLookupKey = DB_PUBLICKEY + '_' + pubkey._id; - this._localDbDao.persist(pkLookupKey, pubkey, callback); + this._lawnchairDAO.persist(pkLookupKey, pubkey, callback); }; Keychain.prototype.saveLocalPrivateKey = function(privkey, callback) { // persist private key (email, _id) var prkLookupKey = DB_PRIVATEKEY + '_' + privkey._id; - this._localDbDao.persist(prkLookupKey, privkey, callback); + this._lawnchairDAO.persist(prkLookupKey, privkey, callback); }; \ No newline at end of file diff --git a/src/js/service/lawnchair.js b/src/js/service/lawnchair.js index fcdcee3..4d5e07b 100644 --- a/src/js/service/lawnchair.js +++ b/src/js/service/lawnchair.js @@ -15,25 +15,13 @@ function LawnchairDAO() {} * Initialize the lawnchair database * @param {String} dbName The name of the database */ -LawnchairDAO.prototype.init = function(dbName, callback) { +LawnchairDAO.prototype.init = function(dbName) { if (!dbName) { - callback({ - errMsg: 'Lawnchair DB name must be specified!' - }); - return; + throw new Error('Lawnchair DB name must be specified!'); } this._db = new Lawnchair({ name: dbName - }, function(lc) { - if (!lc) { - callback({ - errMsg: 'Lawnchair init failed!' - }); - return; - } - - callback(); }); }; diff --git a/src/js/service/privatekey.js b/src/js/service/privatekey.js index 0511506..5f00ee5 100644 --- a/src/js/service/privatekey.js +++ b/src/js/service/privatekey.js @@ -4,11 +4,9 @@ var ngModule = angular.module('woServices'); ngModule.service('privateKey', PrivateKey); module.exports = PrivateKey; -var config = require('../app-config').config; - -function PrivateKey(restDao) { +function PrivateKey(restDao, appConfig) { this._restDao = restDao; - this._restDao.setBaseUri(config.privkeyServerUrl); + this._restDao.setBaseUri(appConfig.config.privkeyServerUrl); } // diff --git a/src/js/service/publickey.js b/src/js/service/publickey.js index f2b9736..e162138 100644 --- a/src/js/service/publickey.js +++ b/src/js/service/publickey.js @@ -4,11 +4,9 @@ var ngModule = angular.module('woServices'); ngModule.service('publicKey', PublicKey); module.exports = PublicKey; -var config = require('../app-config').config; - -function PublicKey(restDao) { +function PublicKey(restDao, appConfig) { this._restDao = restDao; - this._restDao.setBaseUri(config.cloudUrl); + this._restDao.setBaseUri(appConfig.config.cloudUrl); } /** diff --git a/src/js/util/update/update-handler.js b/src/js/util/update/update-handler.js index e72fccb..ebfdd11 100644 --- a/src/js/util/update/update-handler.js +++ b/src/js/util/update/update-handler.js @@ -1,7 +1,7 @@ 'use strict'; var ngModule = angular.module('woUtil'); -ngModule.service('updateHandler', ['deviceStorage', 'deviceStorage', 'auth', UpdateHandler]); +ngModule.service('updateHandler', UpdateHandler); module.exports = UpdateHandler; var axe = require('axe-logger'), @@ -15,11 +15,12 @@ var axe = require('axe-logger'), /** * Handles database migration */ -function UpdateHandler(appConfigStorage, userStorage, auth) { - this._appConfigStorage = appConfigStorage; - this._userStorage = userStorage; +function UpdateHandler(appConfigStore, deviceStorage, auth, dialog) { + this._appConfigStorage = appConfigStore; + this._userStorage = deviceStorage; this._updateScripts = [updateV1, updateV2, updateV3, updateV4, updateV5]; this._auth = auth; + this._dialog = dialog; } /** @@ -99,14 +100,16 @@ UpdateHandler.prototype._applyUpdate = function(options, callback) { /** * Check application version and update correspondingly */ -UpdateHandler.prototype.checkForUpdate = function(dialog) { +UpdateHandler.prototype.checkForUpdate = function() { + var self = this; + // Chrome Packaged App if (typeof window.chrome !== 'undefined' && chrome.runtime && chrome.runtime.onUpdateAvailable) { // check for Chrome app update and restart chrome.runtime.onUpdateAvailable.addListener(function(details) { axe.debug('New Chrome App update... requesting reload.'); // Chrome downloaded a new app version - dialog({ + self._dialog.confirm({ title: 'Update available', message: 'A new version ' + details.version + ' of the app is available. Restart the app to update?', positiveBtnStr: 'Restart',