mirror of
https://github.com/moparisthebest/mail
synced 2024-11-25 18:32:20 -05:00
commit
c0ee4a7db7
97
Gruntfile.js
97
Gruntfile.js
@ -155,46 +155,49 @@ module.exports = function(grunt) {
|
||||
unitTest: {
|
||||
files: {
|
||||
'test/unit/index.browserified.js': [
|
||||
'test/unit/oauth-test.js',
|
||||
'test/unit/auth-test.js',
|
||||
'test/unit/email-dao-test.js',
|
||||
'test/unit/app-controller-test.js',
|
||||
'test/unit/pgp-test.js',
|
||||
'test/unit/crypto-test.js',
|
||||
'test/unit/backbutton-handler-test.js',
|
||||
'test/unit/rest-dao-test.js',
|
||||
'test/unit/admin-dao-test.js',
|
||||
'test/unit/publickey-dao-test.js',
|
||||
'test/unit/privatekey-dao-test.js',
|
||||
'test/unit/lawnchair-dao-test.js',
|
||||
'test/unit/keychain-dao-test.js',
|
||||
'test/unit/devicestorage-dao-test.js',
|
||||
'test/unit/newsletter-service-test.js',
|
||||
'test/unit/mail-config-service-test.js',
|
||||
'test/unit/dialog-ctrl-test.js',
|
||||
'test/unit/add-account-ctrl-test.js',
|
||||
'test/unit/create-account-ctrl-test.js',
|
||||
'test/unit/validate-phone-ctrl-test.js',
|
||||
'test/unit/account-ctrl-test.js',
|
||||
'test/unit/set-passphrase-ctrl-test.js',
|
||||
'test/unit/contacts-ctrl-test.js',
|
||||
'test/unit/login-existing-ctrl-test.js',
|
||||
'test/unit/login-initial-ctrl-test.js',
|
||||
'test/unit/login-new-device-ctrl-test.js',
|
||||
'test/unit/login-privatekey-download-ctrl-test.js',
|
||||
'test/unit/login-set-credentials-ctrl-test.js',
|
||||
'test/unit/privatekey-upload-ctrl-test.js',
|
||||
'test/unit/login-ctrl-test.js',
|
||||
'test/unit/read-ctrl-test.js',
|
||||
'test/unit/navigation-ctrl-test.js',
|
||||
'test/unit/mail-list-ctrl-test.js',
|
||||
'test/unit/write-ctrl-test.js',
|
||||
'test/unit/action-bar-ctrl-test.js',
|
||||
'test/unit/outbox-bo-test.js',
|
||||
'test/unit/invitation-dao-test.js',
|
||||
'test/unit/update-handler-test.js',
|
||||
'test/unit/connection-doctor-test.js',
|
||||
'test/main.js'
|
||||
'test/main.js',
|
||||
'test/unit/util/dialog-test.js',
|
||||
'test/unit/util/connection-doctor-test.js',
|
||||
'test/unit/util/update-handler-test.js',
|
||||
'test/unit/util/backbutton-handler-test.js',
|
||||
'test/unit/util/status-display-test.js',
|
||||
'test/unit/crypto/pgp-test.js',
|
||||
'test/unit/crypto/crypto-test.js',
|
||||
'test/unit/service/rest-dao-test.js',
|
||||
'test/unit/service/admin-dao-test.js',
|
||||
'test/unit/service/auth-test.js',
|
||||
'test/unit/service/oauth-test.js',
|
||||
'test/unit/service/publickey-dao-test.js',
|
||||
'test/unit/service/privatekey-dao-test.js',
|
||||
'test/unit/service/lawnchair-dao-test.js',
|
||||
'test/unit/service/keychain-dao-test.js',
|
||||
'test/unit/service/devicestorage-dao-test.js',
|
||||
'test/unit/service/newsletter-service-test.js',
|
||||
'test/unit/service/mail-config-service-test.js',
|
||||
'test/unit/service/invitation-dao-test.js',
|
||||
'test/unit/email/outbox-bo-test.js',
|
||||
'test/unit/email/email-dao-test.js',
|
||||
'test/unit/email/account-test.js',
|
||||
'test/unit/email/search-test.js',
|
||||
'test/unit/controller/login/add-account-ctrl-test.js',
|
||||
'test/unit/controller/login/create-account-ctrl-test.js',
|
||||
'test/unit/controller/login/validate-phone-ctrl-test.js',
|
||||
'test/unit/controller/login/login-existing-ctrl-test.js',
|
||||
'test/unit/controller/login/login-initial-ctrl-test.js',
|
||||
'test/unit/controller/login/login-new-device-ctrl-test.js',
|
||||
'test/unit/controller/login/login-privatekey-download-ctrl-test.js',
|
||||
'test/unit/controller/login/login-set-credentials-ctrl-test.js',
|
||||
'test/unit/controller/login/login-ctrl-test.js',
|
||||
'test/unit/controller/app/dialog-ctrl-test.js',
|
||||
'test/unit/controller/app/privatekey-upload-ctrl-test.js',
|
||||
'test/unit/controller/app/account-ctrl-test.js',
|
||||
'test/unit/controller/app/set-passphrase-ctrl-test.js',
|
||||
'test/unit/controller/app/contacts-ctrl-test.js',
|
||||
'test/unit/controller/app/read-ctrl-test.js',
|
||||
'test/unit/controller/app/navigation-ctrl-test.js',
|
||||
'test/unit/controller/app/mail-list-ctrl-test.js',
|
||||
'test/unit/controller/app/write-ctrl-test.js',
|
||||
'test/unit/controller/app/action-bar-ctrl-test.js',
|
||||
]
|
||||
},
|
||||
options: browserifyOpt
|
||||
@ -202,8 +205,8 @@ module.exports = function(grunt) {
|
||||
integrationTest: {
|
||||
files: {
|
||||
'test/integration/index.browserified.js': [
|
||||
'test/integration/email-dao-test.js',
|
||||
'test/main.js'
|
||||
'test/main.js',
|
||||
'test/integration/email-dao-test.js'
|
||||
]
|
||||
},
|
||||
options: browserifyOpt
|
||||
@ -256,7 +259,7 @@ module.exports = function(grunt) {
|
||||
readSandbox: {
|
||||
src: [
|
||||
'node_modules/dompurify/purify.js',
|
||||
'src/js/controller/read-sandbox.js'
|
||||
'src/js/controller/app/read-sandbox.js'
|
||||
],
|
||||
dest: 'dist/js/read-sandbox.min.js'
|
||||
},
|
||||
@ -278,7 +281,6 @@ module.exports = function(grunt) {
|
||||
'node_modules/jquery/dist/jquery.min.js',
|
||||
'src/lib/angular/angular.js',
|
||||
'src/lib/angular/angular-route.js',
|
||||
'src/lib/angular/angular-animate.js',
|
||||
'src/lib/angular/angular-mocks.js',
|
||||
'src/lib/lawnchair/lawnchair-git.js',
|
||||
'src/lib/lawnchair/lawnchair-adapter-webkit-sqlite-git.js',
|
||||
@ -293,6 +295,9 @@ module.exports = function(grunt) {
|
||||
integrationTest: {
|
||||
src: [
|
||||
'src/lib/underscore/underscore.js',
|
||||
'node_modules/jquery/dist/jquery.min.js',
|
||||
'src/lib/angular/angular.js',
|
||||
'src/lib/angular/angular-mocks.js',
|
||||
'src/lib/lawnchair/lawnchair-git.js',
|
||||
'src/lib/lawnchair/lawnchair-adapter-webkit-sqlite-git.js',
|
||||
'src/lib/lawnchair/lawnchair-adapter-indexed-db-git.js',
|
||||
@ -446,11 +451,11 @@ module.exports = function(grunt) {
|
||||
tasks: ['dist-js-app']
|
||||
},
|
||||
jsUnitTest: {
|
||||
files: ['test/unit/*-test.js'],
|
||||
files: ['test/unit/**/*-test.js', 'test/*.js'],
|
||||
tasks: ['dist-js-unitTest']
|
||||
},
|
||||
jsIntegrationTest: {
|
||||
files: ['test/integration/*-test.js'],
|
||||
files: ['test/integration/*-test.js', 'test/*.js'],
|
||||
tasks: ['dist-js-integrationTest']
|
||||
},
|
||||
icons: {
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html ng-app="mail" ng-csp manifest="appcache.manifest">
|
||||
<html ng-csp manifest="appcache.manifest">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Whiteout Mail</title>
|
||||
|
@ -1,9 +1,17 @@
|
||||
'use strict';
|
||||
|
||||
var appCfg = {};
|
||||
|
||||
var ngModule = angular.module('woAppConfig', []);
|
||||
ngModule.factory('appConfig', function() {
|
||||
return appCfg;
|
||||
});
|
||||
module.exports = appCfg;
|
||||
|
||||
/**
|
||||
* Global app configurations
|
||||
*/
|
||||
exports.config = {
|
||||
appCfg.config = {
|
||||
cloudUrl: 'https://keys.whiteout.io',
|
||||
privkeyServerUrl: 'https://keychain.whiteout.io',
|
||||
adminUrl: 'https://admin-node.whiteout.io',
|
||||
@ -38,7 +46,7 @@ if (typeof chrome !== 'undefined' && chrome.runtime && chrome.runtime.getManifes
|
||||
}
|
||||
|
||||
function setConfigParams(manifest) {
|
||||
var cfg = exports.config;
|
||||
var cfg = appCfg.config;
|
||||
|
||||
function getUrl(beginsWith) {
|
||||
return _.find(manifest.permissions, function(permission) {
|
||||
@ -57,7 +65,7 @@ function setConfigParams(manifest) {
|
||||
/**
|
||||
* Strings are maintained here
|
||||
*/
|
||||
exports.string = {
|
||||
appCfg.string = {
|
||||
fallbackSubject: '(no subject)',
|
||||
invitationSubject: 'Invitation to a private conversation',
|
||||
invitationMessage: 'Hi,\n\nI use Whiteout Mail to send and receive encrypted email. I would like to exchange encrypted messages with you as well.\n\nPlease install the Whiteout Mail application. This application makes it easy to read and write messages securely with PGP encryption applied.\n\nGo to the Whiteout Networks homepage to learn more and to download the application: https://whiteout.io\n\n',
|
||||
|
@ -1,307 +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;
|
||||
|
||||
// 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.');
|
||||
|
||||
ctrl.buildModules();
|
||||
|
||||
// Handle offline and online gracefully
|
||||
window.addEventListener('online', ctrl.onConnect.bind(ctrl, ctrl.onError));
|
||||
window.addEventListener('offline', ctrl.onDisconnect.bind(ctrl));
|
||||
|
||||
ctrl._appConfigStore.init('app-config', callback);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the dependency tree.
|
||||
*/
|
||||
ctrl.buildModules = function() {
|
||||
var lawnchairDao, restDao, pubkeyDao, privkeyDao, crypto, emailDao, keychain, pgp, userStorage, pgpbuilder, oauth, appConfigStore, auth;
|
||||
|
||||
// start the mailreader's worker thread
|
||||
mailreader.startWorker(config.workerPath + '/mailreader-parser-worker.min.js');
|
||||
|
||||
// init objects and inject dependencies
|
||||
restDao = new RestDAO();
|
||||
lawnchairDao = new LawnchairDAO();
|
||||
pubkeyDao = new PublicKeyDAO(restDao);
|
||||
privkeyDao = new PrivateKeyDAO(new RestDAO(config.privkeyServerUrl));
|
||||
oauth = new OAuth(new RestDAO('https://www.googleapis.com'));
|
||||
|
||||
crypto = new Crypto();
|
||||
ctrl._pgp = pgp = new PGP();
|
||||
ctrl._keychain = keychain = new KeychainDAO(lawnchairDao, pubkeyDao, privkeyDao, crypto, pgp);
|
||||
keychain.requestPermissionForKeyUpdate = function(params, callback) {
|
||||
var message = params.newKey ? str.updatePublicKeyMsgNewKey : str.updatePublicKeyMsgRemovedKey;
|
||||
message = message.replace('{0}', params.userId);
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
/**
|
||||
* 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;
|
||||
};
|
||||
|
||||
/**
|
||||
* Event handler that is called when the user agent goes offline.
|
||||
*/
|
||||
ctrl.onDisconnect = function() {
|
||||
ctrl._emailDao.onDisconnect();
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 = '/';
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 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;
|
@ -14,36 +14,49 @@ if (typeof window.applicationCache !== 'undefined') {
|
||||
};
|
||||
}
|
||||
|
||||
var DialogCtrl = require('./controller/dialog'),
|
||||
AddAccountCtrl = require('./controller/add-account'),
|
||||
CreateAccountCtrl = require('./controller/create-account'),
|
||||
ValidatePhoneCtrl = require('./controller/validate-phone'),
|
||||
AccountCtrl = require('./controller/account'),
|
||||
SetPassphraseCtrl = require('./controller/set-passphrase'),
|
||||
PrivateKeyUploadCtrl = require('./controller/privatekey-upload'),
|
||||
ContactsCtrl = require('./controller/contacts'),
|
||||
AboutCtrl = require('./controller/about'),
|
||||
LoginCtrl = require('./controller/login'),
|
||||
LoginInitialCtrl = require('./controller/login-initial'),
|
||||
LoginNewDeviceCtrl = require('./controller/login-new-device'),
|
||||
LoginExistingCtrl = require('./controller/login-existing'),
|
||||
LoginPrivateKeyDownloadCtrl = require('./controller/login-privatekey-download'),
|
||||
LoginSetCredentialsCtrl = require('./controller/login-set-credentials'),
|
||||
MailListCtrl = require('./controller/mail-list'),
|
||||
ReadCtrl = require('./controller/read'),
|
||||
WriteCtrl = require('./controller/write'),
|
||||
NavigationCtrl = require('./controller/navigation'),
|
||||
ActionBarCtrl = require('./controller/action-bar'),
|
||||
errorUtil = require('./util/error'),
|
||||
var axe = require('axe-logger'),
|
||||
AddAccountCtrl = require('./controller/login/add-account'),
|
||||
CreateAccountCtrl = require('./controller/login/create-account'),
|
||||
ValidatePhoneCtrl = require('./controller/login/validate-phone'),
|
||||
LoginCtrl = require('./controller/login/login'),
|
||||
LoginInitialCtrl = require('./controller/login/login-initial'),
|
||||
LoginNewDeviceCtrl = require('./controller/login/login-new-device'),
|
||||
LoginExistingCtrl = require('./controller/login/login-existing'),
|
||||
LoginPrivateKeyDownloadCtrl = require('./controller/login/login-privatekey-download'),
|
||||
LoginSetCredentialsCtrl = require('./controller/login/login-set-credentials'),
|
||||
DialogCtrl = require('./controller/app/dialog'),
|
||||
AccountCtrl = require('./controller/app/account'),
|
||||
SetPassphraseCtrl = require('./controller/app/set-passphrase'),
|
||||
PrivateKeyUploadCtrl = require('./controller/app/privatekey-upload'),
|
||||
ContactsCtrl = require('./controller/app/contacts'),
|
||||
AboutCtrl = require('./controller/app/about'),
|
||||
MailListCtrl = require('./controller/app/mail-list'),
|
||||
ReadCtrl = require('./controller/app/read'),
|
||||
WriteCtrl = require('./controller/app/write'),
|
||||
NavigationCtrl = require('./controller/app/navigation'),
|
||||
ActionBarCtrl = require('./controller/app/action-bar'),
|
||||
StatusDisplayCtrl = require('./controller/app/status-display'),
|
||||
backButtonUtil = require('./util/backbutton-handler');
|
||||
require('./directive/common'),
|
||||
require('./service/newsletter'),
|
||||
require('./service/mail-config');
|
||||
|
||||
// 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',
|
||||
@ -51,10 +64,7 @@ var app = angular.module('mail', [
|
||||
'contacts',
|
||||
'login-new-device',
|
||||
'privatekey-upload',
|
||||
'infinite-scroll',
|
||||
'ngTagsInput',
|
||||
'woDirectives',
|
||||
'woServices'
|
||||
'infinite-scroll'
|
||||
]);
|
||||
|
||||
// set router paths
|
||||
@ -111,9 +121,6 @@ app.run(function($rootScope) {
|
||||
// global state... inherited to all child scopes
|
||||
$rootScope.state = {};
|
||||
|
||||
// attach global error handler
|
||||
errorUtil.attachHandler($rootScope);
|
||||
|
||||
// attach the back button handler to the root scope
|
||||
backButtonUtil.attachHandler($rootScope);
|
||||
|
||||
@ -131,4 +138,26 @@ app.controller('PrivateKeyUploadCtrl', PrivateKeyUploadCtrl);
|
||||
app.controller('ContactsCtrl', ContactsCtrl);
|
||||
app.controller('AboutCtrl', AboutCtrl);
|
||||
app.controller('DialogCtrl', DialogCtrl);
|
||||
app.controller('ActionBarCtrl', ActionBarCtrl);
|
||||
app.controller('ActionBarCtrl', ActionBarCtrl);
|
||||
app.controller('StatusDisplayCtrl', StatusDisplayCtrl);
|
||||
|
||||
//
|
||||
// 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']);
|
||||
});
|
||||
}
|
@ -1,12 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
var cfg = require('../app-config').config;
|
||||
|
||||
//
|
||||
// Controller
|
||||
//
|
||||
|
||||
var AboutCtrl = function($scope) {
|
||||
var AboutCtrl = function($scope, appConfig) {
|
||||
|
||||
$scope.state.about = {
|
||||
toggle: function(to) {
|
||||
@ -18,7 +12,7 @@ var AboutCtrl = function($scope) {
|
||||
// scope variables
|
||||
//
|
||||
|
||||
$scope.version = cfg.appVersion + ' (beta)';
|
||||
$scope.version = appConfig.config.appVersion + ' (beta)';
|
||||
$scope.date = new Date();
|
||||
|
||||
//
|
@ -1,18 +1,10 @@
|
||||
'use strict';
|
||||
|
||||
var appController = require('../app-controller'),
|
||||
dl = require('../util/download'),
|
||||
config = require('../app-config').config,
|
||||
pgp, keychain, userId;
|
||||
|
||||
//
|
||||
// Controller
|
||||
//
|
||||
|
||||
var AccountCtrl = function($scope) {
|
||||
userId = appController._emailDao._account.emailAddress;
|
||||
keychain = appController._keychain;
|
||||
pgp = appController._pgp;
|
||||
var AccountCtrl = function($scope, auth, keychain, pgp, appConfig, download, dialog) {
|
||||
var userId = auth.emailAddress;
|
||||
if (!userId) {
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.state.account = {
|
||||
toggle: function(to) {
|
||||
@ -31,7 +23,7 @@ var AccountCtrl = function($scope) {
|
||||
var fpr = keyParams.fingerprint;
|
||||
$scope.fingerprint = fpr.slice(0, 4) + ' ' + fpr.slice(4, 8) + ' ' + fpr.slice(8, 12) + ' ' + fpr.slice(12, 16) + ' ' + fpr.slice(16, 20) + ' ' + fpr.slice(20, 24) + ' ' + fpr.slice(24, 28) + ' ' + fpr.slice(28, 32) + ' ' + fpr.slice(32, 36) + ' ' + fpr.slice(36);
|
||||
$scope.keysize = keyParams.bitSize;
|
||||
$scope.publicKeyUrl = config.cloudUrl + '/' + userId;
|
||||
$scope.publicKeyUrl = appConfig.config.cloudUrl + '/' + userId;
|
||||
|
||||
//
|
||||
// scope functions
|
||||
@ -40,14 +32,14 @@ var AccountCtrl = function($scope) {
|
||||
$scope.exportKeyFile = function() {
|
||||
keychain.getUserKeyPair(userId, function(err, keys) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
dialog.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
var keyId = keys.publicKey._id;
|
||||
var file = 'whiteout_mail_' + userId + '_' + keyId.substring(8, keyId.length);
|
||||
|
||||
dl.createDownload({
|
||||
download.createDownload({
|
||||
content: keys.publicKey.publicKey + '\r\n' + keys.privateKey.encryptedKey,
|
||||
filename: file + '.asc',
|
||||
contentType: 'text/plain'
|
@ -1,15 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
var appController = require('../app-controller'),
|
||||
emailDao;
|
||||
|
||||
//
|
||||
// Controller
|
||||
//
|
||||
|
||||
var ActionBarCtrl = function($scope) {
|
||||
|
||||
emailDao = appController._emailDao;
|
||||
var ActionBarCtrl = function($scope, email, dialog, statusDisplay) {
|
||||
|
||||
/**
|
||||
* Move a single message from the currently selected folder to another folder
|
||||
@ -24,9 +15,9 @@ var ActionBarCtrl = function($scope) {
|
||||
// close read state
|
||||
$scope.state.read.open = false;
|
||||
|
||||
$scope.state.mailList.updateStatus('Moving message...');
|
||||
statusDisplay.update('Moving message...');
|
||||
|
||||
emailDao.moveMessage({
|
||||
email.moveMessage({
|
||||
folder: currentFolder(),
|
||||
destination: destination,
|
||||
message: message
|
||||
@ -35,14 +26,14 @@ var ActionBarCtrl = function($scope) {
|
||||
// show errors where appropriate
|
||||
if (err.code === 42) {
|
||||
$scope.select(message);
|
||||
$scope.state.mailList.updateStatus('Unable to move message in offline mode!');
|
||||
statusDisplay.update('Unable to move message in offline mode!');
|
||||
return;
|
||||
}
|
||||
$scope.state.mailList.updateStatus('Error during move!');
|
||||
$scope.onError(err);
|
||||
statusDisplay.update('Error during move!');
|
||||
dialog.error(err);
|
||||
return;
|
||||
}
|
||||
$scope.state.mailList.updateStatus('Message moved.');
|
||||
statusDisplay.update('Message moved.');
|
||||
$scope.$apply();
|
||||
});
|
||||
};
|
||||
@ -70,9 +61,9 @@ var ActionBarCtrl = function($scope) {
|
||||
// close read state
|
||||
$scope.state.read.open = false;
|
||||
|
||||
$scope.state.mailList.updateStatus('Deleting message...');
|
||||
statusDisplay.update('Deleting message...');
|
||||
|
||||
emailDao.deleteMessage({
|
||||
email.deleteMessage({
|
||||
folder: currentFolder(),
|
||||
message: message
|
||||
}, function(err) {
|
||||
@ -80,14 +71,14 @@ var ActionBarCtrl = function($scope) {
|
||||
// show errors where appropriate
|
||||
if (err.code === 42) {
|
||||
$scope.select(message);
|
||||
$scope.state.mailList.updateStatus('Unable to delete message in offline mode!');
|
||||
statusDisplay.update('Unable to delete message in offline mode!');
|
||||
return;
|
||||
}
|
||||
$scope.state.mailList.updateStatus('Error during delete!');
|
||||
$scope.onError(err);
|
||||
statusDisplay.update('Error during delete!');
|
||||
dialog.error(err);
|
||||
return;
|
||||
}
|
||||
$scope.state.mailList.updateStatus('Message deleted.');
|
||||
statusDisplay.update('Message deleted.');
|
||||
$scope.$apply();
|
||||
});
|
||||
};
|
||||
@ -105,36 +96,38 @@ var ActionBarCtrl = function($scope) {
|
||||
* @param {Object} message The message to be marked
|
||||
* @param {boolean} unread If the message should be marked as read or unread
|
||||
*/
|
||||
$scope.markMessage = function(message, unread) {
|
||||
$scope.markMessage = function(message, unread, keepOpen) {
|
||||
if (!message) {
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.state.mailList.updateStatus('Updating unread flag...');
|
||||
statusDisplay.update('Updating unread flag...');
|
||||
|
||||
// close read state
|
||||
$scope.state.read.open = false;
|
||||
if (!keepOpen) {
|
||||
$scope.state.read.open = false;
|
||||
}
|
||||
|
||||
var originalState = message.unread;
|
||||
message.unread = unread;
|
||||
emailDao.setFlags({
|
||||
email.setFlags({
|
||||
folder: currentFolder(),
|
||||
message: message
|
||||
}, function(err) {
|
||||
if (err && err.code === 42) {
|
||||
// offline, restore
|
||||
message.unread = originalState;
|
||||
$scope.state.mailList.updateStatus('Unable to mark message in offline mode!');
|
||||
statusDisplay.update('Unable to mark message in offline mode!');
|
||||
return;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
$scope.state.mailList.updateStatus('Error on sync!');
|
||||
$scope.onError(err);
|
||||
statusDisplay.update('Error on sync!');
|
||||
dialog.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.state.mailList.updateStatus('Online');
|
||||
statusDisplay.update('Online');
|
||||
$scope.$apply();
|
||||
});
|
||||
};
|
@ -1,15 +1,10 @@
|
||||
'use strict';
|
||||
|
||||
var appController = require('../app-controller'),
|
||||
keychain, pgp;
|
||||
|
||||
//
|
||||
// Controller
|
||||
//
|
||||
|
||||
var ContactsCtrl = function($scope) {
|
||||
keychain = appController._keychain,
|
||||
pgp = appController._pgp;
|
||||
var ContactsCtrl = function($scope, keychain, pgp, dialog) {
|
||||
|
||||
$scope.state.contacts = {
|
||||
toggle: function(to) {
|
||||
@ -26,7 +21,7 @@ var ContactsCtrl = function($scope) {
|
||||
$scope.listKeys = function() {
|
||||
keychain.listLocalPublicKeys(function(err, keys) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
dialog.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -54,7 +49,7 @@ var ContactsCtrl = function($scope) {
|
||||
|
||||
// verifiy public key string
|
||||
if (publicKeyArmored.indexOf('-----BEGIN PGP PUBLIC KEY BLOCK-----') < 0) {
|
||||
$scope.onError({
|
||||
dialog.error({
|
||||
showBugReporter: false,
|
||||
message: 'Invalid public key!'
|
||||
});
|
||||
@ -64,7 +59,7 @@ var ContactsCtrl = function($scope) {
|
||||
try {
|
||||
keyParams = pgp.getKeyParams(publicKeyArmored);
|
||||
} catch (e) {
|
||||
$scope.onError(new Error('Error reading public key params!'));
|
||||
dialog.error(new Error('Error reading public key params!'));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -78,7 +73,7 @@ var ContactsCtrl = function($scope) {
|
||||
|
||||
keychain.saveLocalPublicKey(pubkey, function(err) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
dialog.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -90,7 +85,7 @@ var ContactsCtrl = function($scope) {
|
||||
$scope.removeKey = function(key) {
|
||||
keychain.removeLocalPublicKey(key._id, function(err) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
dialog.error(err);
|
||||
return;
|
||||
}
|
||||
|
62
src/js/controller/app/dialog.js
Normal file
62
src/js/controller/app/dialog.js
Normal file
@ -0,0 +1,62 @@
|
||||
'use strict';
|
||||
|
||||
var DialogCtrl = function($scope, $timeout, dialog) {
|
||||
|
||||
$scope.state.dialog = {
|
||||
open: false
|
||||
};
|
||||
|
||||
//
|
||||
// Set dialog disply functions
|
||||
//
|
||||
|
||||
dialog.displayInfo = function(options) {
|
||||
return $timeout(function() {
|
||||
setOptions(options);
|
||||
});
|
||||
};
|
||||
|
||||
dialog.displayError = function(options) {
|
||||
return $timeout(function() {
|
||||
if (!options) {
|
||||
return;
|
||||
}
|
||||
|
||||
setOptions(options);
|
||||
$scope.title = options.title || 'Error';
|
||||
$scope.showBugReporter = (typeof options.showBugReporter !== 'undefined' ? options.showBugReporter : !options.title); // if title is set, presume it's not an error by default
|
||||
});
|
||||
};
|
||||
|
||||
dialog.displayConfirm = function(options) {
|
||||
return $timeout(function() {
|
||||
setOptions(options);
|
||||
});
|
||||
};
|
||||
|
||||
function setOptions(options) {
|
||||
$scope.state.dialog.open = true;
|
||||
$scope.title = options.title;
|
||||
$scope.message = options.errMsg || options.message;
|
||||
$scope.faqLink = options.faqLink;
|
||||
$scope.positiveBtnStr = options.positiveBtnStr || 'Ok';
|
||||
$scope.negativeBtnStr = options.negativeBtnStr || 'Cancel';
|
||||
$scope.showNegativeBtn = options.showNegativeBtn || false;
|
||||
$scope.callback = options.callback;
|
||||
}
|
||||
|
||||
//
|
||||
// Scope functions
|
||||
//
|
||||
|
||||
$scope.confirm = function(ok) {
|
||||
$scope.state.dialog.open = false;
|
||||
|
||||
if ($scope.callback) {
|
||||
$scope.callback(ok);
|
||||
}
|
||||
$scope.callback = undefined;
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = DialogCtrl;
|
@ -1,8 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
var appController = require('../app-controller'),
|
||||
notification = require('../util/notification'),
|
||||
emailDao, outboxBo, keychainDao, searchTimeout, firstSelect;
|
||||
var searchTimeout, firstSelect;
|
||||
|
||||
//
|
||||
// Constants
|
||||
@ -13,14 +11,13 @@ var INIT_DISPLAY_LEN = 20,
|
||||
FOLDER_TYPE_INBOX = 'Inbox',
|
||||
NOTIFICATION_INBOX_TIMEOUT = 5000;
|
||||
|
||||
var MailListCtrl = function($scope, $routeParams) {
|
||||
var MailListCtrl = function($scope, $timeout, $routeParams, statusDisplay, notification, email, keychain, dialog, search, dummy) {
|
||||
|
||||
//
|
||||
// Init
|
||||
//
|
||||
|
||||
emailDao = appController._emailDao;
|
||||
outboxBo = appController._outboxBo;
|
||||
keychainDao = appController._keychain;
|
||||
$scope.state.mailList = {};
|
||||
|
||||
/**
|
||||
* Gathers unread notifications to be cancelled later
|
||||
@ -31,39 +28,39 @@ var MailListCtrl = function($scope, $routeParams) {
|
||||
// scope functions
|
||||
//
|
||||
|
||||
$scope.getBody = function(email) {
|
||||
emailDao.getBody({
|
||||
$scope.getBody = function(message) {
|
||||
email.getBody({
|
||||
folder: currentFolder(),
|
||||
message: email
|
||||
message: message
|
||||
}, function(err) {
|
||||
if (err && err.code !== 42) {
|
||||
$scope.onError(err);
|
||||
dialog.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// display fetched body
|
||||
$scope.$digest();
|
||||
|
||||
// automatically decrypt if it's the selected email
|
||||
if (email === currentMessage()) {
|
||||
emailDao.decryptBody({
|
||||
message: email
|
||||
}, $scope.onError);
|
||||
// automatically decrypt if it's the selected message
|
||||
if (message === currentMessage()) {
|
||||
email.decryptBody({
|
||||
message: message
|
||||
}, dialog.error);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when clicking on an email list item
|
||||
* Called when clicking on an message list item
|
||||
*/
|
||||
$scope.select = function(email) {
|
||||
$scope.select = function(message) {
|
||||
// unselect an item
|
||||
if (!email) {
|
||||
if (!message) {
|
||||
$scope.state.mailList.selected = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.state.mailList.selected = email;
|
||||
$scope.state.mailList.selected = message;
|
||||
|
||||
if (!firstSelect) {
|
||||
// only toggle to read view on 2nd select in mobile mode
|
||||
@ -71,22 +68,22 @@ var MailListCtrl = function($scope, $routeParams) {
|
||||
}
|
||||
firstSelect = false;
|
||||
|
||||
keychainDao.refreshKeyForUserId({
|
||||
userId: email.from[0].address
|
||||
keychain.refreshKeyForUserId({
|
||||
userId: message.from[0].address
|
||||
}, onKeyRefreshed);
|
||||
|
||||
function onKeyRefreshed(err) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
dialog.error(err);
|
||||
}
|
||||
|
||||
emailDao.decryptBody({
|
||||
message: email
|
||||
}, $scope.onError);
|
||||
email.decryptBody({
|
||||
message: message
|
||||
}, dialog.error);
|
||||
|
||||
// if the email is unread, please sync the new state.
|
||||
// if the message is unread, please sync the new state.
|
||||
// otherweise forget about it.
|
||||
if (!email.unread) {
|
||||
if (!message.unread) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -97,21 +94,16 @@ var MailListCtrl = function($scope, $routeParams) {
|
||||
}
|
||||
}
|
||||
|
||||
$scope.state.actionBar.markMessage(email, false);
|
||||
$scope.state.actionBar.markMessage(message, false, true);
|
||||
}
|
||||
};
|
||||
|
||||
// share local scope functions with root state
|
||||
$scope.state.mailList = {
|
||||
updateStatus: updateStatus
|
||||
};
|
||||
|
||||
//
|
||||
// watch tasks
|
||||
//
|
||||
|
||||
/**
|
||||
* List emails from folder when user changes folder
|
||||
* List messages from folder when user changes folder
|
||||
*/
|
||||
$scope._stopWatchTask = $scope.$watch('state.nav.currentFolder', function() {
|
||||
if (!currentFolder()) {
|
||||
@ -123,8 +115,8 @@ var MailListCtrl = function($scope, $routeParams) {
|
||||
|
||||
// in development, display dummy mail objects
|
||||
if ($routeParams.dev) {
|
||||
updateStatus('Last update: ', new Date());
|
||||
currentFolder().messages = createDummyMails();
|
||||
statusDisplay.update('Last update: ', new Date());
|
||||
currentFolder().messages = dummy.listMails();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -183,104 +175,37 @@ var MailListCtrl = function($scope, $routeParams) {
|
||||
if (!searchText) {
|
||||
// set display buffer to first messages
|
||||
$scope.displayMessages = currentFolder().messages.slice(0, INIT_DISPLAY_LEN);
|
||||
setSearching(false);
|
||||
updateStatus('Online');
|
||||
statusDisplay.setSearching(false);
|
||||
statusDisplay.update('Online');
|
||||
return;
|
||||
}
|
||||
|
||||
// display searching spinner
|
||||
setSearching(true);
|
||||
updateStatus('Searching ...');
|
||||
statusDisplay.setSearching(true);
|
||||
statusDisplay.update('Searching ...');
|
||||
searchTimeout = setTimeout(function() {
|
||||
$scope.$apply(function() {
|
||||
// filter relevant messages
|
||||
$scope.displayMessages = $scope.search(currentFolder().messages, searchText);
|
||||
setSearching(false);
|
||||
updateStatus('Matches in this folder');
|
||||
$scope.displayMessages = search.filter(currentFolder().messages, searchText);
|
||||
statusDisplay.setSearching(false);
|
||||
statusDisplay.update('Matches in this folder');
|
||||
});
|
||||
}, 500);
|
||||
};
|
||||
|
||||
/**
|
||||
* Do full text search on messages. Parse meta data first
|
||||
*/
|
||||
$scope.search = function(messages, searchText) {
|
||||
// don't filter on empty searchText
|
||||
if (!searchText) {
|
||||
return messages;
|
||||
}
|
||||
|
||||
// escape search string
|
||||
searchText = searchText.replace(/([.*+?^${}()|\[\]\/\\])/g, "\\$1");
|
||||
// compare all strings (case insensitive)
|
||||
var regex = new RegExp(searchText, 'i');
|
||||
|
||||
function contains(input) {
|
||||
if (!input) {
|
||||
return false;
|
||||
}
|
||||
return regex.test(input);
|
||||
}
|
||||
|
||||
function checkAddresses(header) {
|
||||
if (!header || !header.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < header.length; i++) {
|
||||
if (contains(header[i].name) || contains(header[i].address)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter meta data first and then only look at plaintext and decrypted message bodies
|
||||
*/
|
||||
function matchMetaDataFirst(m) {
|
||||
// compare subject
|
||||
if (contains(m.subject)) {
|
||||
return true;
|
||||
}
|
||||
// compares address headers
|
||||
if (checkAddresses(m.from) || checkAddresses(m.to) || checkAddresses(m.cc) || checkAddresses(m.bcc)) {
|
||||
return true;
|
||||
}
|
||||
// compare plaintext body
|
||||
if (m.body && !m.encrypted && contains(m.body)) {
|
||||
return true;
|
||||
}
|
||||
// compare decrypted body
|
||||
if (m.body && m.encrypted && m.decrypted && contains(m.body)) {
|
||||
return true;
|
||||
}
|
||||
// compare plaintex html body
|
||||
if (m.html && !m.encrypted && contains(m.html)) {
|
||||
return true;
|
||||
}
|
||||
// compare decrypted html body
|
||||
if (m.html && m.encrypted && m.decrypted && contains(m.html)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// user native js Array.filter
|
||||
return messages.filter(matchMetaDataFirst);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sync current folder when client comes back online
|
||||
*/
|
||||
$scope.watchOnline = $scope.$watch('account.online', function(isOnline) {
|
||||
if (isOnline) {
|
||||
updateStatus('Online');
|
||||
openCurrentFolder();
|
||||
} else {
|
||||
updateStatus('Offline mode');
|
||||
}
|
||||
// wait one cycle for the status display controllers to init
|
||||
$timeout(function() {
|
||||
if (isOnline) {
|
||||
statusDisplay.update('Online');
|
||||
openCurrentFolder();
|
||||
} else {
|
||||
statusDisplay.update('Offline mode');
|
||||
}
|
||||
});
|
||||
}, true);
|
||||
|
||||
//
|
||||
@ -292,7 +217,7 @@ var MailListCtrl = function($scope, $routeParams) {
|
||||
return;
|
||||
}
|
||||
|
||||
emailDao.openFolder({
|
||||
email.openFolder({
|
||||
folder: currentFolder()
|
||||
}, function(error) {
|
||||
// dont wait until scroll to load visible mail bodies
|
||||
@ -302,21 +227,12 @@ var MailListCtrl = function($scope, $routeParams) {
|
||||
if (error && error.code === 42) {
|
||||
return;
|
||||
}
|
||||
$scope.onError(error);
|
||||
dialog.error(error);
|
||||
});
|
||||
}
|
||||
|
||||
function updateStatus(lbl, time) {
|
||||
$scope.state.mailList.lastUpdateLbl = lbl;
|
||||
$scope.state.mailList.lastUpdate = (time) ? time : '';
|
||||
}
|
||||
|
||||
function setSearching(state) {
|
||||
$scope.state.mailList.searching = state;
|
||||
}
|
||||
|
||||
function currentFolder() {
|
||||
return $scope.state.nav.currentFolder;
|
||||
return $scope.state.nav && $scope.state.nav.currentFolder;
|
||||
}
|
||||
|
||||
function currentMessage() {
|
||||
@ -327,7 +243,7 @@ var MailListCtrl = function($scope, $routeParams) {
|
||||
// Notification API
|
||||
//
|
||||
|
||||
(emailDao || {}).onIncomingMessage = function(msgs) {
|
||||
(email || {}).onIncomingMessage = function(msgs) {
|
||||
var note, title, message, unreadMsgs;
|
||||
|
||||
unreadMsgs = msgs.filter(function(msg) {
|
||||
@ -402,7 +318,7 @@ ngModule.directive('listScroll', function() {
|
||||
}
|
||||
|
||||
for (var i = 0, len = listItems.length; i < len; i++) {
|
||||
// the n-th list item (the dom representation of an email) corresponds to
|
||||
// the n-th list item (the dom representation of an message) corresponds to
|
||||
// the n-th message model in the filteredMessages array
|
||||
listItem = listItems.item(i).getBoundingClientRect();
|
||||
|
||||
@ -458,72 +374,4 @@ function byUidDescending(a, b) {
|
||||
}
|
||||
}
|
||||
|
||||
// Helper for development mode
|
||||
|
||||
function createDummyMails() {
|
||||
var uid = 1000000;
|
||||
|
||||
var Email = function(unread, attachments, answered) {
|
||||
this.uid = uid--;
|
||||
this.from = [{
|
||||
name: 'Whiteout Support',
|
||||
address: 'support@whiteout.io'
|
||||
}]; // sender address
|
||||
this.to = [{
|
||||
address: 'max.musterman@gmail.com'
|
||||
}, {
|
||||
address: 'max.musterman@gmail.com'
|
||||
}]; // list of receivers
|
||||
this.cc = [{
|
||||
address: 'john.doe@gmail.com'
|
||||
}]; // list of receivers
|
||||
this.attachments = attachments ? [{
|
||||
"filename": "a.md",
|
||||
"filesize": 123,
|
||||
"mimeType": "text/x-markdown",
|
||||
"part": "2",
|
||||
"content": null
|
||||
}, {
|
||||
"filename": "b.md",
|
||||
"filesize": 456,
|
||||
"mimeType": "text/x-markdown",
|
||||
"part": "3",
|
||||
"content": null
|
||||
}, {
|
||||
"filename": "c.md",
|
||||
"filesize": 789,
|
||||
"mimeType": "text/x-markdown",
|
||||
"part": "4",
|
||||
"content": null
|
||||
}] : [];
|
||||
this.unread = unread;
|
||||
this.answered = answered;
|
||||
this.sentDate = new Date('Thu Sep 19 2013 20:41:23 GMT+0200 (CEST)');
|
||||
this.subject = 'Getting started'; // Subject line
|
||||
this.body = 'And a good day to you too sir. \n' +
|
||||
'\n' +
|
||||
'Thursday, Apr 24, 2014 3:33 PM safewithme.testuser@gmail.com wrote:\n' +
|
||||
'> adsfadfasdfasdfasfdasdfasdfas\n' +
|
||||
'\n' +
|
||||
'http://example.com\n' +
|
||||
'\n' +
|
||||
'> Tuesday, Mar 25, 2014 4:19 PM gianniarcore@gmail.com wrote:\n' +
|
||||
'>> from 0.7.0.1\n' +
|
||||
'>>\n' +
|
||||
'>> God speed!'; // plaintext body
|
||||
//this.html = '<!DOCTYPE html><html><head></head><body><h1 style="border: 1px solid red; width: 500px;">Hello there' + Math.random() + '</h1></body></html>';
|
||||
this.encrypted = true;
|
||||
this.decrypted = true;
|
||||
};
|
||||
|
||||
var dummies = [],
|
||||
i = 100;
|
||||
while (i--) {
|
||||
// every second/third/fourth dummy mail with unread/attachments/answered
|
||||
dummies.push(new Email((i % 2 === 0), (i % 3 === 0), (i % 5 === 0)));
|
||||
}
|
||||
|
||||
return dummies;
|
||||
}
|
||||
|
||||
module.exports = MailListCtrl;
|
@ -1,12 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
var appController = require('../app-controller'),
|
||||
notification = require('../util/notification'),
|
||||
backBtnHandler = require('../util/backbutton-handler'),
|
||||
appCfg = require('../app-config'),
|
||||
config = appCfg.config,
|
||||
str = appCfg.string,
|
||||
emailDao, outboxBo;
|
||||
var backBtnHandler = require('../../util/backbutton-handler');
|
||||
|
||||
//
|
||||
// Constants
|
||||
@ -19,14 +13,14 @@ var NOTIFICATION_SENT_TIMEOUT = 2000;
|
||||
// Controller
|
||||
//
|
||||
|
||||
var NavigationCtrl = function($scope, $routeParams, $location) {
|
||||
if (!appController._emailDao && !$routeParams.dev) {
|
||||
var NavigationCtrl = function($scope, $routeParams, $location, account, email, outbox, notification, appConfig, dialog) {
|
||||
if (!$routeParams.dev && !account.isLoggedIn()) {
|
||||
$location.path('/'); // init app
|
||||
return;
|
||||
}
|
||||
|
||||
emailDao = appController._emailDao;
|
||||
outboxBo = appController._outboxBo;
|
||||
var str = appConfig.string,
|
||||
config = appConfig.config;
|
||||
|
||||
//
|
||||
// scope functions
|
||||
@ -46,32 +40,31 @@ var NavigationCtrl = function($scope, $routeParams, $location) {
|
||||
|
||||
$scope.onOutboxUpdate = function(err, count) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
dialog.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// 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
|
||||
}, $scope.onError);
|
||||
email.refreshFolder({
|
||||
folder: ob
|
||||
}, dialog.error);
|
||||
};
|
||||
|
||||
$scope.logout = function() {
|
||||
$scope.onError({
|
||||
dialog.confirm({
|
||||
title: str.logoutTitle,
|
||||
message: str.logoutMessage,
|
||||
callback: function(confirm) {
|
||||
if (confirm) {
|
||||
appController.logout();
|
||||
account.logout();
|
||||
}
|
||||
},
|
||||
sync: true
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@ -89,9 +82,9 @@ var NavigationCtrl = function($scope, $routeParams, $location) {
|
||||
$scope.openFolder($scope.account.folders[0]);
|
||||
}
|
||||
// connect imap/smtp clients on first startup
|
||||
appController.onConnect(function(err) {
|
||||
account.onConnect(function(err) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
dialog.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -114,18 +107,18 @@ var NavigationCtrl = function($scope, $routeParams, $location) {
|
||||
}
|
||||
|
||||
// get pointer to account/folder/message tree on root scope
|
||||
$scope.$root.account = emailDao._account;
|
||||
$scope.$root.account = account.list()[0];
|
||||
|
||||
// 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() {});
|
||||
}
|
@ -1,12 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
var appController = require('../app-controller'),
|
||||
util = require('crypto-lib').util,
|
||||
keychain, pgp;
|
||||
var util = require('crypto-lib').util;
|
||||
|
||||
var PrivateKeyUploadCtrl = function($scope) {
|
||||
keychain = appController._keychain;
|
||||
pgp = keychain._pgp;
|
||||
var PrivateKeyUploadCtrl = function($scope, keychain, pgp, dialog, auth) {
|
||||
|
||||
$scope.state.privateKeyUpload = {
|
||||
toggle: function(to) {
|
||||
@ -24,7 +20,7 @@ var PrivateKeyUploadCtrl = function($scope) {
|
||||
// close lightbox
|
||||
$scope.state.lightbox = undefined;
|
||||
// show message
|
||||
$scope.onError({
|
||||
dialog.info({
|
||||
title: 'Info',
|
||||
message: 'Your PGP key has already been synced.'
|
||||
});
|
||||
@ -64,7 +60,7 @@ var PrivateKeyUploadCtrl = function($scope) {
|
||||
keyId: keyParams._id
|
||||
}, function(err, privateKeySynced) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
dialog.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -93,8 +89,7 @@ var PrivateKeyUploadCtrl = function($scope) {
|
||||
|
||||
if (inputCode.toUpperCase() !== $scope.code) {
|
||||
var err = new Error('The code does not match. Please go back and check the generated code.');
|
||||
err.sync = true;
|
||||
$scope.onError(err);
|
||||
dialog.error(err);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -106,7 +101,7 @@ var PrivateKeyUploadCtrl = function($scope) {
|
||||
};
|
||||
|
||||
$scope.encryptAndUploadKey = function(callback) {
|
||||
var userId = appController._emailDao._account.emailAddress;
|
||||
var userId = auth.emailAddress;
|
||||
var code = $scope.code;
|
||||
|
||||
// register device to keychain service
|
||||
@ -114,7 +109,7 @@ var PrivateKeyUploadCtrl = function($scope) {
|
||||
userId: userId
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
dialog.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -147,7 +142,7 @@ var PrivateKeyUploadCtrl = function($scope) {
|
||||
// set device name to local storage
|
||||
$scope.setDeviceName(function(err) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
dialog.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -158,14 +153,14 @@ var PrivateKeyUploadCtrl = function($scope) {
|
||||
// init key sync
|
||||
$scope.encryptAndUploadKey(function(err) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
dialog.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// close sync dialog
|
||||
$scope.state.privateKeyUpload.toggle(false);
|
||||
// show success message
|
||||
$scope.onError({
|
||||
dialog.info({
|
||||
title: 'Success',
|
||||
message: 'Whiteout Keychain setup successful!'
|
||||
});
|
@ -1,21 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
var appController = require('../app-controller'),
|
||||
download = require('../util/download'),
|
||||
str = require('../app-config').string,
|
||||
emailDao, invitationDao, outbox, pgp, keychain;
|
||||
|
||||
//
|
||||
// Controller
|
||||
//
|
||||
|
||||
var ReadCtrl = function($scope) {
|
||||
var ReadCtrl = function($scope, email, invitation, outbox, pgp, keychain, appConfig, download, auth, dialog) {
|
||||
|
||||
emailDao = appController._emailDao;
|
||||
invitationDao = appController._invitationDao;
|
||||
outbox = appController._outboxBo;
|
||||
pgp = appController._pgp;
|
||||
keychain = appController._keychain;
|
||||
var str = appConfig.string;
|
||||
|
||||
// set default value so that the popover height is correct on init
|
||||
$scope.keyId = 'No key found.';
|
||||
@ -31,7 +22,7 @@ var ReadCtrl = function($scope) {
|
||||
$scope.keyId = 'Searching...';
|
||||
keychain.getReceiverPublicKey(address, function(err, pubkey) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
dialog.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -71,7 +62,7 @@ var ReadCtrl = function($scope) {
|
||||
|
||||
keychain.getReceiverPublicKey(user.address, function(err, pubkey) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
dialog.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -97,12 +88,12 @@ var ReadCtrl = function($scope) {
|
||||
}
|
||||
|
||||
var folder = $scope.state.nav.currentFolder;
|
||||
var email = $scope.state.mailList.selected;
|
||||
emailDao.getAttachment({
|
||||
var message = $scope.state.mailList.selected;
|
||||
email.getAttachment({
|
||||
folder: folder,
|
||||
uid: email.uid,
|
||||
uid: message.uid,
|
||||
attachment: attachment
|
||||
}, $scope.onError);
|
||||
}, dialog.error);
|
||||
};
|
||||
|
||||
$scope.invite = function(user) {
|
||||
@ -113,15 +104,15 @@ var ReadCtrl = function($scope) {
|
||||
|
||||
$scope.keyId = 'Sending invitation...';
|
||||
|
||||
var sender = emailDao._account.emailAddress,
|
||||
var sender = auth.emailAddress,
|
||||
recipient = user.address;
|
||||
|
||||
invitationDao.invite({
|
||||
invitation.invite({
|
||||
recipient: recipient,
|
||||
sender: sender
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
dialog.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -139,7 +130,7 @@ var ReadCtrl = function($scope) {
|
||||
};
|
||||
|
||||
// send invitation mail
|
||||
outbox.put(invitationMail, $scope.onError);
|
||||
outbox.put(invitationMail, dialog.error);
|
||||
});
|
||||
};
|
||||
};
|
@ -1,15 +1,10 @@
|
||||
'use strict';
|
||||
|
||||
var appController = require('../app-controller'),
|
||||
pgp, keychain;
|
||||
var SetPassphraseCtrl = function($scope, pgp, keychain, dialog) {
|
||||
|
||||
//
|
||||
// Controller
|
||||
//
|
||||
|
||||
var SetPassphraseCtrl = function($scope) {
|
||||
keychain = appController._keychain;
|
||||
pgp = appController._pgp;
|
||||
//
|
||||
// scope variables
|
||||
//
|
||||
|
||||
$scope.state.setPassphrase = {
|
||||
toggle: function(to) {
|
||||
@ -22,10 +17,6 @@ var SetPassphraseCtrl = function($scope) {
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// scope variables
|
||||
//
|
||||
|
||||
//
|
||||
// scope functions
|
||||
//
|
||||
@ -87,7 +78,7 @@ var SetPassphraseCtrl = function($scope) {
|
||||
var keyId = pgp.getKeyParams()._id;
|
||||
keychain.lookupPrivateKey(keyId, function(err, savedKey) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
dialog.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -102,7 +93,7 @@ var SetPassphraseCtrl = function($scope) {
|
||||
function onPassphraseChanged(err, newPrivateKeyArmored) {
|
||||
if (err) {
|
||||
err.showBugReporter = false;
|
||||
$scope.onError(err);
|
||||
dialog.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -120,13 +111,13 @@ var SetPassphraseCtrl = function($scope) {
|
||||
|
||||
function onKeyPersisted(err) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
dialog.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.state.setPassphrase.toggle(false);
|
||||
$scope.$apply();
|
||||
$scope.onError({
|
||||
dialog.info({
|
||||
title: 'Success',
|
||||
message: 'Passphrase change complete.'
|
||||
});
|
16
src/js/controller/app/status-display.js
Normal file
16
src/js/controller/app/status-display.js
Normal file
@ -0,0 +1,16 @@
|
||||
'use strict';
|
||||
|
||||
var StatusDisplayCtrl = function($scope) {
|
||||
|
||||
$scope.$on('status', function(e, text, time) {
|
||||
$scope.text = text;
|
||||
$scope.time = (time) ? time : '';
|
||||
});
|
||||
|
||||
$scope.$on('searching', function(e, state) {
|
||||
$scope.searching = state;
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
module.exports = StatusDisplayCtrl;
|
@ -1,21 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
var appController = require('../app-controller'),
|
||||
axe = require('axe-logger'),
|
||||
util = require('crypto-lib').util,
|
||||
str = require('../app-config').string,
|
||||
pgp, emailDao, outbox, keychainDao, auth;
|
||||
var util = require('crypto-lib').util;
|
||||
|
||||
//
|
||||
// Controller
|
||||
//
|
||||
|
||||
var WriteCtrl = function($scope, $filter, $q) {
|
||||
pgp = appController._pgp;
|
||||
auth = appController._auth;
|
||||
emailDao = appController._emailDao;
|
||||
outbox = appController._outboxBo;
|
||||
keychainDao = appController._keychain;
|
||||
var WriteCtrl = function($scope, $filter, $q, appConfig, auth, keychain, pgp, email, outbox, dialog, axe) {
|
||||
|
||||
var str = appConfig.string;
|
||||
|
||||
// set default value so that the popover height is correct on init
|
||||
$scope.keyId = 'XXXXXXXX';
|
||||
@ -133,7 +126,7 @@ var WriteCtrl = function($scope, $filter, $q) {
|
||||
}
|
||||
if (replyAll) {
|
||||
re.to.concat(re.cc).forEach(function(recipient) {
|
||||
var me = emailDao._account.emailAddress;
|
||||
var me = auth.emailAddress;
|
||||
if (recipient.address === me && replyTo !== me) {
|
||||
// don't reply to yourself
|
||||
return;
|
||||
@ -225,15 +218,15 @@ var WriteCtrl = function($scope, $filter, $q) {
|
||||
return;
|
||||
}
|
||||
|
||||
// keychainDao is undefined in local dev environment
|
||||
if (keychainDao) {
|
||||
// keychain is undefined in local dev environment
|
||||
if (keychain) {
|
||||
// check if to address is contained in known public keys
|
||||
// when we write an email, we always need to work with the latest keys available
|
||||
keychainDao.refreshKeyForUserId({
|
||||
keychain.refreshKeyForUserId({
|
||||
userId: recipient.address
|
||||
}, function(err, key) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
dialog.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -318,13 +311,13 @@ var WriteCtrl = function($scope, $filter, $q) {
|
||||
//
|
||||
|
||||
$scope.sendToOutbox = function() {
|
||||
var email;
|
||||
var message;
|
||||
|
||||
// build email model for smtp-client
|
||||
email = {
|
||||
message = {
|
||||
from: [{
|
||||
name: emailDao._account.realname,
|
||||
address: emailDao._account.emailAddress
|
||||
name: auth.realname,
|
||||
address: auth.emailAddress
|
||||
}],
|
||||
to: $scope.to.filter(filterEmptyAddresses),
|
||||
cc: $scope.cc.filter(filterEmptyAddresses),
|
||||
@ -337,11 +330,11 @@ var WriteCtrl = function($scope, $filter, $q) {
|
||||
};
|
||||
|
||||
if ($scope.inReplyTo) {
|
||||
email.headers['in-reply-to'] = '<' + $scope.inReplyTo + '>';
|
||||
message.headers['in-reply-to'] = '<' + $scope.inReplyTo + '>';
|
||||
}
|
||||
|
||||
if ($scope.references && $scope.references.length) {
|
||||
email.headers.references = $scope.references.map(function(reference) {
|
||||
message.headers.references = $scope.references.map(function(reference) {
|
||||
return '<' + reference + '>';
|
||||
}).join(' ');
|
||||
}
|
||||
@ -350,9 +343,9 @@ var WriteCtrl = function($scope, $filter, $q) {
|
||||
$scope.state.writer.close();
|
||||
|
||||
// persist the email to disk for later sending
|
||||
outbox.put(email, function(err) {
|
||||
outbox.put(message, function(err) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
dialog.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -363,12 +356,12 @@ var WriteCtrl = function($scope, $filter, $q) {
|
||||
}
|
||||
|
||||
$scope.replyTo.answered = true;
|
||||
emailDao.setFlags({
|
||||
email.setFlags({
|
||||
folder: currentFolder(),
|
||||
message: $scope.replyTo
|
||||
}, function(err) {
|
||||
if (err && err.code !== 42) {
|
||||
$scope.onError(err);
|
||||
dialog.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -396,9 +389,9 @@ var WriteCtrl = function($scope, $filter, $q) {
|
||||
|
||||
if (!$scope.addressBookCache) {
|
||||
// populate address book cache
|
||||
keychainDao.listLocalPublicKeys(function(err, keys) {
|
||||
keychain.listLocalPublicKeys(function(err, keys) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
dialog.error(err);
|
||||
return;
|
||||
}
|
||||
|
@ -1,14 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var DialogCtrl = function($scope) {
|
||||
$scope.confirm = function(ok) {
|
||||
$scope.state.dialog.open = false;
|
||||
|
||||
if ($scope.state.dialog.callback) {
|
||||
$scope.state.dialog.callback(ok);
|
||||
}
|
||||
$scope.state.dialog.callback = undefined;
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = DialogCtrl;
|
@ -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, dialog) {
|
||||
!$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 {
|
||||
@ -41,7 +36,7 @@ var AddAccountCtrl = function($scope, $location, $routeParams, mailConfig) {
|
||||
|
||||
$scope.oauthPossible = function() {
|
||||
// ask user to use the platform's native OAuth api
|
||||
$scope.onError({
|
||||
dialog.confirm({
|
||||
title: 'Google Account Login',
|
||||
message: 'You are signing into a Google account. Would you like to sign in with Google or just continue with a password login?',
|
||||
positiveBtnStr: 'Google sign in',
|
||||
@ -62,9 +57,9 @@ 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);
|
||||
return dialog.error(err);
|
||||
}
|
||||
$scope.setCredentials();
|
||||
$scope.$apply();
|
@ -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
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -1,45 +1,35 @@
|
||||
'use strict';
|
||||
|
||||
var appController = require('../app-controller');
|
||||
var LoginCtrl = function($scope, $timeout, $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();
|
||||
});
|
||||
// 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;
|
||||
}
|
||||
|
||||
// check if account needs to be selected
|
||||
if (!info.emailAddress) {
|
||||
goTo('/add-account');
|
||||
$scope.goTo('/add-account');
|
||||
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;
|
||||
}
|
||||
|
||||
@ -51,54 +41,54 @@ 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) {
|
||||
if (err) {
|
||||
goTo('/login-existing');
|
||||
$scope.goTo('/login-existing');
|
||||
return;
|
||||
}
|
||||
|
||||
appController._auth.storeCredentials(function(err) {
|
||||
auth.storeCredentials(function(err) {
|
||||
if (err) {
|
||||
return $scope.onError(err);
|
||||
return dialog.error(err);
|
||||
}
|
||||
|
||||
goTo('/desktop');
|
||||
$scope.goTo('/desktop');
|
||||
});
|
||||
});
|
||||
} 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;
|
||||
}
|
||||
|
||||
if (privateKeySynced) {
|
||||
// private key is synced, proceed to download
|
||||
goTo('/login-privatekey-download');
|
||||
$scope.goTo('/login-privatekey-download');
|
||||
return;
|
||||
}
|
||||
|
||||
// no private key, import key file
|
||||
goTo('/login-new-device');
|
||||
$scope.goTo('/login-new-device');
|
||||
});
|
||||
} else {
|
||||
// no public key available, start onboarding process
|
||||
goTo('/login-initial');
|
||||
$scope.goTo('/login-initial');
|
||||
}
|
||||
}
|
||||
|
||||
function goTo(location) {
|
||||
$scope.$apply(function() {
|
||||
$scope.goTo = function(location) {
|
||||
return $timeout(function() {
|
||||
$location.path(location);
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = LoginCtrl;
|
@ -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
|
||||
|
||||
$scope.validateUser = function() {
|
||||
if ($scope.form.$invalid) {
|
||||
@ -18,8 +13,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) {
|
||||
@ -35,14 +30,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),
|
||||
@ -65,4 +60,4 @@ var ValidatePhoneCtrl = function($scope, $location, $routeParams, mailConfig) {
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = ValidatePhoneCtrl;
|
||||
module.exports = ValidatePhoneCtrl;
|
@ -1,16 +1,19 @@
|
||||
/**
|
||||
* High level crypto api that invokes native crypto (if available) and
|
||||
* gracefully degrades to JS crypto (if unavailable)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var ngModule = angular.module('woCrypto');
|
||||
ngModule.service('crypto', Crypto);
|
||||
module.exports = Crypto;
|
||||
|
||||
var aes = require('crypto-lib').aes,
|
||||
pbkdf2 = require('./pbkdf2'),
|
||||
config = require('../app-config').config,
|
||||
axe = require('axe-logger');
|
||||
|
||||
var Crypto = function() {};
|
||||
/**
|
||||
* High level crypto api that invokes native crypto (if available) and
|
||||
* gracefully degrades to JS crypto (if unavailable)
|
||||
*/
|
||||
function Crypto() {}
|
||||
|
||||
/**
|
||||
* Encrypt plaintext using AES-GCM.
|
||||
@ -115,6 +118,4 @@ function startWorker(options) {
|
||||
return;
|
||||
}
|
||||
options.callback(null, result);
|
||||
}
|
||||
|
||||
module.exports = Crypto;
|
||||
}
|
6
src/js/crypto/index.js
Normal file
6
src/js/crypto/index.js
Normal file
@ -0,0 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
angular.module('woCrypto', ['woAppConfig', 'woUtil']);
|
||||
|
||||
require('./pgp');
|
||||
require('./crypto');
|
@ -1,16 +1,19 @@
|
||||
/**
|
||||
* High level crypto api that handles all calls to OpenPGP.js
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var ngModule = angular.module('woCrypto');
|
||||
ngModule.service('pgp', PGP);
|
||||
module.exports = PGP;
|
||||
|
||||
var util = openpgp.util,
|
||||
config = require('../app-config').config;
|
||||
|
||||
var PGP = function() {
|
||||
/**
|
||||
* High level crypto api that handles all calls to OpenPGP.js
|
||||
*/
|
||||
function PGP() {
|
||||
openpgp.config.prefer_hash_algorithm = openpgp.enums.hash.sha256;
|
||||
openpgp.initWorker(config.workerPath + '/openpgp.worker.min.js');
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a key pair for the user
|
||||
@ -426,6 +429,4 @@ function checkSignatureValidity(signatures) {
|
||||
|
||||
// everything is in order
|
||||
return true;
|
||||
}
|
||||
|
||||
module.exports = PGP;
|
||||
}
|
236
src/js/email/account.js
Normal file
236
src/js/email/account.js
Normal file
@ -0,0 +1,236 @@
|
||||
'use strict';
|
||||
|
||||
var ngModule = angular.module('woEmail');
|
||||
ngModule.service('account', Account);
|
||||
module.exports = Account;
|
||||
|
||||
var axe = require('axe-logger'),
|
||||
util = require('crypto-lib').util,
|
||||
PgpMailer = require('pgpmailer'),
|
||||
ImapClient = require('imap-client');
|
||||
|
||||
function Account(appConfig, auth, accountStore, email, outbox, keychain, updateHandler, pgpbuilder, dialog) {
|
||||
this._appConfig = appConfig;
|
||||
this._auth = auth;
|
||||
this._accountStore = accountStore;
|
||||
this._emailDao = email;
|
||||
this._outbox = outbox;
|
||||
this._keychain = keychain;
|
||||
this._updateHandler = updateHandler;
|
||||
this._pgpbuilder = pgpbuilder;
|
||||
this._dialog = dialog;
|
||||
this._accounts = []; // init accounts list
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the account is already logged in.
|
||||
* @return {Boolean} if the account is logged in
|
||||
*/
|
||||
Account.prototype.isLoggedIn = function() {
|
||||
return (this._accounts.length > 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Lists all of the current accounts connected to the app
|
||||
* @return {Array<Object>} The account objects containing folder and message objects
|
||||
*/
|
||||
Account.prototype.list = function() {
|
||||
return this._accounts;
|
||||
};
|
||||
|
||||
/**
|
||||
* Fire up the database, retrieve the available keys for the user and initialize the email data access object
|
||||
*/
|
||||
Account.prototype.init = function(options, callback) {
|
||||
var self = this;
|
||||
|
||||
// account information for the email dao
|
||||
var account = {
|
||||
realname: options.realname,
|
||||
emailAddress: options.emailAddress,
|
||||
asymKeySize: this._appConfig.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() {
|
||||
try {
|
||||
self._accountStore.init(options.emailAddress);
|
||||
} catch (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// Migrate the databases if necessary
|
||||
self._updateHandler.update(function(err) {
|
||||
if (err) {
|
||||
return callback(new Error('Updating the internal database failed. Please reinstall the app! Reason: ' + err.message));
|
||||
}
|
||||
|
||||
prepareKeys();
|
||||
});
|
||||
}
|
||||
|
||||
// retrieve keypair fom devicestorage/cloud, refresh public key if signup was incomplete before
|
||||
function prepareKeys() {
|
||||
self._keychain.getUserKeyPair(options.emailAddress, function(err, keys) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
// this is either a first start on a new device, OR a subsequent start without completing the signup,
|
||||
// since we can't differenciate those cases here, do a public key refresh because it might be outdated
|
||||
if (keys && keys.publicKey && !keys.privateKey) {
|
||||
self._keychain.refreshKeyForUserId({
|
||||
userId: options.emailAddress,
|
||||
overridePermission: true
|
||||
}, function(err, publicKey) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
initEmailDao({
|
||||
publicKey: publicKey
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// either signup was complete or no pubkey is available, so we're good here.
|
||||
initEmailDao(keys);
|
||||
});
|
||||
}
|
||||
|
||||
function initEmailDao(keys) {
|
||||
self._emailDao.init({
|
||||
account: account
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
// Handle offline and online gracefully ... arm dom event
|
||||
window.addEventListener('online', self.onConnect.bind(self));
|
||||
window.addEventListener('offline', self.onDisconnect.bind(self));
|
||||
|
||||
// add account object to the accounts array for the ng controllers
|
||||
self._accounts.push(account);
|
||||
|
||||
callback(null, keys);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the user agent is online.
|
||||
*/
|
||||
Account.prototype.isOnline = function() {
|
||||
return navigator.onLine;
|
||||
};
|
||||
|
||||
/**
|
||||
* Event that is called when the user agent goes online. This create new instances of the imap-client and pgp-mailer and connects to the mail server.
|
||||
*/
|
||||
Account.prototype.onConnect = function() {
|
||||
var self = this;
|
||||
var config = self._appConfig.config;
|
||||
|
||||
if (!self.isOnline() || !self._emailDao || !self._emailDao._account) {
|
||||
// prevent connection infinite loop
|
||||
return;
|
||||
}
|
||||
|
||||
self._auth.getCredentials(function(err, credentials) {
|
||||
if (err) {
|
||||
self._dialog.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
initClients(credentials);
|
||||
});
|
||||
|
||||
function initClients(credentials) {
|
||||
// add the maximum update batch size for imap folders to the imap configuration
|
||||
credentials.imap.maxUpdateSize = config.imapUpdateBatchSize;
|
||||
|
||||
// tls socket worker path for multithreaded tls in non-native tls environments
|
||||
credentials.imap.tlsWorkerPath = credentials.smtp.tlsWorkerPath = config.workerPath + '/tcp-socket-tls-worker.min.js';
|
||||
|
||||
var pgpMailer = new PgpMailer(credentials.smtp, self._pgpbuilder);
|
||||
var imapClient = new ImapClient(credentials.imap);
|
||||
imapClient.onError = onConnectionError;
|
||||
pgpMailer.onError = onConnectionError;
|
||||
|
||||
// certificate update handling
|
||||
imapClient.onCert = self._auth.handleCertificateUpdate.bind(self._auth, 'imap', self.onConnect.bind(self), self._dialog.error);
|
||||
pgpMailer.onCert = self._auth.handleCertificateUpdate.bind(self._auth, 'smtp', self.onConnect.bind(self), self._dialog.error);
|
||||
|
||||
// connect to clients
|
||||
self._emailDao.onConnect({
|
||||
imapClient: imapClient,
|
||||
pgpMailer: pgpMailer,
|
||||
ignoreUploadOnSent: self._emailDao.checkIgnoreUploadOnSent(credentials.imap.host)
|
||||
}, self._dialog.error);
|
||||
}
|
||||
|
||||
function onConnectionError(error) {
|
||||
axe.debug('Connection error. Attempting reconnect in ' + config.reconnectInterval + ' ms. Error: ' + (error.errMsg || error.message) + (error.stack ? ('\n' + error.stack) : ''));
|
||||
|
||||
setTimeout(function() {
|
||||
axe.debug('Reconnecting...');
|
||||
// re-init client modules on error
|
||||
self.onConnect(function(err) {
|
||||
if (err) {
|
||||
axe.error('Reconnect attempt failed! ' + (err.errMsg || err.message) + (err.stack ? ('\n' + err.stack) : ''));
|
||||
return;
|
||||
}
|
||||
|
||||
axe.debug('Reconnect attempt complete.');
|
||||
});
|
||||
}, config.reconnectInterval);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Event handler that is called when the user agent goes offline.
|
||||
*/
|
||||
Account.prototype.onDisconnect = function() {
|
||||
this._emailDao.onDisconnect();
|
||||
};
|
||||
|
||||
/**
|
||||
* Logout of an email account. Log the current user out by clear the app config store and deleting instances of imap-client and pgp-mailer.
|
||||
*/
|
||||
Account.prototype.logout = function() {
|
||||
var self = this;
|
||||
|
||||
// clear app config store
|
||||
self._auth.logout(function(err) {
|
||||
if (err) {
|
||||
self._dialog.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// delete instance of imap-client and pgp-mailer
|
||||
self._emailDao.onDisconnect(function(err) {
|
||||
if (err) {
|
||||
self._dialog.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof window.chrome !== 'undefined' && chrome.runtime && chrome.runtime.reload) {
|
||||
// reload chrome app
|
||||
chrome.runtime.reload();
|
||||
} else {
|
||||
// navigate to login
|
||||
window.location.href = '/';
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
@ -1,5 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
var ngModule = angular.module('woEmail');
|
||||
ngModule.service('email', Email);
|
||||
module.exports = Email;
|
||||
|
||||
var config = require('../app-config').config,
|
||||
str = require('../app-config').string;
|
||||
|
||||
@ -32,7 +36,7 @@ var MSG_PART_TYPE_HTML = 'html';
|
||||
|
||||
//
|
||||
//
|
||||
// Email Dao
|
||||
// Email Service
|
||||
//
|
||||
//
|
||||
|
||||
@ -46,13 +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
|
||||
*/
|
||||
var EmailDAO = function(keychain, pgp, devicestorage, pgpbuilder, mailreader) {
|
||||
function Email(keychain, pgp, accountStore, pgpbuilder, mailreader, dialog) {
|
||||
this._keychain = keychain;
|
||||
this._pgp = pgp;
|
||||
this._devicestorage = devicestorage;
|
||||
this._devicestorage = accountStore;
|
||||
this._pgpbuilder = pgpbuilder;
|
||||
this._mailreader = mailreader;
|
||||
};
|
||||
this._dialog = dialog;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
@ -71,7 +76,7 @@ var EmailDAO = function(keychain, pgp, devicestorage, pgpbuilder, mailreader) {
|
||||
* @param {String} options.account.realname The user's id
|
||||
* @param {Function} callback(error, keypair) Invoked with the keypair or error information when the email dao is initialized
|
||||
*/
|
||||
EmailDAO.prototype.init = function(options, callback) {
|
||||
Email.prototype.init = function(options, callback) {
|
||||
this._account = options.account;
|
||||
this._account.busy = 0; // > 0 triggers the spinner
|
||||
this._account.online = false;
|
||||
@ -86,7 +91,7 @@ EmailDAO.prototype.init = function(options, callback) {
|
||||
* @param {String} options.passphrase The passphrase to decrypt the private key
|
||||
* @param {Function} callback(error) Invoked when the the keychain is unlocked or when an error occurred buring unlocking
|
||||
*/
|
||||
EmailDAO.prototype.unlock = function(options, callback) {
|
||||
Email.prototype.unlock = function(options, callback) {
|
||||
var self = this;
|
||||
|
||||
if (options.keypair) {
|
||||
@ -202,7 +207,7 @@ EmailDAO.prototype.unlock = function(options, callback) {
|
||||
* @param {Object} options.folder The folder to be opened
|
||||
* @param {Function} callback(error) Invoked when the folder has been opened
|
||||
*/
|
||||
EmailDAO.prototype.openFolder = function(options, callback) {
|
||||
Email.prototype.openFolder = function(options, callback) {
|
||||
var self = this,
|
||||
err;
|
||||
|
||||
@ -229,7 +234,7 @@ EmailDAO.prototype.openFolder = function(options, callback) {
|
||||
* @param {Object} options.folder The folder to synchronize
|
||||
* @param {Function} callback [description]
|
||||
*/
|
||||
EmailDAO.prototype.refreshFolder = function(options, callback) {
|
||||
Email.prototype.refreshFolder = function(options, callback) {
|
||||
var self = this,
|
||||
folder = options.folder;
|
||||
|
||||
@ -289,7 +294,7 @@ EmailDAO.prototype.refreshFolder = function(options, callback) {
|
||||
* @param {Object} options.folder The folder for which to fetch the message
|
||||
* @param {Function} callback(error) Invoked when the message is persisted and added to folder.messages
|
||||
*/
|
||||
EmailDAO.prototype.fetchMessages = function(options, callback) {
|
||||
Email.prototype.fetchMessages = function(options, callback) {
|
||||
var self = this,
|
||||
folder = options.folder;
|
||||
|
||||
@ -440,7 +445,7 @@ EmailDAO.prototype.fetchMessages = function(options, callback) {
|
||||
* @param {Boolean} options.localOnly Indicated if the message should not be removed from IMAP
|
||||
* @param {Function} callback(error) Invoked when the message was delete, or an error occurred
|
||||
*/
|
||||
EmailDAO.prototype.deleteMessage = function(options, callback) {
|
||||
Email.prototype.deleteMessage = function(options, callback) {
|
||||
var self = this,
|
||||
folder = options.folder,
|
||||
message = options.message;
|
||||
@ -508,7 +513,7 @@ EmailDAO.prototype.deleteMessage = function(options, callback) {
|
||||
* @param {[type]} options [description]
|
||||
* @param {Function} callback [description]
|
||||
*/
|
||||
EmailDAO.prototype.setFlags = function(options, callback) {
|
||||
Email.prototype.setFlags = function(options, callback) {
|
||||
var self = this,
|
||||
folder = options.folder,
|
||||
message = options.message;
|
||||
@ -600,7 +605,7 @@ EmailDAO.prototype.setFlags = function(options, callback) {
|
||||
* @param {Object} options.message The message that should be moved
|
||||
* @param {Function} callback(error) Invoked when the message was moved, or an error occurred
|
||||
*/
|
||||
EmailDAO.prototype.moveMessage = function(options, callback) {
|
||||
Email.prototype.moveMessage = function(options, callback) {
|
||||
var self = this,
|
||||
folder = options.folder,
|
||||
destination = options.destination,
|
||||
@ -652,7 +657,7 @@ EmailDAO.prototype.moveMessage = function(options, callback) {
|
||||
* @param {Object} options.folder The IMAP folder
|
||||
* @param {Function} callback(error, message) Invoked when the message is streamed, or provides information if an error occurred
|
||||
*/
|
||||
EmailDAO.prototype.getBody = function(options, callback) {
|
||||
Email.prototype.getBody = function(options, callback) {
|
||||
var self = this,
|
||||
message = options.message,
|
||||
folder = options.folder;
|
||||
@ -837,7 +842,7 @@ EmailDAO.prototype.getBody = function(options, callback) {
|
||||
}
|
||||
};
|
||||
|
||||
EmailDAO.prototype._checkSignatures = function(message, callback) {
|
||||
Email.prototype._checkSignatures = function(message, callback) {
|
||||
var self = this;
|
||||
|
||||
self._keychain.getReceiverPublicKey(message.from[0].address, function(err, senderPublicKey) {
|
||||
@ -866,7 +871,7 @@ EmailDAO.prototype._checkSignatures = function(message, callback) {
|
||||
* @param {Object} options.attachment The attachment body part to fetch and parse from IMAP
|
||||
* @param {Function} callback(error, attachment) Invoked when the attachment body part was retrieved and parsed, or an error occurred
|
||||
*/
|
||||
EmailDAO.prototype.getAttachment = function(options, callback) {
|
||||
Email.prototype.getAttachment = function(options, callback) {
|
||||
var self = this,
|
||||
attachment = options.attachment;
|
||||
|
||||
@ -896,7 +901,7 @@ EmailDAO.prototype.getAttachment = function(options, callback) {
|
||||
* @param {Object} options.message The message
|
||||
* @param {Function} callback(error, message)
|
||||
*/
|
||||
EmailDAO.prototype.decryptBody = function(options, callback) {
|
||||
Email.prototype.decryptBody = function(options, callback) {
|
||||
var self = this,
|
||||
message = options.message;
|
||||
|
||||
@ -1016,7 +1021,7 @@ EmailDAO.prototype.decryptBody = function(options, callback) {
|
||||
* @param {Object} options.email The message to be sent
|
||||
* @param {Function} callback(error) Invoked when the message was sent, or an error occurred
|
||||
*/
|
||||
EmailDAO.prototype.sendEncrypted = function(options, callback) {
|
||||
Email.prototype.sendEncrypted = function(options, callback) {
|
||||
var self = this;
|
||||
|
||||
if (!self._account.online) {
|
||||
@ -1056,7 +1061,7 @@ EmailDAO.prototype.sendEncrypted = function(options, callback) {
|
||||
* @param {Object} options.email The message to be sent
|
||||
* @param {Function} callback(error) Invoked when the message was sent, or an error occurred
|
||||
*/
|
||||
EmailDAO.prototype.sendPlaintext = function(options, callback) {
|
||||
Email.prototype.sendPlaintext = function(options, callback) {
|
||||
var self = this;
|
||||
|
||||
if (!self._account.online) {
|
||||
@ -1097,7 +1102,7 @@ EmailDAO.prototype.sendPlaintext = function(options, callback) {
|
||||
* @param {Object} options.email The message to be encrypted
|
||||
* @param {Function} callback(error, message) Invoked when the message was encrypted, or an error occurred
|
||||
*/
|
||||
EmailDAO.prototype.encrypt = function(options, callback) {
|
||||
Email.prototype.encrypt = function(options, callback) {
|
||||
var self = this;
|
||||
|
||||
self.busy();
|
||||
@ -1124,7 +1129,7 @@ EmailDAO.prototype.encrypt = function(options, callback) {
|
||||
* @param {Object} options.pgpMailer The SMTP client used to send messages
|
||||
* @param {Function} callback [description]
|
||||
*/
|
||||
EmailDAO.prototype.onConnect = function(options, callback) {
|
||||
Email.prototype.onConnect = function(options, callback) {
|
||||
var self = this;
|
||||
|
||||
self._account.loggingIn = true;
|
||||
@ -1212,7 +1217,7 @@ EmailDAO.prototype.onConnect = function(options, callback) {
|
||||
* This handler should be invoked when navigator.onLine === false.
|
||||
* It will discard the imap client and pgp mailer
|
||||
*/
|
||||
EmailDAO.prototype.onDisconnect = function(callback) {
|
||||
Email.prototype.onDisconnect = function(callback) {
|
||||
var self = this;
|
||||
|
||||
// logout of imap-client
|
||||
@ -1239,7 +1244,7 @@ EmailDAO.prototype.onDisconnect = function(callback) {
|
||||
* @param {String} options.path The mailbox for which updates are available
|
||||
* @param {Array} options.list Array containing update information. Number (uid) or mail with Object (uid and flags), respectively
|
||||
*/
|
||||
EmailDAO.prototype._onSyncUpdate = function(options) {
|
||||
Email.prototype._onSyncUpdate = function(options) {
|
||||
var self = this;
|
||||
|
||||
var folder = _.findWhere(self._account.folders, {
|
||||
@ -1257,7 +1262,7 @@ EmailDAO.prototype._onSyncUpdate = function(options) {
|
||||
folder: folder,
|
||||
firstUid: Math.min.apply(null, options.list),
|
||||
lastUid: Math.max.apply(null, options.list)
|
||||
}, self.onError.bind(self));
|
||||
}, self._dialog.error);
|
||||
} else if (options.type === SYNC_TYPE_DELETED) {
|
||||
// messages have been deleted, remove from local storage and memory
|
||||
options.list.forEach(function(uid) {
|
||||
@ -1273,7 +1278,7 @@ EmailDAO.prototype._onSyncUpdate = function(options) {
|
||||
folder: folder,
|
||||
message: message,
|
||||
localOnly: true
|
||||
}, self.onError.bind(self));
|
||||
}, self._dialog.error);
|
||||
});
|
||||
} else if (options.type === SYNC_TYPE_MSGS) {
|
||||
// NB! several possible reasons why this could be called.
|
||||
@ -1300,7 +1305,7 @@ EmailDAO.prototype._onSyncUpdate = function(options) {
|
||||
folder: folder,
|
||||
message: message,
|
||||
localOnly: true
|
||||
}, self.onError.bind(self));
|
||||
}, self._dialog.error);
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -1319,7 +1324,7 @@ EmailDAO.prototype._onSyncUpdate = function(options) {
|
||||
*
|
||||
* @param {Function} callback Invoked when the folders are up to date
|
||||
*/
|
||||
EmailDAO.prototype._initFoldersFromDisk = function(callback) {
|
||||
Email.prototype._initFoldersFromDisk = function(callback) {
|
||||
var self = this;
|
||||
|
||||
self.busy(); // start the spinner
|
||||
@ -1347,7 +1352,7 @@ EmailDAO.prototype._initFoldersFromDisk = function(callback) {
|
||||
*
|
||||
* @param {Function} callback Invoked when the folders are up to date
|
||||
*/
|
||||
EmailDAO.prototype._initFoldersFromImap = function(callback) {
|
||||
Email.prototype._initFoldersFromImap = function(callback) {
|
||||
var self = this;
|
||||
|
||||
self.busy(); // start the spinner
|
||||
@ -1498,7 +1503,7 @@ EmailDAO.prototype._initFoldersFromImap = function(callback) {
|
||||
*
|
||||
* @param {Function} callback Invoked when the folders are filled with messages
|
||||
*/
|
||||
EmailDAO.prototype._initMessagesFromDisk = function(callback) {
|
||||
Email.prototype._initMessagesFromDisk = function(callback) {
|
||||
var self = this;
|
||||
|
||||
if (!self._account.folders || self._account.folders.length === 0) {
|
||||
@ -1526,11 +1531,11 @@ EmailDAO.prototype._initMessagesFromDisk = function(callback) {
|
||||
});
|
||||
};
|
||||
|
||||
EmailDAO.prototype.busy = function() {
|
||||
Email.prototype.busy = function() {
|
||||
this._account.busy++;
|
||||
};
|
||||
|
||||
EmailDAO.prototype.done = function() {
|
||||
Email.prototype.done = function() {
|
||||
if (this._account.busy > 0) {
|
||||
this._account.busy--;
|
||||
}
|
||||
@ -1552,7 +1557,7 @@ EmailDAO.prototype.done = function() {
|
||||
* @param {Number} options.unread Un-/Read flag
|
||||
* @param {Number} options.answered Un-/Answered flag
|
||||
*/
|
||||
EmailDAO.prototype._imapMark = function(options, callback) {
|
||||
Email.prototype._imapMark = function(options, callback) {
|
||||
if (!this._account.online) {
|
||||
callback({
|
||||
errMsg: 'Client is currently offline!',
|
||||
@ -1573,7 +1578,7 @@ EmailDAO.prototype._imapMark = function(options, callback) {
|
||||
* @param {Number} options.uid The uid of the message
|
||||
* @param {Function} callback(error) Callback with an error object in case something went wrong.
|
||||
*/
|
||||
EmailDAO.prototype._imapDeleteMessage = function(options, callback) {
|
||||
Email.prototype._imapDeleteMessage = function(options, callback) {
|
||||
if (!this._account.online) {
|
||||
callback({
|
||||
errMsg: 'Client is currently offline!',
|
||||
@ -1611,7 +1616,7 @@ EmailDAO.prototype._imapDeleteMessage = function(options, callback) {
|
||||
* @param {String} options.uid the message's uid
|
||||
* @param {Function} callback (error) The callback when the message is moved
|
||||
*/
|
||||
EmailDAO.prototype._imapMoveMessage = function(options, callback) {
|
||||
Email.prototype._imapMoveMessage = function(options, callback) {
|
||||
this._imapClient.moveMessage({
|
||||
path: options.folder.path,
|
||||
destination: options.destination.path,
|
||||
@ -1628,7 +1633,7 @@ EmailDAO.prototype._imapMoveMessage = function(options, callback) {
|
||||
* @param {Number} options.lastUid The upper bound of the uid range (inclusive)
|
||||
* @param {Function} callback (error, messages) The callback when the imap client is done fetching message metadata
|
||||
*/
|
||||
EmailDAO.prototype._imapListMessages = function(options, callback) {
|
||||
Email.prototype._imapListMessages = function(options, callback) {
|
||||
var self = this;
|
||||
|
||||
if (!this._account.online) {
|
||||
@ -1650,7 +1655,7 @@ EmailDAO.prototype._imapListMessages = function(options, callback) {
|
||||
* @param {String} options.message The rfc2822 compatible raw ASCII e-mail source
|
||||
* @param {Function} callback (error) The callback when the imap client is done uploading
|
||||
*/
|
||||
EmailDAO.prototype._imapUploadMessage = function(options, callback) {
|
||||
Email.prototype._imapUploadMessage = function(options, callback) {
|
||||
this._imapClient.uploadMessage({
|
||||
path: options.folder.path,
|
||||
message: options.message
|
||||
@ -1664,7 +1669,7 @@ EmailDAO.prototype._imapUploadMessage = function(options, callback) {
|
||||
* @param {Object} options.bodyParts The message, as retrieved by _imapListMessages
|
||||
* @param {Function} callback (error, message) The callback when the imap client is done streaming message text content
|
||||
*/
|
||||
EmailDAO.prototype._getBodyParts = function(options, callback) {
|
||||
Email.prototype._getBodyParts = function(options, callback) {
|
||||
var self = this;
|
||||
|
||||
if (!self._account.online) {
|
||||
@ -1702,7 +1707,7 @@ EmailDAO.prototype._getBodyParts = function(options, callback) {
|
||||
* @param {Object} options.uid A specific uid to look up locally in the folder
|
||||
* @param {Function} callback(error, list) Invoked with the results of the query, or further information, if an error occurred
|
||||
*/
|
||||
EmailDAO.prototype._localListMessages = function(options, callback) {
|
||||
Email.prototype._localListMessages = function(options, callback) {
|
||||
var dbType = 'email_' + options.folder.path + (options.uid ? '_' + options.uid : '');
|
||||
this._devicestorage.listItems(dbType, 0, null, callback);
|
||||
};
|
||||
@ -1714,7 +1719,7 @@ EmailDAO.prototype._localListMessages = function(options, callback) {
|
||||
* @param {Array} options.messages The messages to store
|
||||
* @param {Function} callback(error, list) Invoked with the results of the query, or further information, if an error occurred
|
||||
*/
|
||||
EmailDAO.prototype._localStoreMessages = function(options, callback) {
|
||||
Email.prototype._localStoreMessages = function(options, callback) {
|
||||
var dbType = 'email_' + options.folder.path;
|
||||
this._devicestorage.storeList(options.emails, dbType, callback);
|
||||
};
|
||||
@ -1726,7 +1731,7 @@ EmailDAO.prototype._localStoreMessages = function(options, callback) {
|
||||
* @param {Array} options.messages The messages to store
|
||||
* @param {Function} callback(error, list) Invoked with the results of the query, or further information, if an error occurred
|
||||
*/
|
||||
EmailDAO.prototype._localDeleteMessage = function(options, callback) {
|
||||
Email.prototype._localDeleteMessage = function(options, callback) {
|
||||
var path = options.folder.path,
|
||||
uid = options.uid,
|
||||
id = options.id;
|
||||
@ -1756,7 +1761,7 @@ EmailDAO.prototype._localDeleteMessage = function(options, callback) {
|
||||
* @param {String} options.message The rfc2822 compatible raw ASCII e-mail source
|
||||
* @param {Function} callback (error) The callback when the imap client is done uploading
|
||||
*/
|
||||
EmailDAO.prototype._uploadToSent = function(options, callback) {
|
||||
Email.prototype._uploadToSent = function(options, callback) {
|
||||
var self = this;
|
||||
|
||||
self.busy();
|
||||
@ -1796,7 +1801,7 @@ EmailDAO.prototype._uploadToSent = function(options, callback) {
|
||||
* @param {String} hostname The hostname to check
|
||||
* @return {Boolean} true if upload can be ignored, otherwise false
|
||||
*/
|
||||
EmailDAO.prototype.checkIgnoreUploadOnSent = function(hostname) {
|
||||
Email.prototype.checkIgnoreUploadOnSent = function(hostname) {
|
||||
for (var i = 0; i < config.ignoreUploadOnSentDomains.length; i++) {
|
||||
if (config.ignoreUploadOnSentDomains[i].test(hostname)) {
|
||||
return true;
|
||||
@ -1876,6 +1881,4 @@ function inlineExternalImages(message) {
|
||||
|
||||
return prefix + localSource + suffix;
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = EmailDAO;
|
||||
}
|
10
src/js/email/index.js
Normal file
10
src/js/email/index.js
Normal file
@ -0,0 +1,10 @@
|
||||
'use strict';
|
||||
|
||||
angular.module('woEmail', ['woAppConfig', 'woUtil', 'woServices', 'woCrypto']);
|
||||
|
||||
require('./mailreader');
|
||||
require('./pgpbuilder');
|
||||
require('./email');
|
||||
require('./outbox');
|
||||
require('./account');
|
||||
require('./search');
|
9
src/js/email/mailreader.js
Normal file
9
src/js/email/mailreader.js
Normal file
@ -0,0 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
var mailreader = require('mailreader');
|
||||
|
||||
var ngModule = angular.module('woEmail');
|
||||
ngModule.factory('mailreader', function(appConfig) {
|
||||
mailreader.startWorker(appConfig.config.workerPath + '/mailreader-parser-worker.min.js');
|
||||
return mailreader;
|
||||
});
|
@ -1,5 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
var ngModule = angular.module('woEmail');
|
||||
ngModule.service('outbox', Outbox);
|
||||
module.exports = Outbox;
|
||||
|
||||
var util = require('crypto-lib').util,
|
||||
config = require('../app-config').config,
|
||||
outboxDb = 'email_OUTBOX';
|
||||
@ -9,27 +13,27 @@ 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.
|
||||
*/
|
||||
var OutboxBO = function(emailDao, keychain, devicestorage) {
|
||||
function Outbox(email, keychain, accountStore) {
|
||||
/** @private */
|
||||
this._emailDao = emailDao;
|
||||
this._emailDao = email;
|
||||
|
||||
/** @private */
|
||||
this._keychain = keychain;
|
||||
|
||||
/** @private */
|
||||
this._devicestorage = devicestorage;
|
||||
this._devicestorage = accountStore;
|
||||
|
||||
/**
|
||||
* Semaphore-esque flag to avoid 'concurrent' calls to _processOutbox when the timeout fires, but a call is still in process.
|
||||
* @private */
|
||||
this._outboxBusy = false;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* This function activates the periodic checking of the local device storage for pending mails.
|
||||
* @param {Function} callback(error, pendingMailsCount) Callback that informs you about the count of pending mails.
|
||||
*/
|
||||
OutboxBO.prototype.startChecking = function(callback) {
|
||||
Outbox.prototype.startChecking = function(callback) {
|
||||
// remember global callback
|
||||
this._onUpdate = callback;
|
||||
// start periodic checking of outbox
|
||||
@ -39,7 +43,7 @@ OutboxBO.prototype.startChecking = function(callback) {
|
||||
/**
|
||||
* Outbox stops the periodic checking of the local device storage for pending mails.
|
||||
*/
|
||||
OutboxBO.prototype.stopChecking = function() {
|
||||
Outbox.prototype.stopChecking = function() {
|
||||
if (!this._intervalId) {
|
||||
return;
|
||||
}
|
||||
@ -53,7 +57,7 @@ OutboxBO.prototype.stopChecking = function() {
|
||||
* @param {Object} mail The Email DTO
|
||||
* @param {Function} callback Invoked when the object was encrypted and persisted to disk
|
||||
*/
|
||||
OutboxBO.prototype.put = function(mail, callback) {
|
||||
Outbox.prototype.put = function(mail, callback) {
|
||||
var self = this,
|
||||
allReaders = mail.from.concat(mail.to.concat(mail.cc.concat(mail.bcc))); // all the users that should be able to read the mail
|
||||
|
||||
@ -133,7 +137,7 @@ OutboxBO.prototype.put = function(mail, callback) {
|
||||
* Checks the local device storage for pending mails.
|
||||
* @param {Function} callback(error, pendingMailsCount) Callback that informs you about the count of pending mails.
|
||||
*/
|
||||
OutboxBO.prototype._processOutbox = function(callback) {
|
||||
Outbox.prototype._processOutbox = function(callback) {
|
||||
var self = this,
|
||||
unsentMails = 0;
|
||||
|
||||
@ -224,6 +228,4 @@ OutboxBO.prototype._processOutbox = function(callback) {
|
||||
done();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = OutboxBO;
|
||||
};
|
8
src/js/email/pgpbuilder.js
Normal file
8
src/js/email/pgpbuilder.js
Normal file
@ -0,0 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
var PgpBuilder = require('pgpbuilder');
|
||||
|
||||
var ngModule = angular.module('woEmail');
|
||||
ngModule.factory('pgpbuilder', function() {
|
||||
return new PgpBuilder();
|
||||
});
|
80
src/js/email/search.js
Normal file
80
src/js/email/search.js
Normal file
@ -0,0 +1,80 @@
|
||||
'use strict';
|
||||
|
||||
var ngModule = angular.module('woEmail');
|
||||
ngModule.service('search', Search);
|
||||
module.exports = Search;
|
||||
|
||||
function Search() {}
|
||||
|
||||
/**
|
||||
* Do full text search on messages. Parse meta data first.
|
||||
* @param {Array} messages The messages to be filtered
|
||||
* @param {String} query The text query used to filter messages
|
||||
* @return {Array} The filtered messages
|
||||
*/
|
||||
Search.prototype.filter = function(messages, query) {
|
||||
// don't filter on empty query
|
||||
if (!query) {
|
||||
return messages;
|
||||
}
|
||||
|
||||
// escape search string
|
||||
query = query.replace(/([.*+?^${}()|\[\]\/\\])/g, "\\$1");
|
||||
// compare all strings (case insensitive)
|
||||
var regex = new RegExp(query, 'i');
|
||||
|
||||
function contains(input) {
|
||||
if (!input) {
|
||||
return false;
|
||||
}
|
||||
return regex.test(input);
|
||||
}
|
||||
|
||||
function checkAddresses(header) {
|
||||
if (!header || !header.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < header.length; i++) {
|
||||
if (contains(header[i].name) || contains(header[i].address)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter meta data first and then only look at plaintext and decrypted message bodies
|
||||
*/
|
||||
function matchMetaDataFirst(m) {
|
||||
// compare subject
|
||||
if (contains(m.subject)) {
|
||||
return true;
|
||||
}
|
||||
// compares address headers
|
||||
if (checkAddresses(m.from) || checkAddresses(m.to) || checkAddresses(m.cc) || checkAddresses(m.bcc)) {
|
||||
return true;
|
||||
}
|
||||
// compare plaintext body
|
||||
if (m.body && !m.encrypted && contains(m.body)) {
|
||||
return true;
|
||||
}
|
||||
// compare decrypted body
|
||||
if (m.body && m.encrypted && m.decrypted && contains(m.body)) {
|
||||
return true;
|
||||
}
|
||||
// compare plaintex html body
|
||||
if (m.html && !m.encrypted && contains(m.html)) {
|
||||
return true;
|
||||
}
|
||||
// compare decrypted html body
|
||||
if (m.html && m.encrypted && m.decrypted && contains(m.html)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// user native js Array.filter
|
||||
return messages.filter(matchMetaDataFirst);
|
||||
};
|
@ -1,8 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
var AdminDAO = function(restDao) {
|
||||
this._restDao = restDao;
|
||||
};
|
||||
var ngModule = angular.module('woServices');
|
||||
ngModule.service('admin', Admin);
|
||||
module.exports = Admin;
|
||||
|
||||
function Admin(adminRestDao) {
|
||||
this._restDao = adminRestDao;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new email account.
|
||||
@ -11,7 +15,7 @@ var AdminDAO = function(restDao) {
|
||||
* @param {String} options.phone The user's mobile phone number (required for verification and password reset).
|
||||
* @param {Function} callback(error)
|
||||
*/
|
||||
AdminDAO.prototype.createUser = function(options, callback) {
|
||||
Admin.prototype.createUser = function(options, callback) {
|
||||
var uri;
|
||||
|
||||
if (!options.emailAddress || !options.password || !options.phone) {
|
||||
@ -39,7 +43,7 @@ AdminDAO.prototype.createUser = function(options, callback) {
|
||||
* @param {String} options.token The validation token.
|
||||
* @param {Function} callback(error)
|
||||
*/
|
||||
AdminDAO.prototype.validateUser = function(options, callback) {
|
||||
Admin.prototype.validateUser = function(options, callback) {
|
||||
var uri;
|
||||
|
||||
if (!options.emailAddress || !options.token) {
|
||||
@ -56,6 +60,4 @@ AdminDAO.prototype.validateUser = function(options, callback) {
|
||||
callback(new Error('Validation failed!'));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = AdminDAO;
|
||||
};
|
@ -1,5 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
var ngModule = angular.module('woServices');
|
||||
ngModule.service('auth', Auth);
|
||||
module.exports = Auth;
|
||||
|
||||
var axe = require('axe-logger'),
|
||||
cfg = require('../app-config').config,
|
||||
str = require('../app-config').string;
|
||||
@ -21,10 +25,24 @@ 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, ...
|
||||
*/
|
||||
var Auth = function(appConfigStore, oauth, pgp) {
|
||||
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;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -439,6 +457,4 @@ Auth.prototype.logout = function(callback) {
|
||||
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = Auth;
|
||||
};
|
@ -1,15 +1,38 @@
|
||||
/**
|
||||
* High level storage api that handles all persistence on the device.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var DeviceStorageDAO = function(localDbDao) {
|
||||
this._localDbDao = localDbDao;
|
||||
};
|
||||
var ngModule = angular.module('woServices');
|
||||
|
||||
DeviceStorageDAO.prototype.init = function(emailAddress, callback) {
|
||||
this._localDbDao.init(emailAddress, callback);
|
||||
// expose an instance with the static dbName 'app-config' to store configuration data
|
||||
ngModule.factory('appConfigStore', function(appConfigLawnchair) {
|
||||
var deviceStorage = new DeviceStorage(appConfigLawnchair);
|
||||
deviceStorage.init('app-config');
|
||||
return deviceStorage;
|
||||
});
|
||||
|
||||
// expose a singleton instance of DeviceStorage called 'accountStore' to persist user data
|
||||
ngModule.factory('accountStore', function(accountLawnchair) {
|
||||
return new DeviceStorage(accountLawnchair);
|
||||
});
|
||||
|
||||
module.exports = DeviceStorage;
|
||||
|
||||
//
|
||||
// Implementation
|
||||
//
|
||||
|
||||
/**
|
||||
* High level storage api that handles all persistence of a user's data on the device.
|
||||
*/
|
||||
function DeviceStorage(lawnchairDAO) {
|
||||
this._lawnchairDAO = lawnchairDAO;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the lawnchair database
|
||||
* @param {String} dbName The name of the database
|
||||
*/
|
||||
DeviceStorage.prototype.init = function(dbName) {
|
||||
this._lawnchairDAO.init(dbName);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -17,7 +40,7 @@ DeviceStorageDAO.prototype.init = function(emailAddress, callback) {
|
||||
* @param list [Array] The list of items to be persisted
|
||||
* @param type [String] The type of item to be persisted e.g. 'email'
|
||||
*/
|
||||
DeviceStorageDAO.prototype.storeList = function(list, type, callback) {
|
||||
DeviceStorage.prototype.storeList = function(list, type, callback) {
|
||||
var key, items = [];
|
||||
|
||||
// nothing to store
|
||||
@ -43,14 +66,14 @@ DeviceStorageDAO.prototype.storeList = function(list, type, callback) {
|
||||
});
|
||||
});
|
||||
|
||||
this._localDbDao.batch(items, callback);
|
||||
this._lawnchairDAO.batch(items, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Deletes items of a certain type from storage
|
||||
*/
|
||||
DeviceStorageDAO.prototype.removeList = function(type, callback) {
|
||||
this._localDbDao.removeList(type, callback);
|
||||
DeviceStorage.prototype.removeList = function(type, callback) {
|
||||
this._lawnchairDAO.removeList(type, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -59,16 +82,16 @@ DeviceStorageDAO.prototype.removeList = function(type, callback) {
|
||||
* @param offset [Number] The offset of items to fetch (0 is the last stored item)
|
||||
* @param num [Number] The number of items to fetch (null means fetch all)
|
||||
*/
|
||||
DeviceStorageDAO.prototype.listItems = function(type, offset, num, callback) {
|
||||
DeviceStorage.prototype.listItems = function(type, offset, num, callback) {
|
||||
// fetch all items of a certain type from the data-store
|
||||
this._localDbDao.list(type, offset, num, callback);
|
||||
this._lawnchairDAO.list(type, offset, num, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear the whole device data-store
|
||||
*/
|
||||
DeviceStorageDAO.prototype.clear = function(callback) {
|
||||
this._localDbDao.clear(callback);
|
||||
DeviceStorage.prototype.clear = function(callback) {
|
||||
this._lawnchairDAO.clear(callback);
|
||||
};
|
||||
|
||||
//
|
||||
@ -88,6 +111,4 @@ function createKey(i, type) {
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
module.exports = DeviceStorageDAO;
|
||||
}
|
16
src/js/service/index.js
Normal file
16
src/js/service/index.js
Normal file
@ -0,0 +1,16 @@
|
||||
'use strict';
|
||||
|
||||
angular.module('woServices', ['woAppConfig', 'woUtil', 'woCrypto']);
|
||||
|
||||
require('./rest');
|
||||
require('./invitation');
|
||||
require('./mail-config');
|
||||
require('./newsletter');
|
||||
require('./oauth');
|
||||
require('./privatekey');
|
||||
require('./publickey');
|
||||
require('./admin');
|
||||
require('./lawnchair');
|
||||
require('./devicestorage');
|
||||
require('./auth');
|
||||
require('./keychain');
|
@ -1,20 +1,24 @@
|
||||
'use strict';
|
||||
|
||||
var ngModule = angular.module('woServices');
|
||||
ngModule.service('invitation', Invitation);
|
||||
module.exports = Invitation;
|
||||
|
||||
/**
|
||||
* The InvitationDAO 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
|
||||
*/
|
||||
var InvitationDAO = function(restDao) {
|
||||
this._restDao = restDao;
|
||||
};
|
||||
function Invitation(invitationRestDao) {
|
||||
this._restDao = invitationRestDao;
|
||||
}
|
||||
|
||||
//
|
||||
// Constants
|
||||
//
|
||||
|
||||
InvitationDAO.INVITE_MISSING = 1;
|
||||
InvitationDAO.INVITE_PENDING = 2;
|
||||
InvitationDAO.INVITE_SUCCESS = 4;
|
||||
Invitation.INVITE_MISSING = 1;
|
||||
Invitation.INVITE_PENDING = 2;
|
||||
Invitation.INVITE_SUCCESS = 4;
|
||||
|
||||
//
|
||||
// API
|
||||
@ -26,7 +30,7 @@ InvitationDAO.INVITE_SUCCESS = 4;
|
||||
* @param {String} options.sender User ID of the sender
|
||||
* @param {Function} callback(error, status) Returns information if the invitation worked (INVITE_SUCCESS), if an invitation is already pendin (INVITE_PENDING), or information if an error occurred.
|
||||
*/
|
||||
InvitationDAO.prototype.invite = function(options, callback) {
|
||||
Invitation.prototype.invite = function(options, callback) {
|
||||
if (typeof options !== 'object' || typeof options.recipient !== 'string' || typeof options.recipient !== 'string') {
|
||||
callback({
|
||||
errMsg: 'erroneous usage of api: incorrect parameters!'
|
||||
@ -44,10 +48,10 @@ InvitationDAO.prototype.invite = function(options, callback) {
|
||||
}
|
||||
|
||||
if (status === 201) {
|
||||
callback(null, InvitationDAO.INVITE_SUCCESS);
|
||||
callback(null, Invitation.INVITE_SUCCESS);
|
||||
return;
|
||||
} else if (status === 304) {
|
||||
callback(null, InvitationDAO.INVITE_PENDING);
|
||||
callback(null, Invitation.INVITE_PENDING);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -55,6 +59,4 @@ InvitationDAO.prototype.invite = function(options, callback) {
|
||||
errMsg: 'unexpected invitation state'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = InvitationDAO;
|
||||
};
|
@ -1,36 +1,60 @@
|
||||
/**
|
||||
* A high-level Data-Access Api for handling Keypair synchronization
|
||||
* between the cloud service and the device's local storage
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var util = require('crypto-lib').util,
|
||||
config = require('../app-config').config;
|
||||
var ngModule = angular.module('woServices');
|
||||
ngModule.service('keychain', Keychain);
|
||||
module.exports = Keychain;
|
||||
|
||||
var util = require('crypto-lib').util;
|
||||
|
||||
var DB_PUBLICKEY = 'publickey',
|
||||
DB_PRIVATEKEY = 'privatekey',
|
||||
DB_DEVICENAME = 'devicename',
|
||||
DB_DEVICE_SECRET = 'devicesecret';
|
||||
|
||||
var KeychainDAO = function(localDbDao, publicKeyDao, privateKeyDao, crypto, pgp) {
|
||||
this._localDbDao = localDbDao;
|
||||
this._publicKeyDao = publicKeyDao;
|
||||
this._privateKeyDao = privateKeyDao;
|
||||
/**
|
||||
* A high-level Data-Access Api for handling Keypair synchronization
|
||||
* between the cloud service and the device's local storage
|
||||
*/
|
||||
function Keychain(accountLawnchair, publicKey, privateKey, crypto, pgp, dialog, appConfig) {
|
||||
this._lawnchairDAO = accountLawnchair;
|
||||
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
|
||||
* @param {Function} callback(error) Callback with an optional error object when the verification is done. If the was an error, the error object contains the information for it.
|
||||
*/
|
||||
KeychainDAO.prototype.verifyPublicKey = function(uuid, callback) {
|
||||
Keychain.prototype.verifyPublicKey = function(uuid, callback) {
|
||||
this._publicKeyDao.verify(uuid, callback);
|
||||
};
|
||||
|
||||
@ -40,7 +64,7 @@ KeychainDAO.prototype.verifyPublicKey = function(uuid, callback) {
|
||||
* @param ids [Array] the key ids as [{_id, userId}]
|
||||
* @return [PublicKeyCollection] The requiested public keys
|
||||
*/
|
||||
KeychainDAO.prototype.getPublicKeys = function(ids, callback) {
|
||||
Keychain.prototype.getPublicKeys = function(ids, callback) {
|
||||
var self = this,
|
||||
after, already, pubkeys = [];
|
||||
|
||||
@ -85,7 +109,7 @@ KeychainDAO.prototype.getPublicKeys = function(ids, callback) {
|
||||
* @param {String} options.overridePermission (optional) Indicates if the update should happen automatically (true) or with the user being queried (false). Defaults to false
|
||||
* @param {Function} callback(error, key) Invoked when the key has been updated or an error occurred
|
||||
*/
|
||||
KeychainDAO.prototype.refreshKeyForUserId = function(options, callback) {
|
||||
Keychain.prototype.refreshKeyForUserId = function(options, callback) {
|
||||
var self = this,
|
||||
userId = options.userId,
|
||||
overridePermission = options.overridePermission;
|
||||
@ -189,11 +213,11 @@ KeychainDAO.prototype.refreshKeyForUserId = function(options, callback) {
|
||||
* Look up a reveiver's public key by user id
|
||||
* @param userId [String] the receiver's email address
|
||||
*/
|
||||
KeychainDAO.prototype.getReceiverPublicKey = function(userId, callback) {
|
||||
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;
|
||||
@ -266,13 +290,13 @@ KeychainDAO.prototype.getReceiverPublicKey = function(userId, callback) {
|
||||
* @param {String} deviceName The device name
|
||||
* @param {Function} callback(error)
|
||||
*/
|
||||
KeychainDAO.prototype.setDeviceName = function(deviceName, callback) {
|
||||
Keychain.prototype.setDeviceName = function(deviceName, callback) {
|
||||
if (!deviceName) {
|
||||
callback(new Error('Please set a device name!'));
|
||||
return;
|
||||
}
|
||||
|
||||
this._localDbDao.persist(DB_DEVICENAME, deviceName, callback);
|
||||
this._lawnchairDAO.persist(DB_DEVICENAME, deviceName, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -280,9 +304,9 @@ KeychainDAO.prototype.setDeviceName = function(deviceName, callback) {
|
||||
* @param {Function} callback(error, deviceName)
|
||||
* @return {String} The device name
|
||||
*/
|
||||
KeychainDAO.prototype.getDeviceName = function(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;
|
||||
@ -301,11 +325,12 @@ KeychainDAO.prototype.getDeviceName = function(callback) {
|
||||
* Geneate a device specific key and secret to authenticate to the private key service.
|
||||
* @param {Function} callback(error, deviceSecret:[base64 encoded string])
|
||||
*/
|
||||
KeychainDAO.prototype.getDeviceSecret = function(callback) {
|
||||
var self = this;
|
||||
Keychain.prototype.getDeviceSecret = function(callback) {
|
||||
var self = this,
|
||||
config = self._appConfig.config;
|
||||
|
||||
// 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;
|
||||
@ -320,7 +345,7 @@ KeychainDAO.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;
|
||||
@ -336,9 +361,10 @@ KeychainDAO.prototype.getDeviceSecret = function(callback) {
|
||||
* @param {String} options.userId The user's email address
|
||||
* @param {Function} callback(error)
|
||||
*/
|
||||
KeychainDAO.prototype.registerDevice = function(options, callback) {
|
||||
Keychain.prototype.registerDevice = function(options, callback) {
|
||||
var self = this,
|
||||
devName;
|
||||
devName,
|
||||
config = self._appConfig.config;
|
||||
|
||||
// check if deviceName is already persisted in storage
|
||||
self.getDeviceName(function(err, deviceName) {
|
||||
@ -435,9 +461,10 @@ KeychainDAO.prototype.registerDevice = function(options, callback) {
|
||||
* @param {Function} callback(error, authSessionKey)
|
||||
* @return {Object} {sessionId:String, sessionKey:[base64 encoded]}
|
||||
*/
|
||||
KeychainDAO.prototype._authenticateToPrivateKeyServer = function(userId, callback) {
|
||||
Keychain.prototype._authenticateToPrivateKeyServer = function(userId, callback) {
|
||||
var self = this,
|
||||
sessionId;
|
||||
sessionId,
|
||||
config = self._appConfig.config;
|
||||
|
||||
// request auth session key required for upload
|
||||
self._privateKeyDao.requestAuthSessionKey({
|
||||
@ -552,8 +579,9 @@ KeychainDAO.prototype._authenticateToPrivateKeyServer = function(userId, callbac
|
||||
* @param {String} options.code The randomly generated or self selected code used to derive the key for the encryption of the private PGP key
|
||||
* @param {Function} callback(error)
|
||||
*/
|
||||
KeychainDAO.prototype.uploadPrivateKey = function(options, callback) {
|
||||
Keychain.prototype.uploadPrivateKey = function(options, callback) {
|
||||
var self = this,
|
||||
config = self._appConfig.config,
|
||||
keySize = config.symKeySize,
|
||||
salt;
|
||||
|
||||
@ -646,7 +674,7 @@ KeychainDAO.prototype.uploadPrivateKey = function(options, callback) {
|
||||
* @param {String} options.keyId The private PGP key id
|
||||
* @param {Function} callback(error)
|
||||
*/
|
||||
KeychainDAO.prototype.requestPrivateKeyDownload = function(options, callback) {
|
||||
Keychain.prototype.requestPrivateKeyDownload = function(options, callback) {
|
||||
this._privateKeyDao.requestDownload(options, callback);
|
||||
};
|
||||
|
||||
@ -656,7 +684,7 @@ KeychainDAO.prototype.requestPrivateKeyDownload = function(options, callback) {
|
||||
* @param {String} options.keyId The private PGP key id
|
||||
* @param {Function} callback(error)
|
||||
*/
|
||||
KeychainDAO.prototype.hasPrivateKey = function(options, callback) {
|
||||
Keychain.prototype.hasPrivateKey = function(options, callback) {
|
||||
this._privateKeyDao.hasPrivateKey(options, callback);
|
||||
};
|
||||
|
||||
@ -667,7 +695,7 @@ KeychainDAO.prototype.hasPrivateKey = function(options, callback) {
|
||||
* @param {String} options.recoveryToken The recovery token acquired via email/sms from the key server
|
||||
* @param {Function} callback(error, encryptedPrivateKey)
|
||||
*/
|
||||
KeychainDAO.prototype.downloadPrivateKey = function(options, callback) {
|
||||
Keychain.prototype.downloadPrivateKey = function(options, callback) {
|
||||
this._privateKeyDao.download(options, callback);
|
||||
};
|
||||
|
||||
@ -681,10 +709,11 @@ KeychainDAO.prototype.downloadPrivateKey = function(options, callback) {
|
||||
* @param {String} options.iv The iv used to encrypt the private PGP key
|
||||
* @param {Function} callback(error, keyObject)
|
||||
*/
|
||||
KeychainDAO.prototype.decryptAndStorePrivateKeyLocally = function(options, callback) {
|
||||
Keychain.prototype.decryptAndStorePrivateKeyLocally = function(options, callback) {
|
||||
var self = this,
|
||||
code = options.code,
|
||||
salt = options.salt,
|
||||
config = self._appConfig.config,
|
||||
keySize = config.symKeySize;
|
||||
|
||||
if (!options._id || !options.userId || !options.code || !options.salt || !options.encryptedPrivateKey || !options.iv) {
|
||||
@ -756,11 +785,11 @@ KeychainDAO.prototype.decryptAndStorePrivateKeyLocally = function(options, callb
|
||||
* If no key pair exists, null is returned.
|
||||
* return [Object] The user's key pair {publicKey, privateKey}
|
||||
*/
|
||||
KeychainDAO.prototype.getUserKeyPair = function(userId, callback) {
|
||||
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;
|
||||
@ -833,7 +862,7 @@ KeychainDAO.prototype.getUserKeyPair = function(userId, callback) {
|
||||
* locally and in the cloud and persist arccordingly
|
||||
* @param [Object] The user's key pair {publicKey, privateKey}
|
||||
*/
|
||||
KeychainDAO.prototype.putUserKeyPair = function(keypair, callback) {
|
||||
Keychain.prototype.putUserKeyPair = function(keypair, callback) {
|
||||
var self = this;
|
||||
|
||||
// validate input
|
||||
@ -872,7 +901,7 @@ KeychainDAO.prototype.putUserKeyPair = function(keypair, callback) {
|
||||
// Helper functions
|
||||
//
|
||||
|
||||
KeychainDAO.prototype.lookupPublicKey = function(id, callback) {
|
||||
Keychain.prototype.lookupPublicKey = function(id, callback) {
|
||||
var self = this;
|
||||
|
||||
if (!id) {
|
||||
@ -883,7 +912,7 @@ KeychainDAO.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;
|
||||
@ -917,30 +946,28 @@ KeychainDAO.prototype.lookupPublicKey = function(id, callback) {
|
||||
/**
|
||||
* List all the locally stored public keys
|
||||
*/
|
||||
KeychainDAO.prototype.listLocalPublicKeys = function(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);
|
||||
};
|
||||
|
||||
KeychainDAO.prototype.removeLocalPublicKey = function(id, callback) {
|
||||
this._localDbDao.remove(DB_PUBLICKEY + '_' + id, callback);
|
||||
Keychain.prototype.removeLocalPublicKey = function(id, callback) {
|
||||
this._lawnchairDAO.remove(DB_PUBLICKEY + '_' + id, callback);
|
||||
};
|
||||
|
||||
KeychainDAO.prototype.lookupPrivateKey = function(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);
|
||||
};
|
||||
|
||||
KeychainDAO.prototype.saveLocalPublicKey = function(pubkey, 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);
|
||||
};
|
||||
|
||||
KeychainDAO.prototype.saveLocalPrivateKey = function(privkey, callback) {
|
||||
Keychain.prototype.saveLocalPrivateKey = function(privkey, callback) {
|
||||
// persist private key (email, _id)
|
||||
var prkLookupKey = DB_PRIVATEKEY + '_' + privkey._id;
|
||||
this._localDbDao.persist(prkLookupKey, privkey, callback);
|
||||
};
|
||||
|
||||
module.exports = KeychainDAO;
|
||||
this._lawnchairDAO.persist(prkLookupKey, privkey, callback);
|
||||
};
|
@ -1,30 +1,26 @@
|
||||
'use strict';
|
||||
|
||||
var ngModule = angular.module('woServices');
|
||||
ngModule.service('appConfigLawnchair', LawnchairDAO);
|
||||
ngModule.service('accountLawnchair', LawnchairDAO);
|
||||
module.exports = LawnchairDAO;
|
||||
|
||||
/**
|
||||
* Handles generic caching of JSON objects in a lawnchair adapter
|
||||
*/
|
||||
function LawnchairDAO() {}
|
||||
|
||||
'use strict';
|
||||
|
||||
var LawnchairDAO = function() {};
|
||||
|
||||
LawnchairDAO.prototype.init = function(dbName, callback) {
|
||||
/**
|
||||
* Initialize the lawnchair database
|
||||
* @param {String} dbName The name of the database
|
||||
*/
|
||||
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();
|
||||
});
|
||||
};
|
||||
|
||||
@ -212,5 +208,3 @@ LawnchairDAO.prototype.removeList = function(type, callback) {
|
||||
LawnchairDAO.prototype.clear = function(callback) {
|
||||
this._db.nuke(callback);
|
||||
};
|
||||
|
||||
module.exports = LawnchairDAO;
|
@ -2,12 +2,12 @@
|
||||
|
||||
var ngModule = angular.module('woServices');
|
||||
ngModule.service('mailConfig', MailConfig);
|
||||
module.exports = MailConfig;
|
||||
|
||||
var cfg = require('../app-config').config;
|
||||
|
||||
function MailConfig($http, $q) {
|
||||
function MailConfig($http, $q, appConfig) {
|
||||
this._http = $http;
|
||||
this._q = $q;
|
||||
this._appConfig = appConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -20,7 +20,7 @@ MailConfig.prototype.get = function(emailAddress) {
|
||||
});
|
||||
}
|
||||
|
||||
var url = cfg.settingsUrl + emailAddress.split('@')[1];
|
||||
var url = this._appConfig.config.settingsUrl + emailAddress.split('@')[1];
|
||||
return this._http.get(url).then(function(res) {
|
||||
return res.data;
|
||||
});
|
||||
|
@ -1,7 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
var ngModule = angular.module('woServices', []);
|
||||
var ngModule = angular.module('woServices');
|
||||
ngModule.service('newsletter', Newsletter);
|
||||
module.exports = Newsletter;
|
||||
|
||||
function Newsletter($q) {
|
||||
this._q = $q;
|
||||
|
@ -1,8 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
var OAuth = function(googleApi) {
|
||||
this._googleApi = googleApi;
|
||||
};
|
||||
var ngModule = angular.module('woServices');
|
||||
ngModule.service('oauth', OAuth);
|
||||
module.exports = OAuth;
|
||||
|
||||
function OAuth(oauthRestDao) {
|
||||
this._googleApi = oauthRestDao;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if chrome.identity api is supported
|
||||
@ -94,6 +98,4 @@ OAuth.prototype.queryEmailAddress = function(token, callback) {
|
||||
|
||||
callback(null, info.email);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = OAuth;
|
||||
};
|
@ -1,8 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
var PrivateKeyDAO = function(restDao) {
|
||||
this._restDao = restDao;
|
||||
};
|
||||
var ngModule = angular.module('woServices');
|
||||
ngModule.service('privateKey', PrivateKey);
|
||||
module.exports = PrivateKey;
|
||||
|
||||
function PrivateKey(privateKeyRestDao) {
|
||||
this._restDao = privateKeyRestDao;
|
||||
}
|
||||
|
||||
//
|
||||
// Device registration functions
|
||||
@ -15,7 +19,7 @@ var PrivateKeyDAO = function(restDao) {
|
||||
* @param {Function} callback(error, regSessionKey)
|
||||
* @return {Object} {encryptedRegSessionKey:[base64]}
|
||||
*/
|
||||
PrivateKeyDAO.prototype.requestDeviceRegistration = function(options, callback) {
|
||||
PrivateKey.prototype.requestDeviceRegistration = function(options, callback) {
|
||||
var uri;
|
||||
|
||||
if (!options.userId || !options.deviceName) {
|
||||
@ -35,7 +39,7 @@ PrivateKeyDAO.prototype.requestDeviceRegistration = function(options, callback)
|
||||
* @param {String} options.iv The iv used for encryption
|
||||
* @param {Function} callback(error)
|
||||
*/
|
||||
PrivateKeyDAO.prototype.uploadDeviceSecret = function(options, callback) {
|
||||
PrivateKey.prototype.uploadDeviceSecret = function(options, callback) {
|
||||
var uri;
|
||||
|
||||
if (!options.userId || !options.deviceName || !options.encryptedDeviceSecret || !options.iv) {
|
||||
@ -57,7 +61,7 @@ PrivateKeyDAO.prototype.uploadDeviceSecret = function(options, callback) {
|
||||
* @param {Function} callback(error, authSessionKey)
|
||||
* @return {Object} {sessionId, encryptedAuthSessionKey:[base64 encoded], encryptedChallenge:[base64 encoded]}
|
||||
*/
|
||||
PrivateKeyDAO.prototype.requestAuthSessionKey = function(options, callback) {
|
||||
PrivateKey.prototype.requestAuthSessionKey = function(options, callback) {
|
||||
var uri;
|
||||
|
||||
if (!options.userId) {
|
||||
@ -77,7 +81,7 @@ PrivateKeyDAO.prototype.requestAuthSessionKey = function(options, callback) {
|
||||
* @param {String} options.iv The iv used for encryption
|
||||
* @param {Function} callback(error)
|
||||
*/
|
||||
PrivateKeyDAO.prototype.verifyAuthentication = function(options, callback) {
|
||||
PrivateKey.prototype.verifyAuthentication = function(options, callback) {
|
||||
var uri;
|
||||
|
||||
if (!options.userId || !options.sessionId || !options.encryptedChallenge || !options.encryptedDeviceSecret || !options.iv) {
|
||||
@ -97,7 +101,7 @@ PrivateKeyDAO.prototype.verifyAuthentication = function(options, callback) {
|
||||
* @param {String} options.sessionId The session id
|
||||
* @param {Function} callback(error)
|
||||
*/
|
||||
PrivateKeyDAO.prototype.upload = function(options, callback) {
|
||||
PrivateKey.prototype.upload = function(options, callback) {
|
||||
var uri;
|
||||
|
||||
if (!options._id || !options.userId || !options.encryptedPrivateKey || !options.sessionId || !options.salt || !options.iv) {
|
||||
@ -116,7 +120,7 @@ PrivateKeyDAO.prototype.upload = function(options, callback) {
|
||||
* @param {Function} callback(error, found)
|
||||
* @return {Boolean} whether the key was found on the server or not.
|
||||
*/
|
||||
PrivateKeyDAO.prototype.hasPrivateKey = function(options, callback) {
|
||||
PrivateKey.prototype.hasPrivateKey = function(options, callback) {
|
||||
if (!options.userId || !options.keyId) {
|
||||
callback(new Error('Incomplete arguments!'));
|
||||
return;
|
||||
@ -147,7 +151,7 @@ PrivateKeyDAO.prototype.hasPrivateKey = function(options, callback) {
|
||||
* @param {Function} callback(error, found)
|
||||
* @return {Boolean} whether the key was found on the server or not.
|
||||
*/
|
||||
PrivateKeyDAO.prototype.requestDownload = function(options, callback) {
|
||||
PrivateKey.prototype.requestDownload = function(options, callback) {
|
||||
if (!options.userId || !options.keyId) {
|
||||
callback(new Error('Incomplete arguments!'));
|
||||
return;
|
||||
@ -179,7 +183,7 @@ PrivateKeyDAO.prototype.requestDownload = function(options, callback) {
|
||||
* @param {Function} callback(error, encryptedPrivateKey)
|
||||
* @return {Object} {_id:[hex encoded capital 16 char key id], encryptedPrivateKey:[base64 encoded], encryptedUserId: [base64 encoded]}
|
||||
*/
|
||||
PrivateKeyDAO.prototype.download = function(options, callback) {
|
||||
PrivateKey.prototype.download = function(options, callback) {
|
||||
var uri;
|
||||
|
||||
if (!options.userId || !options.keyId || !options.recoveryToken) {
|
||||
@ -191,6 +195,4 @@ PrivateKeyDAO.prototype.download = function(options, callback) {
|
||||
this._restDao.get({
|
||||
uri: uri
|
||||
}, callback);
|
||||
};
|
||||
|
||||
module.exports = PrivateKeyDAO;
|
||||
};
|
@ -1,13 +1,17 @@
|
||||
'use strict';
|
||||
|
||||
var PublicKeyDAO = function(restDao) {
|
||||
this._restDao = restDao;
|
||||
};
|
||||
var ngModule = angular.module('woServices');
|
||||
ngModule.service('publicKey', PublicKey);
|
||||
module.exports = PublicKey;
|
||||
|
||||
function PublicKey(publicKeyRestDao) {
|
||||
this._restDao = publicKeyRestDao;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the public key behind the given uuid
|
||||
*/
|
||||
PublicKeyDAO.prototype.verify = function(uuid, callback) {
|
||||
PublicKey.prototype.verify = function(uuid, callback) {
|
||||
var uri = '/verify/' + uuid;
|
||||
|
||||
this._restDao.get({
|
||||
@ -27,7 +31,7 @@ PublicKeyDAO.prototype.verify = function(uuid, callback) {
|
||||
/**
|
||||
* Find the user's corresponding public key
|
||||
*/
|
||||
PublicKeyDAO.prototype.get = function(keyId, callback) {
|
||||
PublicKey.prototype.get = function(keyId, callback) {
|
||||
var uri = '/publickey/key/' + keyId;
|
||||
|
||||
this._restDao.get({
|
||||
@ -50,7 +54,7 @@ PublicKeyDAO.prototype.get = function(keyId, callback) {
|
||||
/**
|
||||
* Find the user's corresponding public key by email
|
||||
*/
|
||||
PublicKeyDAO.prototype.getByUserId = function(userId, callback) {
|
||||
PublicKey.prototype.getByUserId = function(userId, callback) {
|
||||
var uri = '/publickey/user/' + userId;
|
||||
|
||||
this._restDao.get({
|
||||
@ -87,7 +91,7 @@ PublicKeyDAO.prototype.getByUserId = function(userId, callback) {
|
||||
/**
|
||||
* Persist the user's publc key
|
||||
*/
|
||||
PublicKeyDAO.prototype.put = function(pubkey, callback) {
|
||||
PublicKey.prototype.put = function(pubkey, callback) {
|
||||
var uri = '/publickey/user/' + pubkey.userId + '/key/' + pubkey._id;
|
||||
this._restDao.put(pubkey, uri, callback);
|
||||
};
|
||||
@ -95,9 +99,7 @@ PublicKeyDAO.prototype.put = function(pubkey, callback) {
|
||||
/**
|
||||
* Delete the public key from the cloud storage service
|
||||
*/
|
||||
PublicKeyDAO.prototype.remove = function(keyId, callback) {
|
||||
PublicKey.prototype.remove = function(keyId, callback) {
|
||||
var uri = '/publickey/key/' + keyId;
|
||||
this._restDao.remove(uri, callback);
|
||||
};
|
||||
|
||||
module.exports = PublicKeyDAO;
|
||||
};
|
@ -1,13 +1,52 @@
|
||||
'use strict';
|
||||
|
||||
var config = require('../app-config').config;
|
||||
var ngModule = angular.module('woServices');
|
||||
|
||||
var RestDAO = function(baseUri) {
|
||||
if (baseUri) {
|
||||
this._baseUri = baseUri;
|
||||
} else {
|
||||
this._baseUri = config.cloudUrl;
|
||||
}
|
||||
// rest dao for use in the public key service
|
||||
ngModule.factory('publicKeyRestDao', function(appConfig) {
|
||||
var dao = new RestDAO();
|
||||
dao.setBaseUri(appConfig.config.cloudUrl);
|
||||
return dao;
|
||||
});
|
||||
|
||||
// rest dao for use in the private key service
|
||||
ngModule.factory('privateKeyRestDao', function(appConfig) {
|
||||
var dao = new RestDAO();
|
||||
dao.setBaseUri(appConfig.config.privkeyServerUrl);
|
||||
return dao;
|
||||
});
|
||||
|
||||
// rest dao for use in the invitation service
|
||||
ngModule.factory('invitationRestDao', function(appConfig) {
|
||||
var dao = new RestDAO();
|
||||
dao.setBaseUri(appConfig.config.cloudUrl);
|
||||
return dao;
|
||||
});
|
||||
|
||||
// rest dao for use in the admin service
|
||||
ngModule.factory('adminRestDao', function(appConfig) {
|
||||
var dao = new RestDAO();
|
||||
dao.setBaseUri(appConfig.config.adminUrl);
|
||||
return dao;
|
||||
});
|
||||
|
||||
// rest dao for use in the oauth service
|
||||
ngModule.factory('oauthRestDao', function() {
|
||||
var dao = new RestDAO();
|
||||
dao.setBaseUri('https://www.googleapis.com');
|
||||
return dao;
|
||||
});
|
||||
|
||||
module.exports = RestDAO;
|
||||
|
||||
function RestDAO() {}
|
||||
|
||||
/**
|
||||
* Set the REST DAO's base url
|
||||
* @param {String} baseUri The base url e.g. https://api.example.com
|
||||
*/
|
||||
RestDAO.prototype.setBaseUri = function(baseUri) {
|
||||
this._baseUri = baseUri;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -116,6 +155,4 @@ RestDAO.prototype._processRequest = function(options, callback) {
|
||||
};
|
||||
|
||||
xhr.send(options.payload ? JSON.stringify(options.payload) : undefined);
|
||||
};
|
||||
|
||||
module.exports = RestDAO;
|
||||
};
|
8
src/js/util/axe.js
Normal file
8
src/js/util/axe.js
Normal file
@ -0,0 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
var axe = require('axe-logger');
|
||||
|
||||
var ngModule = angular.module('woUtil');
|
||||
ngModule.factory('axe', function() {
|
||||
return axe;
|
||||
});
|
@ -1,9 +1,10 @@
|
||||
'use strict';
|
||||
|
||||
var ngModule = angular.module('woUtil');
|
||||
ngModule.service('connectionDoctor', ConnectionDoctor);
|
||||
module.exports = ConnectionDoctor;
|
||||
|
||||
var TCPSocket = require('tcp-socket'),
|
||||
appConfig = require('../app-config'),
|
||||
cfg = appConfig.config,
|
||||
strings = appConfig.string,
|
||||
ImapClient = require('imap-client'),
|
||||
SmtpClient = require('wo-smtpclient');
|
||||
|
||||
@ -14,8 +15,10 @@ var TCPSocket = require('tcp-socket'),
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var ConnectionDoctor = function() {};
|
||||
|
||||
function ConnectionDoctor(appConfig) {
|
||||
this._appConfig = appConfig;
|
||||
this._workerPath = appConfig.config.workerPath + '/tcp-socket-tls-worker.min.js';
|
||||
}
|
||||
|
||||
//
|
||||
// Error codes
|
||||
@ -29,9 +32,6 @@ var AUTH_REJECTED = ConnectionDoctor.AUTH_REJECTED = 46;
|
||||
var NO_INBOX = ConnectionDoctor.NO_INBOX = 47;
|
||||
var GENERIC_ERROR = ConnectionDoctor.GENERIC_ERROR = 48;
|
||||
|
||||
|
||||
var WORKER_PATH = cfg.workerPath + '/tcp-socket-tls-worker.min.js';
|
||||
|
||||
//
|
||||
// Public API
|
||||
//
|
||||
@ -54,7 +54,7 @@ ConnectionDoctor.prototype.configure = function(credentials) {
|
||||
secure: this.credentials.imap.secure,
|
||||
ignoreTLS: this.credentials.imap.ignoreTLS,
|
||||
ca: this.credentials.imap.ca,
|
||||
tlsWorkerPath: WORKER_PATH,
|
||||
tlsWorkerPath: this._workerPath,
|
||||
auth: {
|
||||
user: this.credentials.username,
|
||||
pass: this.credentials.password,
|
||||
@ -66,7 +66,7 @@ ConnectionDoctor.prototype.configure = function(credentials) {
|
||||
useSecureTransport: this.credentials.smtp.secure,
|
||||
ignoreTLS: this.credentials.smtp.ignoreTLS,
|
||||
ca: this.credentials.smtp.ca,
|
||||
tlsWorkerPath: WORKER_PATH,
|
||||
tlsWorkerPath: this._workerPath,
|
||||
auth: {
|
||||
user: this.credentials.username,
|
||||
pass: this.credentials.password,
|
||||
@ -133,7 +133,7 @@ ConnectionDoctor.prototype._checkOnline = function(callback) {
|
||||
if (navigator.onLine) {
|
||||
callback();
|
||||
} else {
|
||||
callback(createError(OFFLINE, strings.connDocOffline));
|
||||
callback(createError(OFFLINE, this._appConfig.string.connDocOffline));
|
||||
}
|
||||
};
|
||||
|
||||
@ -150,18 +150,20 @@ ConnectionDoctor.prototype._checkReachable = function(options, callback) {
|
||||
error, // remember the error message
|
||||
timeout, // remember the timeout object
|
||||
host = options.host + ':' + options.port,
|
||||
hasTimedOut = false; // prevents multiple callbacks
|
||||
hasTimedOut = false, // prevents multiple callbacks
|
||||
cfg = this._appConfig.config,
|
||||
str = this._appConfig.string;
|
||||
|
||||
timeout = setTimeout(function() {
|
||||
hasTimedOut = true;
|
||||
callback(createError(HOST_TIMEOUT, strings.connDocHostTimeout.replace('{0}', host).replace('{1}', cfg.connDocTimeout)));
|
||||
callback(createError(HOST_TIMEOUT, str.connDocHostTimeout.replace('{0}', host).replace('{1}', cfg.connDocTimeout)));
|
||||
}, cfg.connDocTimeout);
|
||||
|
||||
socket = TCPSocket.open(options.host, options.port, {
|
||||
binaryType: 'arraybuffer',
|
||||
useSecureTransport: options.secure,
|
||||
ca: options.ca,
|
||||
tlsWorkerPath: WORKER_PATH
|
||||
tlsWorkerPath: this._workerPath
|
||||
});
|
||||
|
||||
socket.ondata = function() {}; // we don't actually care about the data
|
||||
@ -174,14 +176,14 @@ ConnectionDoctor.prototype._checkReachable = function(options, callback) {
|
||||
socket.oncert = function() {
|
||||
if (options.ca) {
|
||||
// the certificate we already have is outdated
|
||||
error = createError(TLS_WRONG_CERT, strings.connDocTlsWrongCert.replace('{0}', host));
|
||||
error = createError(TLS_WRONG_CERT, str.connDocTlsWrongCert.replace('{0}', host));
|
||||
}
|
||||
};
|
||||
} catch (e) {}
|
||||
|
||||
socket.onerror = function(e) {
|
||||
if (!error) {
|
||||
error = createError(HOST_UNREACHABLE, strings.connDocHostUnreachable.replace('{0}', host), e.data);
|
||||
error = createError(HOST_UNREACHABLE, str.connDocHostUnreachable.replace('{0}', host), e.data);
|
||||
}
|
||||
};
|
||||
|
||||
@ -206,8 +208,8 @@ ConnectionDoctor.prototype._checkReachable = function(options, callback) {
|
||||
ConnectionDoctor.prototype._checkImap = function(callback) {
|
||||
var self = this,
|
||||
loggedIn = false,
|
||||
host = self.credentials.imap.host + ':' + self.credentials.imap.port;
|
||||
|
||||
host = self.credentials.imap.host + ':' + self.credentials.imap.port,
|
||||
str = this._appConfig.string;
|
||||
|
||||
self._imap.onCert = function(pemEncodedCert) {
|
||||
if (!self.credentials.imap.ca) {
|
||||
@ -219,9 +221,9 @@ ConnectionDoctor.prototype._checkImap = function(callback) {
|
||||
// the global onError handler, so we need to track if login was successful
|
||||
self._imap.onError = function(error) {
|
||||
if (!loggedIn) {
|
||||
callback(createError(AUTH_REJECTED, strings.connDocAuthRejected.replace('{0}', host), error));
|
||||
callback(createError(AUTH_REJECTED, str.connDocAuthRejected.replace('{0}', host), error));
|
||||
} else {
|
||||
callback(createError(GENERIC_ERROR, strings.connDocGenericError.replace('{0}', host).replace('{1}', error.message), error));
|
||||
callback(createError(GENERIC_ERROR, str.connDocGenericError.replace('{0}', host).replace('{1}', error.message), error));
|
||||
}
|
||||
};
|
||||
|
||||
@ -230,12 +232,12 @@ ConnectionDoctor.prototype._checkImap = function(callback) {
|
||||
|
||||
self._imap.listWellKnownFolders(function(error, wellKnownFolders) {
|
||||
if (error) {
|
||||
return callback(createError(GENERIC_ERROR, strings.connDocGenericError.replace('{0}', host).replace('{1}', error.message), error));
|
||||
return callback(createError(GENERIC_ERROR, str.connDocGenericError.replace('{0}', host).replace('{1}', error.message), error));
|
||||
}
|
||||
|
||||
if (wellKnownFolders.Inbox.length === 0) {
|
||||
// the client needs at least an inbox folder to work properly
|
||||
return callback(createError(NO_INBOX, strings.connDocNoInbox.replace('{0}', host)));
|
||||
return callback(createError(NO_INBOX, str.connDocNoInbox.replace('{0}', host)));
|
||||
}
|
||||
|
||||
self._imap.logout(function() {
|
||||
@ -254,7 +256,8 @@ ConnectionDoctor.prototype._checkImap = function(callback) {
|
||||
ConnectionDoctor.prototype._checkSmtp = function(callback) {
|
||||
var self = this,
|
||||
host = self.credentials.smtp.host + ':' + self.credentials.smtp.port,
|
||||
errored = false; // tracks if we need to invoke the callback at onclose or not
|
||||
errored = false, // tracks if we need to invoke the callback at onclose or not
|
||||
str = this._appConfig.string;
|
||||
|
||||
self._smtp.oncert = function(pemEncodedCert) {
|
||||
if (!self.credentials.smtp.ca) {
|
||||
@ -265,7 +268,7 @@ ConnectionDoctor.prototype._checkSmtp = function(callback) {
|
||||
self._smtp.onerror = function(error) {
|
||||
if (error) {
|
||||
errored = true;
|
||||
callback(createError(AUTH_REJECTED, strings.connDocAuthRejected.replace('{0}', host), error));
|
||||
callback(createError(AUTH_REJECTED, str.connDocAuthRejected.replace('{0}', host), error));
|
||||
}
|
||||
};
|
||||
|
||||
@ -293,6 +296,4 @@ function createError(code, message, underlyingError) {
|
||||
error.underlyingError = underlyingError;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
module.exports = ConnectionDoctor;
|
||||
}
|
68
src/js/util/dialog.js
Normal file
68
src/js/util/dialog.js
Normal file
@ -0,0 +1,68 @@
|
||||
'use strict';
|
||||
|
||||
var ngModule = angular.module('woUtil');
|
||||
ngModule.service('dialog', Dialog);
|
||||
module.exports = Dialog;
|
||||
|
||||
/**
|
||||
* A central service to display messages to the user in a dialog
|
||||
*/
|
||||
function Dialog($q, axe) {
|
||||
this._q = $q;
|
||||
this._axe = axe;
|
||||
|
||||
// binds the methods to the instance of the dialog service so that we can e.g.
|
||||
// pass dialog.error as a callback to asynchronous functions without having to
|
||||
// do dialog.error.bind(dialog) every time
|
||||
this.info = this.info.bind(this);
|
||||
this.error = this.error.bind(this);
|
||||
this.confirm = this.confirm.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show an information dialog
|
||||
* @param {String} options.title The title of the displayed dialog
|
||||
* @param {String} options.message The message to be displayed
|
||||
* @return {Promise}
|
||||
*/
|
||||
Dialog.prototype.info = function(options) {
|
||||
return this._handle(options, this.displayInfo, 'displayInfo');
|
||||
};
|
||||
|
||||
/**
|
||||
* Show an error dialog
|
||||
* @param {String} options.title (optional) The title of the displayed dialog
|
||||
* @param {String} options.message The message to be displayed
|
||||
* @return {Promise}
|
||||
*/
|
||||
Dialog.prototype.error = function(options) {
|
||||
// log the error
|
||||
if (options) {
|
||||
this._axe.error((options.errMsg || options.message) + (options.stack ? ('\n' + options.stack) : ''));
|
||||
}
|
||||
return this._handle(options, this.displayError, 'displayError');
|
||||
};
|
||||
|
||||
/**
|
||||
* Show an confirm dialog
|
||||
* @param {String} options.title The title of the displayed dialog
|
||||
* @param {String} options.message The message to be displayed
|
||||
* @param {String} options.callback The callback that is called after the confirmation has been granted or denied
|
||||
* @return {Promise}
|
||||
*/
|
||||
Dialog.prototype.confirm = function(options) {
|
||||
return this._handle(options, this.displayConfirm, 'displayConfirm');
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function which returns a promise
|
||||
*/
|
||||
Dialog.prototype._handle = function(options, fn, errMsg) {
|
||||
return this._q(function(resolve, reject) {
|
||||
if (fn) {
|
||||
return resolve(fn(options));
|
||||
} else {
|
||||
reject(new Error('Dialog service ' + errMsg + ' not set!'));
|
||||
}
|
||||
});
|
||||
};
|
@ -1,10 +1,20 @@
|
||||
'use strict';
|
||||
|
||||
var ngModule = angular.module('woUtil');
|
||||
ngModule.service('download', Download);
|
||||
module.exports = Download;
|
||||
|
||||
var util = require('crypto-lib').util;
|
||||
|
||||
var dl = {};
|
||||
/**
|
||||
* A download helper to abstract platform specific behavior
|
||||
*/
|
||||
function Download() {}
|
||||
|
||||
dl.createDownload = function(options) {
|
||||
/**
|
||||
* Create download link and click on it.
|
||||
*/
|
||||
Download.prototype.createDownload = function(options) {
|
||||
var contentType = options.contentType || 'application/octet-stream';
|
||||
var filename = options.filename || 'file';
|
||||
var content = options.content;
|
||||
@ -53,6 +63,4 @@ dl.createDownload = function(options) {
|
||||
}
|
||||
window.open('data:' + contentType + ';base64,' + btoa(content), "_blank");
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = dl;
|
||||
};
|
73
src/js/util/dummy.js
Normal file
73
src/js/util/dummy.js
Normal file
@ -0,0 +1,73 @@
|
||||
'use strict';
|
||||
|
||||
var ngModule = angular.module('woUtil');
|
||||
ngModule.service('dummy', Dummy);
|
||||
module.exports = Dummy;
|
||||
|
||||
function Dummy() {}
|
||||
|
||||
Dummy.prototype.listMails = function() {
|
||||
var uid = 1000000;
|
||||
|
||||
var Email = function(unread, attachments, answered) {
|
||||
this.uid = uid--;
|
||||
this.from = [{
|
||||
name: 'Whiteout Support',
|
||||
address: 'support@whiteout.io'
|
||||
}]; // sender address
|
||||
this.to = [{
|
||||
address: 'max.musterman@gmail.com'
|
||||
}, {
|
||||
address: 'max.musterman@gmail.com'
|
||||
}]; // list of receivers
|
||||
this.cc = [{
|
||||
address: 'john.doe@gmail.com'
|
||||
}]; // list of receivers
|
||||
this.attachments = attachments ? [{
|
||||
"filename": "a.md",
|
||||
"filesize": 123,
|
||||
"mimeType": "text/x-markdown",
|
||||
"part": "2",
|
||||
"content": null
|
||||
}, {
|
||||
"filename": "b.md",
|
||||
"filesize": 456,
|
||||
"mimeType": "text/x-markdown",
|
||||
"part": "3",
|
||||
"content": null
|
||||
}, {
|
||||
"filename": "c.md",
|
||||
"filesize": 789,
|
||||
"mimeType": "text/x-markdown",
|
||||
"part": "4",
|
||||
"content": null
|
||||
}] : [];
|
||||
this.unread = unread;
|
||||
this.answered = answered;
|
||||
this.sentDate = new Date('Thu Sep 19 2013 20:41:23 GMT+0200 (CEST)');
|
||||
this.subject = 'Getting started'; // Subject line
|
||||
this.body = 'And a good day to you too sir. \n' +
|
||||
'\n' +
|
||||
'Thursday, Apr 24, 2014 3:33 PM safewithme.testuser@gmail.com wrote:\n' +
|
||||
'> adsfadfasdfasdfasfdasdfasdfas\n' +
|
||||
'\n' +
|
||||
'http://example.com\n' +
|
||||
'\n' +
|
||||
'> Tuesday, Mar 25, 2014 4:19 PM gianniarcore@gmail.com wrote:\n' +
|
||||
'>> from 0.7.0.1\n' +
|
||||
'>>\n' +
|
||||
'>> God speed!'; // plaintext body
|
||||
//this.html = '<!DOCTYPE html><html><head></head><body><h1 style="border: 1px solid red; width: 500px;">Hello there' + Math.random() + '</h1></body></html>';
|
||||
this.encrypted = true;
|
||||
this.decrypted = true;
|
||||
};
|
||||
|
||||
var dummies = [],
|
||||
i = 100;
|
||||
while (i--) {
|
||||
// every second/third/fourth dummy mail with unread/attachments/answered
|
||||
dummies.push(new Email((i % 2 === 0), (i % 3 === 0), (i % 5 === 0)));
|
||||
}
|
||||
|
||||
return dummies;
|
||||
};
|
@ -1,33 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var axe = require('axe-logger');
|
||||
|
||||
var er = {};
|
||||
er.attachHandler = function(scope) {
|
||||
scope.onError = function(options) {
|
||||
if (!options) {
|
||||
scope.$apply();
|
||||
return;
|
||||
}
|
||||
|
||||
axe.error((options.errMsg || options.message) + (options.stack ? ('\n' + options.stack) : ''));
|
||||
|
||||
scope.state.dialog = {
|
||||
open: true,
|
||||
title: options.title || 'Error',
|
||||
message: options.errMsg || options.message,
|
||||
faqLink: options.faqLink,
|
||||
positiveBtnStr: options.positiveBtnStr || 'Ok',
|
||||
negativeBtnStr: options.negativeBtnStr || 'Cancel',
|
||||
showNegativeBtn: options.showNegativeBtn || false,
|
||||
showBugReporter: (typeof options.showBugReporter !== 'undefined' ? options.showBugReporter : !options.title), // if title is set, presume it's not an error by default
|
||||
callback: options.callback
|
||||
};
|
||||
// don't call apply for synchronous calls
|
||||
if (!options.sync) {
|
||||
scope.$apply();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = er;
|
12
src/js/util/index.js
Normal file
12
src/js/util/index.js
Normal file
@ -0,0 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
angular.module('woUtil', []);
|
||||
|
||||
require('./axe');
|
||||
require('./dummy');
|
||||
require('./dialog');
|
||||
require('./connection-doctor');
|
||||
require('./update/update-handler');
|
||||
require('./status-display');
|
||||
require('./download');
|
||||
require('./notification');
|
@ -1,11 +1,15 @@
|
||||
'use strict';
|
||||
|
||||
var cfg = require('../app-config').config;
|
||||
var ngModule = angular.module('woUtil');
|
||||
ngModule.service('notification', Notif);
|
||||
module.exports = Notif;
|
||||
|
||||
var notif = {};
|
||||
function Notif(appConfig) {
|
||||
this._appConfig = appConfig;
|
||||
|
||||
if (window.Notification) {
|
||||
notif.hasPermission = Notification.permission === "granted";
|
||||
if (window.Notification) {
|
||||
this.hasPermission = Notification.permission === "granted";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -17,25 +21,27 @@ if (window.Notification) {
|
||||
* @param {Function} options.onClick (optional) callback when the notification is clicked
|
||||
* @returns {Notification} A notification instance
|
||||
*/
|
||||
notif.create = function(options) {
|
||||
Notif.prototype.create = function(options) {
|
||||
var self = this;
|
||||
|
||||
options.onClick = options.onClick || function() {};
|
||||
|
||||
if (!window.Notification) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!notif.hasPermission) {
|
||||
if (!self.hasPermission) {
|
||||
// don't wait until callback returns
|
||||
Notification.requestPermission(function(permission) {
|
||||
if (permission === "granted") {
|
||||
notif.hasPermission = true;
|
||||
self.hasPermission = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var notification = new Notification(options.title, {
|
||||
body: options.message,
|
||||
icon: cfg.iconPath
|
||||
icon: self._appConfig.config.iconPath
|
||||
});
|
||||
notification.onclick = function() {
|
||||
window.focus();
|
||||
@ -51,8 +57,6 @@ notif.create = function(options) {
|
||||
return notification;
|
||||
};
|
||||
|
||||
notif.close = function(notification) {
|
||||
Notif.prototype.close = function(notification) {
|
||||
notification.close();
|
||||
};
|
||||
|
||||
module.exports = notif;
|
||||
};
|
31
src/js/util/status-display.js
Normal file
31
src/js/util/status-display.js
Normal file
@ -0,0 +1,31 @@
|
||||
'use strict';
|
||||
|
||||
var ngModule = angular.module('woUtil');
|
||||
ngModule.service('statusDisplay', StatusDisplay);
|
||||
module.exports = StatusDisplay;
|
||||
|
||||
/**
|
||||
* A central service to display status updates to the user
|
||||
*/
|
||||
function StatusDisplay($rootScope, axe) {
|
||||
this._rootScope = $rootScope;
|
||||
this._axe = axe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the status disply in the lower left of the screen
|
||||
* @param {String} text The status message that is to be displayed to the user
|
||||
* @param {Date} time The time of the last update
|
||||
*/
|
||||
StatusDisplay.prototype.update = function(text, time) {
|
||||
this._axe.info('status display', text);
|
||||
this._rootScope.$broadcast('status', text, time);
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the searching status to show a spinner while searching
|
||||
* @param {Boolean} state If the spinner should be displayed or not
|
||||
*/
|
||||
StatusDisplay.prototype.setSearching = function(state) {
|
||||
this._rootScope.$broadcast('searching', state);
|
||||
};
|
@ -1,5 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
var ngModule = angular.module('woUtil');
|
||||
ngModule.service('updateHandler', UpdateHandler);
|
||||
module.exports = UpdateHandler;
|
||||
|
||||
var axe = require('axe-logger'),
|
||||
cfg = require('../../app-config').config,
|
||||
updateV1 = require('./update-v1'),
|
||||
@ -11,12 +15,13 @@ var axe = require('axe-logger'),
|
||||
/**
|
||||
* Handles database migration
|
||||
*/
|
||||
var UpdateHandler = function(appConfigStorage, userStorage, auth) {
|
||||
this._appConfigStorage = appConfigStorage;
|
||||
this._userStorage = userStorage;
|
||||
function UpdateHandler(appConfigStore, accountStore, auth, dialog) {
|
||||
this._appConfigStorage = appConfigStore;
|
||||
this._userStorage = accountStore;
|
||||
this._updateScripts = [updateV1, updateV2, updateV3, updateV4, updateV5];
|
||||
this._auth = auth;
|
||||
};
|
||||
this._dialog = dialog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes all the necessary updates
|
||||
@ -95,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',
|
||||
@ -125,6 +132,4 @@ UpdateHandler.prototype.checkForUpdate = function(dialog) {
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = UpdateHandler;
|
||||
};
|
@ -1,19 +1,19 @@
|
||||
<div class="lightbox__body" ng-controller="DialogCtrl">
|
||||
<header class="lightbox__header">
|
||||
<h2>{{state.dialog.title}}</h2>
|
||||
<h2>{{title}}</h2>
|
||||
<button class="lightbox__close" wo-touch="confirm(false)" data-action="lightbox-close">
|
||||
<svg><use xlink:href="#icon-close" /><title>Close</title></svg>
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<div class="lightbox__content">
|
||||
<p class="typo-paragraph">{{state.dialog.message}} <a ng-show="state.dialog.faqLink" href="{{state.dialog.faqLink}}" target="_blank">Learn more</a></p>
|
||||
<p class="typo-paragraph">{{message}} <a ng-show="faqLink" href="{{faqLink}}" target="_blank">Learn more</a></p>
|
||||
</div>
|
||||
|
||||
<footer class="lightbox__controls">
|
||||
<button wo-touch="confirm(false)" class="btn btn--secondary" ng-show="state.dialog.showNegativeBtn">{{state.dialog.negativeBtnStr}}</button>
|
||||
<button wo-touch="confirm(false)" class="btn btn--secondary" ng-show="showNegativeBtn">{{negativeBtnStr}}</button>
|
||||
<!-- only show bug report button if we can actually report a bug, i.e. the writer is attached to the scope -->
|
||||
<button wo-touch="confirm(true)" class="btn" ng-show="!state.writer || !state.dialog.showBugReporter">{{state.dialog.positiveBtnStr}}</button>
|
||||
<button wo-touch="confirm(true); state.writer.reportBug()" class="btn" ng-show="state.writer && state.dialog.showBugReporter">Report bug</button>
|
||||
<button wo-touch="confirm(true)" class="btn" ng-show="!state.writer || !showBugReporter">{{positiveBtnStr}}</button>
|
||||
<button wo-touch="confirm(true); state.writer.reportBug()" class="btn" ng-show="state.writer && showBugReporter">Report bug</button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -12,6 +12,7 @@
|
||||
<svg><use xlink:href="#icon-write" /><title>New mail</title></svg>
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<div class="mail-list__search">
|
||||
<div class="search">
|
||||
<svg><use xlink:href="#icon-search" /><title>Search</title></svg>
|
||||
@ -71,13 +72,5 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<span class="spinner" ng-show="account.loggingIn || account.busy || state.mailList.searching"></span>
|
||||
<span class="text" ng-switch="account.online">
|
||||
<span ng-switch-when="false">
|
||||
<svg><use xlink:href="#icon-offline" /></svg>
|
||||
</span>
|
||||
{{state.mailList.lastUpdateLbl}} {{state.mailList.lastUpdate | date:'shortTime'}}
|
||||
</span>
|
||||
</footer>
|
||||
</div>
|
||||
<footer ng-include="'tpl/status-display.html'"></footer>
|
||||
</div>
|
||||
|
@ -69,13 +69,5 @@
|
||||
</li>
|
||||
</ul><!--/nav__secondary-->
|
||||
|
||||
<footer>
|
||||
<span class="spinner" ng-show="account.loggingIn || account.busy || state.mailList.searching"></span>
|
||||
<span class="text" ng-switch="account.online">
|
||||
<span ng-switch-when="false">
|
||||
<svg><use xlink:href="#icon-offline" /></svg>
|
||||
</span>
|
||||
{{state.mailList.lastUpdateLbl}} {{state.mailList.lastUpdate | date:'shortTime'}}
|
||||
</span>
|
||||
</footer>
|
||||
<footer ng-include="'tpl/status-display.html'"></footer>
|
||||
</nav>
|
9
src/tpl/status-display.html
Normal file
9
src/tpl/status-display.html
Normal file
@ -0,0 +1,9 @@
|
||||
<div ng-controller="StatusDisplayCtrl">
|
||||
<span class="spinner" ng-show="account.loggingIn || account.busy || searching"></span>
|
||||
<span class="text" ng-switch="account.online">
|
||||
<span ng-switch-when="false">
|
||||
<svg><use xlink:href="#icon-offline" /></svg>
|
||||
</span>
|
||||
{{text}} {{time | date:'shortTime'}}
|
||||
</span>
|
||||
</div>
|
File diff suppressed because one or more lines are too long
@ -26,7 +26,9 @@
|
||||
if (window.mochaPhantomJS) {
|
||||
mochaPhantomJS.run();
|
||||
} else {
|
||||
mocha.run();
|
||||
setTimeout(function() {
|
||||
mocha.run();
|
||||
}, 1000)
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
15
test/main.js
15
test/main.js
@ -46,8 +46,21 @@ if (!Function.prototype.bind) {
|
||||
}
|
||||
})();
|
||||
|
||||
//
|
||||
// Test setup
|
||||
//
|
||||
|
||||
chai.config.includeStack = true;
|
||||
|
||||
// set worker path for tests
|
||||
require('../src/js/app-config').config.workerPath = '../lib';
|
||||
|
||||
var axe = require('axe-logger');
|
||||
axe.removeAppender(axe.defaultAppender);
|
||||
axe.removeAppender(axe.defaultAppender);
|
||||
|
||||
// include angular modules
|
||||
require('../src/js/app-config');
|
||||
require('../src/js/util');
|
||||
require('../src/js/crypto');
|
||||
require('../src/js/service');
|
||||
require('../src/js/email');
|
@ -1,282 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var controller = require('../../src/js/app-controller'),
|
||||
EmailDAO = require('../../src/js/dao/email-dao'),
|
||||
OutboxBO = require('../../src/js/bo/outbox'),
|
||||
DeviceStorageDAO = require('../../src/js/dao/devicestorage-dao'),
|
||||
UpdateHandler = require('../../src/js/util/update/update-handler'),
|
||||
KeychainDAO = require('../../src/js/dao/keychain-dao'),
|
||||
config = require('../../src/js/app-config').config,
|
||||
Auth = require('../../src/js/bo/auth');
|
||||
|
||||
describe('App Controller unit tests', function() {
|
||||
var emailDaoStub, outboxStub, updateHandlerStub, appConfigStoreStub, devicestorageStub, isOnlineStub, authStub, keychainStub;
|
||||
|
||||
beforeEach(function() {
|
||||
controller._emailDao = emailDaoStub = sinon.createStubInstance(EmailDAO);
|
||||
controller._outboxBo = outboxStub = sinon.createStubInstance(OutboxBO);
|
||||
controller._appConfigStore = appConfigStoreStub = sinon.createStubInstance(DeviceStorageDAO);
|
||||
controller._userStorage = devicestorageStub = sinon.createStubInstance(DeviceStorageDAO);
|
||||
controller._updateHandler = updateHandlerStub = sinon.createStubInstance(UpdateHandler);
|
||||
controller._auth = authStub = sinon.createStubInstance(Auth);
|
||||
controller._keychain = keychainStub = sinon.createStubInstance(KeychainDAO);
|
||||
|
||||
isOnlineStub = sinon.stub(controller, 'isOnline');
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
isOnlineStub.restore();
|
||||
});
|
||||
|
||||
describe('buildModules', function() {
|
||||
it('should work', function() {
|
||||
controller.buildModules({
|
||||
onError: function() {}
|
||||
});
|
||||
expect(controller._appConfigStore).to.exist;
|
||||
expect(controller._auth).to.exist;
|
||||
expect(controller._userStorage).to.exist;
|
||||
expect(controller._invitationDao).to.exist;
|
||||
expect(controller._keychain).to.exist;
|
||||
expect(controller._pgp).to.exist;
|
||||
expect(controller._pgpbuilder).to.exist;
|
||||
expect(controller._emailDao).to.exist;
|
||||
expect(controller._outboxBo).to.exist;
|
||||
expect(controller._updateHandler).to.exist;
|
||||
});
|
||||
});
|
||||
|
||||
describe('start', function() {
|
||||
it('should not explode', function(done) {
|
||||
controller.start({
|
||||
onError: function() {}
|
||||
}, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('onDisconnect', function() {
|
||||
it('should work', function() {
|
||||
controller.onDisconnect();
|
||||
|
||||
expect(emailDaoStub.onDisconnect.calledOnce).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('logout', function() {
|
||||
it('should work', function(done) {
|
||||
authStub.logout.yields();
|
||||
emailDaoStub.onDisconnect.yields(new Error());
|
||||
|
||||
controller.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(authStub.logout.calledOnce).to.be.true;
|
||||
expect(emailDaoStub.onDisconnect.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
controller.logout();
|
||||
});
|
||||
});
|
||||
|
||||
describe('onConnect', function() {
|
||||
beforeEach(function() {
|
||||
controller._emailDao._account = {};
|
||||
});
|
||||
|
||||
it('should not connect if offline', function(done) {
|
||||
isOnlineStub.returns(false);
|
||||
|
||||
controller.onConnect(function(err) {
|
||||
expect(err).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not connect if account is not initialized', function(done) {
|
||||
controller._emailDao._account = null;
|
||||
|
||||
controller.onConnect(function(err) {
|
||||
expect(err).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail due to error in auth.getCredentials', function(done) {
|
||||
isOnlineStub.returns(true);
|
||||
authStub.getCredentials.yields(new Error());
|
||||
|
||||
controller.onConnect(function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(authStub.getCredentials.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
isOnlineStub.returns(true);
|
||||
authStub.getCredentials.yields(null, {
|
||||
emailAddress: 'asdf@example.com',
|
||||
oauthToken: 'token',
|
||||
sslCert: 'cert',
|
||||
imap: {},
|
||||
smtp: {}
|
||||
});
|
||||
emailDaoStub.onConnect.yields();
|
||||
|
||||
controller.onConnect(function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(authStub.getCredentials.calledOnce).to.be.true;
|
||||
expect(emailDaoStub.onConnect.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('init', function() {
|
||||
var onConnectStub, emailAddress, keysWithPubKey;
|
||||
|
||||
beforeEach(function() {
|
||||
emailAddress = 'alice@bob.com';
|
||||
keysWithPubKey = {
|
||||
publicKey: {}
|
||||
};
|
||||
|
||||
// onConnect
|
||||
onConnectStub = sinon.stub(controller, 'onConnect');
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
onConnectStub.restore();
|
||||
});
|
||||
|
||||
it('should fail due to malformed email address', function(done) {
|
||||
controller.init({
|
||||
emailAddress: 'ishallfail'
|
||||
}, function(err, keypair) {
|
||||
expect(err).to.exist;
|
||||
expect(keypair).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail due to error in storage initialization', function(done) {
|
||||
devicestorageStub.init.withArgs(emailAddress).yields(new Error());
|
||||
|
||||
controller.init({
|
||||
emailAddress: emailAddress
|
||||
}, function(err, keypair) {
|
||||
expect(err).to.exist;
|
||||
expect(keypair).to.not.exist;
|
||||
expect(devicestorageStub.init.calledOnce).to.be.true;
|
||||
expect(updateHandlerStub.update.calledOnce).to.be.false;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail due to error in update handler', function(done) {
|
||||
devicestorageStub.init.yields();
|
||||
updateHandlerStub.update.yields(new Error());
|
||||
|
||||
controller.init({
|
||||
emailAddress: emailAddress
|
||||
}, function(err, keypair) {
|
||||
expect(err).to.exist;
|
||||
expect(keypair).to.not.exist;
|
||||
expect(updateHandlerStub.update.calledOnce).to.be.true;
|
||||
expect(devicestorageStub.init.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail due to error in getUserKeyPair', function(done) {
|
||||
devicestorageStub.init.yields();
|
||||
updateHandlerStub.update.yields();
|
||||
keychainStub.getUserKeyPair.yields(new Error());
|
||||
|
||||
controller.init({
|
||||
emailAddress: emailAddress
|
||||
}, function(err, keypair) {
|
||||
expect(err).to.exist;
|
||||
expect(keypair).to.not.exist;
|
||||
expect(updateHandlerStub.update.calledOnce).to.be.true;
|
||||
expect(devicestorageStub.init.calledOnce).to.be.true;
|
||||
expect(keychainStub.getUserKeyPair.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail due to error in refreshKeyForUserId', function(done) {
|
||||
devicestorageStub.init.yields();
|
||||
updateHandlerStub.update.yields();
|
||||
keychainStub.getUserKeyPair.yields(null, keysWithPubKey);
|
||||
keychainStub.refreshKeyForUserId.yields(new Error());
|
||||
|
||||
controller.init({
|
||||
emailAddress: emailAddress
|
||||
}, function(err, keypair) {
|
||||
expect(err).to.exist;
|
||||
expect(keypair).to.not.exist;
|
||||
expect(updateHandlerStub.update.calledOnce).to.be.true;
|
||||
expect(devicestorageStub.init.calledOnce).to.be.true;
|
||||
expect(keychainStub.getUserKeyPair.calledOnce).to.be.true;
|
||||
expect(keychainStub.refreshKeyForUserId.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail due to error in emailDao.init', function(done) {
|
||||
devicestorageStub.init.yields();
|
||||
updateHandlerStub.update.yields();
|
||||
keychainStub.getUserKeyPair.yields(null, keysWithPubKey);
|
||||
keychainStub.refreshKeyForUserId.yields();
|
||||
emailDaoStub.init.yields(new Error());
|
||||
|
||||
controller.init({
|
||||
emailAddress: emailAddress
|
||||
}, function(err, keypair) {
|
||||
expect(err).to.exist;
|
||||
expect(keypair).to.not.exist;
|
||||
expect(updateHandlerStub.update.calledOnce).to.be.true;
|
||||
expect(devicestorageStub.init.calledOnce).to.be.true;
|
||||
expect(keychainStub.getUserKeyPair.calledOnce).to.be.true;
|
||||
expect(keychainStub.refreshKeyForUserId.calledOnce).to.be.true;
|
||||
expect(emailDaoStub.init.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work and not return a keypair', function(done) {
|
||||
devicestorageStub.init.withArgs(emailAddress).yields();
|
||||
updateHandlerStub.update.yields();
|
||||
keychainStub.getUserKeyPair.withArgs(emailAddress).yields(null, keysWithPubKey);
|
||||
keychainStub.refreshKeyForUserId.withArgs({
|
||||
userId: emailAddress,
|
||||
overridePermission: true
|
||||
}).yields();
|
||||
emailDaoStub.init.withArgs({
|
||||
account: {
|
||||
realname: undefined,
|
||||
emailAddress: emailAddress,
|
||||
asymKeySize: config.asymKeySize
|
||||
}
|
||||
}).yields();
|
||||
|
||||
controller.init({
|
||||
emailAddress: emailAddress
|
||||
}, function(err, keypair) {
|
||||
expect(err).to.not.exist;
|
||||
expect(keypair.publicKey).to.not.exist;
|
||||
expect(updateHandlerStub.update.calledOnce).to.be.true;
|
||||
expect(devicestorageStub.init.calledOnce).to.be.true;
|
||||
expect(keychainStub.getUserKeyPair.calledOnce).to.be.true;
|
||||
expect(keychainStub.refreshKeyForUserId.calledOnce).to.be.true;
|
||||
expect(emailDaoStub.init.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -1,50 +1,53 @@
|
||||
'use strict';
|
||||
|
||||
var mocks = angular.mock,
|
||||
AccountCtrl = require('../../src/js/controller/account'),
|
||||
PGP = require('../../src/js/crypto/pgp'),
|
||||
dl = require('../../src/js/util/download'),
|
||||
appController = require('../../src/js/app-controller'),
|
||||
KeychainDAO = require('../../src/js/dao/keychain-dao');
|
||||
var AccountCtrl = require('../../../../src/js/controller/app/account'),
|
||||
PGP = require('../../../../src/js/crypto/pgp'),
|
||||
Download = require('../../../../src/js/util/download'),
|
||||
Keychain = require('../../../../src/js/service/keychain'),
|
||||
Auth = require('../../../../src/js/service/auth'),
|
||||
Dialog = require('../../../../src/js/util/dialog');
|
||||
|
||||
describe('Account Controller unit test', function() {
|
||||
var scope, accountCtrl,
|
||||
dummyFingerprint, expectedFingerprint,
|
||||
dummyKeyId, expectedKeyId,
|
||||
emailAddress, keySize, pgpMock, keychainMock;
|
||||
emailAddress, keySize, pgpStub, keychainStub, authStub, dialogStub, downloadStub;
|
||||
|
||||
beforeEach(function() {
|
||||
appController._pgp = pgpMock = sinon.createStubInstance(PGP);
|
||||
appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
pgpStub = sinon.createStubInstance(PGP);
|
||||
authStub = sinon.createStubInstance(Auth);
|
||||
keychainStub = sinon.createStubInstance(Keychain);
|
||||
dialogStub = sinon.createStubInstance(Dialog);
|
||||
downloadStub = sinon.createStubInstance(Download);
|
||||
|
||||
dummyFingerprint = '3A2D39B4E1404190B8B949DE7D7E99036E712926';
|
||||
expectedFingerprint = '3A2D 39B4 E140 4190 B8B9 49DE 7D7E 9903 6E71 2926';
|
||||
dummyKeyId = '9FEB47936E712926';
|
||||
expectedKeyId = '6E712926';
|
||||
pgpMock.getFingerprint.returns(dummyFingerprint);
|
||||
pgpMock.getKeyId.returns(dummyKeyId);
|
||||
pgpStub.getFingerprint.returns(dummyFingerprint);
|
||||
pgpStub.getKeyId.returns(dummyKeyId);
|
||||
emailAddress = 'fred@foo.com';
|
||||
keySize = 1234;
|
||||
appController._emailDao = {
|
||||
_account: {
|
||||
emailAddress: emailAddress,
|
||||
asymKeySize: keySize
|
||||
}
|
||||
};
|
||||
pgpMock.getKeyParams.returns({
|
||||
authStub.emailAddress = emailAddress;
|
||||
pgpStub.getKeyParams.returns({
|
||||
_id: dummyKeyId,
|
||||
fingerprint: dummyFingerprint,
|
||||
userId: emailAddress,
|
||||
bitSize: keySize
|
||||
});
|
||||
|
||||
angular.module('accounttest', []);
|
||||
mocks.module('accounttest');
|
||||
mocks.inject(function($rootScope, $controller) {
|
||||
angular.module('accounttest', ['woServices']);
|
||||
angular.mock.module('accounttest');
|
||||
angular.mock.inject(function($rootScope, $controller) {
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
accountCtrl = $controller(AccountCtrl, {
|
||||
$scope: scope
|
||||
$scope: scope,
|
||||
auth: authStub,
|
||||
keychain: keychainStub,
|
||||
pgp: pgpStub,
|
||||
download: downloadStub,
|
||||
dialog: dialogStub
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -61,8 +64,7 @@ describe('Account Controller unit test', function() {
|
||||
});
|
||||
describe('export to key file', function() {
|
||||
it('should work', function() {
|
||||
var createDownloadMock = sinon.stub(dl, 'createDownload');
|
||||
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(null, {
|
||||
keychainStub.getUserKeyPair.withArgs(emailAddress).yields(null, {
|
||||
publicKey: {
|
||||
_id: dummyKeyId,
|
||||
publicKey: 'a'
|
||||
@ -71,27 +73,23 @@ describe('Account Controller unit test', function() {
|
||||
encryptedKey: 'b'
|
||||
}
|
||||
});
|
||||
createDownloadMock.withArgs(sinon.match(function(arg) {
|
||||
downloadStub.createDownload.withArgs(sinon.match(function(arg) {
|
||||
return arg.content === 'a\r\nb' && arg.filename === 'whiteout_mail_' + emailAddress + '_' + expectedKeyId + '.asc' && arg.contentType === 'text/plain';
|
||||
})).returns();
|
||||
|
||||
scope.exportKeyFile();
|
||||
|
||||
expect(scope.state.lightbox).to.equal(undefined);
|
||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||
expect(dl.createDownload.calledOnce).to.be.true;
|
||||
dl.createDownload.restore();
|
||||
expect(keychainStub.getUserKeyPair.calledOnce).to.be.true;
|
||||
expect(downloadStub.createDownload.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should not work when key export failed', function(done) {
|
||||
keychainMock.getUserKeyPair.yields(new Error('Boom!'));
|
||||
scope.onError = function(err) {
|
||||
expect(err.message).to.equal('Boom!');
|
||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
it('should not work when key export failed', function() {
|
||||
keychainStub.getUserKeyPair.yields(new Error());
|
||||
|
||||
scope.exportKeyFile();
|
||||
|
||||
expect(dialogStub.error.calledOnce).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
@ -1,21 +1,21 @@
|
||||
'use strict';
|
||||
|
||||
var mocks = angular.mock,
|
||||
EmailDAO = require('../../src/js/dao/email-dao'),
|
||||
appController = require('../../src/js/app-controller'),
|
||||
ActionBarCtrl = require('../../src/js/controller/action-bar');
|
||||
var Email = require('../../../../src/js/email/email'),
|
||||
Dialog = require('../../../../src/js/util/dialog'),
|
||||
StatusDisplay = require('../../../../src/js/util/status-display'),
|
||||
ActionBarCtrl = require('../../../../src/js/controller/app/action-bar');
|
||||
|
||||
describe('Action Bar Controller unit test', function() {
|
||||
var scope, actionBarCtrl, emailDaoMock, origEmailDao;
|
||||
var scope, actionBarCtrl, emailMock, dialogMock, statusDisplayMock;
|
||||
|
||||
beforeEach(function() {
|
||||
origEmailDao = appController._emailDao;
|
||||
emailDaoMock = sinon.createStubInstance(EmailDAO);
|
||||
appController._emailDao = emailDaoMock;
|
||||
emailMock = sinon.createStubInstance(Email);
|
||||
dialogMock = sinon.createStubInstance(Dialog);
|
||||
statusDisplayMock = sinon.createStubInstance(StatusDisplay);
|
||||
|
||||
angular.module('actionbartest', []);
|
||||
mocks.module('actionbartest');
|
||||
mocks.inject(function($rootScope, $controller) {
|
||||
angular.mock.module('actionbartest');
|
||||
angular.mock.inject(function($rootScope, $controller) {
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {
|
||||
mailList: {
|
||||
@ -40,15 +40,15 @@ describe('Action Bar Controller unit test', function() {
|
||||
};
|
||||
|
||||
actionBarCtrl = $controller(ActionBarCtrl, {
|
||||
$scope: scope
|
||||
$scope: scope,
|
||||
email: emailMock,
|
||||
dialog: dialogMock,
|
||||
statusDisplay: statusDisplayMock
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// restore the module
|
||||
appController._emailDao = origEmailDao;
|
||||
});
|
||||
afterEach(function() {});
|
||||
|
||||
describe('deleteMessage', function() {
|
||||
it('should not delete without a selected mail', function() {
|
||||
@ -56,11 +56,11 @@ describe('Action Bar Controller unit test', function() {
|
||||
});
|
||||
|
||||
it('should delete the selected mail', function() {
|
||||
emailDaoMock.deleteMessage.yields();
|
||||
emailMock.deleteMessage.yields();
|
||||
|
||||
scope.deleteMessage({});
|
||||
|
||||
expect(emailDaoMock.deleteMessage.calledOnce).to.be.true;
|
||||
expect(emailMock.deleteMessage.calledOnce).to.be.true;
|
||||
expect(scope.state.read.open).to.be.false;
|
||||
});
|
||||
});
|
||||
@ -88,11 +88,11 @@ describe('Action Bar Controller unit test', function() {
|
||||
});
|
||||
|
||||
it('should move the selected mail', function() {
|
||||
emailDaoMock.moveMessage.yields();
|
||||
emailMock.moveMessage.yields();
|
||||
|
||||
scope.moveMessage({}, {});
|
||||
|
||||
expect(emailDaoMock.moveMessage.calledOnce).to.be.true;
|
||||
expect(emailMock.moveMessage.calledOnce).to.be.true;
|
||||
expect(scope.state.read.open).to.be.false;
|
||||
});
|
||||
});
|
||||
@ -120,11 +120,11 @@ describe('Action Bar Controller unit test', function() {
|
||||
});
|
||||
|
||||
it('should move the selected mail', function() {
|
||||
emailDaoMock.setFlags.yields();
|
||||
emailMock.setFlags.yields();
|
||||
|
||||
scope.markMessage({}, true);
|
||||
|
||||
expect(emailDaoMock.setFlags.calledOnce).to.be.true;
|
||||
expect(emailMock.setFlags.calledOnce).to.be.true;
|
||||
expect(scope.state.read.open).to.be.false;
|
||||
});
|
||||
});
|
@ -1,38 +1,33 @@
|
||||
'use strict';
|
||||
|
||||
var mocks = angular.mock,
|
||||
ContactsCtrl = require('../../src/js/controller/contacts'),
|
||||
appController = require('../../src/js/app-controller'),
|
||||
KeychainDAO = require('../../src/js/dao/keychain-dao'),
|
||||
PGP = require('../../src/js/crypto/pgp');
|
||||
var ContactsCtrl = require('../../../../src/js/controller/app/contacts'),
|
||||
Keychain = require('../../../../src/js/service/keychain'),
|
||||
PGP = require('../../../../src/js/crypto/pgp'),
|
||||
Dialog = require('../../../../src/js/util/dialog');
|
||||
|
||||
describe('Contacts Controller unit test', function() {
|
||||
var scope, contactsCtrl,
|
||||
origKeychain, keychainMock,
|
||||
origPgp, pgpMock;
|
||||
var scope, contactsCtrl, keychainStub, pgpStub, dialogStub;
|
||||
|
||||
beforeEach(function() {
|
||||
origPgp = appController._pgp;
|
||||
appController._pgp = pgpMock = sinon.createStubInstance(PGP);
|
||||
origKeychain = appController._keychain;
|
||||
appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
pgpStub = sinon.createStubInstance(PGP);
|
||||
keychainStub = sinon.createStubInstance(Keychain);
|
||||
dialogStub = sinon.createStubInstance(Dialog);
|
||||
|
||||
angular.module('contactstest', []);
|
||||
mocks.module('contactstest');
|
||||
mocks.inject(function($rootScope, $controller) {
|
||||
angular.module('contactstest', ['woServices']);
|
||||
angular.mock.module('contactstest');
|
||||
angular.mock.inject(function($rootScope, $controller) {
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
contactsCtrl = $controller(ContactsCtrl, {
|
||||
$scope: scope
|
||||
$scope: scope,
|
||||
keychain: keychainStub,
|
||||
pgp: pgpStub,
|
||||
dialog: dialogStub
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// restore the module
|
||||
appController._pgp = origPgp;
|
||||
appController._keychain = origKeychain;
|
||||
});
|
||||
afterEach(function() {});
|
||||
|
||||
describe('scope variables', function() {
|
||||
it('should be set correctly', function() {
|
||||
@ -41,34 +36,28 @@ describe('Contacts Controller unit test', function() {
|
||||
});
|
||||
|
||||
describe('listKeys', function() {
|
||||
it('should fail due to error in keychain.listLocalPublicKeys', function(done) {
|
||||
keychainMock.listLocalPublicKeys.yields(42);
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.equal(42);
|
||||
done();
|
||||
};
|
||||
it('should fail due to error in keychain.listLocalPublicKeys', function() {
|
||||
keychainStub.listLocalPublicKeys.yields(42);
|
||||
|
||||
scope.listKeys();
|
||||
|
||||
expect(dialogStub.error.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
keychainMock.listLocalPublicKeys.yields(null, [{
|
||||
it('should work', function() {
|
||||
keychainStub.listLocalPublicKeys.yields(null, [{
|
||||
_id: '12345'
|
||||
}]);
|
||||
pgpMock.getKeyParams.returns({
|
||||
pgpStub.getKeyParams.returns({
|
||||
fingerprint: 'asdf'
|
||||
});
|
||||
|
||||
scope.$apply = function() {
|
||||
expect(scope.keys.length).to.equal(1);
|
||||
expect(scope.keys[0]._id).to.equal('12345');
|
||||
expect(scope.keys[0].fingerprint).to.equal('asdf');
|
||||
done();
|
||||
};
|
||||
|
||||
expect(scope.keys).to.not.exist;
|
||||
scope.listKeys();
|
||||
|
||||
expect(scope.keys.length).to.equal(1);
|
||||
expect(scope.keys[0]._id).to.equal('12345');
|
||||
expect(scope.keys[0].fingerprint).to.equal('asdf');
|
||||
});
|
||||
});
|
||||
|
||||
@ -88,13 +77,13 @@ describe('Contacts Controller unit test', function() {
|
||||
it('should work', function(done) {
|
||||
var keyArmored = '-----BEGIN PGP PUBLIC KEY BLOCK-----';
|
||||
|
||||
pgpMock.getKeyParams.returns({
|
||||
pgpStub.getKeyParams.returns({
|
||||
_id: '12345',
|
||||
userId: 'max@example.com',
|
||||
userIds: []
|
||||
});
|
||||
|
||||
keychainMock.saveLocalPublicKey.withArgs({
|
||||
keychainStub.saveLocalPublicKey.withArgs({
|
||||
_id: '12345',
|
||||
userId: 'max@example.com',
|
||||
userIds: [],
|
||||
@ -109,46 +98,36 @@ describe('Contacts Controller unit test', function() {
|
||||
scope.importKey(keyArmored);
|
||||
});
|
||||
|
||||
it('should fail due to invalid armored key', function(done) {
|
||||
it('should fail due to invalid armored key', function() {
|
||||
var keyArmored = '-----BEGIN PGP PRIVATE KEY BLOCK-----';
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.importKey(keyArmored);
|
||||
|
||||
expect(dialogStub.error.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should fail due to error in pgp.getKeyParams', function(done) {
|
||||
it('should fail due to error in pgp.getKeyParams', function() {
|
||||
var keyArmored = '-----BEGIN PGP PUBLIC KEY BLOCK-----';
|
||||
|
||||
pgpMock.getKeyParams.throws(new Error('WAT'));
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
};
|
||||
pgpStub.getKeyParams.throws(new Error('WAT'));
|
||||
|
||||
scope.importKey(keyArmored);
|
||||
|
||||
expect(dialogStub.error.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should fail due to error in keychain.saveLocalPublicKey', function(done) {
|
||||
it('should fail due to error in keychain.saveLocalPublicKey', function() {
|
||||
var keyArmored = '-----BEGIN PGP PUBLIC KEY BLOCK-----';
|
||||
|
||||
pgpMock.getKeyParams.returns({
|
||||
pgpStub.getKeyParams.returns({
|
||||
_id: '12345',
|
||||
userId: 'max@example.com'
|
||||
});
|
||||
|
||||
keychainMock.saveLocalPublicKey.yields(42);
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.equal(42);
|
||||
done();
|
||||
};
|
||||
keychainStub.saveLocalPublicKey.yields(42);
|
||||
|
||||
scope.importKey(keyArmored);
|
||||
|
||||
expect(dialogStub.error.calledOnce).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
@ -158,7 +137,7 @@ describe('Contacts Controller unit test', function() {
|
||||
_id: '12345'
|
||||
};
|
||||
|
||||
keychainMock.removeLocalPublicKey.withArgs('12345').yields();
|
||||
keychainStub.removeLocalPublicKey.withArgs('12345').yields();
|
||||
|
||||
scope.listKeys = function() {
|
||||
done();
|
||||
@ -167,19 +146,16 @@ describe('Contacts Controller unit test', function() {
|
||||
scope.removeKey(key);
|
||||
});
|
||||
|
||||
it('should fail due to error in keychain.removeLocalPublicKey', function(done) {
|
||||
it('should fail due to error in keychain.removeLocalPublicKey', function() {
|
||||
var key = {
|
||||
_id: '12345'
|
||||
};
|
||||
|
||||
keychainMock.removeLocalPublicKey.withArgs('12345').yields(42);
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.equal(42);
|
||||
done();
|
||||
};
|
||||
keychainStub.removeLocalPublicKey.withArgs('12345').yields(42);
|
||||
|
||||
scope.removeKey(key);
|
||||
|
||||
expect(dialogStub.error.calledOnce).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
@ -1,21 +1,22 @@
|
||||
'use strict';
|
||||
|
||||
var mocks = angular.mock,
|
||||
DialogCtrl = require('../../src/js/controller/dialog');
|
||||
var DialogCtrl = require('../../../../src/js/controller/app/dialog');
|
||||
|
||||
describe('Dialog Controller unit test', function() {
|
||||
var scope, dialogCtrl;
|
||||
var scope, dialogCtrl, dialogService;
|
||||
|
||||
beforeEach(function() {
|
||||
angular.module('dialogtest', []);
|
||||
mocks.module('dialogtest');
|
||||
mocks.inject(function($rootScope, $controller) {
|
||||
angular.module('dialogtest', ['woUtil']);
|
||||
angular.mock.module('dialogtest');
|
||||
angular.mock.inject(function($rootScope, $controller, dialog) {
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {
|
||||
dialog: {}
|
||||
};
|
||||
dialogService = dialog;
|
||||
dialogCtrl = $controller(DialogCtrl, {
|
||||
$scope: scope
|
||||
$scope: scope,
|
||||
dialog: dialog
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -24,7 +25,7 @@ describe('Dialog Controller unit test', function() {
|
||||
|
||||
describe('confirm', function() {
|
||||
it('should work', function(done) {
|
||||
scope.state.dialog.callback = function(confirmed) {
|
||||
scope.callback = function(confirmed) {
|
||||
expect(confirmed).to.be.true;
|
||||
expect(scope.state.dialog.open).to.be.false;
|
||||
done();
|
||||
@ -35,7 +36,7 @@ describe('Dialog Controller unit test', function() {
|
||||
|
||||
describe('cancel', function() {
|
||||
it('should work', function(done) {
|
||||
scope.state.dialog.callback = function(confirmed) {
|
||||
scope.callback = function(confirmed) {
|
||||
expect(confirmed).to.be.false;
|
||||
expect(scope.state.dialog.open).to.be.false;
|
||||
done();
|
@ -1,17 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
var mocks = angular.mock,
|
||||
MailListCtrl = require('../../src/js/controller/mail-list'),
|
||||
EmailDAO = require('../../src/js/dao/email-dao'),
|
||||
DeviceStorageDAO = require('../../src/js/dao/devicestorage-dao'),
|
||||
KeychainDAO = require('../../src/js/dao/keychain-dao'),
|
||||
appController = require('../../src/js/app-controller'),
|
||||
notification = require('../../src/js/util/notification');
|
||||
|
||||
chai.config.includeStack = true;
|
||||
var MailListCtrl = require('../../../../src/js/controller/app/mail-list'),
|
||||
EmailDAO = require('../../../../src/js/email/email'),
|
||||
KeychainDAO = require('../../../../src/js/service/keychain'),
|
||||
StatusDisplay = require('../../../../src/js/util/status-display'),
|
||||
Dialog = require('../../../../src/js/util/dialog'),
|
||||
Search = require('../../../../src/js/email/search');
|
||||
|
||||
describe('Mail List controller unit test', function() {
|
||||
var scope, ctrl, origEmailDao, emailDaoMock, keychainMock, deviceStorageMock,
|
||||
var scope, ctrl, statusDisplayMock, notificationMock, emailMock, keychainMock, dialogMock, searchMock,
|
||||
emailAddress, emails,
|
||||
hasChrome, hasSocket, hasRuntime, hasIdentity;
|
||||
|
||||
@ -41,28 +38,23 @@ describe('Mail List controller unit test', function() {
|
||||
}, {
|
||||
unread: true
|
||||
}];
|
||||
appController._outboxBo = {
|
||||
pendingEmails: emails
|
||||
};
|
||||
|
||||
origEmailDao = appController._emailDao;
|
||||
emailDaoMock = sinon.createStubInstance(EmailDAO);
|
||||
appController._emailDao = emailDaoMock;
|
||||
emailAddress = 'fred@foo.com';
|
||||
emailDaoMock._account = {
|
||||
emailAddress: emailAddress,
|
||||
|
||||
notificationMock = {
|
||||
create: function() {},
|
||||
close: function() {}
|
||||
};
|
||||
|
||||
|
||||
statusDisplayMock = sinon.createStubInstance(StatusDisplay);
|
||||
emailMock = sinon.createStubInstance(EmailDAO);
|
||||
keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
appController._keychain = keychainMock;
|
||||
dialogMock = sinon.createStubInstance(Dialog);
|
||||
searchMock = sinon.createStubInstance(Search);
|
||||
|
||||
deviceStorageMock = sinon.createStubInstance(DeviceStorageDAO);
|
||||
emailDaoMock._devicestorage = deviceStorageMock;
|
||||
|
||||
angular.module('maillisttest', []);
|
||||
mocks.module('maillisttest');
|
||||
mocks.inject(function($rootScope, $controller) {
|
||||
angular.module('maillisttest', ['woEmail', 'woServices', 'woUtil']);
|
||||
angular.mock.module('maillisttest');
|
||||
angular.mock.inject(function($rootScope, $controller) {
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {
|
||||
read: {
|
||||
@ -73,7 +65,13 @@ describe('Mail List controller unit test', function() {
|
||||
scope.loadVisibleBodies = function() {};
|
||||
ctrl = $controller(MailListCtrl, {
|
||||
$scope: scope,
|
||||
$routeParams: {}
|
||||
$routeParams: {},
|
||||
statusDisplay: statusDisplayMock,
|
||||
notification: notificationMock,
|
||||
email: emailMock,
|
||||
keychain: keychainMock,
|
||||
dialog: dialogMock,
|
||||
search: searchMock
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -91,9 +89,6 @@ describe('Mail List controller unit test', function() {
|
||||
if (!hasIdentity) {
|
||||
delete window.chrome.identity;
|
||||
}
|
||||
|
||||
// restore the module
|
||||
appController._emailDao = origEmailDao;
|
||||
});
|
||||
|
||||
describe('displayMore', function() {
|
||||
@ -137,104 +132,21 @@ describe('Mail List controller unit test', function() {
|
||||
|
||||
it('should show initial message on empty', function() {
|
||||
scope.displaySearchResults();
|
||||
expect(scope.state.mailList.searching).to.be.false;
|
||||
expect(scope.state.mailList.lastUpdateLbl).to.equal('Online');
|
||||
expect(statusDisplayMock.setSearching.withArgs(false).calledOnce).to.be.true;
|
||||
expect(statusDisplayMock.update.withArgs('Online').calledOnce).to.be.true;
|
||||
expect(scope.displayMessages.length).to.equal(2);
|
||||
});
|
||||
it('should show initial message on empty', function() {
|
||||
var searchStub = sinon.stub(scope, 'search');
|
||||
searchStub.returns(['a']);
|
||||
|
||||
searchMock.filter.returns(['a']);
|
||||
|
||||
scope.displaySearchResults('query');
|
||||
expect(scope.state.mailList.searching).to.be.true;
|
||||
expect(scope.state.mailList.lastUpdateLbl).to.equal('Searching ...');
|
||||
expect(statusDisplayMock.setSearching.withArgs(true).calledOnce).to.be.true;
|
||||
expect(statusDisplayMock.update.withArgs('Searching ...').calledOnce).to.be.true;
|
||||
clock.tick(500);
|
||||
|
||||
expect(scope.displayMessages).to.deep.equal(['a']);
|
||||
expect(scope.state.mailList.searching).to.be.false;
|
||||
expect(scope.state.mailList.lastUpdateLbl).to.equal('Matches in this folder');
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('search', function() {
|
||||
var message1 = {
|
||||
to: [{
|
||||
name: 'name1',
|
||||
address: 'address1'
|
||||
}],
|
||||
subject: 'subject1',
|
||||
body: 'body1',
|
||||
html: 'html1'
|
||||
},
|
||||
message2 = {
|
||||
to: [{
|
||||
name: 'name2',
|
||||
address: 'address2'
|
||||
}],
|
||||
subject: 'subject2',
|
||||
body: 'body2',
|
||||
html: 'html2'
|
||||
},
|
||||
message3 = {
|
||||
to: [{
|
||||
name: 'name3',
|
||||
address: 'address3'
|
||||
}],
|
||||
subject: 'subject3',
|
||||
body: 'body1',
|
||||
html: 'html1',
|
||||
encrypted: true
|
||||
},
|
||||
message4 = {
|
||||
to: [{
|
||||
name: 'name4',
|
||||
address: 'address4'
|
||||
}],
|
||||
subject: 'subject4',
|
||||
body: 'body1',
|
||||
html: 'html1',
|
||||
encrypted: true,
|
||||
decrypted: true
|
||||
},
|
||||
testMessages = [message1, message2, message3, message4];
|
||||
|
||||
it('return same messages array on empty query string', function() {
|
||||
var result = scope.search(testMessages, '');
|
||||
expect(result).to.equal(testMessages);
|
||||
});
|
||||
|
||||
it('return message1 on matching subject', function() {
|
||||
var result = scope.search(testMessages, 'subject1');
|
||||
expect(result.length).to.equal(1);
|
||||
expect(result[0]).to.equal(message1);
|
||||
});
|
||||
|
||||
it('return message1 on matching name', function() {
|
||||
var result = scope.search(testMessages, 'name1');
|
||||
expect(result.length).to.equal(1);
|
||||
expect(result[0]).to.equal(message1);
|
||||
});
|
||||
|
||||
it('return message1 on matching address', function() {
|
||||
var result = scope.search(testMessages, 'address1');
|
||||
expect(result.length).to.equal(1);
|
||||
expect(result[0]).to.equal(message1);
|
||||
});
|
||||
|
||||
it('return plaintext and decrypted messages on matching body', function() {
|
||||
var result = scope.search(testMessages, 'body1');
|
||||
expect(result.length).to.equal(2);
|
||||
expect(result[0]).to.equal(message1);
|
||||
expect(result[1]).to.equal(message4);
|
||||
});
|
||||
|
||||
it('return plaintext and decrypted messages on matching html', function() {
|
||||
var result = scope.search(testMessages, 'html1');
|
||||
expect(result.length).to.equal(2);
|
||||
expect(result[0]).to.equal(message1);
|
||||
expect(result[1]).to.equal(message4);
|
||||
expect(statusDisplayMock.setSearching.withArgs(false).calledOnce).to.be.true;
|
||||
expect(statusDisplayMock.update.withArgs('Matches in this folder').calledOnce).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
@ -251,7 +163,7 @@ describe('Mail List controller unit test', function() {
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
notification.create.restore();
|
||||
notificationMock.create.restore();
|
||||
});
|
||||
|
||||
it('should succeed for single mail', function(done) {
|
||||
@ -264,7 +176,7 @@ describe('Mail List controller unit test', function() {
|
||||
unread: true
|
||||
};
|
||||
|
||||
sinon.stub(notification, 'create', function(opts) {
|
||||
sinon.stub(notificationMock, 'create', function(opts) {
|
||||
expect(opts.title).to.equal(mail.from[0].address);
|
||||
expect(opts.message).to.equal(mail.subject);
|
||||
|
||||
@ -280,7 +192,7 @@ describe('Mail List controller unit test', function() {
|
||||
}
|
||||
};
|
||||
|
||||
emailDaoMock.onIncomingMessage([mail]);
|
||||
emailMock.onIncomingMessage([mail]);
|
||||
});
|
||||
|
||||
it('should succeed for multiple mails', function(done) {
|
||||
@ -307,7 +219,7 @@ describe('Mail List controller unit test', function() {
|
||||
unread: false
|
||||
}];
|
||||
|
||||
sinon.stub(notification, 'create', function(opts) {
|
||||
sinon.stub(notificationMock, 'create', function(opts) {
|
||||
expect(opts.title).to.equal('2 new messages');
|
||||
expect(opts.message).to.equal(mails[0].subject + '\n' + mails[1].subject);
|
||||
|
||||
@ -323,7 +235,7 @@ describe('Mail List controller unit test', function() {
|
||||
}
|
||||
};
|
||||
|
||||
emailDaoMock.onIncomingMessage(mails);
|
||||
emailMock.onIncomingMessage(mails);
|
||||
});
|
||||
});
|
||||
|
||||
@ -336,14 +248,14 @@ describe('Mail List controller unit test', function() {
|
||||
};
|
||||
|
||||
scope.getBody();
|
||||
expect(emailDaoMock.getBody.calledOnce).to.be.true;
|
||||
expect(emailMock.getBody.calledOnce).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('select', function() {
|
||||
it('should decrypt, focus mark an unread mail as read', function() {
|
||||
scope.pendingNotifications = ['asd'];
|
||||
sinon.stub(notification, 'close');
|
||||
sinon.stub(notificationMock, 'close');
|
||||
|
||||
var mail = {
|
||||
from: [{
|
||||
@ -372,13 +284,13 @@ describe('Mail List controller unit test', function() {
|
||||
|
||||
scope.select(mail);
|
||||
|
||||
expect(emailDaoMock.decryptBody.calledOnce).to.be.true;
|
||||
expect(emailMock.decryptBody.calledOnce).to.be.true;
|
||||
expect(keychainMock.refreshKeyForUserId.calledOnce).to.be.true;
|
||||
expect(scope.state.mailList.selected).to.equal(mail);
|
||||
expect(notification.close.calledWith('asd')).to.be.true;
|
||||
expect(notification.close.calledOnce).to.be.true;
|
||||
expect(notificationMock.close.calledWith('asd')).to.be.true;
|
||||
expect(notificationMock.close.calledOnce).to.be.true;
|
||||
|
||||
notification.close.restore();
|
||||
notificationMock.close.restore();
|
||||
});
|
||||
|
||||
it('should decrypt and focus a read mail', function() {
|
||||
@ -407,7 +319,7 @@ describe('Mail List controller unit test', function() {
|
||||
|
||||
scope.select(mail);
|
||||
|
||||
expect(emailDaoMock.decryptBody.calledOnce).to.be.true;
|
||||
expect(emailMock.decryptBody.calledOnce).to.be.true;
|
||||
expect(keychainMock.refreshKeyForUserId.calledOnce).to.be.true;
|
||||
expect(scope.state.mailList.selected).to.equal(mail);
|
||||
});
|
@ -1,19 +1,17 @@
|
||||
'use strict';
|
||||
|
||||
var mocks = angular.mock,
|
||||
NavigationCtrl = require('../../src/js/controller/navigation'),
|
||||
EmailDAO = require('../../src/js/dao/email-dao'),
|
||||
OutboxBO = require('../../src/js/bo/outbox'),
|
||||
appController = require('../../src/js/app-controller');
|
||||
var NavigationCtrl = require('../../../../src/js/controller/app/navigation'),
|
||||
Email = require('../../../../src/js/email/email'),
|
||||
Account = require('../../../../src/js/email/account'),
|
||||
Outbox = require('../../../../src/js/email/outbox'),
|
||||
Dialog = require('../../../../src/js/util/dialog'),
|
||||
Notif = require('../../../../src/js/util/notification');
|
||||
|
||||
describe('Navigation Controller unit test', function() {
|
||||
var scope, ctrl, origEmailDao, emailDaoMock, outboxBoMock, outboxFolder, onConnectStub;
|
||||
var scope, ctrl, emailDaoMock, accountMock, notificationStub, dialogStub, outboxBoMock, outboxFolder;
|
||||
|
||||
beforeEach(function(done) {
|
||||
// remember original module to restore later
|
||||
origEmailDao = appController._emailDao;
|
||||
emailDaoMock = sinon.createStubInstance(EmailDAO);
|
||||
emailDaoMock._account = {
|
||||
beforeEach(function() {
|
||||
var account = {
|
||||
folders: [{
|
||||
type: 'Inbox',
|
||||
count: 2,
|
||||
@ -24,32 +22,35 @@ describe('Navigation Controller unit test', function() {
|
||||
path: 'OUTBOX'
|
||||
}]
|
||||
};
|
||||
outboxFolder = emailDaoMock._account.folders[1];
|
||||
appController._emailDao = emailDaoMock;
|
||||
outboxBoMock = sinon.createStubInstance(OutboxBO);
|
||||
appController._outboxBo = outboxBoMock;
|
||||
outboxBoMock.startChecking.returns();
|
||||
onConnectStub = sinon.stub(appController, 'onConnect');
|
||||
onConnectStub.yields();
|
||||
|
||||
angular.module('navigationtest', []);
|
||||
mocks.module('navigationtest');
|
||||
mocks.inject(function($rootScope, $controller) {
|
||||
emailDaoMock = sinon.createStubInstance(Email);
|
||||
outboxFolder = account.folders[1];
|
||||
outboxBoMock = sinon.createStubInstance(Outbox);
|
||||
outboxBoMock.startChecking.returns();
|
||||
dialogStub = sinon.createStubInstance(Dialog);
|
||||
notificationStub = sinon.createStubInstance(Notif);
|
||||
accountMock = sinon.createStubInstance(Account);
|
||||
accountMock.list.returns([account]);
|
||||
accountMock.isLoggedIn.returns(true);
|
||||
|
||||
angular.module('navigationtest', ['woServices', 'woEmail', 'woUtil']);
|
||||
angular.mock.module('navigationtest');
|
||||
angular.mock.inject(function($rootScope, $controller) {
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
ctrl = $controller(NavigationCtrl, {
|
||||
$scope: scope,
|
||||
$routeParams: {}
|
||||
$routeParams: {},
|
||||
account: accountMock,
|
||||
email: emailDaoMock,
|
||||
outbox: outboxBoMock,
|
||||
notification: notificationStub,
|
||||
dialog: dialogStub
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// restore the module
|
||||
appController._emailDao = origEmailDao;
|
||||
onConnectStub.restore();
|
||||
});
|
||||
afterEach(function() {});
|
||||
|
||||
describe('initial state', function() {
|
||||
it('should be well defined', function() {
|
@ -1,48 +1,39 @@
|
||||
'use strict';
|
||||
|
||||
var mocks = angular.mock,
|
||||
PrivateKeyUploadCtrl = require('../../src/js/controller/privatekey-upload'),
|
||||
appController = require('../../src/js/app-controller'),
|
||||
KeychainDAO = require('../../src/js/dao/keychain-dao'),
|
||||
PGP = require('../../src/js/crypto/pgp');
|
||||
var PrivateKeyUploadCtrl = require('../../../../src/js/controller/app/privatekey-upload'),
|
||||
KeychainDAO = require('../../../../src/js/service/keychain'),
|
||||
PGP = require('../../../../src/js/crypto/pgp'),
|
||||
Dialog = require('../../../../src/js/util/dialog');
|
||||
|
||||
describe('Private Key Upload Controller unit test', function() {
|
||||
var scope, location, ctrl,
|
||||
origEmailDao, emailDaoMock,
|
||||
origKeychain, keychainMock,
|
||||
pgpStub,
|
||||
keychainMock, pgpStub, dialogStub,
|
||||
emailAddress = 'fred@foo.com';
|
||||
|
||||
beforeEach(function(done) {
|
||||
// remember original module to restore later, then replace it
|
||||
origEmailDao = appController._emailDao;
|
||||
appController._emailDao = emailDaoMock = {
|
||||
_account: {
|
||||
emailAddress: emailAddress
|
||||
}
|
||||
};
|
||||
origKeychain = appController._keychain;
|
||||
appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
keychainMock._pgp = pgpStub = sinon.createStubInstance(PGP);
|
||||
beforeEach(function() {
|
||||
keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
pgpStub = sinon.createStubInstance(PGP);
|
||||
dialogStub = sinon.createStubInstance(Dialog);
|
||||
|
||||
angular.module('login-privatekey-download-test', []);
|
||||
mocks.module('login-privatekey-download-test');
|
||||
mocks.inject(function($controller, $rootScope) {
|
||||
angular.module('login-privatekey-download-test', ['woServices']);
|
||||
angular.mock.module('login-privatekey-download-test');
|
||||
angular.mock.inject(function($controller, $rootScope) {
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
ctrl = $controller(PrivateKeyUploadCtrl, {
|
||||
$location: location,
|
||||
$scope: scope
|
||||
$scope: scope,
|
||||
keychain: keychainMock,
|
||||
pgp: pgpStub,
|
||||
dialog: dialogStub,
|
||||
auth: {
|
||||
emailAddress: emailAddress
|
||||
}
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// restore the app controller module
|
||||
appController._keychain = origKeychain;
|
||||
appController._emailDao = origEmailDao;
|
||||
});
|
||||
afterEach(function() {});
|
||||
|
||||
describe('checkServerForKey', function() {
|
||||
var keyParams = {
|
||||
@ -50,17 +41,14 @@ describe('Private Key Upload Controller unit test', function() {
|
||||
_id: 'keyId',
|
||||
};
|
||||
|
||||
it('should fail', function(done) {
|
||||
it('should fail', function() {
|
||||
pgpStub.getKeyParams.returns(keyParams);
|
||||
keychainMock.hasPrivateKey.yields(42);
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(keychainMock.hasPrivateKey.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.checkServerForKey();
|
||||
|
||||
expect(dialogStub.error.calledOnce).to.be.true;
|
||||
expect(keychainMock.hasPrivateKey.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should return true', function(done) {
|
||||
@ -165,16 +153,13 @@ describe('Private Key Upload Controller unit test', function() {
|
||||
});
|
||||
|
||||
describe('encryptAndUploadKey', function() {
|
||||
it('should fail due to keychain.registerDevice', function(done) {
|
||||
it('should fail due to keychain.registerDevice', function() {
|
||||
keychainMock.registerDevice.yields(42);
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(keychainMock.registerDevice.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.encryptAndUploadKey();
|
||||
|
||||
expect(dialogStub.error.calledOnce).to.be.true;
|
||||
expect(keychainMock.registerDevice.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
@ -237,45 +222,36 @@ describe('Private Key Upload Controller unit test', function() {
|
||||
expect(scope.step).to.equal(2);
|
||||
});
|
||||
|
||||
it('should fail for 3 due to error in setDeviceName', function(done) {
|
||||
it('should fail for 3 due to error in setDeviceName', function() {
|
||||
scope.step = 3;
|
||||
setDeviceNameStub.yields(42);
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(scope.step).to.equal(3);
|
||||
done();
|
||||
};
|
||||
|
||||
scope.goForward();
|
||||
|
||||
expect(dialogStub.error.calledOnce).to.be.true;
|
||||
expect(scope.step).to.equal(3);
|
||||
});
|
||||
|
||||
it('should fail for 3 due to error in encryptAndUploadKey', function(done) {
|
||||
it('should fail for 3 due to error in encryptAndUploadKey', function() {
|
||||
scope.step = 3;
|
||||
setDeviceNameStub.yields();
|
||||
encryptAndUploadKeyStub.yields(42);
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(scope.step).to.equal(4);
|
||||
done();
|
||||
};
|
||||
|
||||
scope.goForward();
|
||||
|
||||
expect(dialogStub.error.calledOnce).to.be.true;
|
||||
expect(scope.step).to.equal(4);
|
||||
});
|
||||
|
||||
it('should work for 3', function(done) {
|
||||
it('should work for 3', function() {
|
||||
scope.step = 3;
|
||||
setDeviceNameStub.yields();
|
||||
encryptAndUploadKeyStub.yields();
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err.title).to.equal('Success');
|
||||
expect(scope.step).to.equal(4);
|
||||
done();
|
||||
};
|
||||
|
||||
scope.goForward();
|
||||
|
||||
expect(dialogStub.info.calledOnce).to.be.true;
|
||||
expect(scope.step).to.equal(4);
|
||||
});
|
||||
});
|
||||
});
|
@ -1,57 +1,49 @@
|
||||
'use strict';
|
||||
|
||||
var mocks = angular.mock,
|
||||
KeychainDAO = require('../../src/js/dao/keychain-dao'),
|
||||
InvitationDAO = require('../../src/js/dao/invitation-dao'),
|
||||
PGP = require('../../src/js/crypto/pgp'),
|
||||
ReadCtrl = require('../../src/js/controller/read'),
|
||||
OutboxBO = require('../../src/js/bo/outbox'),
|
||||
appController = require('../../src/js/app-controller');
|
||||
var Keychain = require('../../../../src/js/service/keychain'),
|
||||
InvitationDAO = require('../../../../src/js/service/invitation'),
|
||||
Email = require('../../../../src/js/email/email'),
|
||||
PGP = require('../../../../src/js/crypto/pgp'),
|
||||
ReadCtrl = require('../../../../src/js/controller/app/read'),
|
||||
Outbox = require('../../../../src/js/email/outbox'),
|
||||
Dialog = require('../../../../src/js/util/dialog'),
|
||||
Auth = require('../../../../src/js/service/auth'),
|
||||
Download = require('../../../../src/js/util/download');
|
||||
|
||||
describe('Read Controller unit test', function() {
|
||||
var scope, ctrl,
|
||||
origKeychain, keychainMock,
|
||||
origInvitation, invitationMock,
|
||||
origCrypto, cryptoMock,
|
||||
origOutbox, outboxMock,
|
||||
origEmailDao;
|
||||
var scope, ctrl, keychainMock, invitationMock, emailMock, pgpMock, outboxMock, dialogMock, authMock, downloadMock,
|
||||
emailAddress = 'sender@example.com';
|
||||
|
||||
beforeEach(function() {
|
||||
origKeychain = appController._keychain;
|
||||
appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
keychainMock = sinon.createStubInstance(Keychain);
|
||||
invitationMock = sinon.createStubInstance(InvitationDAO);
|
||||
pgpMock = sinon.createStubInstance(PGP);
|
||||
outboxMock = sinon.createStubInstance(Outbox);
|
||||
emailMock = sinon.createStubInstance(Email);
|
||||
dialogMock = sinon.createStubInstance(Dialog);
|
||||
authMock = sinon.createStubInstance(Auth);
|
||||
downloadMock = sinon.createStubInstance(Download);
|
||||
|
||||
origInvitation = appController._invitationDao;
|
||||
appController._invitationDao = invitationMock = sinon.createStubInstance(InvitationDAO);
|
||||
|
||||
origCrypto = appController._pgp;
|
||||
appController._pgp = cryptoMock = sinon.createStubInstance(PGP);
|
||||
|
||||
origOutbox = appController._outboxBo;
|
||||
appController._outboxBo = outboxMock = sinon.createStubInstance(OutboxBO);
|
||||
|
||||
origEmailDao = appController._emailDao;
|
||||
appController._emailDao = {
|
||||
_account: 'sender@example.com'
|
||||
};
|
||||
|
||||
angular.module('readtest', []);
|
||||
mocks.module('readtest');
|
||||
mocks.inject(function($rootScope, $controller) {
|
||||
angular.module('readtest', ['woServices']);
|
||||
angular.mock.module('readtest');
|
||||
angular.mock.inject(function($rootScope, $controller) {
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
ctrl = $controller(ReadCtrl, {
|
||||
$scope: scope
|
||||
$scope: scope,
|
||||
email: emailMock,
|
||||
invitation: invitationMock,
|
||||
outbox: outboxMock,
|
||||
pgp: pgpMock,
|
||||
keychain: keychainMock,
|
||||
download: downloadMock,
|
||||
auth: authMock,
|
||||
dialog: dialogMock
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
appController._keychain = origKeychain;
|
||||
appController._invitationDao = origInvitation;
|
||||
appController._pgp = origCrypto;
|
||||
appController._outboxBo = origOutbox;
|
||||
appController._emailDao = origEmailDao;
|
||||
});
|
||||
afterEach(function() {});
|
||||
|
||||
describe('scope variables', function() {
|
||||
it('should be set correctly', function() {
|
||||
@ -78,23 +70,18 @@ describe('Read Controller unit test', function() {
|
||||
expect(scope.keyId).to.equal('No key found.');
|
||||
keychainMock.getReceiverPublicKey.yields(42);
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.equal(42);
|
||||
expect(scope.keyId).to.equal('Searching...');
|
||||
};
|
||||
|
||||
scope.getKeyId(address);
|
||||
|
||||
expect(dialogMock.error.calledOnce).to.be.true;
|
||||
expect(scope.keyId).to.equal('Searching...');
|
||||
});
|
||||
|
||||
it('should allow invitation on empty key', function() {
|
||||
keychainMock.getReceiverPublicKey.yields();
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).not.exist;
|
||||
expect(scope.keyId).to.equal('User has no key. Click to invite.');
|
||||
};
|
||||
|
||||
scope.getKeyId(address);
|
||||
|
||||
expect(scope.keyId).to.equal('User has no key. Click to invite.');
|
||||
});
|
||||
|
||||
it('should show searching on error', function() {
|
||||
@ -102,14 +89,10 @@ describe('Read Controller unit test', function() {
|
||||
publicKey: 'PUBLIC KEY'
|
||||
});
|
||||
|
||||
cryptoMock.getFingerprint.returns('XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX');
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(scope.keyId).to.equal('PGP key: XXXXXXXX');
|
||||
};
|
||||
pgpMock.getFingerprint.returns('XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX');
|
||||
|
||||
scope.getKeyId(address);
|
||||
expect(scope.keyId).to.equal('PGP key: XXXXXXXX');
|
||||
});
|
||||
});
|
||||
|
||||
@ -128,39 +111,33 @@ describe('Read Controller unit test', function() {
|
||||
it('should show error on invitation dao invite error', function() {
|
||||
invitationMock.invite.yields(42);
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.equal(42);
|
||||
};
|
||||
|
||||
scope.invite({
|
||||
address: 'asdf@asdf.de'
|
||||
});
|
||||
|
||||
expect(dialogMock.error.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should show error on outbox put error', function() {
|
||||
invitationMock.invite.yields();
|
||||
outboxMock.put.yields(42);
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.equal(42);
|
||||
};
|
||||
|
||||
scope.invite({
|
||||
address: 'asdf@asdf.de'
|
||||
});
|
||||
|
||||
expect(dialogMock.error.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should work', function() {
|
||||
invitationMock.invite.yields();
|
||||
outboxMock.put.yields();
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.not.exist;
|
||||
};
|
||||
|
||||
scope.invite({
|
||||
address: 'asdf@asdf.de'
|
||||
});
|
||||
|
||||
expect(dialogMock.error.calledOnce).to.be.true;
|
||||
});
|
||||
});
|
||||
|
@ -1,31 +1,31 @@
|
||||
'use strict';
|
||||
|
||||
var mocks = angular.mock,
|
||||
SetPassphraseCtrl = require('../../src/js/controller/set-passphrase'),
|
||||
PGP = require('../../src/js/crypto/pgp'),
|
||||
appController = require('../../src/js/app-controller'),
|
||||
KeychainDAO = require('../../src/js/dao/keychain-dao');
|
||||
var SetPassphraseCtrl = require('../../../../src/js/controller/app/set-passphrase'),
|
||||
PGP = require('../../../../src/js/crypto/pgp'),
|
||||
Keychain = require('../../../../src/js/service/keychain'),
|
||||
Dialog = require('../../../../src/js/util/dialog');
|
||||
|
||||
describe('Set Passphrase Controller unit test', function() {
|
||||
var scope, setPassphraseCtrl,
|
||||
dummyFingerprint, expectedFingerprint,
|
||||
dummyKeyId, expectedKeyId,
|
||||
emailAddress, keySize, cryptoMock, keychainMock;
|
||||
emailAddress, keySize, pgpStub, keychainStub, dialogStub;
|
||||
|
||||
beforeEach(function() {
|
||||
appController._pgp = cryptoMock = sinon.createStubInstance(PGP);
|
||||
appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
pgpStub = sinon.createStubInstance(PGP);
|
||||
keychainStub = sinon.createStubInstance(Keychain);
|
||||
dialogStub = sinon.createStubInstance(Dialog);
|
||||
|
||||
dummyFingerprint = '3A2D39B4E1404190B8B949DE7D7E99036E712926';
|
||||
expectedFingerprint = '3A2D 39B4 E140 4190 B8B9 49DE 7D7E 9903 6E71 2926';
|
||||
dummyKeyId = '9FEB47936E712926';
|
||||
expectedKeyId = '6E712926';
|
||||
cryptoMock.getFingerprint.returns(dummyFingerprint);
|
||||
cryptoMock.getKeyId.returns(dummyKeyId);
|
||||
pgpStub.getFingerprint.returns(dummyFingerprint);
|
||||
pgpStub.getKeyId.returns(dummyKeyId);
|
||||
emailAddress = 'fred@foo.com';
|
||||
keySize = 1234;
|
||||
|
||||
cryptoMock.getKeyParams.returns({
|
||||
pgpStub.getKeyParams.returns({
|
||||
_id: dummyKeyId,
|
||||
fingerprint: dummyFingerprint,
|
||||
userId: emailAddress,
|
||||
@ -33,13 +33,16 @@ describe('Set Passphrase Controller unit test', function() {
|
||||
bitSize: keySize
|
||||
});
|
||||
|
||||
angular.module('setpassphrasetest', []);
|
||||
mocks.module('setpassphrasetest');
|
||||
mocks.inject(function($rootScope, $controller) {
|
||||
angular.module('setpassphrasetest', ['woServices', 'woUtil']);
|
||||
angular.mock.module('setpassphrasetest');
|
||||
angular.mock.inject(function($rootScope, $controller) {
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
setPassphraseCtrl = $controller(SetPassphraseCtrl, {
|
||||
$scope: scope
|
||||
$scope: scope,
|
||||
pgp: pgpStub,
|
||||
keychain: keychainStub,
|
||||
dialog: dialogStub
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -47,33 +50,30 @@ describe('Set Passphrase Controller unit test', function() {
|
||||
afterEach(function() {});
|
||||
|
||||
describe('setPassphrase', function() {
|
||||
it('should work', function(done) {
|
||||
it('should work', function() {
|
||||
scope.oldPassphrase = 'old';
|
||||
scope.newPassphrase = 'new';
|
||||
|
||||
keychainMock.lookupPrivateKey.withArgs(dummyKeyId).yields(null, {
|
||||
keychainStub.lookupPrivateKey.withArgs(dummyKeyId).yields(null, {
|
||||
encryptedKey: 'encrypted'
|
||||
});
|
||||
|
||||
cryptoMock.changePassphrase.withArgs({
|
||||
pgpStub.changePassphrase.withArgs({
|
||||
privateKeyArmored: 'encrypted',
|
||||
oldPassphrase: 'old',
|
||||
newPassphrase: 'new'
|
||||
}).yields(null, 'newArmoredKey');
|
||||
|
||||
keychainMock.saveLocalPrivateKey.withArgs({
|
||||
keychainStub.saveLocalPrivateKey.withArgs({
|
||||
_id: dummyKeyId,
|
||||
userId: emailAddress,
|
||||
userIds: [],
|
||||
encryptedKey: 'newArmoredKey'
|
||||
}).yields();
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err.title).to.equal('Success');
|
||||
done();
|
||||
};
|
||||
|
||||
scope.setPassphrase();
|
||||
|
||||
expect(dialogStub.info.calledOnce).to.be.true;
|
||||
});
|
||||
});
|
||||
|
@ -1,57 +1,50 @@
|
||||
'use strict';
|
||||
|
||||
var mocks = angular.mock,
|
||||
WriteCtrl = require('../../src/js/controller/write'),
|
||||
EmailDAO = require('../../src/js/dao/email-dao'),
|
||||
OutboxBO = require('../../src/js/bo/outbox'),
|
||||
KeychainDAO = require('../../src/js/dao/keychain-dao'),
|
||||
appController = require('../../src/js/app-controller');
|
||||
var WriteCtrl = require('../../../../src/js/controller/app/write'),
|
||||
Email = require('../../../../src/js/email/email'),
|
||||
Outbox = require('../../../../src/js/email/outbox'),
|
||||
Keychain = require('../../../../src/js/service/keychain'),
|
||||
Auth = require('../../../../src/js/service/auth'),
|
||||
PGP = require('../../../../src/js/crypto/pgp'),
|
||||
Dialog = require('../../../../src/js/util/dialog');
|
||||
|
||||
describe('Write controller unit test', function() {
|
||||
var ctrl, scope,
|
||||
origEmailDao, origOutbox, origKeychain,
|
||||
emailDaoMock, keychainMock, outboxMock, emailAddress, realname;
|
||||
authMock, pgpMock, dialogMock, emailMock, keychainMock, outboxMock,
|
||||
emailAddress, realname;
|
||||
|
||||
beforeEach(function() {
|
||||
// the app controller is a singleton, we need to remember the
|
||||
// outbox and email dao to restore it after the tests
|
||||
origEmailDao = appController._emailDao;
|
||||
origOutbox = appController._outboxBo;
|
||||
origKeychain = appController._keychain;
|
||||
|
||||
outboxMock = sinon.createStubInstance(OutboxBO);
|
||||
appController._outboxBo = outboxMock;
|
||||
|
||||
emailDaoMock = sinon.createStubInstance(EmailDAO);
|
||||
appController._emailDao = emailDaoMock;
|
||||
authMock = sinon.createStubInstance(Auth);
|
||||
pgpMock = sinon.createStubInstance(PGP);
|
||||
dialogMock = sinon.createStubInstance(Dialog);
|
||||
outboxMock = sinon.createStubInstance(Outbox);
|
||||
emailMock = sinon.createStubInstance(Email);
|
||||
keychainMock = sinon.createStubInstance(Keychain);
|
||||
|
||||
emailAddress = 'fred@foo.com';
|
||||
realname = 'Fred Foo';
|
||||
emailDaoMock._account = {
|
||||
emailAddress: emailAddress,
|
||||
realname: realname
|
||||
};
|
||||
authMock.emailAddress = emailAddress;
|
||||
authMock.realname = realname;
|
||||
|
||||
keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
appController._keychain = keychainMock;
|
||||
|
||||
angular.module('writetest', []);
|
||||
mocks.module('writetest');
|
||||
mocks.inject(function($rootScope, $controller) {
|
||||
angular.module('writetest', ['woEmail', 'woServices', 'woUtil']);
|
||||
angular.mock.module('writetest');
|
||||
angular.mock.inject(function($rootScope, $controller) {
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
ctrl = $controller(WriteCtrl, {
|
||||
$scope: scope
|
||||
$scope: scope,
|
||||
auth: authMock,
|
||||
keychain: keychainMock,
|
||||
pgp: pgpMock,
|
||||
email: emailMock,
|
||||
outbox: outboxMock,
|
||||
dialog: dialogMock
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// restore the app controller
|
||||
appController._emailDao = origEmailDao;
|
||||
appController._outboxBo = origOutbox;
|
||||
appController._keychain = origKeychain;
|
||||
});
|
||||
afterEach(function() {});
|
||||
|
||||
describe('scope variables', function() {
|
||||
it('should be set correctly', function() {
|
||||
@ -187,7 +180,7 @@ describe('Write controller unit test', function() {
|
||||
expect(keychainMock.getReceiverPublicKey.called).to.be.false;
|
||||
});
|
||||
|
||||
it('should not work for error in keychain', function(done) {
|
||||
it('should not work for error in keychain', function() {
|
||||
var recipient = {
|
||||
address: 'asds@example.com'
|
||||
};
|
||||
@ -198,15 +191,13 @@ describe('Write controller unit test', function() {
|
||||
errMsg: '404 not found yadda yadda'
|
||||
});
|
||||
|
||||
scope.onError = function() {
|
||||
expect(recipient.key).to.be.undefined;
|
||||
expect(recipient.secure).to.be.false;
|
||||
expect(scope.checkSendStatus.callCount).to.equal(1);
|
||||
expect(keychainMock.refreshKeyForUserId.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.verify(recipient);
|
||||
|
||||
expect(dialogMock.error.calledOnce).to.be.true;
|
||||
expect(recipient.key).to.be.undefined;
|
||||
expect(recipient.secure).to.be.false;
|
||||
expect(scope.checkSendStatus.callCount).to.equal(1);
|
||||
expect(keychainMock.refreshKeyForUserId.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should work for main userId', function(done) {
|
||||
@ -337,6 +328,7 @@ describe('Write controller unit test', function() {
|
||||
address: emailAddress,
|
||||
name: realname
|
||||
}]);
|
||||
|
||||
expect(mail.to).to.deep.equal(scope.to);
|
||||
expect(mail.cc).to.deep.equal(scope.cc);
|
||||
expect(mail.bcc).to.deep.equal(scope.bcc);
|
||||
@ -345,19 +337,14 @@ describe('Write controller unit test', function() {
|
||||
expect(mail.attachments).to.be.empty;
|
||||
expect(mail.sentDate).to.exist;
|
||||
|
||||
|
||||
return true;
|
||||
})).yields();
|
||||
emailDaoMock.setFlags.yields();
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.not.exist;
|
||||
};
|
||||
emailMock.setFlags.yields();
|
||||
|
||||
scope.sendToOutbox();
|
||||
|
||||
expect(outboxMock.put.calledOnce).to.be.true;
|
||||
expect(emailDaoMock.setFlags.calledOnce).to.be.true;
|
||||
expect(emailMock.setFlags.calledOnce).to.be.true;
|
||||
expect(scope.state.lightbox).to.be.undefined;
|
||||
expect(scope.replyTo.answered).to.be.true;
|
||||
});
|
@ -1,22 +1,21 @@
|
||||
'use strict';
|
||||
|
||||
var mocks = angular.mock,
|
||||
AddAccountCtrl = require('../../src/js/controller/add-account'),
|
||||
Auth = require('../../src/js/bo/auth'),
|
||||
appController = require('../../src/js/app-controller'),
|
||||
cfg = require('../../src/js/app-config').config;
|
||||
var AddAccountCtrl = require('../../../../src/js/controller/login/add-account'),
|
||||
Auth = require('../../../../src/js/service/auth'),
|
||||
Dialog = require('../../../../src/js/util/dialog'),
|
||||
cfg = require('../../../../src/js/app-config').config;
|
||||
|
||||
describe('Add Account Controller unit test', function() {
|
||||
var scope, location, mailConfigMock, ctrl, authStub, origAuth;
|
||||
var scope, location, mailConfigMock, ctrl, authStub, dialogStub;
|
||||
|
||||
beforeEach(function() {
|
||||
// remember original module to restore later, then replace it
|
||||
origAuth = appController._auth;
|
||||
appController._auth = authStub = sinon.createStubInstance(Auth);
|
||||
authStub = sinon.createStubInstance(Auth);
|
||||
dialogStub = sinon.createStubInstance(Dialog);
|
||||
|
||||
angular.module('addaccounttest', ['woServices']);
|
||||
mocks.module('addaccounttest');
|
||||
mocks.inject(function($controller, $rootScope, $location, mailConfig) {
|
||||
angular.mock.module('addaccounttest');
|
||||
angular.mock.inject(function($controller, $rootScope, $location, mailConfig) {
|
||||
location = $location;
|
||||
mailConfigMock = mailConfig;
|
||||
scope = $rootScope.$new();
|
||||
@ -31,15 +30,14 @@ describe('Add Account Controller unit test', function() {
|
||||
$location: location,
|
||||
$scope: scope,
|
||||
$routeParams: {},
|
||||
mailConfig: mailConfigMock
|
||||
mailConfig: mailConfigMock,
|
||||
auth: authStub,
|
||||
dialog: dialogStub
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// restore the app controller module
|
||||
appController._auth = origAuth;
|
||||
|
||||
location.path.restore();
|
||||
location.search.restore();
|
||||
if (scope.$apply.restore) {
|
||||
@ -119,7 +117,7 @@ describe('Add Account Controller unit test', function() {
|
||||
});
|
||||
|
||||
it('should use oauth', function() {
|
||||
scope.onError = function(options) {
|
||||
dialogStub.confirm = function(options) {
|
||||
options.callback(true);
|
||||
};
|
||||
authStub.getOAuthToken.yields();
|
||||
@ -131,7 +129,7 @@ describe('Add Account Controller unit test', function() {
|
||||
});
|
||||
|
||||
it('should not use oauth', function() {
|
||||
scope.onError = function(options) {
|
||||
dialogStub.confirm = function(options) {
|
||||
options.callback(false);
|
||||
};
|
||||
|
||||
@ -141,19 +139,16 @@ describe('Add Account Controller unit test', function() {
|
||||
expect(authStub.getOAuthToken.called).to.be.false;
|
||||
});
|
||||
|
||||
it('should not forward to login when oauth fails', function(done) {
|
||||
scope.onError = function(options) {
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(setCredentialsStub.called).to.be.false;
|
||||
done();
|
||||
};
|
||||
|
||||
it('should not forward to login when oauth fails', function() {
|
||||
dialogStub.confirm = function(options) {
|
||||
options.callback(true);
|
||||
};
|
||||
authStub.getOAuthToken.yields(new Error());
|
||||
|
||||
scope.oauthPossible();
|
||||
|
||||
expect(dialogStub.error.calledOnce).to.be.true;
|
||||
expect(setCredentialsStub.called).to.be.false;
|
||||
});
|
||||
});
|
||||
|
@ -1,22 +1,20 @@
|
||||
'use strict';
|
||||
|
||||
var mocks = angular.mock,
|
||||
CreateAccountCtrl = require('../../src/js/controller/create-account'),
|
||||
AdminDao = require('../../src/js/dao/admin-dao'),
|
||||
appController = require('../../src/js/app-controller');
|
||||
var CreateAccountCtrl = require('../../../../src/js/controller/login/create-account'),
|
||||
AdminDao = require('../../../../src/js/service/admin'),
|
||||
Auth = require('../../../../src/js/service/auth');
|
||||
|
||||
describe('Create Account Controller unit test', function() {
|
||||
var scope, location, ctrl, authStub, origAuth, adminStub;
|
||||
var scope, location, ctrl, authStub, adminStub;
|
||||
|
||||
beforeEach(function() {
|
||||
// remember original module to restore later, then replace it
|
||||
origAuth = appController._auth;
|
||||
appController._auth = authStub = {};
|
||||
appController._adminDao = adminStub = sinon.createStubInstance(AdminDao);
|
||||
adminStub = sinon.createStubInstance(AdminDao);
|
||||
authStub = sinon.createStubInstance(Auth);
|
||||
|
||||
angular.module('createaccounttest', []);
|
||||
mocks.module('createaccounttest');
|
||||
mocks.inject(function($controller, $rootScope, $location) {
|
||||
angular.module('createaccounttest', ['woServices', 'woAppConfig']);
|
||||
angular.mock.module('createaccounttest');
|
||||
angular.mock.inject(function($controller, $rootScope, $location) {
|
||||
location = $location;
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
@ -30,15 +28,14 @@ describe('Create Account Controller unit test', function() {
|
||||
ctrl = $controller(CreateAccountCtrl, {
|
||||
$location: location,
|
||||
$scope: scope,
|
||||
$routeParams: {}
|
||||
$routeParams: {},
|
||||
auth: authStub,
|
||||
admin: adminStub
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// restore the app controller module
|
||||
appController._auth = origAuth;
|
||||
|
||||
location.path.restore();
|
||||
location.search.restore();
|
||||
if (scope.$apply.restore) {
|
186
test/unit/controller/login/login-ctrl-test.js
Normal file
186
test/unit/controller/login/login-ctrl-test.js
Normal file
@ -0,0 +1,186 @@
|
||||
'use strict';
|
||||
|
||||
var LoginCtrl = require('../../../../src/js/controller/login/login'),
|
||||
Email = require('../../../../src/js/email/email'),
|
||||
Account = require('../../../../src/js/email/account'),
|
||||
Dialog = require('../../../../src/js/util/dialog'),
|
||||
UpdateHandler = require('../../../../src/js/util/update/update-handler'),
|
||||
Auth = require('../../../../src/js/service/auth'),
|
||||
Keychain = require('../../../../src/js/service/keychain');
|
||||
|
||||
describe('Login Controller unit test', function() {
|
||||
var scope, location, ctrl,
|
||||
emailMock, keychainMock, authMock, accountMock, dialogMock, updateHandlerMock, goToStub,
|
||||
emailAddress = 'fred@foo.com';
|
||||
|
||||
beforeEach(function() {
|
||||
emailMock = sinon.createStubInstance(Email);
|
||||
accountMock = sinon.createStubInstance(Account);
|
||||
authMock = sinon.createStubInstance(Auth);
|
||||
keychainMock = sinon.createStubInstance(Keychain);
|
||||
dialogMock = sinon.createStubInstance(Dialog);
|
||||
updateHandlerMock = sinon.createStubInstance(UpdateHandler);
|
||||
|
||||
location = {
|
||||
path: function() {}
|
||||
};
|
||||
|
||||
authMock.emailAddress = emailAddress;
|
||||
});
|
||||
|
||||
function createController() {
|
||||
angular.module('login-test', ['woServices', 'woEmail', 'woUtil']);
|
||||
angular.mock.module('login-test');
|
||||
angular.mock.inject(function($rootScope, $controller) {
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
scope.form = {};
|
||||
scope.goTo = function() {};
|
||||
goToStub = sinon.stub(scope, 'goTo');
|
||||
|
||||
ctrl = $controller(LoginCtrl, {
|
||||
$scope: scope,
|
||||
$location: location,
|
||||
updateHandler: updateHandlerMock,
|
||||
account: accountMock,
|
||||
auth: authMock,
|
||||
email: emailMock,
|
||||
keychain: keychainMock,
|
||||
dialog: dialogMock
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
afterEach(function() {});
|
||||
|
||||
it('should fail for auth.getEmailAddress', function() {
|
||||
authMock.getEmailAddress.yields(new Error());
|
||||
|
||||
createController();
|
||||
|
||||
expect(updateHandlerMock.checkForUpdate.calledOnce).to.be.true;
|
||||
expect(authMock.init.calledOnce).to.be.true;
|
||||
expect(dialogMock.error.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should redirect to /add-account', function() {
|
||||
authMock.getEmailAddress.yields(null, {});
|
||||
|
||||
createController();
|
||||
|
||||
expect(goToStub.withArgs('/add-account').calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should fail for auth.init', function() {
|
||||
authMock.getEmailAddress.yields(null, {
|
||||
emailAddress: emailAddress
|
||||
});
|
||||
accountMock.init.yields(new Error());
|
||||
|
||||
createController();
|
||||
|
||||
expect(accountMock.init.calledOnce).to.be.true;
|
||||
expect(dialogMock.error.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should redirect to /login-existing', function() {
|
||||
authMock.getEmailAddress.yields(null, {
|
||||
emailAddress: emailAddress
|
||||
});
|
||||
accountMock.init.yields(null, {
|
||||
publicKey: 'publicKey',
|
||||
privateKey: 'privateKey'
|
||||
});
|
||||
emailMock.unlock.yields(new Error());
|
||||
|
||||
createController();
|
||||
|
||||
expect(goToStub.withArgs('/login-existing').calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should fail for auth.storeCredentials', function() {
|
||||
authMock.getEmailAddress.yields(null, {
|
||||
emailAddress: emailAddress
|
||||
});
|
||||
accountMock.init.yields(null, {
|
||||
publicKey: 'publicKey',
|
||||
privateKey: 'privateKey'
|
||||
});
|
||||
emailMock.unlock.yields();
|
||||
authMock.storeCredentials.yields(new Error());
|
||||
|
||||
createController();
|
||||
|
||||
expect(dialogMock.error.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should redirect to /desktop', function() {
|
||||
authMock.getEmailAddress.yields(null, {
|
||||
emailAddress: emailAddress
|
||||
});
|
||||
accountMock.init.yields(null, {
|
||||
publicKey: 'publicKey',
|
||||
privateKey: 'privateKey'
|
||||
});
|
||||
emailMock.unlock.yields();
|
||||
authMock.storeCredentials.yields();
|
||||
|
||||
createController();
|
||||
|
||||
expect(goToStub.withArgs('/desktop').calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should fail for keychain.requestPrivateKeyDownload', function() {
|
||||
authMock.getEmailAddress.yields(null, {
|
||||
emailAddress: emailAddress
|
||||
});
|
||||
accountMock.init.yields(null, {
|
||||
publicKey: 'publicKey'
|
||||
});
|
||||
keychainMock.requestPrivateKeyDownload.yields(new Error());
|
||||
|
||||
createController();
|
||||
|
||||
expect(dialogMock.error.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should redirect to /login-privatekey-download', function() {
|
||||
authMock.getEmailAddress.yields(null, {
|
||||
emailAddress: emailAddress
|
||||
});
|
||||
accountMock.init.yields(null, {
|
||||
publicKey: 'publicKey'
|
||||
});
|
||||
keychainMock.requestPrivateKeyDownload.yields(null, true);
|
||||
|
||||
createController();
|
||||
|
||||
expect(goToStub.withArgs('/login-privatekey-download').calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should redirect to /login-new-device', function() {
|
||||
authMock.getEmailAddress.yields(null, {
|
||||
emailAddress: emailAddress
|
||||
});
|
||||
accountMock.init.yields(null, {
|
||||
publicKey: 'publicKey'
|
||||
});
|
||||
keychainMock.requestPrivateKeyDownload.yields();
|
||||
|
||||
createController();
|
||||
|
||||
expect(goToStub.withArgs('/login-new-device').calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should redirect to /login-initial', function() {
|
||||
authMock.getEmailAddress.yields(null, {
|
||||
emailAddress: emailAddress
|
||||
});
|
||||
accountMock.init.yields(null, {});
|
||||
|
||||
createController();
|
||||
|
||||
expect(goToStub.withArgs('/login-initial').calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
});
|
@ -1,53 +1,41 @@
|
||||
'use strict';
|
||||
|
||||
var Auth = require('../../src/js/bo/auth'),
|
||||
mocks = angular.mock,
|
||||
LoginExistingCtrl = require('../../src/js/controller/login-existing'),
|
||||
EmailDAO = require('../../src/js/dao/email-dao'),
|
||||
KeychainDAO = require('../../src/js/dao/keychain-dao'),
|
||||
appController = require('../../src/js/app-controller');
|
||||
var Auth = require('../../../../src/js/service/auth'),
|
||||
LoginExistingCtrl = require('../../../../src/js/controller/login/login-existing'),
|
||||
EmailDAO = require('../../../../src/js/email/email'),
|
||||
KeychainDAO = require('../../../../src/js/service/keychain');
|
||||
|
||||
describe('Login (existing user) Controller unit test', function() {
|
||||
var scope, location, ctrl, origEmailDao, emailDaoMock,
|
||||
origAuth, authMock,
|
||||
var scope, location, ctrl, emailDaoMock, authMock,
|
||||
emailAddress = 'fred@foo.com',
|
||||
passphrase = 'asd',
|
||||
keychainMock;
|
||||
|
||||
beforeEach(function() {
|
||||
// remember original module to restore later
|
||||
origEmailDao = appController._emailDao;
|
||||
origAuth = appController._auth;
|
||||
|
||||
appController._emailDao = emailDaoMock = sinon.createStubInstance(EmailDAO);
|
||||
appController._auth = authMock = sinon.createStubInstance(Auth);
|
||||
|
||||
emailDaoMock = sinon.createStubInstance(EmailDAO);
|
||||
authMock = sinon.createStubInstance(Auth);
|
||||
keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
emailDaoMock._keychain = keychainMock;
|
||||
|
||||
emailDaoMock._account = {
|
||||
emailAddress: emailAddress,
|
||||
};
|
||||
authMock.emailAddress = emailAddress;
|
||||
|
||||
angular.module('loginexistingtest', []);
|
||||
mocks.module('loginexistingtest');
|
||||
mocks.inject(function($rootScope, $controller, $location) {
|
||||
angular.module('loginexistingtest', ['woServices']);
|
||||
angular.mock.module('loginexistingtest');
|
||||
angular.mock.inject(function($rootScope, $controller, $location) {
|
||||
location = $location;
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
scope.form = {};
|
||||
ctrl = $controller(LoginExistingCtrl, {
|
||||
$scope: scope,
|
||||
$routeParams: {}
|
||||
$routeParams: {},
|
||||
email: emailDaoMock,
|
||||
auth: authMock,
|
||||
keychain: keychainMock
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// restore the module
|
||||
appController._emailDao = origEmailDao;
|
||||
appController._auth = origAuth;
|
||||
});
|
||||
afterEach(function() {});
|
||||
|
||||
describe('initial state', function() {
|
||||
it('should be well defined', function() {
|
@ -1,39 +1,26 @@
|
||||
'use strict';
|
||||
|
||||
var Auth = require('../../src/js/bo/auth'),
|
||||
mocks = angular.mock,
|
||||
LoginInitialCtrl = require('../../src/js/controller/login-initial'),
|
||||
PGP = require('../../src/js/crypto/pgp'),
|
||||
EmailDAO = require('../../src/js/dao/email-dao'),
|
||||
appController = require('../../src/js/app-controller');
|
||||
var Auth = require('../../../../src/js/service/auth'),
|
||||
LoginInitialCtrl = require('../../../../src/js/controller/login/login-initial'),
|
||||
Email = require('../../../../src/js/email/email');
|
||||
|
||||
describe('Login (initial user) Controller unit test', function() {
|
||||
var scope, ctrl, location, origEmailDao, emailDaoMock,
|
||||
origAuth, authMock, newsletterStub,
|
||||
var scope, ctrl, location, emailMock, authMock, newsletterStub,
|
||||
emailAddress = 'fred@foo.com',
|
||||
keyId, expectedKeyId,
|
||||
cryptoMock;
|
||||
keyId, expectedKeyId;
|
||||
|
||||
beforeEach(function() {
|
||||
// remember original module to restore later
|
||||
origEmailDao = appController._emailDao;
|
||||
origAuth = appController._auth;
|
||||
|
||||
appController._emailDao = emailDaoMock = sinon.createStubInstance(EmailDAO);
|
||||
appController._auth = authMock = sinon.createStubInstance(Auth);
|
||||
emailMock = sinon.createStubInstance(Email);
|
||||
authMock = sinon.createStubInstance(Auth);
|
||||
|
||||
keyId = '9FEB47936E712926';
|
||||
expectedKeyId = '6E712926';
|
||||
cryptoMock = sinon.createStubInstance(PGP);
|
||||
emailDaoMock._crypto = cryptoMock;
|
||||
|
||||
emailDaoMock._account = {
|
||||
emailAddress: emailAddress,
|
||||
};
|
||||
authMock.emailAddress = emailAddress;
|
||||
|
||||
angular.module('logininitialtest', ['woServices']);
|
||||
mocks.module('logininitialtest');
|
||||
mocks.inject(function($rootScope, $controller, $location, newsletter) {
|
||||
angular.mock.module('logininitialtest');
|
||||
angular.mock.inject(function($rootScope, $controller, $location, newsletter) {
|
||||
scope = $rootScope.$new();
|
||||
location = $location;
|
||||
newsletterStub = sinon.stub(newsletter, 'signup');
|
||||
@ -43,16 +30,14 @@ describe('Login (initial user) Controller unit test', function() {
|
||||
ctrl = $controller(LoginInitialCtrl, {
|
||||
$scope: scope,
|
||||
$routeParams: {},
|
||||
newsletter: newsletter
|
||||
newsletter: newsletter,
|
||||
email: emailMock,
|
||||
auth: authMock
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// restore the module
|
||||
appController._emailDao = origEmailDao;
|
||||
appController._auth = origAuth;
|
||||
});
|
||||
afterEach(function() {});
|
||||
|
||||
describe('initial state', function() {
|
||||
it('should be well defined', function() {
|
||||
@ -92,7 +77,7 @@ describe('Login (initial user) Controller unit test', function() {
|
||||
it('should fail due to error in emailDao.unlock', function() {
|
||||
scope.agree = true;
|
||||
|
||||
emailDaoMock.unlock.withArgs({
|
||||
emailMock.unlock.withArgs({
|
||||
passphrase: undefined
|
||||
}).yields(new Error('asdf'));
|
||||
authMock.storeCredentials.yields();
|
||||
@ -107,7 +92,7 @@ describe('Login (initial user) Controller unit test', function() {
|
||||
it('should unlock crypto', function() {
|
||||
scope.agree = true;
|
||||
|
||||
emailDaoMock.unlock.withArgs({
|
||||
emailMock.unlock.withArgs({
|
||||
passphrase: undefined
|
||||
}).yields();
|
||||
authMock.storeCredentials.yields();
|
||||
@ -118,7 +103,7 @@ describe('Login (initial user) Controller unit test', function() {
|
||||
expect(scope.state.ui).to.equal(2);
|
||||
expect(newsletterStub.called).to.be.true;
|
||||
expect(location.$$path).to.equal('/desktop');
|
||||
expect(emailDaoMock.unlock.calledOnce).to.be.true;
|
||||
expect(emailMock.unlock.calledOnce).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
@ -1,38 +1,32 @@
|
||||
'use strict';
|
||||
|
||||
var mocks = angular.mock,
|
||||
PGP = require('../../src/js/crypto/pgp'),
|
||||
LoginNewDeviceCtrl = require('../../src/js/controller/login-new-device'),
|
||||
KeychainDAO = require('../../src/js/dao/keychain-dao'),
|
||||
EmailDAO = require('../../src/js/dao/email-dao'),
|
||||
appController = require('../../src/js/app-controller');
|
||||
var PGP = require('../../../../src/js/crypto/pgp'),
|
||||
LoginNewDeviceCtrl = require('../../../../src/js/controller/login/login-new-device'),
|
||||
KeychainDAO = require('../../../../src/js/service/keychain'),
|
||||
EmailDAO = require('../../../../src/js/email/email'),
|
||||
Auth = require('../../../../src/js/service/auth');
|
||||
|
||||
describe('Login (new device) Controller unit test', function() {
|
||||
var scope, ctrl, origEmailDao, emailDaoMock, pgpMock,
|
||||
var scope, ctrl, emailMock, pgpMock, authMock,
|
||||
emailAddress = 'fred@foo.com',
|
||||
passphrase = 'asd',
|
||||
keyId,
|
||||
keychainMock;
|
||||
|
||||
beforeEach(function() {
|
||||
// remember original module to restore later
|
||||
origEmailDao = appController._emailDao;
|
||||
|
||||
emailDaoMock = sinon.createStubInstance(EmailDAO);
|
||||
appController._emailDao = emailDaoMock;
|
||||
emailMock = sinon.createStubInstance(EmailDAO);
|
||||
authMock = sinon.createStubInstance(Auth);
|
||||
|
||||
keyId = '9FEB47936E712926';
|
||||
emailDaoMock._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
appController._pgp = pgpMock = sinon.createStubInstance(PGP);
|
||||
keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
pgpMock = sinon.createStubInstance(PGP);
|
||||
pgpMock.extractPublicKey.returns('publicKeyArmored');
|
||||
|
||||
emailDaoMock._account = {
|
||||
emailAddress: emailAddress,
|
||||
};
|
||||
authMock.emailAddress = emailAddress;
|
||||
|
||||
angular.module('loginnewdevicetest', []);
|
||||
mocks.module('loginnewdevicetest');
|
||||
mocks.inject(function($rootScope, $controller) {
|
||||
angular.module('loginnewdevicetest', ['woServices']);
|
||||
angular.mock.module('loginnewdevicetest');
|
||||
angular.mock.inject(function($rootScope, $controller) {
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {
|
||||
ui: {}
|
||||
@ -40,15 +34,16 @@ describe('Login (new device) Controller unit test', function() {
|
||||
scope.form = {};
|
||||
ctrl = $controller(LoginNewDeviceCtrl, {
|
||||
$scope: scope,
|
||||
$routeParams: {}
|
||||
$routeParams: {},
|
||||
email: emailMock,
|
||||
auth: authMock,
|
||||
pgp: pgpMock,
|
||||
keychain: keychainMock
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// restore the module
|
||||
appController._emailDao = origEmailDao;
|
||||
});
|
||||
afterEach(function() {});
|
||||
|
||||
describe('initial state', function() {
|
||||
it('should be well defined', function() {
|
||||
@ -73,12 +68,12 @@ describe('Login (new device) Controller unit test', function() {
|
||||
_id: keyId,
|
||||
publicKey: 'a'
|
||||
});
|
||||
emailDaoMock.unlock.withArgs(sinon.match.any, passphrase).yields();
|
||||
emailMock.unlock.withArgs(sinon.match.any, passphrase).yields();
|
||||
keychainMock.putUserKeyPair.yields();
|
||||
|
||||
scope.confirmPassphrase();
|
||||
|
||||
expect(emailDaoMock.unlock.calledOnce).to.be.true;
|
||||
expect(emailMock.unlock.calledOnce).to.be.true;
|
||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
@ -95,12 +90,12 @@ describe('Login (new device) Controller unit test', function() {
|
||||
});
|
||||
|
||||
keychainMock.getUserKeyPair.withArgs(emailAddress).yields();
|
||||
emailDaoMock.unlock.withArgs(sinon.match.any, passphrase).yields();
|
||||
emailMock.unlock.withArgs(sinon.match.any, passphrase).yields();
|
||||
keychainMock.putUserKeyPair.yields();
|
||||
|
||||
scope.confirmPassphrase();
|
||||
|
||||
expect(emailDaoMock.unlock.calledOnce).to.be.true;
|
||||
expect(emailMock.unlock.calledOnce).to.be.true;
|
||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
@ -119,7 +114,7 @@ describe('Login (new device) Controller unit test', function() {
|
||||
_id: keyId,
|
||||
publicKey: 'a'
|
||||
});
|
||||
emailDaoMock.unlock.yields();
|
||||
emailMock.unlock.yields();
|
||||
keychainMock.putUserKeyPair.yields({
|
||||
errMsg: 'yo mamma.'
|
||||
});
|
||||
@ -127,7 +122,7 @@ describe('Login (new device) Controller unit test', function() {
|
||||
scope.confirmPassphrase();
|
||||
|
||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||
expect(emailDaoMock.unlock.calledOnce).to.be.true;
|
||||
expect(emailMock.unlock.calledOnce).to.be.true;
|
||||
expect(keychainMock.putUserKeyPair.calledOnce).to.be.true;
|
||||
expect(scope.errMsg).to.equal('yo mamma.');
|
||||
});
|
||||
@ -147,7 +142,7 @@ describe('Login (new device) Controller unit test', function() {
|
||||
_id: keyId,
|
||||
publicKey: 'a'
|
||||
});
|
||||
emailDaoMock.unlock.yields({
|
||||
emailMock.unlock.yields({
|
||||
errMsg: 'yo mamma.'
|
||||
});
|
||||
|
||||
@ -155,7 +150,7 @@ describe('Login (new device) Controller unit test', function() {
|
||||
|
||||
expect(scope.incorrect).to.be.true;
|
||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||
expect(emailDaoMock.unlock.calledOnce).to.be.true;
|
||||
expect(emailMock.unlock.calledOnce).to.be.true;
|
||||
expect(scope.errMsg).to.equal('yo mamma.');
|
||||
});
|
||||
|
@ -1,55 +1,43 @@
|
||||
'use strict';
|
||||
|
||||
var mocks = angular.mock,
|
||||
Auth = require('../../src/js/bo/auth'),
|
||||
LoginPrivateKeyDownloadCtrl = require('../../src/js/controller/login-privatekey-download'),
|
||||
EmailDAO = require('../../src/js/dao/email-dao'),
|
||||
appController = require('../../src/js/app-controller'),
|
||||
KeychainDAO = require('../../src/js/dao/keychain-dao');
|
||||
var Auth = require('../../../../src/js/service/auth'),
|
||||
LoginPrivateKeyDownloadCtrl = require('../../../../src/js/controller/login/login-privatekey-download'),
|
||||
Email = require('../../../../src/js/email/email'),
|
||||
Keychain = require('../../../../src/js/service/keychain');
|
||||
|
||||
describe('Login Private Key Download Controller unit test', function() {
|
||||
var scope, location, ctrl,
|
||||
origEmailDao, emailDaoMock,
|
||||
origAuth, authMock,
|
||||
origKeychain, keychainMock,
|
||||
emailDaoMock, authMock, keychainMock,
|
||||
emailAddress = 'fred@foo.com';
|
||||
|
||||
beforeEach(function(done) {
|
||||
// remember original module to restore later, then replace it
|
||||
origEmailDao = appController._emailDao;
|
||||
origKeychain = appController._keychain;
|
||||
origAuth = appController._auth;
|
||||
emailDaoMock = sinon.createStubInstance(Email);
|
||||
keychainMock = sinon.createStubInstance(Keychain);
|
||||
authMock = sinon.createStubInstance(Auth);
|
||||
|
||||
appController._emailDao = emailDaoMock = sinon.createStubInstance(EmailDAO);
|
||||
appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
appController._auth = authMock = sinon.createStubInstance(Auth);
|
||||
authMock.emailAddress = emailAddress;
|
||||
|
||||
emailDaoMock._account = {
|
||||
emailAddress: emailAddress
|
||||
};
|
||||
|
||||
angular.module('login-privatekey-download-test', []);
|
||||
mocks.module('login-privatekey-download-test');
|
||||
mocks.inject(function($controller, $rootScope) {
|
||||
angular.module('login-privatekey-download-test', ['woServices']);
|
||||
angular.mock.module('login-privatekey-download-test');
|
||||
angular.mock.inject(function($controller, $rootScope, $location) {
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
scope.tokenForm = {};
|
||||
scope.codeForm = {};
|
||||
location = $location;
|
||||
ctrl = $controller(LoginPrivateKeyDownloadCtrl, {
|
||||
$location: location,
|
||||
$scope: scope,
|
||||
$routeParams: {}
|
||||
$routeParams: {},
|
||||
auth: authMock,
|
||||
email: emailDaoMock,
|
||||
keychain: keychainMock
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// restore the app controller module
|
||||
appController._emailDao = origEmailDao;
|
||||
appController._keychain = origKeychain;
|
||||
appController._auth = origAuth;
|
||||
});
|
||||
afterEach(function() {});
|
||||
|
||||
describe('initialization', function() {
|
||||
it('should work', function() {
|
||||
@ -208,20 +196,9 @@ describe('Login Private Key Download Controller unit test', function() {
|
||||
});
|
||||
|
||||
describe('goTo', function() {
|
||||
it('should work', function(done) {
|
||||
mocks.inject(function($controller, $rootScope, $location) {
|
||||
location = $location;
|
||||
sinon.stub(location, 'path', function(path) {
|
||||
expect(path).to.equal('/desktop');
|
||||
done();
|
||||
});
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
ctrl = $controller(LoginPrivateKeyDownloadCtrl, {
|
||||
$location: location,
|
||||
$scope: scope,
|
||||
$routeParams: {}
|
||||
});
|
||||
it('should work', function() {
|
||||
sinon.stub(location, 'path', function(path) {
|
||||
expect(path).to.equal('/desktop');
|
||||
});
|
||||
|
||||
scope.goTo('/desktop');
|
@ -1,32 +1,28 @@
|
||||
'use strict';
|
||||
|
||||
var mocks = angular.mock,
|
||||
Auth = require('../../src/js/bo/auth'),
|
||||
ConnectionDoctor = require('../../src/js/util/connection-doctor'),
|
||||
SetCredentialsCtrl = require('../../src/js/controller/login-set-credentials'),
|
||||
appController = require('../../src/js/app-controller');
|
||||
var Auth = require('../../../../src/js/service/auth'),
|
||||
ConnectionDoctor = require('../../../../src/js/util/connection-doctor'),
|
||||
SetCredentialsCtrl = require('../../../../src/js/controller/login/login-set-credentials');
|
||||
|
||||
describe('Login (Set Credentials) Controller unit test', function() {
|
||||
// Angular parameters
|
||||
var scope, location, provider;
|
||||
|
||||
// Stubs
|
||||
var auth, origAuth, doctor, origDoctor;
|
||||
var auth, doctor;
|
||||
|
||||
// SUT
|
||||
var setCredentialsCtrl;
|
||||
|
||||
beforeEach(function() {
|
||||
// remeber pre-test state to restore later
|
||||
origAuth = appController._auth;
|
||||
origDoctor = appController._doctor;
|
||||
auth = appController._auth = sinon.createStubInstance(Auth);
|
||||
doctor = appController._doctor = sinon.createStubInstance(ConnectionDoctor);
|
||||
auth = sinon.createStubInstance(Auth);
|
||||
doctor = sinon.createStubInstance(ConnectionDoctor);
|
||||
|
||||
// setup the controller
|
||||
angular.module('setcredentialstest', []);
|
||||
mocks.module('setcredentialstest');
|
||||
mocks.inject(function($rootScope, $controller, $location) {
|
||||
angular.mock.module('setcredentialstest');
|
||||
angular.mock.inject(function($rootScope, $controller, $location) {
|
||||
scope = $rootScope.$new();
|
||||
location = $location;
|
||||
location.search({
|
||||
@ -43,16 +39,14 @@ describe('Login (Set Credentials) Controller unit test', function() {
|
||||
|
||||
setCredentialsCtrl = $controller(SetCredentialsCtrl, {
|
||||
$scope: scope,
|
||||
$routeParams: {}
|
||||
$routeParams: {},
|
||||
auth: auth,
|
||||
connectionDoctor: doctor
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// restore pre-test state
|
||||
appController._auth = origAuth;
|
||||
appController._doctor = origDoctor;
|
||||
});
|
||||
afterEach(function() {});
|
||||
|
||||
describe('set credentials', function() {
|
||||
it('should work', function() {
|
@ -1,23 +1,19 @@
|
||||
'use strict';
|
||||
|
||||
var mocks = angular.mock,
|
||||
ValidatePhoneCtrl = require('../../src/js/controller/validate-phone'),
|
||||
Auth = require('../../src/js/bo/auth'),
|
||||
AdminDao = require('../../src/js/dao/admin-dao'),
|
||||
appController = require('../../src/js/app-controller');
|
||||
var ValidatePhoneCtrl = require('../../../../src/js/controller/login/validate-phone'),
|
||||
Auth = require('../../../../src/js/service/auth'),
|
||||
AdminDao = require('../../../../src/js/service/admin');
|
||||
|
||||
describe('Validate Phone Controller unit test', function() {
|
||||
var scope, location, mailConfigMock, ctrl, authStub, origAuth, adminStub;
|
||||
var scope, location, mailConfigMock, ctrl, authStub, adminStub;
|
||||
|
||||
beforeEach(function() {
|
||||
// remember original module to restore later, then replace it
|
||||
origAuth = appController._auth;
|
||||
appController._auth = authStub = sinon.createStubInstance(Auth);
|
||||
appController._adminDao = adminStub = sinon.createStubInstance(AdminDao);
|
||||
authStub = sinon.createStubInstance(Auth);
|
||||
adminStub = sinon.createStubInstance(AdminDao);
|
||||
|
||||
angular.module('validatephonetest', ['woServices']);
|
||||
mocks.module('validatephonetest');
|
||||
mocks.inject(function($controller, $rootScope, $location, mailConfig) {
|
||||
angular.mock.module('validatephonetest');
|
||||
angular.mock.inject(function($controller, $rootScope, $location, mailConfig) {
|
||||
location = $location;
|
||||
mailConfigMock = mailConfig;
|
||||
scope = $rootScope.$new();
|
||||
@ -38,15 +34,14 @@ describe('Validate Phone Controller unit test', function() {
|
||||
$location: location,
|
||||
$scope: scope,
|
||||
$routeParams: {},
|
||||
mailConfig: mailConfigMock
|
||||
mailConfig: mailConfigMock,
|
||||
auth: authStub,
|
||||
admin: adminStub
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// restore the app controller module
|
||||
appController._auth = origAuth;
|
||||
|
||||
location.path.restore();
|
||||
location.search.restore();
|
||||
if (scope.$apply.restore) {
|
@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
var Crypto = require('../../src/js/crypto/crypto'),
|
||||
config = require('../../src/js/app-config').config,
|
||||
var Crypto = require('../../../src/js/crypto/crypto'),
|
||||
config = require('../../../src/js/app-config').config,
|
||||
util = require('crypto-lib').util;
|
||||
|
||||
describe('Crypto unit tests', function() {
|
@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
var PGP = require('../../src/js/crypto/pgp');
|
||||
var PGP = require('../../../src/js/crypto/pgp');
|
||||
|
||||
describe('PGP Crypto Api unit tests', function() {
|
||||
this.timeout(20000);
|
275
test/unit/email/account-test.js
Normal file
275
test/unit/email/account-test.js
Normal file
@ -0,0 +1,275 @@
|
||||
'use strict';
|
||||
|
||||
var Account = require('../../../src/js/email/account'),
|
||||
appConfig = require('../../../src/js/app-config'),
|
||||
Auth = require('../../../src/js/service/auth'),
|
||||
DeviceStorageDAO = require('../../../src/js/service/devicestorage'),
|
||||
Email = require('../../../src/js/email/email'),
|
||||
Outbox = require('../../../src/js/email/outbox'),
|
||||
Keychain = require('../../../src/js/service/keychain'),
|
||||
UpdateHandler = require('../../../src/js/util/update/update-handler'),
|
||||
Dialog = require('../../../src/js/util/dialog');
|
||||
|
||||
describe('Account Service unit test', function() {
|
||||
var account, authStub, outboxStub, emailStub, devicestorageStub, keychainStub, updateHandlerStub, pgpbuilderStub, dialogStub,
|
||||
realname = 'John Doe',
|
||||
dummyUser = 'spiderpig@springfield.com';
|
||||
|
||||
chai.config.includeStack = true;
|
||||
|
||||
beforeEach(function() {
|
||||
authStub = sinon.createStubInstance(Auth);
|
||||
outboxStub = sinon.createStubInstance(Outbox);
|
||||
devicestorageStub = sinon.createStubInstance(DeviceStorageDAO);
|
||||
emailStub = sinon.createStubInstance(Email);
|
||||
outboxStub = sinon.createStubInstance(Outbox);
|
||||
keychainStub = sinon.createStubInstance(Keychain);
|
||||
updateHandlerStub = sinon.createStubInstance(UpdateHandler);
|
||||
pgpbuilderStub = {};
|
||||
dialogStub = sinon.createStubInstance(Dialog);
|
||||
account = new Account(appConfig, authStub, devicestorageStub, emailStub, outboxStub, keychainStub, updateHandlerStub, pgpbuilderStub, dialogStub);
|
||||
});
|
||||
|
||||
afterEach(function() {});
|
||||
|
||||
describe('isLoggedIn', function() {
|
||||
it('should be logged in', function() {
|
||||
account._accounts = [{}];
|
||||
expect(account.isLoggedIn()).to.be.true;
|
||||
});
|
||||
it('should not be logged in', function() {
|
||||
account._accounts = [];
|
||||
expect(account.isLoggedIn()).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('list', function() {
|
||||
it('should work', function() {
|
||||
var testAccounts = [{
|
||||
foo: 'bar'
|
||||
}];
|
||||
account._accounts = testAccounts;
|
||||
expect(account.list()).to.deep.equal(testAccounts);
|
||||
});
|
||||
});
|
||||
|
||||
describe('init', function() {
|
||||
it('should fail for invalid email address', function() {
|
||||
account.init({
|
||||
emailAddress: dummyUser.replace('@'),
|
||||
realname: realname
|
||||
}, onInit);
|
||||
|
||||
function onInit(err, keys) {
|
||||
expect(err).to.exist;
|
||||
expect(keys).to.not.exist;
|
||||
}
|
||||
});
|
||||
|
||||
it('should fail for _accountStore.init', function() {
|
||||
devicestorageStub.init.throws(new Error('asdf'));
|
||||
|
||||
account.init({
|
||||
emailAddress: dummyUser,
|
||||
realname: realname
|
||||
}, onInit);
|
||||
|
||||
function onInit(err, keys) {
|
||||
expect(err.message).to.match(/asdf/);
|
||||
expect(keys).to.not.exist;
|
||||
}
|
||||
});
|
||||
|
||||
it('should fail for _updateHandler.update', function() {
|
||||
updateHandlerStub.update.yields(new Error('asdf'));
|
||||
|
||||
account.init({
|
||||
emailAddress: dummyUser,
|
||||
realname: realname
|
||||
}, onInit);
|
||||
|
||||
function onInit(err, keys) {
|
||||
expect(err.message).to.match(/Updating/);
|
||||
expect(keys).to.not.exist;
|
||||
}
|
||||
});
|
||||
|
||||
it('should fail for _keychain.getUserKeyPair', function() {
|
||||
updateHandlerStub.update.yields();
|
||||
keychainStub.getUserKeyPair.yields(new Error('asdf'));
|
||||
|
||||
account.init({
|
||||
emailAddress: dummyUser,
|
||||
realname: realname
|
||||
}, onInit);
|
||||
|
||||
function onInit(err, keys) {
|
||||
expect(err.message).to.match(/asdf/);
|
||||
expect(keys).to.not.exist;
|
||||
}
|
||||
});
|
||||
|
||||
it('should fail for _keychain.refreshKeyForUserId', function() {
|
||||
var storedKeys = {
|
||||
publicKey: 'publicKey'
|
||||
};
|
||||
|
||||
updateHandlerStub.update.yields();
|
||||
keychainStub.getUserKeyPair.yields(null, storedKeys);
|
||||
keychainStub.refreshKeyForUserId.yields(new Error('asdf'));
|
||||
|
||||
account.init({
|
||||
emailAddress: dummyUser,
|
||||
realname: realname
|
||||
}, onInit);
|
||||
|
||||
function onInit(err, keys) {
|
||||
expect(err.message).to.match(/asdf/);
|
||||
expect(keys).to.not.exist;
|
||||
}
|
||||
});
|
||||
|
||||
it('should fail for _emailDao.init after _keychain.refreshKeyForUserId', function() {
|
||||
var storedKeys = {
|
||||
publicKey: 'publicKey'
|
||||
};
|
||||
|
||||
updateHandlerStub.update.yields();
|
||||
keychainStub.getUserKeyPair.yields(null, storedKeys);
|
||||
keychainStub.refreshKeyForUserId.yields(null, storedKeys);
|
||||
emailStub.init.yields(new Error('asdf'));
|
||||
|
||||
account.init({
|
||||
emailAddress: dummyUser,
|
||||
realname: realname
|
||||
}, onInit);
|
||||
|
||||
function onInit(err, keys) {
|
||||
expect(err.message).to.match(/asdf/);
|
||||
expect(keys).to.not.exist;
|
||||
}
|
||||
});
|
||||
|
||||
it('should fail for _emailDao.init', function() {
|
||||
var storedKeys = {
|
||||
publicKey: 'publicKey',
|
||||
privateKey: 'privateKey'
|
||||
};
|
||||
|
||||
updateHandlerStub.update.yields();
|
||||
keychainStub.getUserKeyPair.yields(null, storedKeys);
|
||||
emailStub.init.yields(new Error('asdf'));
|
||||
|
||||
account.init({
|
||||
emailAddress: dummyUser,
|
||||
realname: realname
|
||||
}, onInit);
|
||||
|
||||
function onInit(err, keys) {
|
||||
expect(err.message).to.match(/asdf/);
|
||||
expect(keys).to.not.exist;
|
||||
}
|
||||
});
|
||||
|
||||
it('should work after _keychain.refreshKeyForUserId', function() {
|
||||
var storedKeys = {
|
||||
publicKey: 'publicKey'
|
||||
};
|
||||
|
||||
updateHandlerStub.update.yields();
|
||||
keychainStub.getUserKeyPair.yields(null, storedKeys);
|
||||
keychainStub.refreshKeyForUserId.yields(null, 'publicKey');
|
||||
emailStub.init.yields();
|
||||
|
||||
account.init({
|
||||
emailAddress: dummyUser,
|
||||
realname: realname
|
||||
}, onInit);
|
||||
|
||||
function onInit(err, keys) {
|
||||
expect(err).to.not.exist;
|
||||
expect(keys).to.deep.equal(storedKeys);
|
||||
}
|
||||
});
|
||||
|
||||
it('should work', function() {
|
||||
var storedKeys = {
|
||||
publicKey: 'publicKey',
|
||||
privateKey: 'privateKey'
|
||||
};
|
||||
|
||||
updateHandlerStub.update.yields();
|
||||
keychainStub.getUserKeyPair.yields(null, storedKeys);
|
||||
emailStub.init.yields();
|
||||
|
||||
account.init({
|
||||
emailAddress: dummyUser,
|
||||
realname: realname
|
||||
}, onInit);
|
||||
|
||||
function onInit(err, keys) {
|
||||
expect(err).to.not.exist;
|
||||
expect(keys).to.equal(storedKeys);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('onConnect', function() {
|
||||
var credentials = {
|
||||
imap: {},
|
||||
smtp: {}
|
||||
};
|
||||
beforeEach(function() {
|
||||
emailStub._account = {};
|
||||
sinon.stub(account, 'isOnline').returns(true);
|
||||
});
|
||||
afterEach(function() {
|
||||
account.isOnline.restore();
|
||||
});
|
||||
|
||||
it('should fail due to _auth.getCredentials', function() {
|
||||
authStub.getCredentials.yields(new Error('asdf'));
|
||||
|
||||
account.onConnect();
|
||||
|
||||
expect(dialogStub.error.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should work', function() {
|
||||
authStub.getCredentials.yields(null, credentials);
|
||||
emailStub.onConnect.yields();
|
||||
|
||||
account.onConnect();
|
||||
|
||||
expect(emailStub.onConnect.calledOnce).to.be.true;
|
||||
expect(dialogStub.error.calledOnce).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('onDisconnect', function() {
|
||||
it('should work', function() {
|
||||
account.onDisconnect();
|
||||
expect(emailStub.onDisconnect.calledOnce).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('logout', function() {
|
||||
it('should fail due to _auth.logout', function() {
|
||||
authStub.logout.yields(new Error());
|
||||
|
||||
account.logout();
|
||||
|
||||
expect(dialogStub.error.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should fail due to _emailDao.onDisconnect', function() {
|
||||
authStub.logout.yields();
|
||||
emailStub.onDisconnect.yields(new Error());
|
||||
|
||||
account.logout();
|
||||
|
||||
expect(dialogStub.error.calledOnce).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
});
|
@ -4,11 +4,12 @@ var mailreader = require('mailreader'),
|
||||
ImapClient = require('imap-client'),
|
||||
PgpMailer = require('pgpmailer'),
|
||||
PgpBuilder = require('pgpbuilder'),
|
||||
cfg = require('../../src/js/app-config').config,
|
||||
EmailDAO = require('../../src/js/dao/email-dao'),
|
||||
KeychainDAO = require('../../src/js/dao/keychain-dao'),
|
||||
PGP = require('../../src/js/crypto/pgp'),
|
||||
DeviceStorageDAO = require('../../src/js/dao/devicestorage-dao');
|
||||
cfg = require('../../../src/js/app-config').config,
|
||||
EmailDAO = require('../../../src/js/email/email'),
|
||||
KeychainDAO = require('../../../src/js/service/keychain'),
|
||||
PGP = require('../../../src/js/crypto/pgp'),
|
||||
DeviceStorageDAO = require('../../../src/js/service/devicestorage'),
|
||||
Dialog = require('../../../src/js/util/dialog');
|
||||
|
||||
|
||||
describe('Email DAO unit tests', function() {
|
||||
@ -19,7 +20,7 @@ describe('Email DAO unit tests', function() {
|
||||
var dao;
|
||||
|
||||
// mocks
|
||||
var keychainStub, imapClientStub, pgpMailerStub, pgpBuilderStub, pgpStub, devicestorageStub, parseStub;
|
||||
var keychainStub, imapClientStub, pgpMailerStub, pgpBuilderStub, pgpStub, devicestorageStub, parseStub, dialogStub;
|
||||
|
||||
// config
|
||||
var emailAddress, passphrase, asymKeySize, account;
|
||||
@ -116,11 +117,12 @@ describe('Email DAO unit tests', function() {
|
||||
pgpStub = sinon.createStubInstance(PGP);
|
||||
parseStub = sinon.stub(mailreader, 'parse');
|
||||
devicestorageStub = sinon.createStubInstance(DeviceStorageDAO);
|
||||
dialogStub = sinon.createStubInstance(Dialog);
|
||||
|
||||
//
|
||||
// setup the SUT
|
||||
//
|
||||
dao = new EmailDAO(keychainStub, pgpStub, devicestorageStub, pgpBuilderStub, mailreader);
|
||||
dao = new EmailDAO(keychainStub, pgpStub, devicestorageStub, pgpBuilderStub, mailreader, dialogStub);
|
||||
dao._account = account;
|
||||
dao._pgpMailer = pgpMailerStub;
|
||||
dao._imapClient = imapClientStub;
|
||||
@ -1897,64 +1899,55 @@ describe('Email DAO unit tests', function() {
|
||||
setFlagsStub = sinon.stub(dao, 'setFlags');
|
||||
});
|
||||
|
||||
it('should get new message', function(done) {
|
||||
it('should get new message', function() {
|
||||
fetchMessagesStub.withArgs({
|
||||
folder: inboxFolder,
|
||||
firstUid: 1,
|
||||
lastUid: 3
|
||||
}).yieldsAsync();
|
||||
|
||||
dao.onError = function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(fetchMessagesStub.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
}).yields();
|
||||
|
||||
dao._onSyncUpdate({
|
||||
type: 'new',
|
||||
path: inboxFolder.path,
|
||||
list: [1, 3]
|
||||
});
|
||||
|
||||
expect(dialogStub.error.calledOnce).to.be.true;
|
||||
expect(fetchMessagesStub.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should delete message', function(done) {
|
||||
it('should delete message', function() {
|
||||
deleteMessagesStub.withArgs({
|
||||
folder: inboxFolder,
|
||||
message: msgs[0],
|
||||
localOnly: true
|
||||
}).yieldsAsync();
|
||||
|
||||
dao.onError = function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(deleteMessagesStub.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
}).yields();
|
||||
|
||||
dao._onSyncUpdate({
|
||||
type: 'deleted',
|
||||
path: inboxFolder.path,
|
||||
list: [5]
|
||||
});
|
||||
|
||||
expect(dialogStub.error.calledOnce).to.be.true;
|
||||
expect(deleteMessagesStub.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should fetch flags', function(done) {
|
||||
it('should fetch flags', function() {
|
||||
setFlagsStub.withArgs({
|
||||
folder: inboxFolder,
|
||||
message: msgs[0],
|
||||
localOnly: true
|
||||
}).yieldsAsync();
|
||||
|
||||
dao.onError = function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(setFlagsStub.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
}).yields();
|
||||
|
||||
dao._onSyncUpdate({
|
||||
type: 'messages',
|
||||
path: inboxFolder.path,
|
||||
list: msgs
|
||||
});
|
||||
|
||||
expect(dialogStub.error.calledOnce).to.be.true;
|
||||
expect(setFlagsStub.calledOnce).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
@ -1,9 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
var OutboxBO = require('../../src/js/bo/outbox'),
|
||||
KeychainDAO = require('../../src/js/dao/keychain-dao'),
|
||||
EmailDAO = require('../../src/js/dao/email-dao'),
|
||||
DeviceStorageDAO = require('../../src/js/dao/devicestorage-dao');
|
||||
var OutboxBO = require('../../../src/js/email/outbox'),
|
||||
KeychainDAO = require('../../../src/js/service/keychain'),
|
||||
EmailDAO = require('../../../src/js/email/email'),
|
||||
DeviceStorageDAO = require('../../../src/js/service/devicestorage');
|
||||
|
||||
describe('Outbox Business Object unit test', function() {
|
||||
var outbox, emailDaoStub, devicestorageStub, keychainStub,
|
96
test/unit/email/search-test.js
Normal file
96
test/unit/email/search-test.js
Normal file
@ -0,0 +1,96 @@
|
||||
'use strict';
|
||||
|
||||
describe('Search Service unit test', function() {
|
||||
var search;
|
||||
|
||||
beforeEach(function() {
|
||||
angular.module('search-test', ['woEmail']);
|
||||
angular.mock.module('search-test');
|
||||
angular.mock.inject(function($injector) {
|
||||
search = $injector.get('search');
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {});
|
||||
|
||||
describe('filter', function() {
|
||||
var message1 = {
|
||||
to: [{
|
||||
name: 'name1',
|
||||
address: 'address1'
|
||||
}],
|
||||
subject: 'subject1',
|
||||
body: 'body1',
|
||||
html: 'html1'
|
||||
},
|
||||
message2 = {
|
||||
to: [{
|
||||
name: 'name2',
|
||||
address: 'address2'
|
||||
}],
|
||||
subject: 'subject2',
|
||||
body: 'body2',
|
||||
html: 'html2'
|
||||
},
|
||||
message3 = {
|
||||
to: [{
|
||||
name: 'name3',
|
||||
address: 'address3'
|
||||
}],
|
||||
subject: 'subject3',
|
||||
body: 'body1',
|
||||
html: 'html1',
|
||||
encrypted: true
|
||||
},
|
||||
message4 = {
|
||||
to: [{
|
||||
name: 'name4',
|
||||
address: 'address4'
|
||||
}],
|
||||
subject: 'subject4',
|
||||
body: 'body1',
|
||||
html: 'html1',
|
||||
encrypted: true,
|
||||
decrypted: true
|
||||
},
|
||||
testMessages = [message1, message2, message3, message4];
|
||||
|
||||
it('return same messages array on empty query string', function() {
|
||||
var result = search.filter(testMessages, '');
|
||||
expect(result).to.equal(testMessages);
|
||||
});
|
||||
|
||||
it('return message1 on matching subject', function() {
|
||||
var result = search.filter(testMessages, 'subject1');
|
||||
expect(result.length).to.equal(1);
|
||||
expect(result[0]).to.equal(message1);
|
||||
});
|
||||
|
||||
it('return message1 on matching name', function() {
|
||||
var result = search.filter(testMessages, 'name1');
|
||||
expect(result.length).to.equal(1);
|
||||
expect(result[0]).to.equal(message1);
|
||||
});
|
||||
|
||||
it('return message1 on matching address', function() {
|
||||
var result = search.filter(testMessages, 'address1');
|
||||
expect(result.length).to.equal(1);
|
||||
expect(result[0]).to.equal(message1);
|
||||
});
|
||||
|
||||
it('return plaintext and decrypted messages on matching body', function() {
|
||||
var result = search.filter(testMessages, 'body1');
|
||||
expect(result.length).to.equal(2);
|
||||
expect(result[0]).to.equal(message1);
|
||||
expect(result[1]).to.equal(message4);
|
||||
});
|
||||
|
||||
it('return plaintext and decrypted messages on matching html', function() {
|
||||
var result = search.filter(testMessages, 'html1');
|
||||
expect(result.length).to.equal(2);
|
||||
expect(result[0]).to.equal(message1);
|
||||
expect(result[1]).to.equal(message4);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
@ -1,236 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var mocks = angular.mock,
|
||||
LoginCtrl = require('../../src/js/controller/login'),
|
||||
EmailDAO = require('../../src/js/dao/email-dao'),
|
||||
Auth = require('../../src/js/bo/auth'),
|
||||
appController = require('../../src/js/app-controller'),
|
||||
KeychainDAO = require('../../src/js/dao/keychain-dao');
|
||||
|
||||
describe('Login Controller unit test', function() {
|
||||
var scope, location, ctrl,
|
||||
origEmailDao, emailDaoMock,
|
||||
origKeychain, keychainMock,
|
||||
origAuth, authStub,
|
||||
emailAddress = 'fred@foo.com',
|
||||
startAppStub,
|
||||
checkForUpdateStub,
|
||||
initStub;
|
||||
|
||||
describe('initialization', function() {
|
||||
var hasChrome, hasIdentity;
|
||||
|
||||
beforeEach(function() {
|
||||
hasChrome = !!window.chrome;
|
||||
hasIdentity = !!window.chrome.identity;
|
||||
window.chrome = window.chrome || {};
|
||||
window.chrome.identity = window.chrome.identity || {};
|
||||
|
||||
// remember original module to restore later, then replace it
|
||||
origEmailDao = appController._emailDao;
|
||||
origKeychain = appController._keychain;
|
||||
origAuth = appController._auth;
|
||||
appController._emailDao = emailDaoMock = sinon.createStubInstance(EmailDAO);
|
||||
appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
appController._auth = authStub = sinon.createStubInstance(Auth);
|
||||
|
||||
startAppStub = sinon.stub(appController, 'start');
|
||||
checkForUpdateStub = sinon.stub(appController, 'checkForUpdate');
|
||||
initStub = sinon.stub(appController, 'init');
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// restore the browser
|
||||
if (!hasIdentity) {
|
||||
delete window.chrome.identity;
|
||||
}
|
||||
|
||||
if (!hasChrome) {
|
||||
delete window.chrome;
|
||||
}
|
||||
|
||||
// restore the app controller module
|
||||
appController._emailDao = origEmailDao;
|
||||
appController._keychain = origKeychain;
|
||||
appController._auth = origAuth;
|
||||
appController.start.restore && appController.start.restore();
|
||||
appController.checkForUpdate.restore && appController.checkForUpdate.restore();
|
||||
appController.init.restore && appController.init.restore();
|
||||
location.path.restore && location.path.restore();
|
||||
|
||||
startAppStub.restore();
|
||||
checkForUpdateStub.restore();
|
||||
initStub.restore();
|
||||
});
|
||||
|
||||
it('should forward directly to desktop for empty passphrase', function(done) {
|
||||
var testKeys = {
|
||||
privateKey: 'a',
|
||||
publicKey: 'b'
|
||||
};
|
||||
|
||||
startAppStub.yields();
|
||||
authStub.getEmailAddress.yields(null, {
|
||||
emailAddress: emailAddress,
|
||||
realname: 'asd'
|
||||
});
|
||||
authStub.storeCredentials.yields();
|
||||
initStub.yields(null, testKeys);
|
||||
|
||||
emailDaoMock.unlock.withArgs({
|
||||
keypair: testKeys,
|
||||
passphrase: undefined
|
||||
}).yields();
|
||||
|
||||
angular.module('logintest', []);
|
||||
mocks.module('logintest');
|
||||
mocks.inject(function($controller, $rootScope, $location) {
|
||||
location = $location;
|
||||
sinon.stub(location, 'path', function(path) {
|
||||
expect(path).to.equal('/desktop');
|
||||
expect(startAppStub.calledOnce).to.be.true;
|
||||
expect(checkForUpdateStub.calledOnce).to.be.true;
|
||||
expect(authStub.getEmailAddress.calledOnce).to.be.true;
|
||||
expect(authStub.storeCredentials.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
ctrl = $controller(LoginCtrl, {
|
||||
$location: location,
|
||||
$scope: scope
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should forward to existing user login', function(done) {
|
||||
var testKeys = {
|
||||
privateKey: 'a',
|
||||
publicKey: 'b'
|
||||
};
|
||||
|
||||
startAppStub.yields();
|
||||
authStub.getEmailAddress.yields(null, {
|
||||
emailAddress: emailAddress,
|
||||
realname: 'asd'
|
||||
});
|
||||
initStub.yields(null, testKeys);
|
||||
|
||||
emailDaoMock.unlock.withArgs({
|
||||
keypair: testKeys,
|
||||
passphrase: undefined
|
||||
}).yields({});
|
||||
|
||||
angular.module('logintest', []);
|
||||
mocks.module('logintest');
|
||||
mocks.inject(function($controller, $rootScope, $location) {
|
||||
location = $location;
|
||||
sinon.stub(location, 'path', function(path) {
|
||||
expect(path).to.equal('/login-existing');
|
||||
expect(startAppStub.calledOnce).to.be.true;
|
||||
expect(checkForUpdateStub.calledOnce).to.be.true;
|
||||
expect(authStub.getEmailAddress.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
ctrl = $controller(LoginCtrl, {
|
||||
$location: location,
|
||||
$scope: scope
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should forward to privatekey download login', function(done) {
|
||||
startAppStub.yields();
|
||||
authStub.getEmailAddress.yields(null, {
|
||||
emailAddress: emailAddress,
|
||||
realname: 'asd'
|
||||
});
|
||||
initStub.yields(null, {
|
||||
publicKey: 'b'
|
||||
});
|
||||
keychainMock.requestPrivateKeyDownload.yields(null, {});
|
||||
|
||||
angular.module('logintest', []);
|
||||
mocks.module('logintest');
|
||||
mocks.inject(function($controller, $rootScope, $location) {
|
||||
location = $location;
|
||||
sinon.stub(location, 'path', function(path) {
|
||||
expect(path).to.equal('/login-privatekey-download');
|
||||
expect(startAppStub.calledOnce).to.be.true;
|
||||
expect(checkForUpdateStub.calledOnce).to.be.true;
|
||||
expect(authStub.getEmailAddress.calledOnce).to.be.true;
|
||||
expect(keychainMock.requestPrivateKeyDownload.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
ctrl = $controller(LoginCtrl, {
|
||||
$location: location,
|
||||
$scope: scope
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should forward to new device login', function(done) {
|
||||
startAppStub.yields();
|
||||
authStub.getEmailAddress.yields(null, {
|
||||
emailAddress: emailAddress,
|
||||
realname: 'asd'
|
||||
});
|
||||
initStub.yields(null, {
|
||||
publicKey: 'b'
|
||||
});
|
||||
keychainMock.requestPrivateKeyDownload.yields();
|
||||
|
||||
angular.module('logintest', []);
|
||||
mocks.module('logintest');
|
||||
mocks.inject(function($controller, $rootScope, $location) {
|
||||
location = $location;
|
||||
sinon.stub(location, 'path', function(path) {
|
||||
expect(path).to.equal('/login-new-device');
|
||||
expect(startAppStub.calledOnce).to.be.true;
|
||||
expect(checkForUpdateStub.calledOnce).to.be.true;
|
||||
expect(authStub.getEmailAddress.calledOnce).to.be.true;
|
||||
expect(keychainMock.requestPrivateKeyDownload.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
ctrl = $controller(LoginCtrl, {
|
||||
$location: location,
|
||||
$scope: scope
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should forward to initial login', function(done) {
|
||||
startAppStub.yields();
|
||||
authStub.getEmailAddress.yields(null, {
|
||||
emailAddress: emailAddress,
|
||||
realname: 'asd'
|
||||
});
|
||||
initStub.yields();
|
||||
|
||||
angular.module('logintest', []);
|
||||
mocks.module('logintest');
|
||||
mocks.inject(function($controller, $rootScope, $location) {
|
||||
location = $location;
|
||||
sinon.stub(location, 'path', function(path) {
|
||||
expect(path).to.equal('/login-initial');
|
||||
expect(startAppStub.calledOnce).to.be.true;
|
||||
expect(checkForUpdateStub.calledOnce).to.be.true;
|
||||
expect(authStub.getEmailAddress.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
ctrl = $controller(LoginCtrl, {
|
||||
$location: location,
|
||||
$scope: scope
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -1,7 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
var RestDAO = require('../../src/js/dao/rest-dao'),
|
||||
AdminDAO = require('../../src/js/dao/admin-dao');
|
||||
var RestDAO = require('../../../src/js/service/rest'),
|
||||
AdminDAO = require('../../../src/js/service/admin'),
|
||||
appConfig = require('../../../src/js/app-config');
|
||||
|
||||
describe('Admin DAO unit tests', function() {
|
||||
|
||||
@ -11,7 +12,7 @@ describe('Admin DAO unit tests', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
restDaoStub = sinon.createStubInstance(RestDAO);
|
||||
adminDao = new AdminDAO(restDaoStub);
|
||||
adminDao = new AdminDAO(restDaoStub, appConfig);
|
||||
});
|
||||
|
||||
afterEach(function() {});
|
@ -1,9 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
var Auth = require('../../src/js/bo/auth'),
|
||||
OAuth = require('../../src/js/util/oauth'),
|
||||
PGP = require('../../src/js/crypto/pgp'),
|
||||
DeviceStorageDAO = require('../../src/js/dao/devicestorage-dao');
|
||||
var Auth = require('../../../src/js/service/auth'),
|
||||
OAuth = require('../../../src/js/service/oauth'),
|
||||
PGP = require('../../../src/js/crypto/pgp'),
|
||||
DeviceStorageDAO = require('../../../src/js/service/devicestorage');
|
||||
|
||||
describe('Auth unit tests', function() {
|
||||
// Constancts
|
@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
var LawnchairDAO = require('../../src/js/dao/lawnchair-dao'),
|
||||
DeviceStorageDAO = require('../../src/js/dao/devicestorage-dao');
|
||||
var LawnchairDAO = require('../../../src/js/service/lawnchair'),
|
||||
DeviceStorageDAO = require('../../../src/js/service/devicestorage');
|
||||
|
||||
var testUser = 'test@example.com';
|
||||
|
||||
@ -17,14 +17,9 @@ describe('Device Storage DAO unit tests', function() {
|
||||
afterEach(function() {});
|
||||
|
||||
describe('init', function() {
|
||||
it('should work', function(done) {
|
||||
lawnchairDaoStub.init.yields();
|
||||
|
||||
storageDao.init(testUser, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(lawnchairDaoStub.init.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
it('should work', function() {
|
||||
storageDao.init(testUser);
|
||||
expect(lawnchairDaoStub.init.calledOnce).to.be.true;
|
||||
});
|
||||
});
|
||||
|
@ -1,7 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
var RestDAO = require('../../src/js/dao/rest-dao'),
|
||||
InvitationDAO = require('../../src/js/dao/invitation-dao');
|
||||
var RestDAO = require('../../../src/js/service/rest'),
|
||||
InvitationDAO = require('../../../src/js/service/invitation'),
|
||||
appConfig = require('../../../src/js/app-config');
|
||||
|
||||
describe('Invitation DAO unit tests', function() {
|
||||
var restDaoStub, invitationDao,
|
||||
@ -11,7 +12,7 @@ describe('Invitation DAO unit tests', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
restDaoStub = sinon.createStubInstance(RestDAO);
|
||||
invitationDao = new InvitationDAO(restDaoStub);
|
||||
invitationDao = new InvitationDAO(restDaoStub, appConfig);
|
||||
});
|
||||
|
||||
describe('initialization', function() {
|
@ -1,17 +1,19 @@
|
||||
'use strict';
|
||||
|
||||
var LawnchairDAO = require('../../src/js/dao/lawnchair-dao'),
|
||||
PublicKeyDAO = require('../../src/js/dao/publickey-dao'),
|
||||
KeychainDAO = require('../../src/js/dao/keychain-dao'),
|
||||
PrivateKeyDAO = require('../../src/js/dao/privatekey-dao'),
|
||||
Crypto = require('../../src/js/crypto/crypto'),
|
||||
PGP = require('../../src/js/crypto/pgp');
|
||||
var LawnchairDAO = require('../../../src/js/service/lawnchair'),
|
||||
PublicKeyDAO = require('../../../src/js/service/publickey'),
|
||||
KeychainDAO = require('../../../src/js/service/keychain'),
|
||||
PrivateKeyDAO = require('../../../src/js/service/privatekey'),
|
||||
Crypto = require('../../../src/js/crypto/crypto'),
|
||||
PGP = require('../../../src/js/crypto/pgp'),
|
||||
Dialog = require('../../../src/js/util/dialog'),
|
||||
appConfig = require('../../../src/js/app-config');
|
||||
|
||||
var testUser = 'test@example.com';
|
||||
|
||||
describe('Keychain DAO unit tests', function() {
|
||||
|
||||
var keychainDao, lawnchairDaoStub, pubkeyDaoStub, privkeyDaoStub, cryptoStub, pgpStub;
|
||||
var keychainDao, lawnchairDaoStub, pubkeyDaoStub, privkeyDaoStub, cryptoStub, pgpStub, dialogStub;
|
||||
|
||||
beforeEach(function() {
|
||||
lawnchairDaoStub = sinon.createStubInstance(LawnchairDAO);
|
||||
@ -19,11 +21,25 @@ describe('Keychain DAO unit tests', function() {
|
||||
privkeyDaoStub = sinon.createStubInstance(PrivateKeyDAO);
|
||||
cryptoStub = sinon.createStubInstance(Crypto);
|
||||
pgpStub = sinon.createStubInstance(PGP);
|
||||
keychainDao = new KeychainDAO(lawnchairDaoStub, pubkeyDaoStub, privkeyDaoStub, cryptoStub, pgpStub);
|
||||
dialogStub = sinon.createStubInstance(Dialog);
|
||||
keychainDao = new KeychainDAO(lawnchairDaoStub, pubkeyDaoStub, privkeyDaoStub, cryptoStub, pgpStub, dialogStub, appConfig);
|
||||
});
|
||||
|
||||
afterEach(function() {});
|
||||
|
||||
describe('requestPermissionForKeyUpdate', function() {
|
||||
it('should work', function() {
|
||||
var opt = {
|
||||
newKey: {},
|
||||
userId: 'asdf@example.com'
|
||||
};
|
||||
|
||||
keychainDao.requestPermissionForKeyUpdate(opt, function() {
|
||||
expect(dialogStub.confirm.calledOnce).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('verify public key', function() {
|
||||
it('should verify public key', function(done) {
|
||||
var uuid = 'asdfasdfasdfasdf';
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user