Refactor login controllers and delete AppController

This commit is contained in:
Tankred Hase 2014-11-19 20:54:59 +01:00
parent e6b22bd0a0
commit 4c04ba4e74
30 changed files with 455 additions and 571 deletions

View File

@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html ng-app="mail" ng-csp manifest="appcache.manifest"> <html ng-csp manifest="appcache.manifest">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>Whiteout Mail</title> <title>Whiteout Mail</title>

View File

@ -2,7 +2,7 @@
var appCfg = {}; var appCfg = {};
var ngModule = angular.module('mail'); var ngModule = angular.module('woAppConfig');
ngModule.factory('appConfig', function() { ngModule.factory('appConfig', function() {
return appCfg; return appCfg;
}); });

View File

@ -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;

View File

@ -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'), AddAccountCtrl = require('./controller/add-account'),
CreateAccountCtrl = require('./controller/create-account'), CreateAccountCtrl = require('./controller/create-account'),
ValidatePhoneCtrl = require('./controller/validate-phone'), ValidatePhoneCtrl = require('./controller/validate-phone'),
@ -36,13 +37,26 @@ var DialogCtrl = require('./controller/dialog'),
ActionBarCtrl = require('./controller/action-bar'), ActionBarCtrl = require('./controller/action-bar'),
errorUtil = require('./util/error'), errorUtil = require('./util/error'),
backButtonUtil = require('./util/backbutton-handler'); backButtonUtil = require('./util/backbutton-handler');
require('./directive/common'),
// include angular modules
require('./app-config');
require('./directive/common');
require('./util');
require('./crypto');
require('./service'); require('./service');
require('./email');
// init main angular module including dependencies // init main angular module including dependencies
var app = angular.module('mail', [ var app = angular.module('mail', [
'ngRoute', 'ngRoute',
'ngAnimate', 'ngAnimate',
'ngTagsInput',
'woAppConfig',
'woDirectives',
'woUtil',
'woCrypto,',
'woServices',
'woEmail',
'navigation', 'navigation',
'mail-list', 'mail-list',
'write', 'write',
@ -50,10 +64,7 @@ var app = angular.module('mail', [
'contacts', 'contacts',
'login-new-device', 'login-new-device',
'privatekey-upload', 'privatekey-upload',
'infinite-scroll', 'infinite-scroll'
'ngTagsInput',
'woDirectives',
'woServices'
]); ]);
// set router paths // set router paths
@ -131,3 +142,24 @@ app.controller('ContactsCtrl', ContactsCtrl);
app.controller('AboutCtrl', AboutCtrl); app.controller('AboutCtrl', AboutCtrl);
app.controller('DialogCtrl', DialogCtrl); app.controller('DialogCtrl', DialogCtrl);
app.controller('ActionBarCtrl', ActionBarCtrl); 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']);
});
}

View File

@ -5,8 +5,7 @@ var appController = require('../app-controller'),
backBtnHandler = require('../util/backbutton-handler'), backBtnHandler = require('../util/backbutton-handler'),
appCfg = require('../app-config'), appCfg = require('../app-config'),
config = appCfg.config, config = appCfg.config,
str = appCfg.string, str = appCfg.string;
emailDao, outboxBo;
// //
// Constants // Constants
@ -19,14 +18,8 @@ var NOTIFICATION_SENT_TIMEOUT = 2000;
// Controller // Controller
// //
var NavigationCtrl = function($scope, $routeParams, $location) { var NavigationCtrl = function($scope, $routeParams, $location, account, email, outbox) {
if (!appController._emailDao && !$routeParams.dev) { !$routeParams.dev && !account.isLoggedIn() && $location.path('/'); // init app
$location.path('/'); // init app
return;
}
emailDao = appController._emailDao;
outboxBo = appController._outboxBo;
// //
// scope functions // scope functions
@ -51,14 +44,14 @@ var NavigationCtrl = function($scope, $routeParams, $location) {
} }
// update the outbox mail count // update the outbox mail count
var outbox = _.findWhere($scope.account.folders, { var ob = _.findWhere($scope.account.folders, {
type: config.outboxMailboxType type: config.outboxMailboxType
}); });
outbox.count = count; ob.count = count;
$scope.$apply(); $scope.$apply();
emailDao.refreshFolder({ email.refreshFolder({
folder: outbox folder: ob
}, $scope.onError); }, $scope.onError);
}; };
@ -114,18 +107,19 @@ var NavigationCtrl = function($scope, $routeParams, $location) {
} }
// get pointer to account/folder/message tree on root scope // 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 // set notificatio handler for sent messages
outboxBo.onSent = sentNotification; outbox.onSent = sentNotification;
// start checking outbox periodically // start checking outbox periodically
outboxBo.startChecking($scope.onOutboxUpdate); outbox.startChecking($scope.onOutboxUpdate);
} }
function sentNotification(email) { function sentNotification(message) {
notification.create({ notification.create({
title: 'Message sent', title: 'Message sent',
message: email.subject, message: message.subject,
timeout: NOTIFICATION_SENT_TIMEOUT timeout: NOTIFICATION_SENT_TIMEOUT
}, function() {}); }, function() {});
} }

View File

@ -1,12 +1,7 @@
'use strict'; 'use strict';
var appCtrl = require('../app-controller'); var AddAccountCtrl = function($scope, $location, $routeParams, mailConfig, auth) {
!$routeParams.dev && !auth.isInitialized() && $location.path('/'); // init app
var AddAccountCtrl = function($scope, $location, $routeParams, mailConfig) {
if (!appCtrl._auth && !$routeParams.dev) {
$location.path('/'); // init app
return;
}
$scope.getAccountSettings = function() { $scope.getAccountSettings = function() {
if ($scope.form.$invalid) { if ($scope.form.$invalid) {
@ -25,7 +20,7 @@ var AddAccountCtrl = function($scope, $location, $routeParams, mailConfig) {
}; };
var hostname = config.imap.hostname; var hostname = config.imap.hostname;
if (appCtrl._auth.useOAuth(hostname)) { if (auth.useOAuth(hostname)) {
// check for oauth support // check for oauth support
$scope.oauthPossible(); $scope.oauthPossible();
} else { } else {
@ -62,7 +57,7 @@ var AddAccountCtrl = function($scope, $location, $routeParams, mailConfig) {
function getOAuthToken() { function getOAuthToken() {
// fetches the email address from the chrome identity api // fetches the email address from the chrome identity api
appCtrl._auth.getOAuthToken(function(err) { auth.getOAuthToken(function(err) {
if (err) { if (err) {
return $scope.onError(err); return $scope.onError(err);
} }

View File

@ -1,13 +1,7 @@
'use strict'; 'use strict';
var appCtrl = require('../app-controller'), var CreateAccountCtrl = function($scope, $location, $routeParams, auth, admin, appConfig) {
cfg = require('../app-config').config; !$routeParams.dev && !auth.isInitialized() && $location.path('/'); // init app
var CreateAccountCtrl = function($scope, $location, $routeParams) {
if (!appCtrl._auth && !$routeParams.dev) {
$location.path('/'); // init app
return;
}
$scope.createWhiteoutAccount = function() { $scope.createWhiteoutAccount = function() {
if ($scope.form.$invalid) { if ($scope.form.$invalid) {
@ -17,17 +11,17 @@ var CreateAccountCtrl = function($scope, $location, $routeParams) {
$scope.busy = true; $scope.busy = true;
$scope.errMsg = undefined; // reset error msg $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 // set to state for next view
$scope.state.createAccount = { auth.setCredentials({
emailAddress: emailAddress, emailAddress: emailAddress,
pass: $scope.pass, password: $scope.pass,
realname: $scope.realname realname: $scope.realname
}; });
// call REST api // call REST api
appCtrl._adminDao.createUser({ admin.createUser({
emailAddress: emailAddress, emailAddress: emailAddress,
password: $scope.pass, password: $scope.pass,
phone: $scope.phone.replace(/\s+/g, ''), // remove spaces from the phone number phone: $scope.phone.replace(/\s+/g, ''), // remove spaces from the phone number

View File

@ -1,14 +1,7 @@
'use strict'; 'use strict';
var appController = require('../app-controller'); var LoginExistingCtrl = function($scope, $location, $routeParams, email, auth, keychain) {
!$routeParams.dev && !auth.isInitialized() && $location.path('/'); // init app
var LoginExistingCtrl = function($scope, $location, $routeParams) {
if (!appController._emailDao && !$routeParams.dev) {
$location.path('/'); // init app
return;
}
var emailDao = appController._emailDao;
$scope.confirmPassphrase = function() { $scope.confirmPassphrase = function() {
if ($scope.form.$invalid) { if ($scope.form.$invalid) {
@ -24,14 +17,14 @@ var LoginExistingCtrl = function($scope, $location, $routeParams) {
}; };
function unlockCrypto() { function unlockCrypto() {
var userId = emailDao._account.emailAddress; var userId = auth.emailAddress;
emailDao._keychain.getUserKeyPair(userId, function(err, keypair) { keychain.getUserKeyPair(userId, function(err, keypair) {
if (err) { if (err) {
displayError(err); displayError(err);
return; return;
} }
emailDao.unlock({ email.unlock({
keypair: keypair, keypair: keypair,
passphrase: $scope.passphrase passphrase: $scope.passphrase
}, onUnlock); }, onUnlock);
@ -44,7 +37,7 @@ var LoginExistingCtrl = function($scope, $location, $routeParams) {
return; return;
} }
appController._auth.storeCredentials(function(err) { auth.storeCredentials(function(err) {
if (err) { if (err) {
displayError(err); displayError(err);
return; return;

View File

@ -1,17 +1,9 @@
'use strict'; '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) { var emailAddress = auth.emailAddress;
if (!appController._emailDao && !$routeParams.dev) {
$location.path('/'); // init app
return;
}
if (appController._emailDao) {
var emailDao = appController._emailDao,
emailAddress = emailDao._account.emailAddress;
}
var termsMsg = 'You must accept the Terms of Service to continue.', var termsMsg = 'You must accept the Terms of Service to continue.',
states = { states = {
@ -59,7 +51,7 @@ var LoginInitialCtrl = function($scope, $location, $routeParams, newsletter) {
// go to set keygen screen // go to set keygen screen
$scope.setState(states.PROCESSING); $scope.setState(states.PROCESSING);
emailDao.unlock({ email.unlock({
passphrase: undefined // generate key without passphrase passphrase: undefined // generate key without passphrase
}, function(err) { }, function(err) {
if (err) { if (err) {
@ -67,7 +59,7 @@ var LoginInitialCtrl = function($scope, $location, $routeParams, newsletter) {
return; return;
} }
appController._auth.storeCredentials(function(err) { auth.storeCredentials(function(err) {
if (err) { if (err) {
displayError(err); displayError(err);
return; return;

View File

@ -1,15 +1,7 @@
'use strict'; 'use strict';
var appController = require('../app-controller'); var LoginExistingCtrl = function($scope, $location, $routeParams, email, auth, pgp, keychain) {
!$routeParams.dev && !auth.isInitialized() && $location.path('/'); // init app
var LoginExistingCtrl = function($scope, $location, $routeParams) {
if (!appController._emailDao && !$routeParams.dev) {
$location.path('/'); // init app
return;
}
var emailDao = appController._emailDao,
pgp = appController._pgp;
$scope.incorrect = false; $scope.incorrect = false;
@ -27,9 +19,9 @@ var LoginExistingCtrl = function($scope, $location, $routeParams) {
}; };
function unlockCrypto() { function unlockCrypto() {
var userId = emailDao._account.emailAddress; var userId = auth.emailAddress;
// check if user already has a public key on the key server // 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) { if (err) {
$scope.displayError(err); $scope.displayError(err);
return; return;
@ -76,7 +68,7 @@ var LoginExistingCtrl = function($scope, $location, $routeParams) {
} }
// import and validate keypair // import and validate keypair
emailDao.unlock({ email.unlock({
keypair: keypair, keypair: keypair,
passphrase: $scope.passphrase passphrase: $scope.passphrase
}, function(err) { }, function(err) {
@ -86,7 +78,7 @@ var LoginExistingCtrl = function($scope, $location, $routeParams) {
return; return;
} }
emailDao._keychain.putUserKeyPair(keypair, onUnlock); keychain.putUserKeyPair(keypair, onUnlock);
}); });
}); });
} }
@ -97,7 +89,7 @@ var LoginExistingCtrl = function($scope, $location, $routeParams) {
return; return;
} }
appController._auth.storeCredentials(function(err) { auth.storeCredentials(function(err) {
if (err) { if (err) {
$scope.displayError(err); $scope.displayError(err);
return; return;

View File

@ -1,18 +1,7 @@
'use strict'; 'use strict';
var appController = require('../app-controller'); var LoginPrivateKeyDownloadCtrl = function($scope, $location, $routeParams, auth, email, keychain) {
!$routeParams.dev && !auth.isInitialized() && $location.path('/'); // init app
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;
}
$scope.step = 1; $scope.step = 1;
@ -38,6 +27,7 @@ var LoginPrivateKeyDownloadCtrl = function($scope, $location, $routeParams) {
}; };
$scope.verifyRecoveryToken = function(callback) { $scope.verifyRecoveryToken = function(callback) {
var userId = auth.emailAddress;
keychain.getUserKeyPair(userId, function(err, keypair) { keychain.getUserKeyPair(userId, function(err, keypair) {
if (err) { if (err) {
displayError(err); displayError(err);
@ -95,7 +85,7 @@ var LoginPrivateKeyDownloadCtrl = function($scope, $location, $routeParams) {
$scope.cachedKeypair.privateKey = privateKey; $scope.cachedKeypair.privateKey = privateKey;
// try empty passphrase // try empty passphrase
emailDao.unlock({ email.unlock({
keypair: $scope.cachedKeypair, keypair: $scope.cachedKeypair,
passphrase: undefined passphrase: undefined
}, function(err) { }, function(err) {
@ -106,7 +96,7 @@ var LoginPrivateKeyDownloadCtrl = function($scope, $location, $routeParams) {
} }
// passphrase is corrent ... go to main app // passphrase is corrent ... go to main app
appController._auth.storeCredentials(function(err) { auth.storeCredentials(function(err) {
if (err) { if (err) {
displayError(err); displayError(err);
return; return;

View File

@ -4,16 +4,8 @@ var ENCRYPTION_METHOD_NONE = 0;
var ENCRYPTION_METHOD_STARTTLS = 1; var ENCRYPTION_METHOD_STARTTLS = 1;
var ENCRYPTION_METHOD_TLS = 2; var ENCRYPTION_METHOD_TLS = 2;
var appCtrl = require('../app-controller'); var SetCredentialsCtrl = function($scope, $location, $routeParams, auth, connectionDoctor) {
!$routeParams.dev && !auth.isInitialized() && $location.path('/'); // init app
var SetCredentialsCtrl = function($scope, $location, $routeParams) {
if (!appCtrl._auth && !$routeParams.dev) {
$location.path('/'); // init app
return;
}
var auth = appCtrl._auth;
var doctor = appCtrl._doctor;
// //
// Presets and Settings // Presets and Settings
@ -87,11 +79,11 @@ var SetCredentialsCtrl = function($scope, $location, $routeParams) {
}; };
// use the credentials in the connection doctor // use the credentials in the connection doctor
doctor.configure(credentials); connectionDoctor.configure(credentials);
// run connection doctor test suite // run connection doctor test suite
$scope.busy = true; $scope.busy = true;
doctor.check(function(err) { connectionDoctor.check(function(err) {
if (err) { if (err) {
// display the error in the settings UI // display the error in the settings UI
$scope.connectionError = err; $scope.connectionError = err;

View File

@ -1,31 +1,19 @@
'use strict'; 'use strict';
var appController = require('../app-controller'); var LoginCtrl = function($scope, $location, updateHandler, account, auth, email, keychain, dialog) {
var LoginCtrl = function($scope, $location) { // check for app update
updateHandler.checkForUpdate();
// start main application controller // initialize the user account
appController.start({ initializeUser();
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
function initializeUser() { function initializeUser() {
// init the auth modules
auth.init();
// get OAuth token from chrome // get OAuth token from chrome
appController._auth.getEmailAddress(function(err, info) { auth.getEmailAddress(function(err, info) {
if (err) { if (err) {
$scope.onError(err); dialog.error(err);
return; return;
} }
@ -35,13 +23,13 @@ var LoginCtrl = function($scope, $location) {
return; return;
} }
// initiate controller by creating email dao // initiate the account by initializing the email dao and user storage
appController.init({ account.init({
emailAddress: info.emailAddress, emailAddress: info.emailAddress,
realname: info.realname realname: info.realname
}, function(err, availableKeys) { }, function(err, availableKeys) {
if (err) { if (err) {
$scope.onError(err); dialog.error(err);
return; return;
} }
@ -53,7 +41,7 @@ var LoginCtrl = function($scope, $location) {
function redirect(availableKeys) { function redirect(availableKeys) {
if (availableKeys && availableKeys.publicKey && availableKeys.privateKey) { if (availableKeys && availableKeys.publicKey && availableKeys.privateKey) {
// public and private key available, try empty passphrase // public and private key available, try empty passphrase
appController._emailDao.unlock({ email.unlock({
keypair: availableKeys, keypair: availableKeys,
passphrase: undefined passphrase: undefined
}, function(err) { }, function(err) {
@ -62,9 +50,9 @@ var LoginCtrl = function($scope, $location) {
return; return;
} }
appController._auth.storeCredentials(function(err) { auth.storeCredentials(function(err) {
if (err) { if (err) {
return $scope.onError(err); return dialog.error(err);
} }
goTo('/desktop'); goTo('/desktop');
@ -72,12 +60,12 @@ var LoginCtrl = function($scope, $location) {
}); });
} else if (availableKeys && availableKeys.publicKey && !availableKeys.privateKey) { } else if (availableKeys && availableKeys.publicKey && !availableKeys.privateKey) {
// check if private key is synced // check if private key is synced
appController._keychain.requestPrivateKeyDownload({ keychain.requestPrivateKeyDownload({
userId: availableKeys.publicKey.userId, userId: availableKeys.publicKey.userId,
keyId: availableKeys.publicKey._id, keyId: availableKeys.publicKey._id,
}, function(err, privateKeySynced) { }, function(err, privateKeySynced) {
if (err) { if (err) {
$scope.onError(err); dialog.error(err);
return; return;
} }

View File

@ -1,12 +1,7 @@
'use strict'; 'use strict';
var appCtrl = require('../app-controller'); var ValidatePhoneCtrl = function($scope, $location, $routeParams, mailConfig, auth, admin) {
!$routeParams.dev && !auth.isInitialized() && $location.path('/'); // init app
var ValidatePhoneCtrl = function($scope, $location, $routeParams, mailConfig) {
if (!appCtrl._auth && !$routeParams.dev) {
$location.path('/'); // init app
return;
}
// TODO: move to Account service create function // TODO: move to Account service create function
@ -20,8 +15,8 @@ var ValidatePhoneCtrl = function($scope, $location, $routeParams, mailConfig) {
$scope.errMsg = undefined; // reset error msg $scope.errMsg = undefined; // reset error msg
// verify user to REST api // verify user to REST api
appCtrl._adminDao.validateUser({ admin.validateUser({
emailAddress: $scope.state.createAccount.emailAddress, emailAddress: auth.emailAddress,
token: $scope.token.toUpperCase() token: $scope.token.toUpperCase()
}, function(err) { }, function(err) {
if (err) { if (err) {
@ -37,14 +32,14 @@ var ValidatePhoneCtrl = function($scope, $location, $routeParams, mailConfig) {
}; };
$scope.login = function() { $scope.login = function() {
var address = $scope.state.createAccount.emailAddress; var address = auth.emailAddress;
return mailConfig.get(address).then(function(config) { return mailConfig.get(address).then(function(config) {
// store credentials in memory // store credentials in memory
appCtrl._auth.setCredentials({ auth.setCredentials({
emailAddress: $scope.state.createAccount.emailAddress, emailAddress: auth.emailAddress,
username: $scope.state.createAccount.emailAddress, username: auth.emailAddress,
realname: $scope.state.createAccount.realname, realname: auth.realname,
password: $scope.state.createAccount.pass, password: auth.password,
imap: { imap: {
host: config.imap.hostname, host: config.imap.hostname,
port: parseInt(config.imap.port, 10), port: parseInt(config.imap.port, 10),

View File

@ -1,6 +1,6 @@
'use strict'; 'use strict';
angular.module('woCrypto', []); angular.module('woCrypto', ['woAppConfig', 'woUtil']);
require('./pgp'); require('./pgp');
require('./crypto'); require('./crypto');

View File

@ -4,41 +4,240 @@ var ngModule = angular.module('woServices');
ngModule.service('account', Account); ngModule.service('account', Account);
module.exports = Account; module.exports = Account;
function Account(email, outbox) { var axe = require('axe-logger'),
this._emailDAOs = [email]; util = require('crypto-lib').util,
this._outboxes = [outbox]; PgpMailer = require('pgpmailer'),
this._accounts = undefined; 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 * Lists all of the current accounts connected to the app
* @return {Array<Object>} The account objects containing folder and message objects * @return {Array<Object>} The account objects containing folder and message objects
*/ */
Account.prototype.list = function() { Account.prototype.list = function() {
this._accounts = this._emailDAOs.map(function(emailDao) {
return emailDao._account;
});
return this._accounts; 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. * Fire up the database, retrieve the available keys for the user and initialize the email data access object
* @param {String} options.emailAddress The account's email address
*/ */
Account.prototype.login = function(options) { Account.prototype.init = function(options, callback) {
var emailDao = new Email(); var self = this;
this._emailDAOs.push(emailDao);
// 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. * 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 * @param {String} options.emailAddress The account's email address
*/ */
Account.prototype.create = function(options) {}; Account.prototype.create = function() {};
/**
* 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) {};

View File

@ -50,12 +50,14 @@ var MSG_PART_TYPE_HTML = 'html';
* @param {Object} pgpbuilder Generates and encrypts MIME and SMTP messages * @param {Object} pgpbuilder Generates and encrypts MIME and SMTP messages
* @param {Object} mailreader Parses MIME messages received from IMAP * @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._keychain = keychain;
this._pgp = pgp; this._pgp = pgp;
this._devicestorage = devicestorage; this._devicestorage = deviceStorage;
this._pgpbuilder = pgpbuilder; this._pgpbuilder = pgpbuilder;
this._mailreader = mailreader; this._mailreader = mailreader;
this.onError = dialog.error;
} }

View File

@ -1,6 +1,9 @@
'use strict'; 'use strict';
angular.module('woEmail', []); angular.module('woEmail', ['woAppConfig', 'woUtil', 'woServices']);
require('./mailreader');
require('./pgpbuilder');
require('./email'); require('./email');
require('./outbox');
require('./account'); require('./account');

View File

@ -13,15 +13,15 @@ var util = require('crypto-lib').util,
* The local outbox takes care of the emails before they are being sent. * 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. * 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 */ /** @private */
this._emailDao = emailDao; this._emailDao = email;
/** @private */ /** @private */
this._keychain = keychain; this._keychain = keychain;
/** @private */ /** @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. * Semaphore-esque flag to avoid 'concurrent' calls to _processOutbox when the timeout fires, but a call is still in process.

View File

@ -4,8 +4,9 @@ var ngModule = angular.module('woServices');
ngModule.service('admin', Admin); ngModule.service('admin', Admin);
module.exports = Admin; module.exports = Admin;
function Admin(restDao) { function Admin(restDao, appConfig) {
this._restDao = restDao; this._restDao = restDao;
this._restDao.setBaseUri(appConfig.config.adminUrl);
} }
/** /**

View File

@ -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

View File

@ -25,12 +25,26 @@ var SMTP_DB_KEY = 'smtp';
* auth.getCredentials(...); // called to gather all the information to connect to IMAP/SMTP, * auth.getCredentials(...); // called to gather all the information to connect to IMAP/SMTP,
* username, password / oauth token, IMAP/SMTP server host names, ... * username, password / oauth token, IMAP/SMTP server host names, ...
*/ */
function Auth(deviceStorage, oauth, pgp) { function Auth(appConfigStore, oauth, pgp) {
this._appConfigStore = deviceStorage; this._appConfigStore = appConfigStore;
this._oauth = oauth; this._oauth = oauth;
this._pgp = pgp; 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: * Retrieves credentials and IMAP/SMTP settings:
* 1) Fetches the credentials from disk, then... * 1) Fetches the credentials from disk, then...

View File

@ -1,22 +1,18 @@
'use strict'; 'use strict';
var ngModule = angular.module('woServices'); var ngModule = angular.module('woServices');
ngModule.factory('deviceStorage', ['lawnchairDAO', ngModule.service('deviceStorage', DeviceStorage);
function(lawnchairDAO) {
return new DeviceStorage(lawnchairDAO);
}
]);
module.exports = 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) { function DeviceStorage(lawnchairDAO) {
this._localDbDao = lawnchairDAO; this._localDbDao = lawnchairDAO;
} }
DeviceStorage.prototype.init = function(dbName, callback) { DeviceStorage.prototype.init = function(dbName) {
this._localDbDao.init(dbName, callback); this._localDbDao.init(dbName);
}; };
/** /**

View File

@ -1,8 +1,17 @@
'use strict'; 'use strict';
angular.module('woServices', []); angular.module('woServices', ['woAppConfig', 'woUtil', 'woCrypto']);
require('./newsletter'); require('./rest');
require('./invitation');
require('./mail-config'); require('./mail-config');
require('./account'); require('./newsletter');
require('.pgpbuilder'); require('./oauth');
require('./privatekey');
require('./publickey');
require('./admin');
require('./lawnchair');
require('./devicestorage');
require('./app-config-store');
require('./auth');
require('./keychain');

View File

@ -4,15 +4,13 @@ var ngModule = angular.module('woServices');
ngModule.service('invitation', Invitation); ngModule.service('invitation', Invitation);
module.exports = 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. * 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 * @param {Object} restDao The REST Data Access Object abstraction
*/ */
function Invitation(restDao) { function Invitation(restDao, appConfig) {
this._restDao = restDao; this._restDao = restDao;
this._restDao.setBaseUri(config.cloudUrl); this._restDao.setBaseUri(appConfig.config.cloudUrl);
} }
// //

View File

@ -16,18 +16,40 @@ var DB_PUBLICKEY = 'publickey',
* A high-level Data-Access Api for handling Keypair synchronization * A high-level Data-Access Api for handling Keypair synchronization
* between the cloud service and the device's local storage * between the cloud service and the device's local storage
*/ */
function Keychain(localDbDao, publicKeyDao, privateKeyDao, crypto, pgp) { function Keychain(lawnchairDAO, publicKey, privateKey, crypto, pgp, dialog, appConfig) {
this._localDbDao = localDbDao; this._lawnchairDAO = lawnchairDAO;
this._publicKeyDao = publicKeyDao; this._publicKeyDao = publicKey;
this._privateKeyDao = privateKeyDao; this._privateKeyDao = privateKey;
this._crypto = crypto; this._crypto = crypto;
this._pgp = pgp; this._pgp = pgp;
this._dialog = dialog;
this._appConfig = appConfig;
} }
// //
// Public key functions // 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 * Verifies the public key of a user o nthe public key store
* @param {String} uuid The uuid to verify the key * @param {String} uuid The uuid to verify the key
@ -196,7 +218,7 @@ Keychain.prototype.getReceiverPublicKey = function(userId, callback) {
var self = this; var self = this;
// search local keyring for public key // 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) { if (err) {
callback(err); callback(err);
return; return;
@ -275,7 +297,7 @@ Keychain.prototype.setDeviceName = function(deviceName, callback) {
return; 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) { Keychain.prototype.getDeviceName = function(callback) {
// check if deviceName is already persisted in storage // 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) { if (err) {
callback(err); callback(err);
return; return;
@ -308,7 +330,7 @@ Keychain.prototype.getDeviceSecret = function(callback) {
var self = this; var self = this;
// generate random deviceSecret or get from storage // 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) { if (err) {
callback(err); callback(err);
return; return;
@ -323,7 +345,7 @@ Keychain.prototype.getDeviceSecret = function(callback) {
// generate random deviceSecret // generate random deviceSecret
var deviceSecret = util.random(config.symKeySize); var deviceSecret = util.random(config.symKeySize);
// persist deviceSecret to local storage (in plaintext) // 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) { if (err) {
callback(err); callback(err);
return; return;
@ -763,7 +785,7 @@ Keychain.prototype.getUserKeyPair = function(userId, callback) {
var self = this; var self = this;
// search for user's public key locally // 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) { if (err) {
callback(err); callback(err);
return; return;
@ -886,7 +908,7 @@ Keychain.prototype.lookupPublicKey = function(id, callback) {
} }
// lookup in local storage // lookup in local storage
self._localDbDao.read(DB_PUBLICKEY + '_' + id, function(err, pubkey) { self._lawnchairDAO.read(DB_PUBLICKEY + '_' + id, function(err, pubkey) {
if (err) { if (err) {
callback(err); callback(err);
return; return;
@ -922,26 +944,26 @@ Keychain.prototype.lookupPublicKey = function(id, callback) {
*/ */
Keychain.prototype.listLocalPublicKeys = function(callback) { Keychain.prototype.listLocalPublicKeys = function(callback) {
// search local keyring for public key // 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) { 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) { Keychain.prototype.lookupPrivateKey = function(id, callback) {
// lookup in local storage // lookup in local storage
this._localDbDao.read(DB_PRIVATEKEY + '_' + id, callback); this._lawnchairDAO.read(DB_PRIVATEKEY + '_' + id, callback);
}; };
Keychain.prototype.saveLocalPublicKey = function(pubkey, callback) { Keychain.prototype.saveLocalPublicKey = function(pubkey, callback) {
// persist public key (email, _id) // persist public key (email, _id)
var pkLookupKey = DB_PUBLICKEY + '_' + pubkey._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) { Keychain.prototype.saveLocalPrivateKey = function(privkey, callback) {
// persist private key (email, _id) // persist private key (email, _id)
var prkLookupKey = DB_PRIVATEKEY + '_' + privkey._id; var prkLookupKey = DB_PRIVATEKEY + '_' + privkey._id;
this._localDbDao.persist(prkLookupKey, privkey, callback); this._lawnchairDAO.persist(prkLookupKey, privkey, callback);
}; };

View File

@ -15,25 +15,13 @@ function LawnchairDAO() {}
* Initialize the lawnchair database * Initialize the lawnchair database
* @param {String} dbName The name of the database * @param {String} dbName The name of the database
*/ */
LawnchairDAO.prototype.init = function(dbName, callback) { LawnchairDAO.prototype.init = function(dbName) {
if (!dbName) { if (!dbName) {
callback({ throw new Error('Lawnchair DB name must be specified!');
errMsg: 'Lawnchair DB name must be specified!'
});
return;
} }
this._db = new Lawnchair({ this._db = new Lawnchair({
name: dbName name: dbName
}, function(lc) {
if (!lc) {
callback({
errMsg: 'Lawnchair init failed!'
});
return;
}
callback();
}); });
}; };

View File

@ -4,11 +4,9 @@ var ngModule = angular.module('woServices');
ngModule.service('privateKey', PrivateKey); ngModule.service('privateKey', PrivateKey);
module.exports = PrivateKey; module.exports = PrivateKey;
var config = require('../app-config').config; function PrivateKey(restDao, appConfig) {
function PrivateKey(restDao) {
this._restDao = restDao; this._restDao = restDao;
this._restDao.setBaseUri(config.privkeyServerUrl); this._restDao.setBaseUri(appConfig.config.privkeyServerUrl);
} }
// //

View File

@ -4,11 +4,9 @@ var ngModule = angular.module('woServices');
ngModule.service('publicKey', PublicKey); ngModule.service('publicKey', PublicKey);
module.exports = PublicKey; module.exports = PublicKey;
var config = require('../app-config').config; function PublicKey(restDao, appConfig) {
function PublicKey(restDao) {
this._restDao = restDao; this._restDao = restDao;
this._restDao.setBaseUri(config.cloudUrl); this._restDao.setBaseUri(appConfig.config.cloudUrl);
} }
/** /**

View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
var ngModule = angular.module('woUtil'); var ngModule = angular.module('woUtil');
ngModule.service('updateHandler', ['deviceStorage', 'deviceStorage', 'auth', UpdateHandler]); ngModule.service('updateHandler', UpdateHandler);
module.exports = UpdateHandler; module.exports = UpdateHandler;
var axe = require('axe-logger'), var axe = require('axe-logger'),
@ -15,11 +15,12 @@ var axe = require('axe-logger'),
/** /**
* Handles database migration * Handles database migration
*/ */
function UpdateHandler(appConfigStorage, userStorage, auth) { function UpdateHandler(appConfigStore, deviceStorage, auth, dialog) {
this._appConfigStorage = appConfigStorage; this._appConfigStorage = appConfigStore;
this._userStorage = userStorage; this._userStorage = deviceStorage;
this._updateScripts = [updateV1, updateV2, updateV3, updateV4, updateV5]; this._updateScripts = [updateV1, updateV2, updateV3, updateV4, updateV5];
this._auth = auth; this._auth = auth;
this._dialog = dialog;
} }
/** /**
@ -99,14 +100,16 @@ UpdateHandler.prototype._applyUpdate = function(options, callback) {
/** /**
* Check application version and update correspondingly * Check application version and update correspondingly
*/ */
UpdateHandler.prototype.checkForUpdate = function(dialog) { UpdateHandler.prototype.checkForUpdate = function() {
var self = this;
// Chrome Packaged App // Chrome Packaged App
if (typeof window.chrome !== 'undefined' && chrome.runtime && chrome.runtime.onUpdateAvailable) { if (typeof window.chrome !== 'undefined' && chrome.runtime && chrome.runtime.onUpdateAvailable) {
// check for Chrome app update and restart // check for Chrome app update and restart
chrome.runtime.onUpdateAvailable.addListener(function(details) { chrome.runtime.onUpdateAvailable.addListener(function(details) {
axe.debug('New Chrome App update... requesting reload.'); axe.debug('New Chrome App update... requesting reload.');
// Chrome downloaded a new app version // Chrome downloaded a new app version
dialog({ self._dialog.confirm({
title: 'Update available', title: 'Update available',
message: 'A new version ' + details.version + ' of the app is available. Restart the app to update?', message: 'A new version ' + details.version + ' of the app is available. Restart the app to update?',
positiveBtnStr: 'Restart', positiveBtnStr: 'Restart',