Merge pull request #141 from whiteout-io/dev/WO-567

Dev/wo 567
This commit is contained in:
Tankred Hase 2014-10-16 12:12:07 +02:00
commit 5d09e32320
100 changed files with 19058 additions and 19615 deletions

6
.gitignore vendored
View File

@ -7,7 +7,7 @@ src/css/
dist/
release/
test/integration/src/
src/lib/*.js
src/js/crypto/aes-gcm.js
src/js/crypto/util.js
.elasticbeanstalk/
test/*/index.js
**/*.browserified.js
**/*.js.map

View File

@ -18,31 +18,29 @@
"unused": true,
"predef": [
"console",
"Notification",
"importScripts",
"process",
"Event",
"QUnit",
"test",
"asyncTest",
"ok",
"equal",
"deepEqual",
"start",
"chrome",
"requirejs",
"define",
"self",
"describe",
"it",
"chai",
"importScripts",
"console",
"process",
"chrome",
"Notification",
"Event",
"sinon",
"mocha",
"chai",
"expect",
"describe",
"it",
"before",
"beforeEach",
"after",
"afterEach"
"afterEach",
"FastClick",
"angular",
"forge",
"Lawnchair",
"_",
"openpgp"
],
"globals": {

View File

@ -1,11 +1,22 @@
module.exports = function(grunt) {
'use strict';
require('time-grunt')(grunt);
var version = grunt.option('release'),
zipName = (version) ? version : 'DEV';
var browserifyOpt = {
exclude: ['openpgp', 'node-forge', 'net', 'tls', 'crypto'], // node apis not required at build time
ignore: ['buffer'], // node apis to be stubbed for runtime
browserifyOptions: {
debug: true
}
};
// Project configuration.
grunt.initConfig({
connect: {
dev: {
options: {
@ -23,32 +34,27 @@ module.exports = function(grunt) {
},
jshint: {
all: ['Gruntfile.js', 'src/*.js', 'src/js/**/*.js', 'test/unit/*.js', 'test/integration/*.js'],
all: ['Gruntfile.js', 'src/*.js', 'src/js/**/*.js', 'test/unit/*-test.js', 'test/integration/*-test.js'],
options: {
jshintrc: '.jshintrc'
}
},
mocha: {
mocha_phantomjs: {
all: {
options: {
urls: [
'http://localhost:<%= connect.test.options.port %>/test/unit/index.html',
'http://localhost:<%= connect.test.options.port %>/test/integration/index.html'
],
run: false,
reporter: 'Spec',
log: false,
// phanotmjs is soooo slow
timeout: 100000
]
}
}
},
clean: {
dist: ['dist', 'src/lib/*.js', 'test/lib', 'test/integration/src']
dist: ['dist', 'test/lib', 'test/integration/src']
},
sass: {
dist: {
files: {
@ -57,6 +63,7 @@ module.exports = function(grunt) {
}
}
},
autoprefixer: {
options: {
browsers: ['last 2 versions']
@ -68,9 +75,10 @@ module.exports = function(grunt) {
}
}
},
csso: {
options: {
banner: '/*! Copyright © 2013, Whiteout Networks GmbH. All rights reserved.*/\n'
banner: '/*! Copyright © <%= grunt.template.today("yyyy") %>, Whiteout Networks GmbH.*/\n'
},
dist: {
files: {
@ -79,6 +87,7 @@ module.exports = function(grunt) {
}
}
},
watch: {
css: {
files: ['src/sass/**/*.scss'],
@ -86,7 +95,7 @@ module.exports = function(grunt) {
},
js: {
files: ['src/js/**/*.js'],
tasks: ['copy:js', 'copy:integration', 'manifest']
tasks: ['dist-js', 'copy:integration', 'manifest']
},
lib: {
files: ['src/lib/**/*.js'],
@ -97,59 +106,234 @@ module.exports = function(grunt) {
tasks: ['copy:app', 'copy:ca', 'copy:tpl', 'copy:img', 'copy:font', 'manifest-dev', 'manifest']
}
},
copy: {
npm: {
expand: true,
flatten: true,
cwd: 'node_modules/',
src: [
'requirejs/require.js',
'imap-client/src/*.js',
'imap-client/node_modules/browserbox/src/*.js',
'imap-client/node_modules/browserbox/node_modules/wo-imap-handler/src/*.js',
'imap-client/node_modules/browserbox/node_modules/mimefuncs/src/*.js',
'imap-client/node_modules/browserbox/node_modules/tcp-socket/src/*.js',
'imap-client/node_modules/browserbox/node_modules/wo-utf7/src/*.js',
'mailreader/src/*.js',
'mailreader/node_modules/mimeparser/src/*.js',
'mailreader/node_modules/mimeparser/node_modules/wo-addressparser/src/*.js',
'pgpbuilder/src/*.js',
'pgpbuilder/node_modules/mailbuild/src/*.js',
'pgpbuilder/node_modules/mailbuild/node_modules/mimetypes/src/*.js',
'pgpbuilder/node_modules/mailbuild/node_modules/punycode/punycode.min.js',
'pgpmailer/src/*.js',
'pgpmailer/node_modules/wo-smtpclient/src/*.js',
'pgpmailer/node_modules/wo-smtpclient/node_modules/wo-stringencoding/dist/stringencoding.js',
'axe-logger/axe.js',
'dompurify/purify.js',
'jquery/dist/jquery.min.js',
'ng-infinite-scroll/build/ng-infinite-scroll.min.js'
],
dest: 'src/lib/'
browserify: {
app: {
files: {
'dist/js/app.browserified.js': ['src/js/app.js']
},
options: browserifyOpt
},
pbkdf2Worker: {
files: {
'dist/js/pbkdf2-worker.browserified.js': ['src/js/crypto/pbkdf2-worker.js']
},
options: browserifyOpt
},
mailreaderWorker: {
files: {
'dist/js/mailreader-parser-worker.browserified.js': ['node_modules/mailreader/src/mailreader-parser-worker-browserify.js']
},
options: browserifyOpt
},
tlsWorker: {
files: {
'dist/js/tcp-socket-tls-worker.browserified.js': ['node_modules/tcp-socket/src/tcp-socket-tls-worker.js']
},
options: browserifyOpt
},
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/dialog-ctrl-test.js',
'test/unit/add-account-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/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'
]
},
options: browserifyOpt
},
integrationTest: {
files: {
'test/integration/index.browserified.js': [
'test/integration/email-dao-test.js',
'test/main.js'
]
},
options: browserifyOpt
}
},
exorcise: {
app: {
files: {
'dist/js/app.browserified.js.map': ['dist/js/app.browserified.js'],
}
},
unitTest: {
files: {
'test/unit/index.browserified.js.map': ['test/unit/index.browserified.js'],
}
},
integrationTest: {
files: {
'test/integration/index.browserified.js.map': ['test/integration/index.browserified.js'],
}
}
},
uglify: {
app: {
files: {
'dist/js/app.min.js': [
'src/lib/underscore/underscore-min.js',
'node_modules/jquery/dist/jquery.min.js',
'src/lib/angular/angular.min.js',
'src/lib/angular/angular-route.min.js',
'src/lib/angular/angular-animate.min.js',
'src/lib/ngtagsinput/ng-tags-input.min.js',
'node_modules/ng-infinite-scroll/build/ng-infinite-scroll.min.js',
'src/lib/fastclick/fastclick.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',
'dist/js/app.browserified.js'
]
},
options: {
mangle: false,
sourceMap: true,
sourceMapIn: 'dist/js/app.browserified.js.map',
sourceMapIncludeSources: true,
sourceMapName: 'dist/js/app.min.js.map'
}
},
readSandbox: {
files: {
'dist/js/read-sandbox.min.js': [
'node_modules/dompurify/purify.js',
'src/js/controller/read-sandbox.js'
]
},
options: {
sourceMap: true,
sourceMapName: 'dist/js/read-sandbox.min.js.map'
}
},
pbkdf2Worker: {
files: {
'dist/js/pbkdf2-worker.min.js': ['dist/js/pbkdf2-worker.browserified.js']
}
},
mailreaderWorker: {
files: {
'dist/js/mailreader-parser-worker.min.js': ['dist/js/mailreader-parser-worker.browserified.js']
},
options: {
sourceMap: true,
sourceMapName: 'dist/js/mailreader-parser-worker.min.js.map'
}
},
tlsWorker: {
files: {
'dist/js/tcp-socket-tls-worker.min.js': ['dist/js/tcp-socket-tls-worker.browserified.js']
},
options: {
sourceMap: true,
sourceMapName: 'dist/js/tcp-socket-tls-worker.min.js.map'
}
},
unitTest: {
files: {
'test/unit/index.js': [
'src/lib/underscore/underscore-min.js',
'node_modules/jquery/dist/jquery.min.js',
'src/lib/angular/angular.min.js',
'src/lib/angular/angular-route.min.js',
'src/lib/angular/angular-animate.min.js',
'node_modules/angularjs/src/ngMock/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',
'test/unit/index.browserified.js'
]
},
options: {
mangle: false,
sourceMap: true,
sourceMapIn: 'test/unit/index.browserified.js.map',
sourceMapIncludeSources: true,
sourceMapName: 'test/unit/index.js.map'
}
},
integrationTest: {
files: {
'test/integration/index.js': [
'src/lib/underscore/underscore-min.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',
'test/integration/index.browserified.js'
]
},
options: {
mangle: false,
sourceMap: true,
sourceMapIn: 'test/integration/index.browserified.js.map',
sourceMapIncludeSources: true,
sourceMapName: 'test/integration/index.js.map'
}
},
options: {
banner: '/*! Copyright © <%= grunt.template.today("yyyy") %>, Whiteout Networks GmbH.*/\n'
}
},
copy: {
npmDev: {
expand: true,
flatten: true,
cwd: 'node_modules/',
src: ['requirejs/require.js', 'mocha/mocha.css', 'mocha/mocha.js', 'chai/chai.js', 'sinon/pkg/sinon.js', 'angularjs/src/ngMock/angular-mocks.js', 'browsercrow/src/*.js', 'browsersmtp/src/*.js'],
cwd: './',
src: [
'node_modules/mocha/mocha.css',
'node_modules/mocha/mocha.js',
'node_modules/chai/chai.js',
'node_modules/sinon/pkg/sinon.js',
'node_modules/browsercrow/src/*.js',
'node_modules/browsersmtp/src/*.js',
'src/lib/openpgp/openpgp.js',
'src/lib/openpgp/openpgp.worker.js',
'src/lib/forge/forge.min.js',
'dist/js/pbkdf2-worker.min.js'
],
dest: 'test/lib/'
},
cryptoLib: {
expand: true,
cwd: 'node_modules/crypto-lib/src/',
src: ['*.js'],
dest: 'src/js/crypto/'
},
lib: {
expand: true,
flatten: true,
cwd: 'src/lib/',
src: ['**'],
dest: 'dist/lib/'
},
js: {
expand: true,
cwd: 'src/js/',
src: ['**'],
src: ['openpgp/openpgp.js', 'openpgp/openpgp.worker.js', 'forge/forge.min.js'],
dest: 'dist/js/'
},
font: {
@ -170,23 +354,11 @@ module.exports = function(grunt) {
src: ['*'],
dest: 'dist/tpl/'
},
ca: {
expand: true,
cwd: 'src/ca/',
src: ['*'],
dest: 'dist/ca/'
},
app: {
expand: true,
cwd: 'src/',
src: ['*.html', '*.js', '*.json', 'manifest.*'],
dest: 'dist/'
},
integration: {
expand: true,
cwd: 'src/',
src: ['**'],
dest: 'test/integration/src/'
}
},
@ -200,16 +372,6 @@ module.exports = function(grunt) {
cwd: 'dist/',
src: ['**/*'],
dest: 'release/'
},
nodeWebkit: {
options: {
mode: 'zip',
archive: 'release/whiteout-mail_' + zipName + '.nw'
},
expand: true,
cwd: 'dist/',
src: ['**/*'],
dest: '/'
}
},
@ -220,31 +382,30 @@ module.exports = function(grunt) {
timestamp: true,
hash: true,
cache: ['socket.io/socket.io.js'],
exclude: ['appcache.manifest', 'manifest.webapp'],
exclude: [
'appcache.manifest',
'manifest.webapp',
'js/app.min.js.map',
'js/app.browserified.js',
'js/app.browserified.js.map',
'js/crypto/pbkdf2-worker.browserified.js',
'js/pbkdf2-worker.browserified.js',
'js/read-sandbox.min.js.map'
],
master: ['index.html']
},
src: ['**/*.*'],
dest: 'dist/appcache.manifest'
}
},
}
nodewebkit: {
options: {
version: '0.9.2', // node-webkit version
build_dir: './release/node-webkit/', // Where the build version of my node-webkit app is saved
mac: true, // We want to build it for mac
win: false, // We want to build it for win
linux32: false, // We don't need linux32
linux64: false, // We don't need linux64
},
src: ['./dist/**/*'] // Your node-webkit app
},
});
// Load the plugin(s)
grunt.loadNpmTasks('grunt-browserify');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-connect');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-mocha');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-csso');
grunt.loadNpmTasks('grunt-contrib-sass');
@ -252,18 +413,19 @@ module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-compress');
grunt.loadNpmTasks('grunt-node-webkit-builder');
grunt.loadNpmTasks('grunt-manifest');
grunt.loadNpmTasks('grunt-mocha-phantomjs');
grunt.loadNpmTasks('grunt-exorcise');
// Build tasks
grunt.registerTask('dist-npm', ['copy:npm', 'copy:npmDev', 'copy:cryptoLib']);
grunt.registerTask('dist-css', ['sass', 'autoprefixer', 'csso']);
grunt.registerTask('dist-js', ['browserify', 'exorcise', 'uglify']);
grunt.registerTask('dist-copy', ['copy']);
grunt.registerTask('dist', ['clean', 'dist-npm', 'dist-css', 'dist-copy', 'manifest']);
grunt.registerTask('dist', ['clean', 'dist-css', 'dist-js', 'dist-copy', 'manifest']);
// Test/Dev tasks
grunt.registerTask('dev', ['connect:dev']);
grunt.registerTask('test', ['jshint', 'connect:test', 'mocha']);
grunt.registerTask('test', ['jshint', 'connect:test', 'mocha_phantomjs']);
grunt.registerTask('prod', ['connect:prod']);
//

View File

@ -24,25 +24,27 @@
},
"scripts": {
"test": "grunt && grunt test",
"start": "node server.js"
"start": "node server.js",
"postinstall": "dir=$(pwd) && cd node_modules/mailreader/ && npm install --production && cd $dir"
},
"dependencies": {
"axe-logger": "~0.0.2",
"compression": "^1.0.11",
"config": "^1.0.2",
"crypto-lib": "~0.2.1",
"dompurify": "~0.4.2",
"express": "^4.8.3",
"imap-client": "~0.4.3",
"jquery": "~2.1.1",
"mailreader": "~0.3.5",
"mailreader": "~0.4.0",
"morgan": "^1.2.3",
"ng-infinite-scroll": "~1.1.2",
"npmlog": "^0.1.1",
"pgpbuilder": "~0.4.0",
"pgpmailer": "~0.4.0",
"requirejs": "~2.1.14",
"config": "^1.0.2",
"morgan": "^1.2.3",
"npmlog": "^0.1.1",
"socket.io": "^1.0.6"
"socket.io": "^1.0.6",
"tcp-socket": "~0.3.13",
"wo-smtpclient": "^0.3.8"
},
"devDependencies": {
"angularjs": "https://github.com/whiteout-io/angular.js/tarball/npm-version",
@ -50,19 +52,22 @@
"browsersmtp": "https://github.com/whiteout-io/browsersmtp/tarball/master",
"chai": "~1.7.2",
"grunt": "~0.4.1",
"grunt-contrib-clean": "~0.5.0",
"grunt-contrib-copy": "~0.4.1",
"grunt-manifest": "^0.4.0",
"grunt-autoprefixer": "~0.7.2",
"grunt-browserify": "^3.0.1",
"grunt-contrib-clean": "~0.5.0",
"grunt-contrib-compress": "~0.5.2",
"grunt-contrib-connect": "~0.5.0",
"grunt-contrib-copy": "~0.4.1",
"grunt-contrib-jshint": "~0.6.4",
"grunt-contrib-sass": "~0.7.3",
"grunt-contrib-uglify": "^0.6.0",
"grunt-contrib-watch": "~0.5.3",
"grunt-csso": "~0.6.1",
"grunt-mocha": "~0.4.1",
"grunt-node-webkit-builder": "~0.1.17",
"mocha": "~1.13.0",
"sinon": "~1.7.3"
"grunt-exorcise": "^0.2.0",
"grunt-manifest": "^0.4.0",
"grunt-mocha-phantomjs": "^0.6.0",
"mocha": "^1.21.4",
"sinon": "~1.7.3",
"time-grunt": "^1.0.0"
}
}

View File

@ -26,9 +26,9 @@
<!-- The Scripts -->
<script src="socket.io/socket.io.js"></script>
<script src="lib/require.js"></script>
<script src="require-config.js"></script>
<script src="js/app.js"></script>
<script src="js/openpgp.js"></script>
<script src="js/forge.min.js"></script>
<script src="js/app.min.js"></script>
</head>
<body key-shortcuts>

View File

@ -1,12 +1,9 @@
define(function(require) {
'use strict';
'use strict';
var _ = require('underscore'),
app = {},
appVersion, cloudUrl, keychainUrl, clientId;
var appVersion, cloudUrl, keychainUrl, clientId;
// parse manifest to get configurations for current runtime
try {
// parse manifest to get configurations for current runtime
try {
var manifest = chrome.runtime.getManifest();
// get key server base url
cloudUrl = _.find(manifest.permissions, function(permission) {
@ -24,12 +21,12 @@ define(function(require) {
clientId = manifest.oauth2.client_id;
// get the app version
appVersion = manifest.version;
} catch (e) {}
} catch (e) {}
/**
/**
* Global app configurations
*/
app.config = {
exports.config = {
cloudUrl: cloudUrl || 'https://keys.whiteout.io',
privkeyServerUrl: keychainUrl || 'https://keychain.whiteout.io',
adminUrl: 'https://admin-node.whiteout.io',
@ -180,12 +177,12 @@ define(function(require) {
outboxMailboxType: 'Outbox',
connDocTimeout: 5000,
imapUpdateBatchSize: 25
};
};
/**
/**
* Strings are maintained here
*/
app.string = {
exports.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',
@ -217,7 +214,4 @@ define(function(require) {
connDocAuthRejected: 'Your credentials for {0} were rejected. Please check your username and password!',
connDocNoInbox: 'We could not detect an IMAP inbox folder on {0}. Please have a look at the FAQ for information on how to fix this error.',
connDocGenericError: 'There was an error connecting to {0}: {1}'
};
return app;
});
};

View File

@ -1,46 +1,46 @@
/**
* The main application controller
*/
define(function(require) {
'use strict';
var axe = require('axe'),
Auth = require('js/bo/auth'),
PGP = require('js/crypto/pgp'),
'use strict';
var axe = require('axe-logger'),
Auth = require('./bo/auth'),
PGP = require('./crypto/pgp'),
PgpMailer = require('pgpmailer'),
OAuth = require('js/util/oauth'),
OAuth = require('./util/oauth'),
PgpBuilder = require('pgpbuilder'),
OutboxBO = require('js/bo/outbox'),
OutboxBO = require('./bo/outbox'),
mailreader = require('mailreader'),
ImapClient = require('imap-client'),
Crypto = require('js/crypto/crypto'),
RestDAO = require('js/dao/rest-dao'),
appConfig = require('js/app-config'),
EmailDAO = require('js/dao/email-dao'),
AdminDao = require('js/dao/admin-dao'),
KeychainDAO = require('js/dao/keychain-dao'),
PublicKeyDAO = require('js/dao/publickey-dao'),
LawnchairDAO = require('js/dao/lawnchair-dao'),
PrivateKeyDAO = require('js/dao/privatekey-dao'),
InvitationDAO = require('js/dao/invitation-dao'),
DeviceStorageDAO = require('js/dao/devicestorage-dao'),
ConnectionDoctor = require('js/util/connection-doctor'),
UpdateHandler = require('js/util/update/update-handler'),
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 self = {};
var ctrl = {};
/**
/**
* Start the application.
*/
self.start = function(options, callback) {
if (self.started) {
ctrl.start = function(options, callback) {
if (ctrl.started) {
return callback();
}
self.started = true;
self.onError = options.onError;
ctrl.started = true;
ctrl.onError = options.onError;
// are we running in a cordova app or in a browser environment?
if (window.cordova) {
@ -56,24 +56,24 @@ define(function(require) {
function onDeviceReady() {
axe.debug('Starting app.');
self.buildModules();
ctrl.buildModules();
// Handle offline and online gracefully
window.addEventListener('online', self.onConnect.bind(self, self.onError));
window.addEventListener('offline', self.onDisconnect.bind(self));
window.addEventListener('online', ctrl.onConnect.bind(ctrl, ctrl.onError));
window.addEventListener('offline', ctrl.onDisconnect.bind(ctrl));
self._appConfigStore.init('app-config', callback);
ctrl._appConfigStore.init('app-config', callback);
}
};
};
/**
/**
* Initialize the dependency tree.
*/
self.buildModules = function() {
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 + '/../lib/mailreader-parser-worker.js');
mailreader.startWorker(config.workerPath + '/mailreader-parser-worker.min.js');
// init objects and inject dependencies
restDao = new RestDAO();
@ -83,13 +83,13 @@ define(function(require) {
oauth = new OAuth(new RestDAO('https://www.googleapis.com'));
crypto = new Crypto();
self._pgp = pgp = new PGP();
self._keychain = keychain = new KeychainDAO(lawnchairDao, pubkeyDao, privkeyDao, crypto, pgp);
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);
self.onError({
ctrl.onError({
title: str.updatePublicKeyTitle,
message: message,
positiveBtnStr: str.updatePublicKeyPosBtn,
@ -99,40 +99,40 @@ define(function(require) {
});
};
self._appConfigStore = appConfigStore = new DeviceStorageDAO(new LawnchairDAO());
self._auth = auth = new Auth(appConfigStore, oauth, pgp);
self._userStorage = userStorage = new DeviceStorageDAO(lawnchairDao);
self._invitationDao = new InvitationDAO(restDao);
self._pgpbuilder = pgpbuilder = new PgpBuilder();
self._emailDao = emailDao = new EmailDAO(keychain, pgp, userStorage, pgpbuilder, mailreader);
self._outboxBo = new OutboxBO(emailDao, keychain, userStorage);
self._updateHandler = new UpdateHandler(appConfigStore, userStorage, auth);
self._adminDao = new AdminDao(new RestDAO(config.adminUrl));
self._doctor = new ConnectionDoctor();
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 = self.onError;
};
emailDao.onError = ctrl.onError;
};
/**
/**
* Calls runtime hooks to check if an app update is available.
*/
self.checkForUpdate = function() {
self._updateHandler.checkForUpdate(self.onError);
};
ctrl.checkForUpdate = function() {
ctrl._updateHandler.checkForUpdate(ctrl.onError);
};
/**
/**
* Instanciate the mail email data access object and its dependencies. Login to imap on init.
*/
self.init = function(options, callback) {
ctrl.init = function(options, callback) {
// init user's local database
self._userStorage.init(options.emailAddress, function(err) {
ctrl._userStorage.init(options.emailAddress, function(err) {
if (err) {
callback(err);
return;
}
// Migrate the databases if necessary
self._updateHandler.update(onUpdate);
ctrl._updateHandler.update(onUpdate);
});
function onUpdate(err) {
@ -152,7 +152,7 @@ define(function(require) {
};
// init email dao
self._emailDao.init({
ctrl._emailDao.init({
account: account
}, function(err, keypair) {
if (err) {
@ -163,39 +163,37 @@ define(function(require) {
callback(null, keypair);
});
}
};
};
/**
/**
* Check if the user agent is online.
*/
self.isOnline = function() {
ctrl.isOnline = function() {
return navigator.onLine;
};
};
/**
/**
* Event handler that is called when the user agent goes offline.
*/
self.onDisconnect = function() {
self._emailDao.onDisconnect();
};
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.
*/
self.logout = function() {
var self = this;
ctrl.logout = function() {
// clear app config store
self._auth.logout(function(err) {
ctrl._auth.logout(function(err) {
if (err) {
self.onError(err);
ctrl.onError(err);
return;
}
// delete instance of imap-client and pgp-mailer
self._emailDao.onDisconnect(function(err) {
ctrl._emailDao.onDisconnect(function(err) {
if (err) {
self.onError(err);
ctrl.onError(err);
return;
}
@ -203,19 +201,19 @@ define(function(require) {
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.
*/
self.onConnect = function(callback) {
if (!self.isOnline() || !self._emailDao || !self._emailDao._account) {
ctrl.onConnect = function(callback) {
if (!ctrl.isOnline() || !ctrl._emailDao || !ctrl._emailDao._account) {
// prevent connection infinite loop
callback();
return;
}
self._auth.getCredentials(function(err, credentials) {
ctrl._auth.getCredentials(function(err, credentials) {
if (err) {
callback(err);
return;
@ -228,22 +226,25 @@ define(function(require) {
// add the maximum update batch size for imap folders to the imap configuration
credentials.imap.maxUpdateSize = config.imapUpdateBatchSize;
var pgpMailer = new PgpMailer(credentials.smtp, self._pgpbuilder);
// 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 = self._auth.handleCertificateUpdate.bind(self._auth, 'imap', self.onConnect, self.onError);
pgpMailer.onCert = self._auth.handleCertificateUpdate.bind(self._auth, 'smtp', self.onConnect, self.onError);
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);
// after-setup configuration depending on the provider:
// gmail does not require you to upload to the sent items folder
// after successful sending, whereas most other providers do
self._emailDao.ignoreUploadOnSent = !!(config[self._auth.provider] && config[self._auth.provider].ignoreUploadOnSent);
ctrl._emailDao.ignoreUploadOnSent = !!(config[ctrl._auth.provider] && config[ctrl._auth.provider].ignoreUploadOnSent);
// connect to clients
self._emailDao.onConnect({
ctrl._emailDao.onConnect({
imapClient: imapClient,
pgpMailer: pgpMailer
}, callback);
@ -255,7 +256,7 @@ define(function(require) {
setTimeout(function() {
axe.debug('Reconnecting...');
// re-init client modules on error
self.onConnect(function(err) {
ctrl.onConnect(function(err) {
if (err) {
axe.error('Reconnect attempt failed! ' + (err.errMsg || err.message) + (err.stack ? ('\n' + err.stack) : ''));
return;
@ -265,7 +266,6 @@ define(function(require) {
});
}, config.reconnectInterval);
}
};
};
return self;
});
module.exports = ctrl;

View File

@ -14,67 +14,29 @@ if (typeof window.applicationCache !== 'undefined') {
};
}
// hey Angular, we're bootstrapping manually!
window.name = 'NG_DEFER_BOOTSTRAP!';
var DialogCtrl = require('./controller/dialog'),
PopoverCtrl = require('./controller/popover'),
AddAccountCtrl = require('./controller/add-account'),
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'),
errorUtil = require('./util/error'),
backButtonUtil = require('./util/backbutton-handler');
requirejs([
'angular',
'js/controller/dialog',
'js/controller/popover',
'js/controller/add-account',
'js/controller/account',
'js/controller/set-passphrase',
'js/controller/privatekey-upload',
'js/controller/contacts',
'js/controller/about',
'js/controller/login',
'js/controller/login-initial',
'js/controller/login-new-device',
'js/controller/login-existing',
'js/controller/login-privatekey-download',
'js/controller/login-set-credentials',
'js/controller/mail-list',
'js/controller/read',
'js/controller/write',
'js/controller/navigation',
'js/crypto/util',
'js/util/error',
'js/util/backbutton-handler',
'fastclick',
'angularRoute',
'angularAnimate',
'ngInfiniteScroll',
'ngTagsInput'
], function(
angular,
DialogCtrl,
PopoverCtrl,
AddAccountCtrl,
AccountCtrl,
SetPassphraseCtrl,
PrivateKeyUploadCtrl,
ContactsCtrl,
AboutCtrl,
LoginCtrl,
LoginInitialCtrl,
LoginNewDeviceCtrl,
LoginExistingCtrl,
LoginPrivateKeyDownloadCtrl,
LoginSetCredentialsCtrl,
MailListCtrl,
ReadCtrl,
WriteCtrl,
NavigationCtrl,
util,
errorUtil,
backButtonUtil,
FastClick
) {
// reset window.name
window.name = util.UUID();
// init main angular module including dependencies
var app = angular.module('mail', [
// init main angular module including dependencies
var app = angular.module('mail', [
'ngRoute',
'ngAnimate',
'navigation',
@ -87,10 +49,10 @@ requirejs([
'popover',
'infinite-scroll',
'ngTagsInput'
]);
]);
// set router paths
app.config(function($routeProvider) {
// set router paths
app.config(function($routeProvider) {
$routeProvider.when('/add-account', {
templateUrl: 'tpl/add-account.html',
controller: AddAccountCtrl
@ -126,9 +88,9 @@ requirejs([
$routeProvider.otherwise({
redirectTo: '/login'
});
});
});
app.run(function($rootScope) {
app.run(function($rootScope) {
// global state... inherited to all child scopes
$rootScope.state = {};
@ -140,22 +102,16 @@ requirejs([
// attach fastclick
FastClick.attach(document.body);
});
// inject controllers from ng-included view templates
app.controller('ReadCtrl', ReadCtrl);
app.controller('WriteCtrl', WriteCtrl);
app.controller('MailListCtrl', MailListCtrl);
app.controller('AccountCtrl', AccountCtrl);
app.controller('SetPassphraseCtrl', SetPassphraseCtrl);
app.controller('PrivateKeyUploadCtrl', PrivateKeyUploadCtrl);
app.controller('ContactsCtrl', ContactsCtrl);
app.controller('AboutCtrl', AboutCtrl);
app.controller('DialogCtrl', DialogCtrl);
app.controller('PopoverCtrl', PopoverCtrl);
// manually bootstrap angular due to require.js
angular.element().ready(function() {
angular.bootstrap(document, ['mail']);
});
});
// inject controllers from ng-included view templates
app.controller('ReadCtrl', ReadCtrl);
app.controller('WriteCtrl', WriteCtrl);
app.controller('MailListCtrl', MailListCtrl);
app.controller('AccountCtrl', AccountCtrl);
app.controller('SetPassphraseCtrl', SetPassphraseCtrl);
app.controller('PrivateKeyUploadCtrl', PrivateKeyUploadCtrl);
app.controller('ContactsCtrl', ContactsCtrl);
app.controller('AboutCtrl', AboutCtrl);
app.controller('DialogCtrl', DialogCtrl);
app.controller('PopoverCtrl', PopoverCtrl);

View File

@ -1,18 +1,17 @@
define(function(require) {
'use strict';
'use strict';
var axe = require('axe'),
str = require('js/app-config').string;
var axe = require('axe-logger'),
str = require('../app-config').string;
var EMAIL_ADDR_DB_KEY = 'emailaddress';
var USERNAME_DB_KEY = 'username';
var REALNAME_DB_KEY = 'realname';
var PASSWD_DB_KEY = 'password';
var PROVIDER_DB_KEY = 'provider';
var IMAP_DB_KEY = 'imap';
var SMTP_DB_KEY = 'smtp';
var EMAIL_ADDR_DB_KEY = 'emailaddress';
var USERNAME_DB_KEY = 'username';
var REALNAME_DB_KEY = 'realname';
var PASSWD_DB_KEY = 'password';
var PROVIDER_DB_KEY = 'provider';
var IMAP_DB_KEY = 'imap';
var SMTP_DB_KEY = 'smtp';
/**
/**
* The Auth BO handles the rough edges and gaps between user/password authentication
* and OAuth via Chrome Identity API.
* Typical usage:
@ -22,13 +21,13 @@ define(function(require) {
* auth.getCredentials(...); // called to gather all the information to connect to IMAP/SMTP, e.g. pinned intermediate certificates,
* username, password / oauth token, IMAP/SMTP server host names, ...
*/
var Auth = function(appConfigStore, oauth, pgp) {
var Auth = function(appConfigStore, oauth, pgp) {
this._appConfigStore = appConfigStore;
this._oauth = oauth;
this._pgp = pgp;
};
};
/**
/**
* Retrieves credentials and IMAP/SMTP settings:
* 1) Fetches the credentials from disk, then...
* 2 a) ... in an oauth setting, retrieves a fresh oauth token from the Chrome Identity API.
@ -37,7 +36,7 @@ define(function(require) {
*
* @param {Function} callback(err, credentials)
*/
Auth.prototype.getCredentials = function(callback) {
Auth.prototype.getCredentials = function(callback) {
var self = this;
if (!self.provider || !self.emailAddress) {
@ -115,9 +114,9 @@ define(function(require) {
callback(null, credentials);
}
};
};
/**
/**
* Set the credentials
*
* @param {String} options.provider The service provider, e.g. 'gmail', 'yahoo', 'tonline'. Matches the entry in the app-config.
@ -128,7 +127,7 @@ define(function(require) {
* @param {String} options.smtp The smtp settings (host, port, secure)
* @param {String} options.imap The imap settings (host, port, secure)
*/
Auth.prototype.setCredentials = function(options) {
Auth.prototype.setCredentials = function(options) {
this.credentialsDirty = true;
this.provider = options.provider;
this.emailAddress = options.emailAddress;
@ -137,9 +136,9 @@ define(function(require) {
this.password = options.password;
this.smtp = options.smtp; // host, port, secure, ca, pinned
this.imap = options.imap; // host, port, secure, ca, pinned
};
};
Auth.prototype.storeCredentials = function(callback) {
Auth.prototype.storeCredentials = function(callback) {
var self = this;
if (!self.credentialsDirty) {
@ -215,12 +214,12 @@ define(function(require) {
});
});
});
};
};
/**
/**
* Returns the email address. Loads it from disk, if necessary
*/
Auth.prototype.getEmailAddress = function(callback) {
Auth.prototype.getEmailAddress = function(callback) {
var self = this;
if (self.emailAddress) {
@ -240,9 +239,9 @@ define(function(require) {
realname: self.realname
});
});
};
};
/**
/**
* READ FIRST b/c usage of the oauth api is weird.
* the chrome identity api will let you query an oauth token for an email account without knowing
* the corresponding email address. also, android has multiple accounts whereas desktop chrome only
@ -253,7 +252,7 @@ define(function(require) {
* is android only, since the desktop chrome will query the user that is logged into chrome
* 3) fetch the email address for the oauth token from the chrome identity api
*/
Auth.prototype.getOAuthToken = function(callback) {
Auth.prototype.getOAuthToken = function(callback) {
var self = this;
if (self.oauthToken) {
@ -289,12 +288,12 @@ define(function(require) {
callback();
});
}
};
};
/**
/**
* Loads email address, password, provider, ... from disk and sets them on `this`
*/
Auth.prototype._loadCredentials = function(callback) {
Auth.prototype._loadCredentials = function(callback) {
var self = this;
if (self.initialized) {
@ -364,15 +363,15 @@ define(function(require) {
callback(err, (!err && cachedItems && cachedItems[0]));
});
}
};
};
/**
/**
* Handles certificate updates and errors by notifying the user.
* @param {String} component Either imap or smtp
* @param {Function} callback The error handler
* @param {[type]} pemEncodedCert The PEM encoded SSL certificate
*/
Auth.prototype.handleCertificateUpdate = function(component, onConnect, callback, pemEncodedCert) {
Auth.prototype.handleCertificateUpdate = function(component, onConnect, callback, pemEncodedCert) {
var self = this;
axe.debug('new ssl certificate received: ' + pemEncodedCert);
@ -424,12 +423,12 @@ define(function(require) {
});
}
});
};
};
/**
/**
* Logout of the app by clearing the app config store and in memory credentials
*/
Auth.prototype.logout = function(callback) {
Auth.prototype.logout = function(callback) {
var self = this;
// clear app config db
@ -447,7 +446,6 @@ define(function(require) {
callback();
});
};
};
return Auth;
});
module.exports = Auth;

View File

@ -1,17 +1,15 @@
define(function(require) {
'use strict';
'use strict';
var _ = require('underscore'),
util = require('js/crypto/util'),
config = require('js/app-config').config,
var util = require('crypto-lib').util,
config = require('../app-config').config,
outboxDb = 'email_OUTBOX';
/**
/**
* High level business object that orchestrates the local outbox.
* 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) {
var OutboxBO = function(emailDao, keychain, devicestorage) {
/** @private */
this._emailDao = emailDao;
@ -25,37 +23,37 @@ define(function(require) {
* 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) {
OutboxBO.prototype.startChecking = function(callback) {
// remember global callback
this._onUpdate = callback;
// start periodic checking of outbox
this._intervalId = setInterval(this._processOutbox.bind(this, this._onUpdate), config.checkOutboxInterval);
};
};
/**
/**
* Outbox stops the periodic checking of the local device storage for pending mails.
*/
OutboxBO.prototype.stopChecking = function() {
OutboxBO.prototype.stopChecking = function() {
if (!this._intervalId) {
return;
}
clearInterval(this._intervalId);
delete this._intervalId;
};
};
/**
/**
* Put a email dto in the outbox for sending when ready
* @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) {
OutboxBO.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
@ -129,13 +127,13 @@ define(function(require) {
self._processOutbox(self._onUpdate);
});
}
};
};
/**
/**
* 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) {
OutboxBO.prototype._processOutbox = function(callback) {
var self = this,
unsentMails = 0;
@ -226,7 +224,6 @@ define(function(require) {
done();
});
}
};
};
return OutboxBO;
});
module.exports = OutboxBO;

View File

@ -1,13 +1,12 @@
define(function(require) {
'use strict';
'use strict';
var cfg = require('js/app-config').config;
var cfg = require('../app-config').config;
//
// Controller
//
//
// Controller
//
var AboutCtrl = function($scope) {
var AboutCtrl = function($scope) {
$scope.state.about = {
toggle: function(to) {
@ -25,7 +24,6 @@ define(function(require) {
//
// scope functions
//
};
};
return AboutCtrl;
});
module.exports = AboutCtrl;

View File

@ -1,16 +1,15 @@
define(function(require) {
'use strict';
'use strict';
var appController = require('js/app-controller'),
dl = require('js/util/download'),
config = require('js/app-config').config,
var appController = require('../app-controller'),
dl = require('../util/download'),
config = require('../app-config').config,
pgp, keychain, userId;
//
// Controller
//
//
// Controller
//
var AccountCtrl = function($scope) {
var AccountCtrl = function($scope) {
userId = appController._emailDao._account.emailAddress;
keychain = appController._keychain;
pgp = appController._pgp;
@ -55,7 +54,6 @@ define(function(require) {
});
});
};
};
};
return AccountCtrl;
});
module.exports = AccountCtrl;

View File

@ -1,10 +1,9 @@
define(function(require) {
'use strict';
'use strict';
var appCtrl = require('js/app-controller'),
cfg = require('js/app-config').config;
var appCtrl = require('../app-controller'),
cfg = require('../app-config').config;
var AddAccountCtrl = function($scope, $location, $routeParams) {
var AddAccountCtrl = function($scope, $location, $routeParams) {
if (!appCtrl._auth && !$routeParams.dev) {
$location.path('/'); // init app
return;
@ -114,7 +113,6 @@ define(function(require) {
provider: provider
});
};
};
};
return AddAccountCtrl;
});
module.exports = AddAccountCtrl;

View File

@ -1,16 +1,13 @@
define(function(require) {
'use strict';
'use strict';
var angular = require('angular'),
_ = require('underscore'),
appController = require('js/app-controller'),
var appController = require('../app-controller'),
keychain, pgp;
//
// Controller
//
//
// Controller
//
var ContactsCtrl = function($scope) {
var ContactsCtrl = function($scope) {
keychain = appController._keychain,
pgp = appController._pgp;
@ -104,15 +101,15 @@ define(function(require) {
$scope.listKeys();
});
};
};
};
//
// Directives
//
//
// Directives
//
var ngModule = angular.module('contacts', []);
var ngModule = angular.module('contacts', []);
ngModule.directive('keyfileInput', function() {
ngModule.directive('keyfileInput', function() {
return function(scope, elm) {
elm.on('change', function(e) {
for (var i = 0; i < e.target.files.length; i++) {
@ -128,16 +125,15 @@ define(function(require) {
reader.readAsText(file);
}
};
});
});
ngModule.directive('keyfileBtn', function() {
ngModule.directive('keyfileBtn', function() {
return function(scope, elm) {
elm.on('click touchstart', function(e) {
e.preventDefault();
document.querySelector('#keyfile-input').click();
});
};
});
return ContactsCtrl;
});
module.exports = ContactsCtrl;

View File

@ -1,7 +1,6 @@
define(function() {
'use strict';
'use strict';
var DialogCtrl = function($scope) {
var DialogCtrl = function($scope) {
$scope.confirm = function(ok) {
$scope.state.dialog.open = false;
@ -10,7 +9,6 @@ define(function() {
}
$scope.state.dialog.callback = undefined;
};
};
};
return DialogCtrl;
});
module.exports = DialogCtrl;

View File

@ -1,9 +1,8 @@
define(function(require) {
'use strict';
'use strict';
var appController = require('js/app-controller');
var appController = require('../app-controller');
var LoginExistingCtrl = function($scope, $location, $routeParams) {
var LoginExistingCtrl = function($scope, $location, $routeParams) {
if (!appController._emailDao && !$routeParams.dev) {
$location.path('/'); // init app
return;
@ -67,7 +66,6 @@ define(function(require) {
$scope.buttonEnabled = true;
$scope.onError(err);
}
};
};
return LoginExistingCtrl;
});
module.exports = LoginExistingCtrl;

View File

@ -1,9 +1,8 @@
define(function(require) {
'use strict';
'use strict';
var appController = require('js/app-controller');
var appController = require('../app-controller');
var LoginInitialCtrl = function($scope, $location, $routeParams) {
var LoginInitialCtrl = function($scope, $location, $routeParams) {
if (!appController._emailDao && !$routeParams.dev) {
$location.path('/'); // init app
return;
@ -115,7 +114,6 @@ define(function(require) {
$scope.setState = function(state) {
$scope.state.ui = state;
};
};
};
return LoginInitialCtrl;
});
module.exports = LoginInitialCtrl;

View File

@ -1,10 +1,8 @@
define(function(require) {
'use strict';
'use strict';
var angular = require('angular'),
appController = require('js/app-controller');
var appController = require('../app-controller');
var LoginExistingCtrl = function($scope, $location, $routeParams) {
var LoginExistingCtrl = function($scope, $location, $routeParams) {
if (!appController._emailDao && !$routeParams.dev) {
$location.path('/'); // init app
return;
@ -100,10 +98,10 @@ define(function(require) {
$scope.$apply();
});
}
};
};
var ngModule = angular.module('login-new-device', []);
ngModule.directive('fileReader', function() {
var ngModule = angular.module('login-new-device', []);
ngModule.directive('fileReader', function() {
return function(scope, elm) {
elm.bind('change', function(e) {
var files = e.target.files,
@ -135,7 +133,6 @@ define(function(require) {
reader.readAsText(files[0]);
});
};
});
return LoginExistingCtrl;
});
module.exports = LoginExistingCtrl;

View File

@ -1,9 +1,8 @@
define(function(require) {
'use strict';
'use strict';
var appController = require('js/app-controller');
var appController = require('../app-controller');
var LoginPrivateKeyDownloadCtrl = function($scope, $location, $routeParams) {
var LoginPrivateKeyDownloadCtrl = function($scope, $location, $routeParams) {
if (!appController._emailDao && !$routeParams.dev) {
$location.path('/'); // init app
return;
@ -128,7 +127,6 @@ define(function(require) {
$location.path(location);
$scope.$apply();
};
};
};
return LoginPrivateKeyDownloadCtrl;
});
module.exports = LoginPrivateKeyDownloadCtrl;

View File

@ -1,14 +1,13 @@
define(function(require) {
'use strict';
'use strict';
var ENCRYPTION_METHOD_NONE = 0;
var ENCRYPTION_METHOD_STARTTLS = 1;
var ENCRYPTION_METHOD_TLS = 2;
var ENCRYPTION_METHOD_NONE = 0;
var ENCRYPTION_METHOD_STARTTLS = 1;
var ENCRYPTION_METHOD_TLS = 2;
var appCtrl = require('js/app-controller'),
config = require('js/app-config').config;
var appCtrl = require('../app-controller'),
config = require('../app-config').config;
var SetCredentialsCtrl = function($scope, $location, $routeParams) {
var SetCredentialsCtrl = function($scope, $location, $routeParams) {
if (!appCtrl._auth && !$routeParams.dev) {
$location.path('/'); // init app
return;
@ -114,7 +113,6 @@ define(function(require) {
$scope.$apply();
});
};
};
};
return SetCredentialsCtrl;
});
module.exports = SetCredentialsCtrl;

View File

@ -1,9 +1,8 @@
define(function(require) {
'use strict';
'use strict';
var appController = require('js/app-controller');
var appController = require('../app-controller');
var LoginCtrl = function($scope, $location) {
var LoginCtrl = function($scope, $location) {
// start main application controller
appController.start({
@ -97,7 +96,6 @@ define(function(require) {
$location.path(location);
});
}
};
};
return LoginCtrl;
});
module.exports = LoginCtrl;

View File

@ -1,17 +1,14 @@
define(function(require) {
'use strict';
'use strict';
var angular = require('angular'),
_ = require('underscore'),
appController = require('js/app-controller'),
notification = require('js/util/notification'),
var appController = require('../app-controller'),
notification = require('../util/notification'),
emailDao, outboxBo, keychainDao, searchTimeout, firstSelect;
var INIT_DISPLAY_LEN = 20,
var INIT_DISPLAY_LEN = 20,
SCROLL_DISPLAY_LEN = 10,
FOLDER_TYPE_INBOX = 'Inbox';
var MailListCtrl = function($scope, $routeParams) {
var MailListCtrl = function($scope, $routeParams) {
//
// Init
//
@ -416,15 +413,15 @@ define(function(require) {
});
$scope.pendingNotifications.push(note);
};
};
};
//
// Directives
//
//
// Directives
//
var ngModule = angular.module('mail-list', []);
var ngModule = angular.module('mail-list', []);
ngModule.directive('woTouch', function($parse) {
ngModule.directive('woTouch', function($parse) {
return function(scope, elm, attrs) {
var handler = $parse(attrs.woTouch);
@ -444,9 +441,9 @@ define(function(require) {
});
});
};
});
});
ngModule.directive('listScroll', function() {
ngModule.directive('listScroll', function() {
return {
link: function(scope, elm, attrs) {
var model = attrs.listScroll,
@ -516,9 +513,9 @@ define(function(require) {
});
}
};
});
});
function byUidDescending(a, b) {
function byUidDescending(a, b) {
if (a.uid < b.uid) {
return 1;
} else if (b.uid < a.uid) {
@ -526,11 +523,11 @@ define(function(require) {
} else {
return 0;
}
}
}
// Helper for development mode
// Helper for development mode
function createDummyMails() {
function createDummyMails() {
var uid = 1000000;
var Email = function(unread, attachments, answered) {
@ -594,7 +591,6 @@ define(function(require) {
}
return dummies;
}
}
return MailListCtrl;
});
module.exports = MailListCtrl;

View File

@ -1,19 +1,16 @@
define(function(require) {
'use strict';
'use strict';
var angular = require('angular'),
appController = require('js/app-controller'),
config = require('js/app-config').config,
notification = require('js/util/notification'),
backBtnHandler = require('js/util/backbutton-handler'),
_ = require('underscore'),
var appController = require('../app-controller'),
config = require('../app-config').config,
notification = require('../util/notification'),
backBtnHandler = require('../util/backbutton-handler'),
emailDao, outboxBo;
//
// Controller
//
//
// Controller
//
var NavigationCtrl = function($scope, $routeParams, $location) {
var NavigationCtrl = function($scope, $routeParams, $location) {
if (!appController._emailDao && !$routeParams.dev) {
$location.path('/'); // init app
return;
@ -151,14 +148,14 @@ define(function(require) {
path: 'TRASH'
}];
}
};
};
//
// Directives
//
//
// Directives
//
var ngModule = angular.module('navigation', []);
ngModule.directive('keyShortcuts', function($timeout) {
var ngModule = angular.module('navigation', []);
ngModule.directive('keyShortcuts', function($timeout) {
return function(scope, elm) {
elm.bind('keydown', function(e) {
// global state is not yet set, ignore keybaord shortcuts
@ -204,7 +201,6 @@ define(function(require) {
});
};
});
return NavigationCtrl;
});
module.exports = NavigationCtrl;

View File

@ -1,22 +1,19 @@
define(function(require) {
'use strict';
'use strict';
var angular = require('angular');
//
// Controller
//
//
// Controller
//
var PopoverCtrl = function($scope) {
var PopoverCtrl = function($scope) {
$scope.state.popover = {};
};
};
//
// Directives
//
//
// Directives
//
var ngModule = angular.module('popover', []);
ngModule.directive('popover', function() {
var ngModule = angular.module('popover', []);
ngModule.directive('popover', function() {
return function(scope, elm, attrs) {
var selector = attrs.popover;
var popover = angular.element(document.querySelector(selector));
@ -41,7 +38,6 @@ define(function(require) {
popover[0].style.left = '-9999px';
});
};
});
return PopoverCtrl;
});
module.exports = PopoverCtrl;

View File

@ -1,12 +1,10 @@
define(function(require) {
'use strict';
'use strict';
var angular = require('angular'),
appController = require('js/app-controller'),
util = require('js/crypto/util'),
var appController = require('../app-controller'),
util = require('crypto-lib').util,
keychain, pgp;
var PrivateKeyUploadCtrl = function($scope) {
var PrivateKeyUploadCtrl = function($scope) {
keychain = appController._keychain;
pgp = keychain._pgp;
@ -176,14 +174,14 @@ define(function(require) {
}
};
};
};
//
// Directives
//
//
// Directives
//
var ngModule = angular.module('privatekey-upload', []);
ngModule.directive('focusNext', function() {
var ngModule = angular.module('privatekey-upload', []);
ngModule.directive('focusNext', function() {
return {
link: function(scope, element, attr) {
var maxLen = element[0].maxLength;
@ -198,7 +196,6 @@ define(function(require) {
});
}
};
});
return PrivateKeyUploadCtrl;
});
module.exports = PrivateKeyUploadCtrl;

View File

@ -1,8 +1,7 @@
(function() {
'use strict';
'use strict';
// set listener for event from main window
window.onmessage = function(e) {
// set listener for event from main window
window.onmessage = function(e) {
var html = '';
if (e.data.html) {
@ -28,16 +27,16 @@
document.body.innerHTML = html;
scaleToFit();
};
};
window.addEventListener('resize', scaleToFit);
window.addEventListener('resize', scaleToFit);
/**
/**
* Parse email body and generate conversation nodes
* @param {Object} email The email object
* @return {Node} The root node of the conversion
*/
function parseConversation(textBody) {
function parseConversation(textBody) {
var nodes;
function parseLines(body) {
@ -140,14 +139,14 @@
removeParentReference(nodes);
return nodes;
}
}
/**
/**
* Render the conversation nodes as markup. This is not injected directly into the DOM, but rather send to a sandboxed iframe to be rendered
* @param {Node} root The conversation root node
* @return {Strin} The conversation as markup
*/
function renderNodes(root) {
function renderNodes(root) {
var body = '';
function render(node) {
@ -189,14 +188,14 @@
}
return '<div class="view-read-body">' + body + '</div>';
}
}
/**
/**
* Transform scale content to fit iframe width
*/
function scaleToFit() {
function scaleToFit() {
var view = document.getElementsByClassName('scale-body').item(0);
if(!view) {
if (!view) {
return;
}
@ -204,7 +203,7 @@
var w = view.offsetWidth;
var scale = '';
if(w > parentWidth) {
if (w > parentWidth) {
scale = parentWidth / w;
scale = 'scale(' + scale + ',' + scale + ')';
}
@ -213,6 +212,4 @@
view.style.transformOrigin = '0 0';
view.style['-webkit-transform'] = scale;
view.style.transform = scale;
}
})();
}

View File

@ -1,17 +1,15 @@
define(function(require) {
'use strict';
'use strict';
var appController = require('js/app-controller'),
download = require('js/util/download'),
angular = require('angular'),
str = require('js/app-config').string,
var appController = require('../app-controller'),
download = require('../util/download'),
str = require('../app-config').string,
emailDao, invitationDao, outbox, pgp, keychain;
//
// Controller
//
//
// Controller
//
var ReadCtrl = function($scope) {
var ReadCtrl = function($scope) {
emailDao = appController._emailDao;
invitationDao = appController._invitationDao;
@ -144,15 +142,15 @@ define(function(require) {
outbox.put(invitationMail, $scope.onError);
});
};
};
};
//
// Directives
//
//
// Directives
//
var ngModule = angular.module('read', []);
var ngModule = angular.module('read', []);
ngModule.directive('replySelection', function() {
ngModule.directive('replySelection', function() {
return function(scope, elm) {
var popover, visible;
@ -193,9 +191,9 @@ define(function(require) {
visible = false;
}
};
});
});
ngModule.directive('frameLoad', function($timeout, $window) {
ngModule.directive('frameLoad', function($timeout, $window) {
return function(scope, elm) {
var iframe = elm[0];
@ -280,7 +278,6 @@ define(function(require) {
});
}
};
});
return ReadCtrl;
});
module.exports = ReadCtrl;

View File

@ -1,14 +1,13 @@
define(function(require) {
'use strict';
'use strict';
var appController = require('js/app-controller'),
var appController = require('../app-controller'),
pgp, keychain;
//
// Controller
//
//
// Controller
//
var SetPassphraseCtrl = function($scope) {
var SetPassphraseCtrl = function($scope) {
keychain = appController._keychain;
pgp = appController._pgp;
@ -132,7 +131,6 @@ define(function(require) {
message: 'Passphrase change complete.'
});
}
};
};
return SetPassphraseCtrl;
});
module.exports = SetPassphraseCtrl;

View File

@ -1,19 +1,16 @@
define(function(require) {
'use strict';
'use strict';
var angular = require('angular'),
_ = require('underscore'),
appController = require('js/app-controller'),
axe = require('axe'),
util = require('js/crypto/util'),
str = require('js/app-config').string,
var appController = require('../app-controller'),
axe = require('axe-logger'),
util = require('crypto-lib').util,
str = require('../app-config').string,
pgp, emailDao, outbox, keychainDao, auth;
//
// Controller
//
//
// Controller
//
var WriteCtrl = function($scope, $filter, $q) {
var WriteCtrl = function($scope, $filter, $q) {
pgp = appController._pgp;
auth = appController._auth;
emailDao = appController._emailDao;
@ -440,16 +437,16 @@ define(function(require) {
function filterEmptyAddresses(addr) {
return !!addr.address;
}
};
};
//
// Directives
//
//
// Directives
//
var ngModule = angular.module('write', []);
var ngModule = angular.module('write', []);
ngModule.directive('focusMe', function($timeout, $parse) {
ngModule.directive('focusMe', function($timeout, $parse) {
return {
//scope: true, // optionally create a child scope
link: function(scope, element, attrs) {
@ -469,9 +466,9 @@ define(function(require) {
});
}
};
});
});
ngModule.directive('focusInput', function($timeout, $parse) {
ngModule.directive('focusInput', function($timeout, $parse) {
return {
//scope: true, // optionally create a child scope
link: function(scope, element, attrs) {
@ -485,9 +482,9 @@ define(function(require) {
});
}
};
});
});
ngModule.directive('focusInputOnClick', function() {
ngModule.directive('focusInputOnClick', function() {
return {
//scope: true, // optionally create a child scope
link: function(scope, element) {
@ -496,9 +493,9 @@ define(function(require) {
});
}
};
});
});
ngModule.directive('attachmentInput', function() {
ngModule.directive('attachmentInput', function() {
return function(scope, elm) {
elm.on('change', function(e) {
for (var i = 0; i < e.target.files.length; i++) {
@ -519,16 +516,15 @@ define(function(require) {
reader.readAsArrayBuffer(file);
}
};
});
});
ngModule.directive('attachmentBtn', function() {
ngModule.directive('attachmentBtn', function() {
return function(scope, elm) {
elm.on('click touchstart', function(e) {
e.preventDefault();
document.querySelector('#attachment-input').click();
});
};
});
return WriteCtrl;
});
module.exports = WriteCtrl;

View File

@ -2,19 +2,17 @@
* High level crypto api that invokes native crypto (if available) and
* gracefully degrades to JS crypto (if unavailable)
*/
define(function(require) {
'use strict';
var aes = require('js/crypto/aes-gcm'),
pbkdf2 = require('js/crypto/pbkdf2'),
config = require('js/app-config').config,
axe = require('axe');
'use strict';
var PBKDF2_WORKER = '/crypto/pbkdf2-worker.js';
var aes = require('crypto-lib').aes,
pbkdf2 = require('./pbkdf2'),
config = require('../app-config').config,
axe = require('axe-logger');
var Crypto = function() {};
var Crypto = function() {};
/**
/**
* Encrypt plaintext using AES-GCM.
* @param {String} plaintext The input string in UTF-16
* @param {String} key The base64 encoded key
@ -22,7 +20,7 @@ define(function(require) {
* @param {Function} callback(error, ciphertext)
* @return {String} The base64 encoded ciphertext
*/
Crypto.prototype.encrypt = function(plaintext, key, iv, callback) {
Crypto.prototype.encrypt = function(plaintext, key, iv, callback) {
var ct;
try {
@ -33,9 +31,9 @@ define(function(require) {
}
callback(null, ct);
};
};
/**
/**
* Decrypt ciphertext suing AES-GCM
* @param {String} ciphertext The base64 encoded ciphertext
* @param {String} key The base64 encoded key
@ -43,7 +41,7 @@ define(function(require) {
* @param {Function} callback(error, plaintext)
* @return {String} The decrypted plaintext in UTF-16
*/
Crypto.prototype.decrypt = function(ciphertext, key, iv, callback) {
Crypto.prototype.decrypt = function(ciphertext, key, iv, callback) {
var pt;
try {
@ -54,14 +52,14 @@ define(function(require) {
}
callback(null, pt);
};
};
/**
/**
* Do PBKDF2 key derivation in a WebWorker thread
*/
Crypto.prototype.deriveKey = function(password, salt, keySize, callback) {
Crypto.prototype.deriveKey = function(password, salt, keySize, callback) {
startWorker({
script: PBKDF2_WORKER,
script: config.workerPath + '/pbkdf2-worker.min.js',
args: {
password: password,
salt: salt,
@ -72,17 +70,17 @@ define(function(require) {
return pbkdf2.getKey(password, salt, keySize);
}
});
};
};
//
// helper functions
//
//
// helper functions
//
function startWorker(options) {
function startWorker(options) {
// check for WebWorker support
if (window.Worker) {
// init webworker thread
var worker = new Worker(config.workerPath + options.script);
var worker = new Worker(options.script);
worker.onmessage = function(e) {
if (e.data.err) {
options.callback(e.data.err);
@ -117,7 +115,6 @@ define(function(require) {
return;
}
options.callback(null, result);
}
}
return Crypto;
});
module.exports = Crypto;

View File

@ -1,22 +1,14 @@
(function() {
'use strict';
'use strict';
// import web worker dependencies
importScripts('../../lib/require.js');
importScripts('forge.min.js');
/**
var pbkdf2 = require('./pbkdf2');
/**
* In the web worker thread context, 'this' and 'self' can be used as a global
* variable namespace similar to the 'window' object in the main thread
*/
self.onmessage = function(e) {
// fetch dependencies via require.js
require(['../../require-config'], function() {
require.config({
baseUrl: '../../lib'
});
require(['js/crypto/pbkdf2'], function(pbkdf2) {
self.onmessage = function(e) {
var i = e.data,
key = null;
@ -30,9 +22,4 @@
// pass output back to main thread
self.postMessage(key);
});
});
};
}());
};

View File

@ -1,25 +1,24 @@
/**
* A Wrapper for Forge's PBKDF2 function
*/
define(['forge'], function(forge) {
'use strict';
var self = {};
'use strict';
/**
var pbkdf2 = {};
/**
* PBKDF2-HMAC-SHA256 key derivation with a random salt and 10000 iterations
* @param {String} password The password in UTF8
* @param {String} salt The base64 encoded salt
* @param {String} keySize The key size in bits
* @return {String} The base64 encoded key
*/
self.getKey = function(password, salt, keySize) {
pbkdf2.getKey = function(password, salt, keySize) {
var saltUtf8 = forge.util.decode64(salt);
var md = forge.md.sha256.create();
var key = forge.pkcs5.pbkdf2(password, saltUtf8, 10000, keySize / 8, md);
return forge.util.encode64(key);
};
};
return self;
});
module.exports = pbkdf2;

View File

@ -1,22 +1,21 @@
/**
* High level crypto api that handles all calls to OpenPGP.js
*/
define(function(require) {
'use strict';
var openpgp = require('openpgp'),
util = require('openpgp').util,
config = require('js/app-config').config;
'use strict';
var PGP = function() {
var util = openpgp.util,
config = require('../app-config').config;
var PGP = function() {
openpgp.config.prefer_hash_algorithm = openpgp.enums.hash.sha256;
openpgp.initWorker(config.workerPath + '/../lib/openpgp/openpgp.worker.js');
};
openpgp.initWorker(config.workerPath + '/openpgp.worker.js');
};
/**
/**
* Generate a key pair for the user
*/
PGP.prototype.generateKeys = function(options, callback) {
PGP.prototype.generateKeys = function(options, callback) {
var userId, passphrase;
if (!util.emailRegEx.test(options.emailAddress) || !options.keySize) {
@ -46,12 +45,12 @@ define(function(require) {
publicKeyArmored: keys.publicKeyArmored
});
}
};
};
/**
/**
* Show a user's fingerprint
*/
PGP.prototype.getFingerprint = function(keyArmored) {
PGP.prototype.getFingerprint = function(keyArmored) {
function fingerprint(key) {
return key.primaryKey.getFingerprint().toUpperCase();
}
@ -67,12 +66,12 @@ define(function(require) {
// get local fingerpring
return fingerprint(this._publicKey);
};
};
/**
/**
* Show a user's key id.
*/
PGP.prototype.getKeyId = function(keyArmored) {
PGP.prototype.getKeyId = function(keyArmored) {
var key, pubKeyId, privKeyId;
// process armored key input
@ -94,12 +93,12 @@ define(function(require) {
}
return pubKeyId;
};
};
/**
/**
* Read all relevant params of an armored key.
*/
PGP.prototype.getKeyParams = function(keyArmored) {
PGP.prototype.getKeyParams = function(keyArmored) {
var key, packet, userIds;
// process armored key input
@ -131,23 +130,23 @@ define(function(require) {
bitSize: packet.getBitSize(),
created: packet.created,
};
};
};
/**
/**
* Extract a public key from a private key
* @param {String} privateKeyArmored The private PGP key block
* @return {String} The publick PGP key block
*/
PGP.prototype.extractPublicKey = function(privateKeyArmored) {
PGP.prototype.extractPublicKey = function(privateKeyArmored) {
var privkey = openpgp.key.readArmored(privateKeyArmored).keys[0];
var pubkey = privkey.toPublic();
return pubkey.armor();
};
};
/**
/**
* Import the user's key pair
*/
PGP.prototype.importKeys = function(options, callback) {
PGP.prototype.importKeys = function(options, callback) {
var pubKeyId, privKeyId, self = this;
// check options
@ -188,12 +187,12 @@ define(function(require) {
}
callback();
};
};
/**
/**
* Export the user's key pair
*/
PGP.prototype.exportKeys = function(callback) {
PGP.prototype.exportKeys = function(callback) {
if (!this._publicKey || !this._privateKey) {
callback(new Error('Could not export keys!'));
return;
@ -204,12 +203,12 @@ define(function(require) {
privateKeyArmored: this._privateKey.armor(),
publicKeyArmored: this._publicKey.armor()
});
};
};
/**
/**
* Change the passphrase of an ascii armored private key.
*/
PGP.prototype.changePassphrase = function(options, callback) {
PGP.prototype.changePassphrase = function(options, callback) {
var privKey, packets, newPassphrase, newKeyArmored;
// set undefined instead of empty string as passphrase
@ -259,12 +258,12 @@ define(function(require) {
}
callback(null, newKeyArmored);
};
};
/**
/**
* Encrypt and sign a pgp message for a list of receivers
*/
PGP.prototype.encrypt = function(plaintext, publicKeysArmored, callback) {
PGP.prototype.encrypt = function(plaintext, publicKeysArmored, callback) {
var publicKeys;
// check keys
@ -293,15 +292,15 @@ define(function(require) {
// if no public keys are available encrypt for myself
openpgp.signAndEncryptMessage([this._publicKey], this._privateKey, plaintext, callback);
}
};
};
/**
/**
* Decrypts a ciphertext
* @param {String} ciphertext The encrypted PGP message block
* @param {String} publicKeyArmored The public key used to sign the message
* @param {Function} callback(error, plaintext, signaturesValid) signaturesValid is undefined in case there are no signature, null in case there are signatures but the wrong public key or no key was used to verify, true if the signature was successfully verified, or false if the signataure verification failed.
*/
PGP.prototype.decrypt = function(ciphertext, publicKeyArmored, callback) {
PGP.prototype.decrypt = function(ciphertext, publicKeyArmored, callback) {
var publicKeys, message;
// check keys
@ -337,15 +336,15 @@ define(function(require) {
// return decrypted plaintext
callback(null, decrypted.text, checkSignatureValidity(decrypted.signatures));
}
};
};
/**
/**
* Verifies a clearsigned message
* @param {String} clearSignedText The clearsigned text, usually from a signed pgp/inline message
* @param {String} publicKeyArmored The public key used to signed the message
* @param {Function} callback(error, signaturesValid) signaturesValid is undefined in case there are no signature, null in case there are signatures but the wrong public key or no key was used to verify, true if the signature was successfully verified, or false if the signataure verification failed.
*/
PGP.prototype.verifyClearSignedMessage = function(clearSignedText, publicKeyArmored, callback) {
PGP.prototype.verifyClearSignedMessage = function(clearSignedText, publicKeyArmored, callback) {
var publicKeys,
message;
@ -378,16 +377,16 @@ define(function(require) {
callback(null, checkSignatureValidity(result.signatures));
});
};
};
/**
/**
* Verifies a message with a detached signature
* @param {String} message The signed text, usually from a signed pgp/mime message
* @param {String} pgpSignature The detached signature, usually from a signed pgp/mime message
* @param {String} publicKeyArmored The public key used to signed the message
* @param {Function} callback(error, signaturesValid) signaturesValid is undefined in case there are no signature, null in case there are signatures but the wrong public key or no key was used to verify, true if the signature was successfully verified, or false if the signataure verification failed.
*/
PGP.prototype.verifySignedMessage = function(message, pgpSignature, publicKeyArmored, callback) {
PGP.prototype.verifySignedMessage = function(message, pgpSignature, publicKeyArmored, callback) {
var publicKeys;
// check keys
@ -420,9 +419,9 @@ define(function(require) {
}
callback(null, checkSignatureValidity(signatures));
};
};
/**
/**
* Checks signature validity
* @param {Object} decrypted OpenPGP.js Signature array
* @return {undefined|null|true|false}
@ -431,7 +430,7 @@ define(function(require) {
* If signatures are invalid, returns false.
* If everything is in order, returns true
*/
function checkSignatureValidity(signatures) {
function checkSignatureValidity(signatures) {
if (!signatures.length) {
// signatures array is empty (the message was not signed)
return;
@ -446,7 +445,6 @@ define(function(require) {
// everything is in order
return true;
}
}
return PGP;
});
module.exports = PGP;

View File

@ -1,18 +1,17 @@
define(function() {
'use strict';
'use strict';
var AdminDAO = function(restDao) {
var AdminDAO = function(restDao) {
this._restDao = restDao;
};
};
/**
/**
* Create a new email account.
* @param {String} options.emailAddress The desired email address
* @param {String} options.password The password to be used for the account.
* @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) {
AdminDAO.prototype.createUser = function(options, callback) {
var uri;
if (!options.emailAddress || !options.password || !options.phone) {
@ -32,15 +31,15 @@ define(function() {
callback();
});
};
};
/**
/**
* Verify a user's phone number by confirming a token to the server.
* @param {String} options.emailAddress The desired email address
* @param {String} options.token The validation token.
* @param {Function} callback(error)
*/
AdminDAO.prototype.validateUser = function(options, callback) {
AdminDAO.prototype.validateUser = function(options, callback) {
var uri;
if (!options.emailAddress || !options.token) {
@ -57,7 +56,6 @@ define(function() {
callback(new Error('Validation failed!'));
}
});
};
};
return AdminDAO;
});
module.exports = AdminDAO;

View File

@ -1,26 +1,23 @@
/**
* High level storage api that handles all persistence on the device. If
* SQLcipher/SQLite is available, all data is securely persisted there,
* through transparent encryption. If not, the crypto API is
* used to encrypt data on the fly before persisting via a JSON store.
* High level storage api that handles all persistence on the device.
*/
define(function() {
'use strict';
var DeviceStorageDAO = function(localDbDao) {
'use strict';
var DeviceStorageDAO = function(localDbDao) {
this._localDbDao = localDbDao;
};
};
DeviceStorageDAO.prototype.init = function(emailAddress, callback) {
DeviceStorageDAO.prototype.init = function(emailAddress, callback) {
this._localDbDao.init(emailAddress, callback);
};
};
/**
/**
* Stores a list of encrypted items in the object store
* @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) {
DeviceStorageDAO.prototype.storeList = function(list, type, callback) {
var key, items = [];
// nothing to store
@ -47,38 +44,38 @@ define(function() {
});
this._localDbDao.batch(items, callback);
};
};
/**
/**
* Deletes items of a certain type from storage
*/
DeviceStorageDAO.prototype.removeList = function(type, callback) {
DeviceStorageDAO.prototype.removeList = function(type, callback) {
this._localDbDao.removeList(type, callback);
};
};
/**
/**
* List stored items of a given type
* @param type [String] The type of item e.g. 'email'
* @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) {
DeviceStorageDAO.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);
};
};
/**
/**
* Clear the whole device data-store
*/
DeviceStorageDAO.prototype.clear = function(callback) {
DeviceStorageDAO.prototype.clear = function(callback) {
this._localDbDao.clear(callback);
};
};
//
// helper functions
//
//
// helper functions
//
function createKey(i, type) {
function createKey(i, type) {
var key;
// put uid in key if available... for easy querying
@ -91,7 +88,6 @@ define(function() {
}
return key;
}
}
return DeviceStorageDAO;
});
module.exports = DeviceStorageDAO;

View File

@ -1,44 +1,41 @@
define(function(require) {
'use strict';
'use strict';
var util = require('js/crypto/util'),
_ = require('underscore'),
config = require('js/app-config').config,
str = require('js/app-config').string;
var util = require('crypto-lib').util,
config = require('../app-config').config,
str = require('../app-config').string;
//
//
// Constants
//
//
//
//
// Constants
//
//
var FOLDER_DB_TYPE = 'folders';
var FOLDER_DB_TYPE = 'folders';
var SYNC_TYPE_NEW = 'new';
var SYNC_TYPE_DELETED = 'deleted';
var SYNC_TYPE_MSGS = 'messages';
var SYNC_TYPE_NEW = 'new';
var SYNC_TYPE_DELETED = 'deleted';
var SYNC_TYPE_MSGS = 'messages';
var FOLDER_TYPE_INBOX = 'Inbox';
var FOLDER_TYPE_SENT = 'Sent';
var FOLDER_TYPE_DRAFTS = 'Drafts';
var FOLDER_TYPE_TRASH = 'Trash';
var FOLDER_TYPE_INBOX = 'Inbox';
var FOLDER_TYPE_SENT = 'Sent';
var FOLDER_TYPE_DRAFTS = 'Drafts';
var FOLDER_TYPE_TRASH = 'Trash';
var MSG_ATTR_UID = 'uid';
var MSG_PART_ATTR_CONTENT = 'content';
var MSG_PART_TYPE_ATTACHMENT = 'attachment';
var MSG_PART_TYPE_ENCRYPTED = 'encrypted';
var MSG_PART_TYPE_SIGNED = 'signed';
var MSG_PART_TYPE_TEXT = 'text';
var MSG_PART_TYPE_HTML = 'html';
var MSG_ATTR_UID = 'uid';
var MSG_PART_ATTR_CONTENT = 'content';
var MSG_PART_TYPE_ATTACHMENT = 'attachment';
var MSG_PART_TYPE_ENCRYPTED = 'encrypted';
var MSG_PART_TYPE_SIGNED = 'signed';
var MSG_PART_TYPE_TEXT = 'text';
var MSG_PART_TYPE_HTML = 'html';
//
//
// Email Dao
//
//
//
//
// Email Dao
//
//
/**
/**
* High-level data access object that orchestrates everything around the handling of encrypted mails:
* PGP de-/encryption, receiving via IMAP, sending via SMTP, MIME parsing, local db persistence
*
@ -48,23 +45,23 @@ define(function(require) {
* @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) {
var EmailDAO = function(keychain, pgp, devicestorage, pgpbuilder, mailreader) {
this._keychain = keychain;
this._pgp = pgp;
this._devicestorage = devicestorage;
this._pgpbuilder = pgpbuilder;
this._mailreader = mailreader;
};
};
//
//
// Public API
//
//
//
//
// Public API
//
//
/**
/**
* Initializes the email dao:
* - validates the email address
* - retrieves the user's key pair (if available)
@ -74,7 +71,7 @@ define(function(require) {
* @param {String} options.account.emailAddress 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) {
EmailDAO.prototype.init = function(options, callback) {
var self = this,
keypair;
@ -120,14 +117,14 @@ define(function(require) {
callback(null, keypair);
});
}
};
};
/**
/**
* Unlocks the keychain by either decrypting an existing private key or generating a new keypair
* @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) {
EmailDAO.prototype.unlock = function(options, callback) {
var self = this;
if (options.keypair) {
@ -233,9 +230,9 @@ define(function(require) {
});
});
}
};
};
/**
/**
* Opens a folder in IMAP so that we can receive updates for it.
* Please note that this is a no-op if you try to open the outbox, since it is not an IMAP folder
* but a virtual folder that only exists on disk.
@ -243,7 +240,7 @@ define(function(require) {
* @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) {
EmailDAO.prototype.openFolder = function(options, callback) {
var self = this,
err;
@ -261,9 +258,9 @@ define(function(require) {
this._imapClient.selectMailbox({
path: options.folder.path
}, callback);
};
};
/**
/**
* Synchronizes a folder's contents from disk to memory, i.e. if
* a message has disappeared from the disk, this method will remove it from folder.messages, and
* it adds any messages from disk to memory the are not yet in folder.messages
@ -271,7 +268,7 @@ define(function(require) {
* @param {Object} options.folder The folder to synchronize
* @param {Function} callback [description]
*/
EmailDAO.prototype.refreshFolder = function(options, callback) {
EmailDAO.prototype.refreshFolder = function(options, callback) {
var self = this,
folder = options.folder;
@ -320,9 +317,9 @@ define(function(require) {
updateUnreadCount(folder); // update the unread count
callback(err);
}
};
};
/**
/**
* Fetches a message's headers from IMAP.
*
* NB! If we fetch a message whose subject line correspond's to that of a verification message,
@ -331,7 +328,7 @@ define(function(require) {
* @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) {
EmailDAO.prototype.fetchMessages = function(options, callback) {
var self = this,
folder = options.folder;
@ -469,9 +466,9 @@ define(function(require) {
});
});
}
};
};
/**
/**
* Delete a message from IMAP, disk and folder.messages.
*
* Please note that this deletes from disk only if you delete from the outbox,
@ -482,7 +479,7 @@ define(function(require) {
* @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) {
EmailDAO.prototype.deleteMessage = function(options, callback) {
var self = this,
folder = options.folder,
message = options.message;
@ -539,9 +536,9 @@ define(function(require) {
updateUnreadCount(folder); // update the unread count, if necessary
callback(err);
}
};
};
/**
/**
* Updates a message's 'unread' and 'answered' flags
*
* Please note if you set flags on disk only if you delete from the outbox,
@ -550,7 +547,7 @@ define(function(require) {
* @param {[type]} options [description]
* @param {Function} callback [description]
*/
EmailDAO.prototype.setFlags = function(options, callback) {
EmailDAO.prototype.setFlags = function(options, callback) {
var self = this,
folder = options.folder,
message = options.message;
@ -630,15 +627,15 @@ define(function(require) {
updateUnreadCount(folder); // update the unread count
callback(err);
}
};
};
/**
/**
* Streams message content
* @param {Object} options.message The message for which to retrieve the body
* @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) {
EmailDAO.prototype.getBody = function(options, callback) {
var self = this,
message = options.message,
folder = options.folder;
@ -821,9 +818,9 @@ define(function(require) {
message.loadingBody = false;
callback(err, err ? undefined : message);
}
};
};
EmailDAO.prototype._checkSignatures = function(message, callback) {
EmailDAO.prototype._checkSignatures = function(message, callback) {
var self = this;
self._keychain.getReceiverPublicKey(message.from[0].address, function(err, senderPublicKey) {
@ -842,9 +839,9 @@ define(function(require) {
callback(null, undefined);
}
});
};
};
/**
/**
* Retrieves an attachment matching a body part for a given uid and a folder
*
* @param {Object} options.folder The folder where to find the attachment
@ -852,7 +849,7 @@ define(function(require) {
* @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) {
EmailDAO.prototype.getAttachment = function(options, callback) {
var self = this,
attachment = options.attachment;
@ -873,16 +870,16 @@ define(function(require) {
attachment.content = parsedBodyParts[0].content;
callback(err, err ? undefined : attachment);
});
};
};
/**
/**
* Decrypts a message and replaces sets the decrypted plaintext as the message's body, html, or attachment, respectively.
* The first encrypted body part's ciphertext (in the content property) will be decrypted.
*
* @param {Object} options.message The message
* @param {Function} callback(error, message)
*/
EmailDAO.prototype.decryptBody = function(options, callback) {
EmailDAO.prototype.decryptBody = function(options, callback) {
var self = this,
message = options.message;
@ -994,15 +991,15 @@ define(function(require) {
message.decryptingBody = false;
callback(err, err ? undefined : message);
}
};
};
/**
/**
* Encrypted (if necessary) and sends a message with a predefined clear text greeting.
*
* @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) {
EmailDAO.prototype.sendEncrypted = function(options, callback) {
var self = this;
if (!self._account.online) {
@ -1043,15 +1040,15 @@ define(function(require) {
callback(err);
});
});
};
};
/**
/**
* Sends a signed message in the plain
*
* @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) {
EmailDAO.prototype.sendPlaintext = function(options, callback) {
var self = this;
if (!self._account.online) {
@ -1093,15 +1090,15 @@ define(function(require) {
callback(err);
});
});
};
};
/**
/**
* Signs and encrypts a message
*
* @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) {
EmailDAO.prototype.encrypt = function(options, callback) {
var self = this;
self.busy();
@ -1110,17 +1107,17 @@ define(function(require) {
callback(err);
});
};
};
//
//
// Event Handlers
//
//
//
//
// Event Handlers
//
//
/**
/**
* This handler should be invoked when navigator.onLine === true. It will try to connect a
* given instance of the imap client. If the connection attempt was successful, it will
* update the locally available folders with the newly received IMAP folder listing.
@ -1129,7 +1126,7 @@ define(function(require) {
* @param {Object} options.pgpMailer The SMTP client used to send messages
* @param {Function} callback [description]
*/
EmailDAO.prototype.onConnect = function(options, callback) {
EmailDAO.prototype.onConnect = function(options, callback) {
var self = this;
self._account.loggingIn = true;
@ -1203,13 +1200,13 @@ define(function(require) {
}, callback);
});
});
};
};
/**
/**
* This handler should be invoked when navigator.onLine === false.
* It will discard the imap client and pgp mailer
*/
EmailDAO.prototype.onDisconnect = function(callback) {
EmailDAO.prototype.onDisconnect = function(callback) {
var self = this;
// logout of imap-client
@ -1224,9 +1221,9 @@ define(function(require) {
self._account.online = false;
self._imapClient = undefined;
self._pgpMailer = undefined;
};
};
/**
/**
* The are updates in the IMAP folder of the following type
* - 'new': a list of uids that are newly available
* - 'deleted': a list of uids that were deleted from IMAP available
@ -1236,7 +1233,7 @@ define(function(require) {
* @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) {
EmailDAO.prototype._onSyncUpdate = function(options) {
var self = this;
var folder = _.findWhere(self._account.folders, {
@ -1300,23 +1297,23 @@ define(function(require) {
}, self.onError.bind(self));
});
}
};
};
//
//
// Internal API
//
//
//
//
// Internal API
//
//
/**
/**
* Updates the folder information from memory, and adds/removes folders in account.folders.
* The locally available messages are loaded from memory
*
* @param {Function} callback Invoked when the folders are up to date
*/
EmailDAO.prototype._initFoldersFromDisk = function(callback) {
EmailDAO.prototype._initFoldersFromDisk = function(callback) {
var self = this;
self.busy(); // start the spinner
@ -1335,16 +1332,16 @@ define(function(require) {
self.done(); // stop the spinner
callback(err);
}
};
};
/**
/**
* Updates the folder information from imap (if we're online). Adds/removes folders in account.folders,
* if we added/removed folder in IMAP. If we have an uninitialized folder that lacks folder.messages,
* all the locally available messages are loaded from memory.
*
* @param {Function} callback Invoked when the folders are up to date
*/
EmailDAO.prototype._initFoldersFromImap = function(callback) {
EmailDAO.prototype._initFoldersFromImap = function(callback) {
var self = this;
self.busy(); // start the spinner
@ -1434,14 +1431,14 @@ define(function(require) {
self.done(); // stop the spinner
callback(err);
}
};
};
/**
/**
* Fill uninitialized folders with the locally available messages.
*
* @param {Function} callback Invoked when the folders are filled with messages
*/
EmailDAO.prototype._initMessagesFromDisk = function(callback) {
EmailDAO.prototype._initMessagesFromDisk = function(callback) {
var self = this;
if (!self._account.folders || self._account.folders.length === 0) {
@ -1467,27 +1464,27 @@ define(function(require) {
after();
});
});
};
};
EmailDAO.prototype.busy = function() {
EmailDAO.prototype.busy = function() {
this._account.busy++;
};
};
EmailDAO.prototype.done = function() {
EmailDAO.prototype.done = function() {
if (this._account.busy > 0) {
this._account.busy--;
}
};
};
//
//
// IMAP API
//
//
//
//
// IMAP API
//
//
/**
/**
* Mark messages as un-/read or un-/answered on IMAP
*
* @param {Object} options.folder The folder where to find the message
@ -1495,7 +1492,7 @@ define(function(require) {
* @param {Number} options.unread Un-/Read flag
* @param {Number} options.answered Un-/Answered flag
*/
EmailDAO.prototype._imapMark = function(options, callback) {
EmailDAO.prototype._imapMark = function(options, callback) {
if (!this._account.online) {
callback({
errMsg: 'Client is currently offline!',
@ -1506,9 +1503,9 @@ define(function(require) {
options.path = options.folder.path;
this._imapClient.updateFlags(options, callback);
};
};
/**
/**
* If we're in the trash folder or no trash folder is available, this deletes a message from IMAP.
* Otherwise, it moves a message to the trash folder.
*
@ -1516,7 +1513,7 @@ define(function(require) {
* @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) {
EmailDAO.prototype._imapDeleteMessage = function(options, callback) {
if (!this._account.online) {
callback({
errMsg: 'Client is currently offline!',
@ -1545,9 +1542,9 @@ define(function(require) {
destination: trash.path,
uid: options.uid
}, callback);
};
};
/**
/**
* Get list messsage headers without the body
*
* @param {String} options.folder The folder
@ -1555,7 +1552,7 @@ define(function(require) {
* @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) {
EmailDAO.prototype._imapListMessages = function(options, callback) {
var self = this;
if (!this._account.online) {
@ -1568,16 +1565,16 @@ define(function(require) {
options.path = options.folder.path;
self._imapClient.listMessages(options, callback);
};
};
/**
/**
* Stream an email messsage's body
* @param {String} options.folder The folder
* @param {String} options.uid the message's uid
* @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) {
EmailDAO.prototype._getBodyParts = function(options, callback) {
var self = this;
if (!self._account.online) {
@ -1597,17 +1594,17 @@ define(function(require) {
// interpret the raw content of the email
self._mailreader.parse(options, callback);
});
};
};
//
//
// Local Storage API
//
//
//
//
// Local Storage API
//
//
/**
/**
* List the locally available items form the indexed db stored under "email_[FOLDER PATH]_[MESSAGE UID]" (if a message was provided),
* or "email_[FOLDER PATH]", respectively
*
@ -1615,31 +1612,31 @@ define(function(require) {
* @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) {
EmailDAO.prototype._localListMessages = function(options, callback) {
var dbType = 'email_' + options.folder.path + (options.uid ? '_' + options.uid : '');
this._devicestorage.listItems(dbType, 0, null, callback);
};
};
/**
/**
* Stores a bunch of messages to the indexed db. The messages are stored under "email_[FOLDER PATH]_[MESSAGE UID]"
*
* @param {Object} options.folder The folder for which to list the content
* @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) {
EmailDAO.prototype._localStoreMessages = function(options, callback) {
var dbType = 'email_' + options.folder.path;
this._devicestorage.storeList(options.emails, dbType, callback);
};
};
/**
/**
* Stores a bunch of messages to the indexed db. The messages are stored under "email_[FOLDER PATH]_[MESSAGE UID]"
*
* @param {Object} options.folder The folder for which to list the content
* @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) {
EmailDAO.prototype._localDeleteMessage = function(options, callback) {
var path = options.folder.path,
uid = options.uid,
id = options.id;
@ -1653,38 +1650,38 @@ define(function(require) {
var dbType = 'email_' + path + '_' + (uid || id);
this._devicestorage.removeList(dbType, callback);
};
};
//
//
// Helper Functions
//
//
//
//
// Helper Functions
//
//
/**
/**
* Updates a folder's unread count:
* - For the outbox, that's the total number of messages,
* - For every other folder, it's the number of unread messages
*/
function updateUnreadCount(folder) {
function updateUnreadCount(folder) {
var allMsgs = folder.messages.length,
unreadMsgs = _.filter(folder.messages, function(msg) {
return msg.unread;
}).length;
folder.count = folder.path === config.outboxMailboxPath ? allMsgs : unreadMsgs;
}
}
/**
/**
* Helper function that recursively traverses the body parts tree. Looks for bodyParts that match the provided type and aggregates them
*
* @param {Array} bodyParts The bodyParts array
* @param {String} type The type to look up
* @param {undefined} result Leave undefined, only used for recursion
*/
function filterBodyParts(bodyParts, type, result) {
function filterBodyParts(bodyParts, type, result) {
result = result || [];
bodyParts.forEach(function(part) {
if (part.type === type) {
@ -1694,9 +1691,9 @@ define(function(require) {
}
});
return result;
}
}
/**
/**
* Helper function that looks through the HTML content for <img src="cid:..."> and
* inlines the images linked internally. Manipulates message.html as a side-effect.
* If no attachment matching the internal reference is found, or constructing a data
@ -1704,7 +1701,7 @@ define(function(require) {
*
* @param {Object} message DTO
*/
function inlineExternalImages(message) {
function inlineExternalImages(message) {
message.html = message.html.replace(/(<img[^>]+\bsrc=['"])cid:([^'">]+)(['"])/ig, function(match, prefix, src, suffix) {
var localSource = '',
payload = '';
@ -1725,7 +1722,6 @@ define(function(require) {
return prefix + localSource + suffix;
});
}
}
return EmailDAO;
});
module.exports = EmailDAO;

View File

@ -1,33 +1,32 @@
define(function() {
'use strict';
'use strict';
/**
/**
* The InvitationDAO 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) {
var InvitationDAO = function(restDao) {
this._restDao = restDao;
};
};
//
// Constants
//
//
// Constants
//
InvitationDAO.INVITE_MISSING = 1;
InvitationDAO.INVITE_PENDING = 2;
InvitationDAO.INVITE_SUCCESS = 4;
InvitationDAO.INVITE_MISSING = 1;
InvitationDAO.INVITE_PENDING = 2;
InvitationDAO.INVITE_SUCCESS = 4;
//
// API
//
//
// API
//
/**
/**
* Notes an invite for the recipient by the sender in the invitation web service
* @param {String} options.recipient User ID of the recipient
* @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) {
InvitationDAO.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!'
@ -56,7 +55,6 @@ define(function() {
errMsg: 'unexpected invitation state'
});
}
};
};
return InvitationDAO;
});
module.exports = InvitationDAO;

View File

@ -2,46 +2,45 @@
* A high-level Data-Access Api for handling Keypair synchronization
* between the cloud service and the device's local storage
*/
define(function(require) {
'use strict';
var _ = require('underscore'),
util = require('js/crypto/util'),
config = require('js/app-config').config;
'use strict';
var DB_PUBLICKEY = 'publickey',
var util = require('crypto-lib').util,
config = require('../app-config').config;
var DB_PUBLICKEY = 'publickey',
DB_PRIVATEKEY = 'privatekey',
DB_DEVICENAME = 'devicename',
DB_DEVICE_SECRET = 'devicesecret';
var KeychainDAO = function(localDbDao, publicKeyDao, privateKeyDao, crypto, pgp) {
var KeychainDAO = function(localDbDao, publicKeyDao, privateKeyDao, crypto, pgp) {
this._localDbDao = localDbDao;
this._publicKeyDao = publicKeyDao;
this._privateKeyDao = privateKeyDao;
this._crypto = crypto;
this._pgp = pgp;
};
};
//
// Public key functions
//
//
// Public key functions
//
/**
/**
* 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) {
KeychainDAO.prototype.verifyPublicKey = function(uuid, callback) {
this._publicKeyDao.verify(uuid, callback);
};
};
/**
/**
* Get an array of public keys by looking in local storage and
* fetching missing keys from the cloud service.
* @param ids [Array] the key ids as [{_id, userId}]
* @return [PublicKeyCollection] The requiested public keys
*/
KeychainDAO.prototype.getPublicKeys = function(ids, callback) {
KeychainDAO.prototype.getPublicKeys = function(ids, callback) {
var self = this,
after, already, pubkeys = [];
@ -78,14 +77,14 @@ define(function(require) {
after(); // asynchronously iterate through objects
});
});
};
};
/**
/**
* Checks for public key updates of a given user id
* @param {String} userId The user id (email address) for which to check the key
* @param {Function} callback(error, key) Invoked when the key has been updated or an error occurred
*/
KeychainDAO.prototype.refreshKeyForUserId = function(userId, callback) {
KeychainDAO.prototype.refreshKeyForUserId = function(userId, callback) {
var self = this;
// get the public key corresponding to the userId
@ -174,13 +173,13 @@ define(function(require) {
});
}
};
};
/**
/**
* Look up a reveiver's public key by user id
* @param userId [String] the receiver's email address
*/
KeychainDAO.prototype.getReceiverPublicKey = function(userId, callback) {
KeychainDAO.prototype.getReceiverPublicKey = function(userId, callback) {
var self = this;
// search local keyring for public key
@ -246,32 +245,32 @@ define(function(require) {
callback(null, cloudPubkey);
});
}
};
};
//
// Device registration functions
//
//
// Device registration functions
//
/**
/**
* Set the device's memorable name e.g 'iPhone Work'
* @param {String} deviceName The device name
* @param {Function} callback(error)
*/
KeychainDAO.prototype.setDeviceName = function(deviceName, callback) {
KeychainDAO.prototype.setDeviceName = function(deviceName, callback) {
if (!deviceName) {
callback(new Error('Please set a device name!'));
return;
}
this._localDbDao.persist(DB_DEVICENAME, deviceName, callback);
};
};
/**
/**
* Get the device' memorable name from local storage. Throws an error if not set
* @param {Function} callback(error, deviceName)
* @return {String} The device name
*/
KeychainDAO.prototype.getDeviceName = function(callback) {
KeychainDAO.prototype.getDeviceName = function(callback) {
// check if deviceName is already persisted in storage
this._localDbDao.read(DB_DEVICENAME, function(err, deviceName) {
if (err) {
@ -286,13 +285,13 @@ define(function(require) {
callback(null, deviceName);
});
};
};
/**
/**
* 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) {
KeychainDAO.prototype.getDeviceSecret = function(callback) {
var self = this;
// generate random deviceSecret or get from storage
@ -320,14 +319,14 @@ define(function(require) {
callback(null, deviceSecret);
});
});
};
};
/**
/**
* Register the device on the private key server. This will give the device access to upload an encrypted private key.
* @param {String} options.userId The user's email address
* @param {Function} callback(error)
*/
KeychainDAO.prototype.registerDevice = function(options, callback) {
KeychainDAO.prototype.registerDevice = function(options, callback) {
var self = this,
devName;
@ -414,19 +413,19 @@ define(function(require) {
});
});
}
};
};
//
// Private key functions
//
//
// Private key functions
//
/**
/**
* Authenticate to the private key server (required before private PGP key upload).
* @param {String} userId The user's email address
* @param {Function} callback(error, authSessionKey)
* @return {Object} {sessionId:String, sessionKey:[base64 encoded]}
*/
KeychainDAO.prototype._authenticateToPrivateKeyServer = function(userId, callback) {
KeychainDAO.prototype._authenticateToPrivateKeyServer = function(userId, callback) {
var self = this,
sessionId;
@ -535,15 +534,15 @@ define(function(require) {
});
});
}
};
};
/**
/**
* Encrypt and upload the private PGP key to the server.
* @param {String} options.userId The user's email address
* @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) {
KeychainDAO.prototype.uploadPrivateKey = function(options, callback) {
var self = this,
keySize = config.symKeySize,
salt;
@ -629,40 +628,40 @@ define(function(require) {
});
});
}
};
};
/**
/**
* Request downloading the user's encrypted private key. This will initiate the server to send the recovery token via email/sms to the user.
* @param {String} options.userId The user's email address
* @param {String} options.keyId The private PGP key id
* @param {Function} callback(error)
*/
KeychainDAO.prototype.requestPrivateKeyDownload = function(options, callback) {
KeychainDAO.prototype.requestPrivateKeyDownload = function(options, callback) {
this._privateKeyDao.requestDownload(options, callback);
};
};
/**
/**
* Query if an encrypted private PGP key exists on the server without initializing the recovery procedure
* @param {String} options.userId The user's email address
* @param {String} options.keyId The private PGP key id
* @param {Function} callback(error)
*/
KeychainDAO.prototype.hasPrivateKey = function(options, callback) {
KeychainDAO.prototype.hasPrivateKey = function(options, callback) {
this._privateKeyDao.hasPrivateKey(options, callback);
};
};
/**
/**
* Download the encrypted private PGP key from the server using the recovery token.
* @param {String} options.userId The user's email address
* @param {String} options.keyId The user's email address
* @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) {
KeychainDAO.prototype.downloadPrivateKey = function(options, callback) {
this._privateKeyDao.download(options, callback);
};
};
/**
/**
* This is called after the encrypted private key has successfully been downloaded and it's ready to be decrypted and stored in localstorage.
* @param {String} options._id The private PGP key id
* @param {String} options.userId The user's email address
@ -672,7 +671,7 @@ define(function(require) {
* @param {String} options.iv The iv used to encrypt the private PGP key
* @param {Function} callback(error, keyObject)
*/
KeychainDAO.prototype.decryptAndStorePrivateKeyLocally = function(options, callback) {
KeychainDAO.prototype.decryptAndStorePrivateKeyLocally = function(options, callback) {
var self = this,
code = options.code,
salt = options.salt,
@ -735,19 +734,19 @@ define(function(require) {
});
});
}
};
};
//
// Keypair functions
//
//
// Keypair functions
//
/**
/**
* Gets the local user's key either from local storage
* or fetches it from the cloud. The private key is encrypted.
* If no key pair exists, null is returned.
* return [Object] The user's key pair {publicKey, privateKey}
*/
KeychainDAO.prototype.getUserKeyPair = function(userId, callback) {
KeychainDAO.prototype.getUserKeyPair = function(userId, callback) {
var self = this;
// search for user's public key locally
@ -817,14 +816,14 @@ define(function(require) {
});
});
}
};
};
/**
/**
* Checks to see if the user's key pair is stored both
* locally and in the cloud and persist arccordingly
* @param [Object] The user's key pair {publicKey, privateKey}
*/
KeychainDAO.prototype.putUserKeyPair = function(keypair, callback) {
KeychainDAO.prototype.putUserKeyPair = function(keypair, callback) {
var self = this;
// validate input
@ -857,13 +856,13 @@ define(function(require) {
self.saveLocalPrivateKey(keypair.privateKey, callback);
});
});
};
};
//
// Helper functions
//
//
// Helper functions
//
KeychainDAO.prototype.lookupPublicKey = function(id, callback) {
KeychainDAO.prototype.lookupPublicKey = function(id, callback) {
var self = this;
if (!id) {
@ -903,36 +902,35 @@ define(function(require) {
});
});
});
};
};
/**
/**
* List all the locally stored public keys
*/
KeychainDAO.prototype.listLocalPublicKeys = function(callback) {
KeychainDAO.prototype.listLocalPublicKeys = function(callback) {
// search local keyring for public key
this._localDbDao.list(DB_PUBLICKEY, 0, null, callback);
};
};
KeychainDAO.prototype.removeLocalPublicKey = function(id, callback) {
KeychainDAO.prototype.removeLocalPublicKey = function(id, callback) {
this._localDbDao.remove(DB_PUBLICKEY + '_' + id, callback);
};
};
KeychainDAO.prototype.lookupPrivateKey = function(id, callback) {
KeychainDAO.prototype.lookupPrivateKey = function(id, callback) {
// lookup in local storage
this._localDbDao.read(DB_PRIVATEKEY + '_' + id, callback);
};
};
KeychainDAO.prototype.saveLocalPublicKey = function(pubkey, callback) {
KeychainDAO.prototype.saveLocalPublicKey = function(pubkey, callback) {
// persist public key (email, _id)
var pkLookupKey = DB_PUBLICKEY + '_' + pubkey._id;
this._localDbDao.persist(pkLookupKey, pubkey, callback);
};
};
KeychainDAO.prototype.saveLocalPrivateKey = function(privkey, callback) {
KeychainDAO.prototype.saveLocalPrivateKey = function(privkey, callback) {
// persist private key (email, _id)
var prkLookupKey = DB_PRIVATEKEY + '_' + privkey._id;
this._localDbDao.persist(prkLookupKey, privkey, callback);
};
};
return KeychainDAO;
});
module.exports = KeychainDAO;

View File

@ -1,17 +1,12 @@
/**
* Handles generic caching of JSON objects in a lawnchair adapter
*/
define(function(require) {
'use strict';
var _ = require('underscore'),
Lawnchair = require('lawnchair');
require('lawnchairSQL');
require('lawnchairIDB');
'use strict';
var LawnchairDAO = function() {};
var LawnchairDAO = function() {};
LawnchairDAO.prototype.init = function(dbName, callback) {
LawnchairDAO.prototype.init = function(dbName, callback) {
if (!dbName) {
callback({
errMsg: 'Lawnchair DB name must be specified!'
@ -31,12 +26,12 @@ define(function(require) {
callback();
});
};
};
/**
/**
* Create or update an object
*/
LawnchairDAO.prototype.persist = function(key, object, callback) {
LawnchairDAO.prototype.persist = function(key, object, callback) {
if (!key || !object) {
callback({
errMsg: 'Key and Object must be set!'
@ -57,12 +52,12 @@ define(function(require) {
callback();
});
};
};
/**
/**
* Persist a bunch of items at once
*/
LawnchairDAO.prototype.batch = function(list, callback) {
LawnchairDAO.prototype.batch = function(list, callback) {
if (!(list instanceof Array)) {
callback({
errMsg: 'Input must be of type Array!'
@ -80,12 +75,12 @@ define(function(require) {
callback();
});
};
};
/**
/**
* Read a single item by its key
*/
LawnchairDAO.prototype.read = function(key, callback) {
LawnchairDAO.prototype.read = function(key, callback) {
if (!key) {
callback({
errMsg: 'Key must be specified!'
@ -100,15 +95,15 @@ define(function(require) {
callback();
}
});
};
};
/**
/**
* List all the items of a certain type
* @param type [String] The type of item e.g. 'email'
* @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)
*/
LawnchairDAO.prototype.list = function(type, offset, num, callback) {
LawnchairDAO.prototype.list = function(type, offset, num, callback) {
var self = this,
i, from, to,
matchingKeys = [],
@ -164,19 +159,19 @@ define(function(require) {
});
});
};
};
/**
/**
* Removes an object liter from local storage by its key (delete)
*/
LawnchairDAO.prototype.remove = function(key, callback) {
LawnchairDAO.prototype.remove = function(key, callback) {
this._db.remove(key, callback);
};
};
/**
/**
* Removes an object liter from local storage by its key (delete)
*/
LawnchairDAO.prototype.removeList = function(type, callback) {
LawnchairDAO.prototype.removeList = function(type, callback) {
var self = this,
matchingKeys = [],
after;
@ -209,14 +204,13 @@ define(function(require) {
self._db.remove(key, after);
});
});
};
};
/**
/**
* Clears the whole local storage cache
*/
LawnchairDAO.prototype.clear = function(callback) {
LawnchairDAO.prototype.clear = function(callback) {
this._db.nuke(callback);
};
};
return LawnchairDAO;
});
module.exports = LawnchairDAO;

View File

@ -1,22 +1,21 @@
define(function() {
'use strict';
'use strict';
var PrivateKeyDAO = function(restDao) {
var PrivateKeyDAO = function(restDao) {
this._restDao = restDao;
};
};
//
// Device registration functions
//
//
// Device registration functions
//
/**
/**
* Request registration of a new device by fetching registration session key.
* @param {String} options.userId The user's email address
* @param {String} options.deviceName The device's memorable name
* @param {Function} callback(error, regSessionKey)
* @return {Object} {encryptedRegSessionKey:[base64]}
*/
PrivateKeyDAO.prototype.requestDeviceRegistration = function(options, callback) {
PrivateKeyDAO.prototype.requestDeviceRegistration = function(options, callback) {
var uri;
if (!options.userId || !options.deviceName) {
@ -26,9 +25,9 @@ define(function() {
uri = '/device/user/' + options.userId + '/devicename/' + options.deviceName;
this._restDao.post(undefined, uri, callback);
};
};
/**
/**
* Authenticate device registration by uploading the deviceSecret encrypted with the regSessionKeys.
* @param {String} options.userId The user's email address
* @param {String} options.deviceName The device's memorable name
@ -36,7 +35,7 @@ define(function() {
* @param {String} options.iv The iv used for encryption
* @param {Function} callback(error)
*/
PrivateKeyDAO.prototype.uploadDeviceSecret = function(options, callback) {
PrivateKeyDAO.prototype.uploadDeviceSecret = function(options, callback) {
var uri;
if (!options.userId || !options.deviceName || !options.encryptedDeviceSecret || !options.iv) {
@ -46,19 +45,19 @@ define(function() {
uri = '/device/user/' + options.userId + '/devicename/' + options.deviceName;
this._restDao.put(options, uri, callback);
};
};
//
// Private key functions
//
//
// Private key functions
//
/**
/**
* Request authSessionKeys required for upload the encrypted private PGP key.
* @param {String} options.userId The user's email address
* @param {Function} callback(error, authSessionKey)
* @return {Object} {sessionId, encryptedAuthSessionKey:[base64 encoded], encryptedChallenge:[base64 encoded]}
*/
PrivateKeyDAO.prototype.requestAuthSessionKey = function(options, callback) {
PrivateKeyDAO.prototype.requestAuthSessionKey = function(options, callback) {
var uri;
if (!options.userId) {
@ -68,9 +67,9 @@ define(function() {
uri = '/auth/user/' + options.userId;
this._restDao.post(undefined, uri, callback);
};
};
/**
/**
* Verifiy authentication by uploading the challenge and deviceSecret encrypted with the authSessionKeys as a response.
* @param {String} options.userId The user's email address
* @param {String} options.encryptedChallenge The server's base64 encoded challenge encrypted using the authSessionKey
@ -78,7 +77,7 @@ define(function() {
* @param {String} options.iv The iv used for encryption
* @param {Function} callback(error)
*/
PrivateKeyDAO.prototype.verifyAuthentication = function(options, callback) {
PrivateKeyDAO.prototype.verifyAuthentication = function(options, callback) {
var uri;
if (!options.userId || !options.sessionId || !options.encryptedChallenge || !options.encryptedDeviceSecret || !options.iv) {
@ -88,9 +87,9 @@ define(function() {
uri = '/auth/user/' + options.userId + '/session/' + options.sessionId;
this._restDao.put(options, uri, callback);
};
};
/**
/**
* Upload the encrypted private PGP key.
* @param {String} options._id The hex encoded capital 16 char key id
* @param {String} options.userId The user's email address
@ -98,7 +97,7 @@ define(function() {
* @param {String} options.sessionId The session id
* @param {Function} callback(error)
*/
PrivateKeyDAO.prototype.upload = function(options, callback) {
PrivateKeyDAO.prototype.upload = function(options, callback) {
var uri;
if (!options._id || !options.userId || !options.encryptedPrivateKey || !options.sessionId || !options.salt || !options.iv) {
@ -108,16 +107,16 @@ define(function() {
uri = '/privatekey/user/' + options.userId + '/session/' + options.sessionId;
this._restDao.post(options, uri, callback);
};
};
/**
/**
* Query if an encrypted private PGP key exists on the server without initializing the recovery procedure.
* @param {String} options.userId The user's email address
* @param {String} options.keyId The private PGP key id
* @param {Function} callback(error, found)
* @return {Boolean} whether the key was found on the server or not.
*/
PrivateKeyDAO.prototype.hasPrivateKey = function(options, callback) {
PrivateKeyDAO.prototype.hasPrivateKey = function(options, callback) {
if (!options.userId || !options.keyId) {
callback(new Error('Incomplete arguments!'));
return;
@ -139,16 +138,16 @@ define(function() {
callback(null, true);
});
};
};
/**
/**
* Request download for the encrypted private PGP key.
* @param {String} options.userId The user's email address
* @param {String} options.keyId The private PGP key id
* @param {Function} callback(error, found)
* @return {Boolean} whether the key was found on the server or not.
*/
PrivateKeyDAO.prototype.requestDownload = function(options, callback) {
PrivateKeyDAO.prototype.requestDownload = function(options, callback) {
if (!options.userId || !options.keyId) {
callback(new Error('Incomplete arguments!'));
return;
@ -170,9 +169,9 @@ define(function() {
callback(null, true);
});
};
};
/**
/**
* Verify the download request for the private PGP key using the recovery token sent via email. This downloads the actual encrypted private key.
* @param {String} options.userId The user's email address
* @param {String} options.keyId The private key id
@ -180,7 +179,7 @@ define(function() {
* @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) {
PrivateKeyDAO.prototype.download = function(options, callback) {
var uri;
if (!options.userId || !options.keyId || !options.recoveryToken) {
@ -192,7 +191,6 @@ define(function() {
this._restDao.get({
uri: uri
}, callback);
};
};
return PrivateKeyDAO;
});
module.exports = PrivateKeyDAO;

View File

@ -1,14 +1,13 @@
define(function() {
'use strict';
'use strict';
var PublicKeyDAO = function(restDao) {
var PublicKeyDAO = function(restDao) {
this._restDao = restDao;
};
};
/**
/**
* Verify the public key behind the given uuid
*/
PublicKeyDAO.prototype.verify = function(uuid, callback) {
PublicKeyDAO.prototype.verify = function(uuid, callback) {
var uri = '/verify/' + uuid;
this._restDao.get({
@ -23,12 +22,12 @@ define(function() {
callback(err, res, status);
});
};
};
/**
/**
* Find the user's corresponding public key
*/
PublicKeyDAO.prototype.get = function(keyId, callback) {
PublicKeyDAO.prototype.get = function(keyId, callback) {
var uri = '/publickey/key/' + keyId;
this._restDao.get({
@ -46,12 +45,12 @@ define(function() {
callback(null, (key && key._id) ? key : undefined);
});
};
};
/**
/**
* Find the user's corresponding public key by email
*/
PublicKeyDAO.prototype.getByUserId = function(userId, callback) {
PublicKeyDAO.prototype.getByUserId = function(userId, callback) {
var uri = '/publickey/user/' + userId;
this._restDao.get({
@ -83,23 +82,22 @@ define(function() {
callback(null, keys[0]);
});
};
};
/**
/**
* Persist the user's publc key
*/
PublicKeyDAO.prototype.put = function(pubkey, callback) {
PublicKeyDAO.prototype.put = function(pubkey, callback) {
var uri = '/publickey/user/' + pubkey.userId + '/key/' + pubkey._id;
this._restDao.put(pubkey, uri, callback);
};
};
/**
/**
* Delete the public key from the cloud storage service
*/
PublicKeyDAO.prototype.remove = function(keyId, callback) {
PublicKeyDAO.prototype.remove = function(keyId, callback) {
var uri = '/publickey/key/' + keyId;
this._restDao.remove(uri, callback);
};
};
return PublicKeyDAO;
});
module.exports = PublicKeyDAO;

View File

@ -1,63 +1,62 @@
define(function(require) {
'use strict';
'use strict';
var config = require('js/app-config').config;
var config = require('../app-config').config;
var RestDAO = function(baseUri) {
var RestDAO = function(baseUri) {
if (baseUri) {
this._baseUri = baseUri;
} else {
this._baseUri = config.cloudUrl;
}
};
};
/**
/**
* GET (read) request
* @param {String} options.uri URI relative to the base uri to perform the GET request with.
* @param {String} options.type (optional) The type of data that you're expecting back from the server: json, xml, text. Default: json.
*/
RestDAO.prototype.get = function(options, callback) {
RestDAO.prototype.get = function(options, callback) {
options.method = 'GET';
this._processRequest(options, callback);
};
};
/**
/**
* POST (create) request
*/
RestDAO.prototype.post = function(item, uri, callback) {
RestDAO.prototype.post = function(item, uri, callback) {
this._processRequest({
method: 'POST',
payload: item,
uri: uri
}, callback);
};
};
/**
/**
* PUT (update) request
*/
RestDAO.prototype.put = function(item, uri, callback) {
RestDAO.prototype.put = function(item, uri, callback) {
this._processRequest({
method: 'PUT',
payload: item,
uri: uri
}, callback);
};
};
/**
/**
* DELETE (remove) request
*/
RestDAO.prototype.remove = function(uri, callback) {
RestDAO.prototype.remove = function(uri, callback) {
this._processRequest({
method: 'DELETE',
uri: uri
}, callback);
};
};
//
// helper functions
//
//
// helper functions
//
RestDAO.prototype._processRequest = function(options, callback) {
RestDAO.prototype._processRequest = function(options, callback) {
var xhr, format;
if (typeof options.uri === 'undefined') {
@ -117,7 +116,6 @@ define(function(require) {
};
xhr.send(options.payload ? JSON.stringify(options.payload) : undefined);
};
};
return RestDAO;
});
module.exports = RestDAO;

View File

@ -1,10 +1,9 @@
define(function(require) {
'use strict';
'use strict';
var axe = require('axe'),
var axe = require('axe-logger'),
DEBUG_TAG = 'backbutton handler';
/**
/**
* The back button handler introduces meaningful behavior fo rthe back button:
* if there's an open lightbox, close it;
* if the reader is open in mobile mode, close it;
@ -13,7 +12,7 @@ define(function(require) {
*
* @type {Object}
*/
var backBtnHandler = {
var backBtnHandler = {
attachHandler: function(scope) {
this.scope = scope;
},
@ -23,9 +22,9 @@ define(function(require) {
stop: function() {
document.removeEventListener("backbutton", handleBackButton, false);
}
};
};
function handleBackButton(event) {
function handleBackButton(event) {
axe.debug(DEBUG_TAG, 'back button pressed');
// this disarms the default behavior which we NEVER want
@ -51,7 +50,6 @@ define(function(require) {
// exits the app
navigator.app.exitApp();
}
}
}
return backBtnHandler;
});
module.exports = backBtnHandler;

View File

@ -1,41 +1,42 @@
define(function(require) {
'use strict';
'use strict';
var TCPSocket = require('tcp-socket'),
appConfig = require('js/app-config'),
var TCPSocket = require('tcp-socket'),
appConfig = require('../app-config'),
cfg = appConfig.config,
strings = appConfig.string,
ImapClient = require('imap-client'),
SmtpClient = require('smtpclient');
SmtpClient = require('wo-smtpclient');
/**
/**
* The connection doctor can check your connection. In essence, it reconstructs what happens when
* the app goes online in an abbreviated way. You need to configure() the instance with the IMAP/SMTP
* credentials before running check()!
*
* @constructor
*/
var ConnectionDoctor = function() {};
var ConnectionDoctor = function() {};
//
// Error codes
//
//
// Error codes
//
var OFFLINE = ConnectionDoctor.OFFLINE = 42;
var TLS_WRONG_CERT = ConnectionDoctor.TLS_WRONG_CERT = 43;
var HOST_UNREACHABLE = ConnectionDoctor.HOST_UNREACHABLE = 44;
var HOST_TIMEOUT = ConnectionDoctor.HOST_TIMEOUT = 45;
var AUTH_REJECTED = ConnectionDoctor.AUTH_REJECTED = 46;
var NO_INBOX = ConnectionDoctor.NO_INBOX = 47;
var GENERIC_ERROR = ConnectionDoctor.GENERIC_ERROR = 48;
var OFFLINE = ConnectionDoctor.OFFLINE = 42;
var TLS_WRONG_CERT = ConnectionDoctor.TLS_WRONG_CERT = 43;
var HOST_UNREACHABLE = ConnectionDoctor.HOST_UNREACHABLE = 44;
var HOST_TIMEOUT = ConnectionDoctor.HOST_TIMEOUT = 45;
var AUTH_REJECTED = ConnectionDoctor.AUTH_REJECTED = 46;
var NO_INBOX = ConnectionDoctor.NO_INBOX = 47;
var GENERIC_ERROR = ConnectionDoctor.GENERIC_ERROR = 48;
//
// Public API
//
var WORKER_PATH = cfg.workerPath + '/tcp-socket-tls-worker.min.js';
/**
//
// Public API
//
/**
* Configures the connection doctor
*
* @param {Object} credentials.imap IMAP configuration (host:string, port:number, secure:boolean, ignoreTLS:boolean)
@ -43,7 +44,7 @@ define(function(require) {
* @param {String} credentials.username
* @param {String} credentials.password
*/
ConnectionDoctor.prototype.configure = function(credentials) {
ConnectionDoctor.prototype.configure = function(credentials) {
this.credentials = credentials;
// internal members
@ -53,6 +54,7 @@ define(function(require) {
secure: this.credentials.imap.secure,
ignoreTLS: this.credentials.imap.ignoreTLS,
ca: this.credentials.imap.ca,
tlsWorkerPath: WORKER_PATH,
auth: {
user: this.credentials.username,
pass: this.credentials.password,
@ -64,15 +66,16 @@ define(function(require) {
useSecureTransport: this.credentials.smtp.secure,
ignoreTLS: this.credentials.smtp.ignoreTLS,
ca: this.credentials.smtp.ca,
tlsWorkerPath: WORKER_PATH,
auth: {
user: this.credentials.username,
pass: this.credentials.password,
xoauth2: this.credentials.xoauth2
}
});
};
};
/**
/**
* It conducts the following tests for IMAP and SMTP, respectively:
* 1) Check if browser is online
* 2) Connect to host:port via TCP/TLS
@ -82,7 +85,7 @@ define(function(require) {
*
* @param {Function} callback(error) Invoked when the test suite passed, or with an error object if something went wrong
*/
ConnectionDoctor.prototype.check = function(callback) {
ConnectionDoctor.prototype.check = function(callback) {
var self = this;
if (!self.credentials) {
@ -114,27 +117,27 @@ define(function(require) {
});
});
});
};
};
//
// Internal API
//
//
// Internal API
//
/**
/**
* Checks if the browser is online
*
* @param {Function} callback(error) Invoked when the test suite passed, or with an error object if browser is offline
*/
ConnectionDoctor.prototype._checkOnline = function(callback) {
ConnectionDoctor.prototype._checkOnline = function(callback) {
if (navigator.onLine) {
callback();
} else {
callback(createError(OFFLINE, strings.connDocOffline));
}
};
};
/**
/**
* Checks if a host is reachable via TCP
*
* @param {String} options.host
@ -142,7 +145,7 @@ define(function(require) {
* @param {Boolean} options.secure
* @param {Function} callback(error) Invoked when the test suite passed, or with an error object if something went wrong
*/
ConnectionDoctor.prototype._checkReachable = function(options, callback) {
ConnectionDoctor.prototype._checkReachable = function(options, callback) {
var socket,
error, // remember the error message
timeout, // remember the timeout object
@ -157,7 +160,8 @@ define(function(require) {
socket = TCPSocket.open(options.host, options.port, {
binaryType: 'arraybuffer',
useSecureTransport: options.secure,
ca: options.ca
ca: options.ca,
tlsWorkerPath: WORKER_PATH
});
socket.ondata = function() {}; // we don't actually care about the data
@ -191,15 +195,15 @@ define(function(require) {
callback(error);
}
};
};
};
/**
/**
* Checks if an IMAP server is reachable, accepts the credentials, can list folders and has an inbox and logs out.
* Adds the certificate to the IMAP settings if not provided.
*
* @param {Function} callback(error) Invoked when the test suite passed, or with an error object if something went wrong
*/
ConnectionDoctor.prototype._checkImap = function(callback) {
ConnectionDoctor.prototype._checkImap = function(callback) {
var self = this,
loggedIn = false,
host = self.credentials.imap.host + ':' + self.credentials.imap.port;
@ -239,15 +243,15 @@ define(function(require) {
});
});
});
};
};
/**
/**
* Checks if an SMTP server is reachable and accepts the credentials and logs out.
* Adds the certificate to the SMTP settings if not provided.
*
* @param {Function} callback(error) Invoked when the test suite passed, or with an error object if something went wrong
*/
ConnectionDoctor.prototype._checkSmtp = 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
@ -276,20 +280,19 @@ define(function(require) {
};
self._smtp.connect();
};
};
//
// Helper Functions
//
//
// Helper Functions
//
function createError(code, message, underlyingError) {
function createError(code, message, underlyingError) {
var error = new Error(message);
error.code = code;
error.underlyingError = underlyingError;
return error;
}
}
return ConnectionDoctor;
});
module.exports = ConnectionDoctor;

View File

@ -1,11 +1,10 @@
define(function(require) {
'use strict';
'use strict';
var util = require('js/crypto/util');
var util = require('crypto-lib').util;
var dl = {};
var dl = {};
dl.createDownload = function(options) {
dl.createDownload = function(options) {
var contentType = options.contentType || 'application/octet-stream';
var filename = options.filename || 'file';
var content = options.content;
@ -54,7 +53,6 @@ define(function(require) {
}
window.open('data:' + contentType + ';base64,' + btoa(content), "_blank");
}
};
};
return dl;
});
module.exports = dl;

View File

@ -1,10 +1,9 @@
define(function(require) {
'use strict';
'use strict';
var axe = require('axe');
var axe = require('axe-logger');
var er = {};
er.attachHandler = function(scope) {
var er = {};
er.attachHandler = function(scope) {
scope.onError = function(options) {
if (!options) {
scope.$apply();
@ -29,7 +28,6 @@ define(function(require) {
scope.$apply();
}
};
};
};
return er;
});
module.exports = er;

View File

@ -1,15 +1,14 @@
define(function(require) {
'use strict';
'use strict';
var cfg = require('js/app-config').config;
var cfg = require('../app-config').config;
var self = {};
var notif = {};
if (window.Notification) {
self.hasPermission = Notification.permission === "granted";
}
if (window.Notification) {
notif.hasPermission = Notification.permission === "granted";
}
/**
/**
* Creates a notification. Requests permission if not already granted
*
* @param {String} options.title The notification title
@ -18,18 +17,18 @@ define(function(require) {
* @param {Function} options.onClick (optional) callback when the notification is clicked
* @returns {Notification} A notification instance
*/
self.create = function(options) {
notif.create = function(options) {
options.onClick = options.onClick || function() {};
if (!window.Notification) {
return;
}
if (!self.hasPermission) {
if (!notif.hasPermission) {
// don't wait until callback returns
Notification.requestPermission(function(permission) {
if (permission === "granted") {
self.hasPermission = true;
notif.hasPermission = true;
}
});
}
@ -50,11 +49,10 @@ define(function(require) {
}
return notification;
};
};
self.close = function(notification) {
notif.close = function(notification) {
notification.close();
};
};
return self;
});
module.exports = notif;

View File

@ -1,23 +1,22 @@
define(function() {
'use strict';
'use strict';
var OAuth = function(googleApi) {
var OAuth = function(googleApi) {
this._googleApi = googleApi;
};
};
/**
/**
* Check if chrome.identity api is supported
* @return {Boolean} If is supported
*/
OAuth.prototype.isSupported = function() {
OAuth.prototype.isSupported = function() {
return !!(window.chrome && chrome.identity);
};
};
/**
/**
* Request an OAuth token from chrome for gmail users
* @param {String} emailAddress The user's email address (optional)
*/
OAuth.prototype.getOAuthToken = function(emailAddress, callback) {
OAuth.prototype.getOAuthToken = function(emailAddress, callback) {
var idOptions = {
interactive: true
};
@ -46,14 +45,14 @@ define(function() {
callback(null, token);
});
});
};
};
/**
/**
* Remove an old OAuth token and get a new one.
* @param {String} options.oldToken The old token to be removed
* @param {String} options.emailAddress The user's email address (optional)
*/
OAuth.prototype.refreshToken = function(options, callback) {
OAuth.prototype.refreshToken = function(options, callback) {
var self = this;
if (!options.oldToken) {
@ -68,13 +67,13 @@ define(function() {
// get a new token
self.getOAuthToken(options.emailAddress, callback);
});
};
};
/**
/**
* Get email address from google api
* @param {String} token The oauth token
*/
OAuth.prototype.queryEmailAddress = function(token, callback) {
OAuth.prototype.queryEmailAddress = function(token, callback) {
if (!token) {
callback({
errMsg: 'Invalid OAuth token!'
@ -95,7 +94,6 @@ define(function() {
callback(null, info.email);
});
};
};
return OAuth;
});
module.exports = OAuth;

View File

@ -1,29 +1,28 @@
define(function(require) {
'use strict';
'use strict';
var axe = require('axe'),
cfg = require('js/app-config').config,
updateV1 = require('js/util/update/update-v1'),
updateV2 = require('js/util/update/update-v2'),
updateV3 = require('js/util/update/update-v3'),
updateV4 = require('js/util/update/update-v4'),
updateV5 = require('js/util/update/update-v5');
var axe = require('axe-logger'),
cfg = require('../../app-config').config,
updateV1 = require('./update-v1'),
updateV2 = require('./update-v2'),
updateV3 = require('./update-v3'),
updateV4 = require('./update-v4'),
updateV5 = require('./update-v5');
/**
/**
* Handles database migration
*/
var UpdateHandler = function(appConfigStorage, userStorage, auth) {
var UpdateHandler = function(appConfigStorage, userStorage, auth) {
this._appConfigStorage = appConfigStorage;
this._userStorage = userStorage;
this._updateScripts = [updateV1, updateV2, updateV3, updateV4, updateV5];
this._auth = auth;
};
};
/**
/**
* Executes all the necessary updates
* @param {Function} callback(error) Invoked when all the database updates were executed, or if an error occurred
*/
UpdateHandler.prototype.update = function(callback) {
UpdateHandler.prototype.update = function(callback) {
var self = this,
currentVersion = 0,
targetVersion = cfg.dbVersion,
@ -45,12 +44,12 @@ define(function(require) {
targetVersion: targetVersion
}, callback);
});
};
};
/**
/**
* Schedules necessary updates and executes thom in order
*/
UpdateHandler.prototype._applyUpdate = function(options, callback) {
UpdateHandler.prototype._applyUpdate = function(options, callback) {
var self = this,
scriptOptions,
queue = [];
@ -91,12 +90,12 @@ define(function(require) {
}
executeNextUpdate();
};
};
/**
/**
* Check application version and update correspondingly
*/
UpdateHandler.prototype.checkForUpdate = function(dialog) {
UpdateHandler.prototype.checkForUpdate = function(dialog) {
// Chrome Packaged App
if (typeof window.chrome !== 'undefined' && chrome.runtime && chrome.runtime.onUpdateAvailable) {
// check for Chrome app update and restart
@ -126,7 +125,6 @@ define(function(require) {
}
});
}
};
};
return UpdateHandler;
});
module.exports = UpdateHandler;

View File

@ -1,14 +1,13 @@
define(function() {
'use strict';
'use strict';
/**
/**
* Update handler for transition database version 0 -> 1
*
* In database version 1, the stored email objects have to be purged, otherwise
* every non-prefixed mail in the IMAP folders would be nuked due to the implementation
* of the delta sync.
*/
function updateV1(options, callback) {
function updateV1(options, callback) {
var emailDbType = 'email_',
versionDbType = 'dbVersion',
postUpdateDbVersion = 1;
@ -23,7 +22,6 @@ define(function() {
// update the database version to postUpdateDbVersion
options.appConfigStorage.storeList([postUpdateDbVersion], versionDbType, callback);
});
}
}
return updateV1;
});
module.exports = updateV1;

View File

@ -1,13 +1,12 @@
define(function() {
'use strict';
'use strict';
/**
/**
* Update handler for transition database version 1 -> 2
*
* In database version 2, the stored email objects have to be purged, because the
* new data model stores information about the email structure in the property 'bodyParts'.
*/
function updateV2(options, callback) {
function updateV2(options, callback) {
var emailDbType = 'email_',
versionDbType = 'dbVersion',
postUpdateDbVersion = 2;
@ -22,7 +21,6 @@ define(function() {
// update the database version to postUpdateDbVersion
options.appConfigStorage.storeList([postUpdateDbVersion], versionDbType, callback);
});
}
}
return updateV2;
});
module.exports = updateV2;

View File

@ -1,13 +1,12 @@
define(function() {
'use strict';
'use strict';
/**
/**
* Update handler for transition database version 2 -> 3
*
* In database version 3, we introduced new flags to the messages, also
* the outbox uses artificial uids
*/
function update(options, callback) {
function update(options, callback) {
var emailDbType = 'email_',
versionDbType = 'dbVersion',
postUpdateDbVersion = 3;
@ -22,7 +21,6 @@ define(function() {
// update the database version to postUpdateDbVersion
options.appConfigStorage.storeList([postUpdateDbVersion], versionDbType, callback);
});
}
}
return update;
});
module.exports = update;

View File

@ -1,17 +1,16 @@
define(function(require) {
'use strict';
'use strict';
var config = require('js/app-config').config;
var config = require('../../app-config').config;
/**
/**
* Update handler for transition database version 3 -> 4
*
* In database version 4, we need to add a "provider" flag to the
* indexeddb. only gmail was allowed as a mail service provider before,
* so let's add this...
*/
function update(options, callback) {
function update(options, callback) {
var VERSION_DB_TYPE = 'dbVersion',
EMAIL_ADDR_DB_KEY = 'emailaddress',
USERNAME_DB_KEY = 'username',
@ -99,7 +98,6 @@ define(function(require) {
callback(err, (!err && cachedItems && cachedItems[0]));
});
}
}
}
return update;
});
module.exports = update;

View File

@ -1,23 +1,22 @@
define(function() {
'use strict';
'use strict';
var FOLDER_TYPE_INBOX = 'Inbox';
var FOLDER_TYPE_SENT = 'Sent';
var FOLDER_TYPE_DRAFTS = 'Drafts';
var FOLDER_TYPE_TRASH = 'Trash';
var FOLDER_TYPE_INBOX = 'Inbox';
var FOLDER_TYPE_SENT = 'Sent';
var FOLDER_TYPE_DRAFTS = 'Drafts';
var FOLDER_TYPE_TRASH = 'Trash';
var FOLDER_DB_TYPE = 'folders';
var VERSION_DB_TYPE = 'dbVersion';
var FOLDER_DB_TYPE = 'folders';
var VERSION_DB_TYPE = 'dbVersion';
var POST_UPDATE_DB_VERSION = 5;
var POST_UPDATE_DB_VERSION = 5;
/**
/**
* Update handler for transition database version 4 -> 5
*
* Due to an overlooked issue, there may be multiple folders, e.g. for sent mails.
* This removes the "duplicate" folders.
*/
function update(options, callback) {
function update(options, callback) {
// remove the emails
options.userStorage.listItems(FOLDER_DB_TYPE, 0, null, function(err, stored) {
@ -50,7 +49,6 @@ define(function() {
options.appConfigStorage.storeList([POST_UPDATE_DB_VERSION], VERSION_DB_TYPE, callback);
});
});
}
}
return update;
});
module.exports = update;

File diff suppressed because one or more lines are too long

View File

@ -1,245 +0,0 @@
// uuid.js
//
// (c) 2010-2012 Robert Kieffer
// MIT License
// https://github.com/broofa/node-uuid
(function() {
var _global = this;
// Unique ID creation requires a high quality random # generator. We feature
// detect to determine the best RNG source, normalizing to a function that
// returns 128-bits of randomness, since that's what's usually required
var _rng;
// Node.js crypto-based RNG - http://nodejs.org/docs/v0.6.2/api/crypto.html
//
// Moderately fast, high quality
if (typeof(require) == 'function') {
try {
var _rb = require('crypto').randomBytes;
_rng = _rb && function() {return _rb(16);};
} catch(e) {}
}
if (!_rng && _global.crypto && crypto.getRandomValues) {
// WHATWG crypto-based RNG - http://wiki.whatwg.org/wiki/Crypto
//
// Moderately fast, high quality
var _rnds8 = new Uint8Array(16);
_rng = function whatwgRNG() {
crypto.getRandomValues(_rnds8);
return _rnds8;
};
}
if (!_rng) {
// Math.random()-based (RNG)
//
// If all else fails, use Math.random(). It's fast, but is of unspecified
// quality.
var _rnds = new Array(16);
_rng = function() {
for (var i = 0, r; i < 16; i++) {
if ((i & 0x03) === 0) r = Math.random() * 0x100000000;
_rnds[i] = r >>> ((i & 0x03) << 3) & 0xff;
}
return _rnds;
};
}
// Buffer class to use
var BufferClass = typeof(Buffer) == 'function' ? Buffer : Array;
// Maps for number <-> hex string conversion
var _byteToHex = [];
var _hexToByte = {};
for (var i = 0; i < 256; i++) {
_byteToHex[i] = (i + 0x100).toString(16).substr(1);
_hexToByte[_byteToHex[i]] = i;
}
// **`parse()` - Parse a UUID into it's component bytes**
function parse(s, buf, offset) {
var i = (buf && offset) || 0, ii = 0;
buf = buf || [];
s.toLowerCase().replace(/[0-9a-f]{2}/g, function(oct) {
if (ii < 16) { // Don't overflow!
buf[i + ii++] = _hexToByte[oct];
}
});
// Zero out remaining bytes if string was short
while (ii < 16) {
buf[i + ii++] = 0;
}
return buf;
}
// **`unparse()` - Convert UUID byte array (ala parse()) into a string**
function unparse(buf, offset) {
var i = offset || 0, bth = _byteToHex;
return bth[buf[i++]] + bth[buf[i++]] +
bth[buf[i++]] + bth[buf[i++]] + '-' +
bth[buf[i++]] + bth[buf[i++]] + '-' +
bth[buf[i++]] + bth[buf[i++]] + '-' +
bth[buf[i++]] + bth[buf[i++]] + '-' +
bth[buf[i++]] + bth[buf[i++]] +
bth[buf[i++]] + bth[buf[i++]] +
bth[buf[i++]] + bth[buf[i++]];
}
// **`v1()` - Generate time-based UUID**
//
// Inspired by https://github.com/LiosK/UUID.js
// and http://docs.python.org/library/uuid.html
// random #'s we need to init node and clockseq
var _seedBytes = _rng();
// Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1)
var _nodeId = [
_seedBytes[0] | 0x01,
_seedBytes[1], _seedBytes[2], _seedBytes[3], _seedBytes[4], _seedBytes[5]
];
// Per 4.2.2, randomize (14 bit) clockseq
var _clockseq = (_seedBytes[6] << 8 | _seedBytes[7]) & 0x3fff;
// Previous uuid creation time
var _lastMSecs = 0, _lastNSecs = 0;
// See https://github.com/broofa/node-uuid for API details
function v1(options, buf, offset) {
var i = buf && offset || 0;
var b = buf || [];
options = options || {};
var clockseq = options.clockseq != null ? options.clockseq : _clockseq;
// UUID timestamps are 100 nano-second units since the Gregorian epoch,
// (1582-10-15 00:00). JSNumbers aren't precise enough for this, so
// time is handled internally as 'msecs' (integer milliseconds) and 'nsecs'
// (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00.
var msecs = options.msecs != null ? options.msecs : new Date().getTime();
// Per 4.2.1.2, use count of uuid's generated during the current clock
// cycle to simulate higher resolution clock
var nsecs = options.nsecs != null ? options.nsecs : _lastNSecs + 1;
// Time since last uuid creation (in msecs)
var dt = (msecs - _lastMSecs) + (nsecs - _lastNSecs)/10000;
// Per 4.2.1.2, Bump clockseq on clock regression
if (dt < 0 && options.clockseq == null) {
clockseq = clockseq + 1 & 0x3fff;
}
// Reset nsecs if clock regresses (new clockseq) or we've moved onto a new
// time interval
if ((dt < 0 || msecs > _lastMSecs) && options.nsecs == null) {
nsecs = 0;
}
// Per 4.2.1.2 Throw error if too many uuids are requested
if (nsecs >= 10000) {
throw new Error('uuid.v1(): Can\'t create more than 10M uuids/sec');
}
_lastMSecs = msecs;
_lastNSecs = nsecs;
_clockseq = clockseq;
// Per 4.1.4 - Convert from unix epoch to Gregorian epoch
msecs += 12219292800000;
// `time_low`
var tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000;
b[i++] = tl >>> 24 & 0xff;
b[i++] = tl >>> 16 & 0xff;
b[i++] = tl >>> 8 & 0xff;
b[i++] = tl & 0xff;
// `time_mid`
var tmh = (msecs / 0x100000000 * 10000) & 0xfffffff;
b[i++] = tmh >>> 8 & 0xff;
b[i++] = tmh & 0xff;
// `time_high_and_version`
b[i++] = tmh >>> 24 & 0xf | 0x10; // include version
b[i++] = tmh >>> 16 & 0xff;
// `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant)
b[i++] = clockseq >>> 8 | 0x80;
// `clock_seq_low`
b[i++] = clockseq & 0xff;
// `node`
var node = options.node || _nodeId;
for (var n = 0; n < 6; n++) {
b[i + n] = node[n];
}
return buf ? buf : unparse(b);
}
// **`v4()` - Generate random UUID**
// See https://github.com/broofa/node-uuid for API details
function v4(options, buf, offset) {
// Deprecated - 'format' argument, as supported in v1.2
var i = buf && offset || 0;
if (typeof(options) == 'string') {
buf = options == 'binary' ? new BufferClass(16) : null;
options = null;
}
options = options || {};
var rnds = options.random || (options.rng || _rng)();
// Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
rnds[6] = (rnds[6] & 0x0f) | 0x40;
rnds[8] = (rnds[8] & 0x3f) | 0x80;
// Copy bytes to buffer, if provided
if (buf) {
for (var ii = 0; ii < 16; ii++) {
buf[i + ii] = rnds[ii];
}
}
return buf || unparse(rnds);
}
// Export public API
var uuid = v4;
uuid.v1 = v1;
uuid.v4 = v4;
uuid.parse = parse;
uuid.unparse = unparse;
uuid.BufferClass = BufferClass;
if (_global.define && define.amd) {
// Publish as AMD module
define(function() {return uuid;});
} else if (typeof(module) != 'undefined' && module.exports) {
// Publish as node.js module
module.exports = uuid;
} else {
// Publish as global (in browsers)
var _previousRoot = _global.uuid;
// **`noConflict()` - (browser only) to reset global 'uuid' var**
uuid.noConflict = function() {
_global.uuid = _previousRoot;
return uuid;
};
_global.uuid = uuid;
}
}());

View File

@ -1,10 +0,0 @@
{
"name": "Whiteout Mail",
"version": "0.0.1",
"main": "index.html",
"window": {
"toolbar": false,
"width": 1024,
"height": 768
}
}

View File

@ -1,68 +0,0 @@
(function() {
'use strict';
requirejs.config({
nodeRequire: (typeof module !== 'undefined' && module.exports) ? require : undefined,
baseUrl: 'lib',
paths: {
js: '../js',
test: '../../test',
jquery: 'jquery.min',
underscore: 'underscore/underscore-min',
lawnchair: 'lawnchair/lawnchair-git',
lawnchairSQL: 'lawnchair/lawnchair-adapter-webkit-sqlite-git',
lawnchairIDB: 'lawnchair/lawnchair-adapter-indexed-db-git',
angular: 'angular/angular.min',
angularRoute: 'angular/angular-route.min',
angularAnimate: 'angular/angular-animate.min',
ngInfiniteScroll: 'ng-infinite-scroll.min',
ngTagsInput: 'ngtagsinput/ng-tags-input.min',
uuid: 'uuid/uuid',
forge: 'forge/forge.min',
punycode: 'punycode.min',
openpgp: 'openpgp/openpgp',
fastclick: 'fastclick/fastclick'
},
shim: {
forge: {
exports: 'forge'
},
jquery: {
exports: '$'
},
angular: {
exports: 'angular',
deps: ['jquery']
},
angularRoute: {
exports: 'angular',
deps: ['angular']
},
angularAnimate: {
exports: 'angular',
deps: ['angular']
},
ngInfiniteScroll: {
exports: 'angular',
deps: ['jquery', 'angular']
},
ngTagsInput: {
exports: 'angular',
deps: ['angular']
},
lawnchair: {
exports: 'Lawnchair'
},
lawnchairSQL: {
deps: ['lawnchair']
},
lawnchairIDB: {
deps: ['lawnchair', 'lawnchairSQL']
},
underscore: {
exports: '_'
}
}
});
}());

View File

@ -5,8 +5,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" media="all" href="../css/read-sandbox.min.css" type="text/css">
<script src="../lib/purify.js"></script>
<script src="../js/controller/read-sandbox.js"></script>
<script src="../js/read-sandbox.min.js"></script>
</head>
<body></body>

View File

@ -1,10 +0,0 @@
'use strict';
chrome.app.runtime.onLaunched.addListener(function() {
chrome.app.window.create('integration/index.html', {
'bounds': {
'width': 1024,
'height': 768
}
});
});

View File

@ -1,21 +1,18 @@
define(function(require) {
'use strict';
'use strict';
var expect = chai.expect,
ImapClient = require('imap-client'),
var ImapClient = require('imap-client'),
BrowserCrow = require('browsercrow'),
BrowserSMTP = require('browsersmtp'),
SmtpClient = require('smtpclient'),
LawnchairDAO = require('js/dao/lawnchair-dao'),
DeviceStorageDAO = require('js/dao/devicestorage-dao'),
appController = require('js/app-controller'),
SmtpClient = require('wo-smtpclient'),
LawnchairDAO = require('../../src/js/dao/lawnchair-dao'),
DeviceStorageDAO = require('../../src/js/dao/devicestorage-dao'),
appController = require('../../src/js/app-controller'),
mailreader = require('mailreader'),
openpgp = require('openpgp'),
PgpMailer = require('pgpmailer'),
config = require('js/app-config').config,
str = require('js/app-config').string;
config = require('../../src/js/app-config').config,
str = require('../../src/js/app-config').string;
describe('Email DAO integration tests', function() {
describe('Email DAO integration tests', function() {
this.timeout(100000);
chai.Assertion.includeStack = true;
@ -272,6 +269,10 @@ define(function(require) {
}, function(err) {
expect(err).to.not.exist;
// stub rest request to key server
sinon.stub(appController._emailDao._keychain._publicKeyDao, 'get').yields(null, mockKeyPair.publicKey);
sinon.stub(appController._emailDao._keychain._publicKeyDao, 'getByUserId').yields(null, mockKeyPair.publicKey);
userStorage = appController._userStorage;
appController.init({
@ -826,5 +827,4 @@ define(function(require) {
});
});
});
});
});

View File

@ -2,7 +2,7 @@
<html style="overflow-y: auto">
<head>
<meta charset="utf-8">
<title>JavaScript Unit Tests</title>
<title>JavaScript Integration Tests</title>
<link rel="stylesheet" href="../lib/mocha.css" />
</head>
@ -10,10 +10,24 @@
<div id="mocha"></div>
<script src="../lib/chai.js"></script>
<script src="../lib/sinon.js"></script>
<script src="../lib/mocha.js"></script>
<script src="../lib/sinon.js"></script>
<script data-main="main.js" src="../lib/require.js"></script>
<script>
window.expect = chai.expect;
mocha.setup('bdd');
</script>
<script src="../lib/openpgp.js"></script>
<script src="../lib/forge.min.js"></script>
<script src="index.js"></script>
<script>
if (window.mochaPhantomJS) {
mochaPhantomJS.run();
} else {
mocha.run();
}
</script>
</body>
</html>

View File

@ -1,61 +0,0 @@
'use strict';
// Mozilla bind polyfill because phantomjs is stupid
if (!Function.prototype.bind) {
Function.prototype.bind = function(oThis) {
if (typeof this !== "function") {
// closest thing possible to the ECMAScript 5 internal IsCallable function
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
FNOP = function() {},
fBound = function() {
return fToBind.apply(this instanceof FNOP && oThis ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments)));
};
FNOP.prototype = this.prototype;
fBound.prototype = new FNOP();
return fBound;
};
}
require(['src/require-config'], function() {
require.config({
baseUrl: 'src/lib',
paths: {
'browsercrow': '../../../lib/browsercrow',
'browsercrow-envelope': '../../../lib/browsercrow-envelope',
'browsercrow-bodystructure': '../../../lib/browsercrow-bodystructure',
'browsercrow-mimeparser': '../../../lib/browsercrow-mimeparser',
'browsersmtp': '../../../lib/browsersmtp'
}
});
// Start the main app logic.
require(['js/app-config', 'axe'], function(app, axe) {
window.Worker = undefined; // disable web workers since mocha doesn't support them
app.config.workerPath = '../../src/js';
//app.config.cloudUrl = 'http://localhost:8888';
axe.removeAppender(axe.defaultAppender);
startTests();
});
});
function startTests() {
mocha.setup('bdd');
require(
[
'../../email-dao-test'
], function() {
//Tests loaded, run tests
mocha.run();
}
);
}

50
test/main.js Normal file
View File

@ -0,0 +1,50 @@
'use strict';
//
// Polyfills
//
// Mozilla bind polyfill because phantomjs is stupid
if (!Function.prototype.bind) {
Function.prototype.bind = function(oThis) {
if (typeof this !== "function") {
// closest thing possible to the ECMAScript 5 internal IsCallable function
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
FNOP = function() {},
fBound = function() {
return fToBind.apply(this instanceof FNOP && oThis ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments)));
};
FNOP.prototype = this.prototype;
fBound.prototype = new FNOP();
return fBound;
};
}
// a warm round of applause for phantomjs for missing events
(function() {
if (!window.CustomEvent) {
var CustomEvent = function(event, params) {
params = params || {
bubbles: false,
cancelable: false,
detail: undefined
};
var evt = document.createEvent('CustomEvent');
evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
return evt;
};
CustomEvent.prototype = window.Event.prototype;
window.CustomEvent = CustomEvent;
}
})();
// set worker path for tests
require('../src/js/app-config').config.workerPath = '../lib';

View File

@ -1,16 +1,13 @@
define(function(require) {
'use strict';
'use strict';
var expect = chai.expect,
angular = require('angular'),
mocks = require('angularMocks'),
AccountCtrl = require('js/controller/account'),
PGP = require('js/crypto/pgp'),
dl = require('js/util/download'),
appController = require('js/app-controller'),
KeychainDAO = require('js/dao/keychain-dao');
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');
describe('Account Controller unit test', function() {
describe('Account Controller unit test', function() {
var scope, accountCtrl,
dummyFingerprint, expectedFingerprint,
dummyKeyId, expectedKeyId,
@ -97,5 +94,4 @@ define(function(require) {
scope.exportKeyFile();
});
});
});
});

View File

@ -1,15 +1,12 @@
define(function(require) {
'use strict';
'use strict';
var expect = chai.expect,
angular = require('angular'),
mocks = require('angularMocks'),
AddAccountCtrl = require('js/controller/add-account'),
Auth = require('js/bo/auth'),
AdminDao = require('js/dao/admin-dao'),
appController = require('js/app-controller');
var mocks = angular.mock,
AddAccountCtrl = require('../../src/js/controller/add-account'),
Auth = require('../../src/js/bo/auth'),
AdminDao = require('../../src/js/dao/admin-dao'),
appController = require('../../src/js/app-controller');
describe('Add Account Controller unit test', function() {
describe('Add Account Controller unit test', function() {
var scope, location, ctrl, authStub, origAuth, adminStub;
beforeEach(function() {
@ -212,5 +209,4 @@ define(function(require) {
});
});
});
});

View File

@ -1,11 +1,9 @@
define(function(require) {
'use strict';
'use strict';
var RestDAO = require('js/dao/rest-dao'),
AdminDAO = require('js/dao/admin-dao'),
expect = chai.expect;
var RestDAO = require('../../src/js/dao/rest-dao'),
AdminDAO = require('../../src/js/dao/admin-dao');
describe('Admin DAO unit tests', function() {
describe('Admin DAO unit tests', function() {
var adminDao, restDaoStub,
emailAddress = 'test@example.com',
@ -141,6 +139,4 @@ define(function(require) {
});
});
});
});

View File

@ -1,15 +1,13 @@
define(function(require) {
'use strict';
'use strict';
var controller = require('js/app-controller'),
EmailDAO = require('js/dao/email-dao'),
OutboxBO = require('js/bo/outbox'),
DeviceStorageDAO = require('js/dao/devicestorage-dao'),
UpdateHandler = require('js/util/update/update-handler'),
Auth = require('js/bo/auth'),
expect = chai.expect;
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'),
Auth = require('../../src/js/bo/auth');
describe('App Controller unit tests', function() {
describe('App Controller unit tests', function() {
var emailDaoStub, outboxStub, updateHandlerStub, appConfigStoreStub, devicestorageStub, isOnlineStub, authStub;
beforeEach(function() {
@ -209,5 +207,4 @@ define(function(require) {
});
});
});
});
});

View File

@ -1,13 +1,11 @@
define(function(require) {
'use strict';
'use strict';
var Auth = require('js/bo/auth'),
OAuth = require('js/util/oauth'),
PGP = require('js/crypto/pgp'),
DeviceStorageDAO = require('js/dao/devicestorage-dao'),
expect = chai.expect;
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');
describe('Auth unit tests', function() {
describe('Auth unit tests', function() {
// Constancts
var EMAIL_ADDR_DB_KEY = 'emailaddress';
var USERNAME_DB_KEY = 'username';
@ -374,5 +372,4 @@ define(function(require) {
});
});
});
});
});

View File

@ -1,10 +1,8 @@
define(function(require) {
'use strict';
'use strict';
var btnHandler = require('js/util/backbutton-handler'),
expect = chai.expect;
var btnHandler = require('../../src/js/util/backbutton-handler');
describe('Backbutton Handler', function() {
describe('Backbutton Handler', function() {
chai.Assertion.includeStack = true;
var scope, event;
@ -63,5 +61,4 @@ define(function(require) {
navigator.app.exitApp = done;
document.dispatchEvent(event);
});
});
});

View File

@ -1,16 +1,14 @@
define(function(require) {
'use strict';
'use strict';
var ConnectionDoctor = require('js/util/connection-doctor'),
TCPSocket = require('tcp-socket'),
var TCPSocket = require('tcp-socket'),
ImapClient = require('imap-client'),
SmtpClient = require('smtpclient'),
cfg = require('js/app-config').config,
expect = chai.expect;
SmtpClient = require('wo-smtpclient'),
ConnectionDoctor = require('../../src/js/util/connection-doctor'),
cfg = require('../../src/js/app-config').config;
describe('Connection Doctor', function() {
describe('Connection Doctor', function() {
var doctor;
var socketStub, imapStub, smtpStub, credentials;
var socketStub, imapStub, smtpStub, credentials, workerPath;
beforeEach(function() {
//
@ -24,6 +22,7 @@ define(function(require) {
}
};
workerPath = 'js/tcp-socket-tls-worker.min.js';
imapStub = sinon.createStubInstance(ImapClient);
smtpStub = sinon.createStubInstance(SmtpClient);
@ -86,7 +85,8 @@ define(function(require) {
expect(TCPSocket.open.calledWith(credentials.imap.host, credentials.imap.port, {
binaryType: 'arraybuffer',
useSecureTransport: credentials.imap.secure,
ca: credentials.imap.ca
ca: credentials.imap.ca,
tlsWorkerPath: workerPath
})).to.be.true;
done();
@ -110,7 +110,8 @@ define(function(require) {
expect(TCPSocket.open.calledWith(credentials.imap.host, credentials.imap.port, {
binaryType: 'arraybuffer',
useSecureTransport: credentials.imap.secure,
ca: credentials.imap.ca
ca: credentials.imap.ca,
tlsWorkerPath: workerPath
})).to.be.true;
done();
@ -127,7 +128,8 @@ define(function(require) {
expect(TCPSocket.open.calledWith(credentials.imap.host, credentials.imap.port, {
binaryType: 'arraybuffer',
useSecureTransport: credentials.imap.secure,
ca: credentials.imap.ca
ca: credentials.imap.ca,
tlsWorkerPath: workerPath
})).to.be.true;
done();
@ -410,5 +412,4 @@ define(function(require) {
});
});
});
});
});

View File

@ -1,15 +1,12 @@
define(function(require) {
'use strict';
'use strict';
var expect = chai.expect,
angular = require('angular'),
mocks = require('angularMocks'),
ContactsCtrl = require('js/controller/contacts'),
appController = require('js/app-controller'),
KeychainDAO = require('js/dao/keychain-dao'),
PGP = require('js/crypto/pgp');
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');
describe('Contacts Controller unit test', function() {
describe('Contacts Controller unit test', function() {
var scope, contactsCtrl,
origKeychain, keychainMock,
origPgp, pgpMock;
@ -186,5 +183,4 @@ define(function(require) {
scope.removeKey(key);
});
});
});
});

View File

@ -1,12 +1,10 @@
define(function(require) {
'use strict';
'use strict';
var Crypto = require('js/crypto/crypto'),
util = require('js/crypto/util'),
config = require('js/app-config').config,
expect = chai.expect;
var Crypto = require('../../src/js/crypto/crypto'),
config = require('../../src/js/app-config').config,
util = require('crypto-lib').util;
describe('Crypto unit tests', function() {
describe('Crypto unit tests', function() {
this.timeout(20000);
var crypto,
@ -54,5 +52,4 @@ define(function(require) {
});
});
});

View File

@ -1,13 +1,11 @@
define(function(require) {
'use strict';
'use strict';
var LawnchairDAO = require('js/dao/lawnchair-dao'),
DeviceStorageDAO = require('js/dao/devicestorage-dao'),
expect = chai.expect;
var LawnchairDAO = require('../../src/js/dao/lawnchair-dao'),
DeviceStorageDAO = require('../../src/js/dao/devicestorage-dao');
var testUser = 'test@example.com';
var testUser = 'test@example.com';
describe('Device Storage DAO unit tests', function() {
describe('Device Storage DAO unit tests', function() {
var storageDao, lawnchairDaoStub;
@ -100,6 +98,4 @@ define(function(require) {
});
});
});
});

View File

@ -1,12 +1,9 @@
define(function(require) {
'use strict';
'use strict';
var expect = chai.expect,
angular = require('angular'),
mocks = require('angularMocks'),
DialogCtrl = require('js/controller/dialog');
var mocks = angular.mock,
DialogCtrl = require('../../src/js/controller/dialog');
describe('Dialog Controller unit test', function() {
describe('Dialog Controller unit test', function() {
var scope, dialogCtrl;
beforeEach(function() {
@ -46,5 +43,4 @@ define(function(require) {
scope.confirm(false);
});
});
});
});

View File

@ -1,19 +1,17 @@
define(function(require) {
'use strict';
'use strict';
var EmailDAO = require('js/dao/email-dao'),
KeychainDAO = require('js/dao/keychain-dao'),
var mailreader = require('mailreader'),
ImapClient = require('imap-client'),
PgpMailer = require('pgpmailer'),
PgpBuilder = require('pgpbuilder'),
PGP = require('js/crypto/pgp'),
DeviceStorageDAO = require('js/dao/devicestorage-dao'),
mailreader = require('mailreader'),
cfg = require('js/app-config').config,
expect = chai.expect;
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');
describe('Email DAO unit tests', function() {
describe('Email DAO unit tests', function() {
// show the stack trace when an error occurred
chai.Assertion.includeStack = true;
@ -2300,5 +2298,4 @@ define(function(require) {
});
});
});
});

View File

@ -10,10 +10,27 @@
<div id="mocha"></div>
<script src="../lib/chai.js"></script>
<script src="../lib/sinon.js"></script>
<script src="../lib/mocha.js"></script>
<script src="../lib/sinon.js"></script>
<script data-main="main.js" src="../../src/lib/require.js"></script>
<script>
window.expect = chai.expect;
mocha.setup('bdd');
</script>
<script src="../lib/openpgp.js"></script>
<script src="../lib/forge.min.js"></script>
<script src="index.js"></script>
<script>
mocha.checkLeaks();
mocha.globals(['chrome']);
if (window.mochaPhantomJS) {
mochaPhantomJS.run();
} else {
mocha.run();
}
</script>
</body>
</html>

View File

@ -1,11 +1,9 @@
define(function(require) {
'use strict';
'use strict';
var RestDAO = require('js/dao/rest-dao'),
InvitationDAO = require('js/dao/invitation-dao'),
expect = chai.expect;
var RestDAO = require('../../src/js/dao/rest-dao'),
InvitationDAO = require('../../src/js/dao/invitation-dao');
describe('Invitation DAO unit tests', function() {
describe('Invitation DAO unit tests', function() {
var restDaoStub, invitationDao,
alice = 'zuhause@aol.com',
bob = 'manfred.mustermann@musterdomain.com',
@ -104,5 +102,4 @@ define(function(require) {
}
});
});
});
});

View File

@ -1,17 +1,15 @@
define(function(require) {
'use strict';
'use strict';
var LawnchairDAO = require('js/dao/lawnchair-dao'),
PublicKeyDAO = require('js/dao/publickey-dao'),
KeychainDAO = require('js/dao/keychain-dao'),
PrivateKeyDAO = require('js/dao/privatekey-dao'),
Crypto = require('js/crypto/crypto'),
PGP = require('js/crypto/pgp'),
expect = chai.expect;
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 testUser = 'test@example.com';
var testUser = 'test@example.com';
describe('Keychain DAO unit tests', function() {
describe('Keychain DAO unit tests', function() {
var keychainDao, lawnchairDaoStub, pubkeyDaoStub, privkeyDaoStub, cryptoStub, pgpStub;
@ -1381,6 +1379,4 @@ define(function(require) {
});
});
});
});

View File

@ -1,25 +1,23 @@
define(function(require) {
'use strict';
'use strict';
var LawnchairDAO = require('js/dao/lawnchair-dao'),
expect = chai.expect;
var LawnchairDAO = require('../../src/js/dao/lawnchair-dao');
var dbName = 'lawnchair@test.com';
var dbName = 'lawnchair@test.com';
var key = 'type_1';
var data = {
var key = 'type_1';
var data = {
name: 'testName1',
type: 'testType1'
};
};
var key2 = 'type_2';
var data2 = {
var key2 = 'type_2';
var data2 = {
name: 'testName2',
type: 'testType2'
};
};
describe('Lawnchair DAO unit tests', function() {
describe('Lawnchair DAO unit tests', function() {
var lawnchairDao;
beforeEach(function(done) {
@ -147,6 +145,4 @@ define(function(require) {
});
});
});
});

View File

@ -1,16 +1,13 @@
define(function(require) {
'use strict';
'use strict';
var expect = chai.expect,
angular = require('angular'),
mocks = require('angularMocks'),
LoginCtrl = require('js/controller/login'),
EmailDAO = require('js/dao/email-dao'),
Auth = require('js/bo/auth'),
appController = require('js/app-controller'),
KeychainDAO = require('js/dao/keychain-dao');
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() {
describe('Login Controller unit test', function() {
var scope, location, ctrl,
origEmailDao, emailDaoMock,
origKeychain, keychainMock,
@ -234,5 +231,4 @@ define(function(require) {
});
});
});
});
});

View File

@ -1,16 +1,13 @@
define(function(require) {
'use strict';
'use strict';
var expect = chai.expect,
angular = require('angular'),
Auth = require('js/bo/auth'),
mocks = require('angularMocks'),
LoginExistingCtrl = require('js/controller/login-existing'),
EmailDAO = require('js/dao/email-dao'),
KeychainDAO = require('js/dao/keychain-dao'),
appController = require('js/app-controller');
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');
describe('Login (existing user) Controller unit test', function() {
describe('Login (existing user) Controller unit test', function() {
var scope, location, ctrl, origEmailDao, emailDaoMock,
origAuth, authMock,
emailAddress = 'fred@foo.com',
@ -113,5 +110,4 @@ define(function(require) {
});
});
});
});
});

View File

@ -1,16 +1,13 @@
define(function(require) {
'use strict';
'use strict';
var expect = chai.expect,
angular = require('angular'),
Auth = require('js/bo/auth'),
mocks = require('angularMocks'),
LoginInitialCtrl = require('js/controller/login-initial'),
PGP = require('js/crypto/pgp'),
EmailDAO = require('js/dao/email-dao'),
appController = require('js/app-controller');
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');
describe('Login (initial user) Controller unit test', function() {
describe('Login (initial user) Controller unit test', function() {
var scope, ctrl, location, origEmailDao, emailDaoMock,
origAuth, authMock,
emailAddress = 'fred@foo.com',
@ -197,6 +194,4 @@ define(function(require) {
scope.generateKey();
});
});
});
});

View File

@ -1,16 +1,13 @@
define(function(require) {
'use strict';
'use strict';
var expect = chai.expect,
angular = require('angular'),
mocks = require('angularMocks'),
PGP = require('js/crypto/pgp'),
LoginNewDeviceCtrl = require('js/controller/login-new-device'),
KeychainDAO = require('js/dao/keychain-dao'),
EmailDAO = require('js/dao/email-dao'),
appController = require('js/app-controller');
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');
describe('Login (new device) Controller unit test', function() {
describe('Login (new device) Controller unit test', function() {
var scope, ctrl, origEmailDao, emailDaoMock, pgpMock,
emailAddress = 'fred@foo.com',
passphrase = 'asd',
@ -186,5 +183,4 @@ define(function(require) {
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
});
});
});
});

View File

@ -1,16 +1,13 @@
define(function(require) {
'use strict';
'use strict';
var expect = chai.expect,
angular = require('angular'),
mocks = require('angularMocks'),
Auth = require('js/bo/auth'),
LoginPrivateKeyDownloadCtrl = require('js/controller/login-privatekey-download'),
EmailDAO = require('js/dao/email-dao'),
appController = require('js/app-controller'),
KeychainDAO = require('js/dao/keychain-dao');
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');
describe('Login Private Key Download Controller unit test', function() {
describe('Login Private Key Download Controller unit test', function() {
var scope, location, ctrl,
origEmailDao, emailDaoMock,
origAuth, authMock,
@ -260,5 +257,4 @@ define(function(require) {
scope.goTo('/desktop');
});
});
});
});

View File

@ -1,15 +1,12 @@
define(function(require) {
'use strict';
'use strict';
var expect = chai.expect,
angular = require('angular'),
mocks = require('angularMocks'),
Auth = require('js/bo/auth'),
ConnectionDoctor = require('js/util/connection-doctor'),
SetCredentialsCtrl = require('js/controller/login-set-credentials'),
appController = require('js/app-controller');
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');
describe('Login (Set Credentials) Controller unit test', function() {
describe('Login (Set Credentials) Controller unit test', function() {
// Angular parameters
var scope, location, provider;
@ -97,5 +94,4 @@ define(function(require) {
expect(auth.setCredentials.calledOnce).to.be.true;
});
});
});
});

View File

@ -1,19 +1,16 @@
define(function(require) {
'use strict';
'use strict';
var expect = chai.expect,
angular = require('angular'),
mocks = require('angularMocks'),
MailListCtrl = require('js/controller/mail-list'),
EmailDAO = require('js/dao/email-dao'),
DeviceStorageDAO = require('js/dao/devicestorage-dao'),
KeychainDAO = require('js/dao/keychain-dao'),
appController = require('js/app-controller'),
notification = require('js/util/notification');
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.Assertion.includeStack = true;
chai.Assertion.includeStack = true;
describe('Mail List controller unit test', function() {
describe('Mail List controller unit test', function() {
var scope, ctrl, origEmailDao, emailDaoMock, keychainMock, deviceStorageMock,
emailAddress, emails,
hasChrome, hasSocket, hasRuntime, hasIdentity;
@ -447,5 +444,4 @@ define(function(require) {
expect(scope.state.mailList.selected).to.exist;
});
});
});
});

View File

@ -1,113 +0,0 @@
'use strict';
// Mozilla bind polyfill because phantomjs is stupid
if (!Function.prototype.bind) {
Function.prototype.bind = function(oThis) {
if (typeof this !== "function") {
// closest thing possible to the ECMAScript 5 internal IsCallable function
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
FNOP = function() {},
fBound = function() {
return fToBind.apply(this instanceof FNOP && oThis ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments)));
};
FNOP.prototype = this.prototype;
fBound.prototype = new FNOP();
return fBound;
};
}
// a warm round of applause for phantomjs for missing events
(function() {
function CustomEvent(event, params) {
params = params || {
bubbles: false,
cancelable: false,
detail: undefined
};
var evt = document.createEvent('CustomEvent');
evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
return evt;
}
CustomEvent.prototype = window.Event.prototype;
window.CustomEvent = CustomEvent;
})();
require(['../../src/require-config'], function() {
require.config({
baseUrl: '../../src/lib',
paths: {
angularMocks: '../../test/lib/angular-mocks'
},
shim: {
angularMocks: {
exports: 'angular.mock',
deps: ['angular']
}
}
});
// Start the main app logic.
require(['js/app-config', 'axe'], function(app, axe) {
app.config.workerPath = '../../src/js';
// turn off logging in the test
axe.removeAppender(axe.defaultAppender);
startTests();
});
});
function startTests() {
mocha.setup('bdd');
require(
[
'test/unit/oauth-test',
'test/unit/auth-test',
'test/unit/email-dao-test',
'test/unit/app-controller-test',
'test/unit/pgp-test',
'test/unit/crypto-test',
'test/unit/backbutton-handler-test',
'test/unit/rest-dao-test',
'test/unit/admin-dao-test',
'test/unit/publickey-dao-test',
'test/unit/privatekey-dao-test',
'test/unit/lawnchair-dao-test',
'test/unit/keychain-dao-test',
'test/unit/devicestorage-dao-test',
'test/unit/dialog-ctrl-test',
'test/unit/add-account-ctrl-test',
'test/unit/account-ctrl-test',
'test/unit/set-passphrase-ctrl-test',
'test/unit/contacts-ctrl-test',
'test/unit/login-existing-ctrl-test',
'test/unit/login-initial-ctrl-test',
'test/unit/login-new-device-ctrl-test',
'test/unit/login-privatekey-download-ctrl-test',
'test/unit/login-set-credentials-ctrl-test',
'test/unit/privatekey-upload-ctrl-test',
'test/unit/login-ctrl-test',
'test/unit/read-ctrl-test',
'test/unit/navigation-ctrl-test',
'test/unit/mail-list-ctrl-test',
'test/unit/write-ctrl-test',
'test/unit/outbox-bo-test',
'test/unit/invitation-dao-test',
'test/unit/update-handler-test',
'test/unit/connection-doctor-test'
], function() {
//Tests loaded, run tests
mocha.run();
}
);
}

View File

@ -1,15 +1,12 @@
define(function(require) {
'use strict';
'use strict';
var expect = chai.expect,
angular = require('angular'),
mocks = require('angularMocks'),
NavigationCtrl = require('js/controller/navigation'),
EmailDAO = require('js/dao/email-dao'),
OutboxBO = require('js/bo/outbox'),
appController = require('js/app-controller');
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');
describe('Navigation Controller unit test', function() {
describe('Navigation Controller unit test', function() {
var scope, ctrl, origEmailDao, emailDaoMock, outboxBoMock, outboxFolder, onConnectStub;
beforeEach(function(done) {
@ -97,5 +94,4 @@ define(function(require) {
expect(outboxFolder.count).to.equal(5);
});
});
});
});

View File

@ -1,11 +1,9 @@
define(function(require) {
'use strict';
'use strict';
var OAuth = require('js/util/oauth'),
RestDAO = require('js/dao/rest-dao'),
expect = chai.expect;
var OAuth = require('../../src/js/util/oauth'),
RestDAO = require('../../src/js/dao/rest-dao');
describe('OAuth unit tests', function() {
describe('OAuth unit tests', function() {
var oauth, googleApiStub, identityStub, getPlatformInfoStub, removeCachedStub,
testEmail = 'test@example.com';
@ -197,5 +195,4 @@ define(function(require) {
});
});
});
});

View File

@ -1,15 +1,13 @@
define(function(require) {
'use strict';
'use strict';
var expect = chai.expect,
OutboxBO = require('js/bo/outbox'),
KeychainDAO = require('js/dao/keychain-dao'),
EmailDAO = require('js/dao/email-dao'),
DeviceStorageDAO = require('js/dao/devicestorage-dao');
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');
chai.Assertion.includeStack = true;
chai.Assertion.includeStack = true;
describe('Outbox Business Object unit test', function() {
describe('Outbox Business Object unit test', function() {
var outbox, emailDaoStub, devicestorageStub, keychainStub,
dummyUser = 'spiderpig@springfield.com';
@ -276,5 +274,4 @@ define(function(require) {
});
});
});
});
});

View File

@ -1,11 +1,8 @@
define(function(require) {
'use strict';
'use strict';
var PGP = require('js/crypto/pgp'),
openpgp = require('openpgp'),
expect = chai.expect;
var PGP = require('../../src/js/crypto/pgp');
describe('PGP Crypto Api unit tests', function() {
describe('PGP Crypto Api unit tests', function() {
this.timeout(20000);
var pgp,
@ -460,5 +457,4 @@ define(function(require) {
});
});
});
});
});

View File

@ -1,11 +1,9 @@
define(function(require) {
'use strict';
'use strict';
var RestDAO = require('js/dao/rest-dao'),
PrivateKeyDAO = require('js/dao/privatekey-dao'),
expect = chai.expect;
var RestDAO = require('../../src/js/dao/rest-dao'),
PrivateKeyDAO = require('../../src/js/dao/privatekey-dao');
describe('Private Key DAO unit tests', function() {
describe('Private Key DAO unit tests', function() {
var privkeyDao, restDaoStub,
emailAddress = 'test@example.com',
@ -221,6 +219,4 @@ define(function(require) {
});
});
});
});

View File

@ -1,15 +1,12 @@
define(function(require) {
'use strict';
'use strict';
var expect = chai.expect,
angular = require('angular'),
mocks = require('angularMocks'),
PrivateKeyUploadCtrl = require('js/controller/privatekey-upload'),
appController = require('js/app-controller'),
KeychainDAO = require('js/dao/keychain-dao'),
PGP = require('js/crypto/pgp');
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');
describe('Private Key Upload Controller unit test', function() {
describe('Private Key Upload Controller unit test', function() {
var scope, location, ctrl,
origEmailDao, emailDaoMock,
origKeychain, keychainMock,
@ -281,6 +278,4 @@ define(function(require) {
scope.goForward();
});
});
});
});

View File

@ -1,11 +1,9 @@
define(function(require) {
'use strict';
'use strict';
var RestDAO = require('js/dao/rest-dao'),
PublicKeyDAO = require('js/dao/publickey-dao'),
expect = chai.expect;
var RestDAO = require('../../src/js/dao/rest-dao'),
PublicKeyDAO = require('../../src/js/dao/publickey-dao');
describe('Public Key DAO unit tests', function() {
describe('Public Key DAO unit tests', function() {
var pubkeyDao, restDaoStub;
@ -72,7 +70,7 @@ define(function(require) {
pubkeyDao.verify(uuid, function(err) {
expect(err).to.not.exist;
expect(restDaoStub.get.calledWith(sinon.match(function(arg){
expect(restDaoStub.get.calledWith(sinon.match(function(arg) {
return arg.uri === '/verify/' + uuid && arg.type === 'text';
}))).to.be.true;
done();
@ -160,6 +158,4 @@ define(function(require) {
});
});
});
});

View File

@ -1,17 +1,14 @@
define(function(require) {
'use strict';
'use strict';
var expect = chai.expect,
angular = require('angular'),
mocks = require('angularMocks'),
KeychainDAO = require('js/dao/keychain-dao'),
InvitationDAO = require('js/dao/invitation-dao'),
PGP = require('js/crypto/pgp'),
ReadCtrl = require('js/controller/read'),
OutboxBO = require('js/bo/outbox'),
appController = require('js/app-controller');
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');
describe('Read Controller unit test', function() {
describe('Read Controller unit test', function() {
var scope, ctrl,
origKeychain, keychainMock,
origInvitation, invitationMock,
@ -193,5 +190,4 @@ define(function(require) {
});
});
});
});

View File

@ -1,10 +1,8 @@
define(function(require) {
'use strict';
'use strict';
var RestDAO = require('js/dao/rest-dao'),
expect = chai.expect;
var RestDAO = require('../../src/js/dao/rest-dao');
describe('Rest DAO unit tests', function() {
describe('Rest DAO unit tests', function() {
var restDao, xhrMock, requests;
@ -220,6 +218,4 @@ define(function(require) {
});
});
});
});

View File

@ -1,15 +1,12 @@
define(function(require) {
'use strict';
'use strict';
var expect = chai.expect,
angular = require('angular'),
mocks = require('angularMocks'),
SetPassphraseCtrl = require('js/controller/set-passphrase'),
PGP = require('js/crypto/pgp'),
appController = require('js/app-controller'),
KeychainDAO = require('js/dao/keychain-dao');
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');
describe('Set Passphrase Controller unit test', function() {
describe('Set Passphrase Controller unit test', function() {
var scope, setPassphraseCtrl,
dummyFingerprint, expectedFingerprint,
dummyKeyId, expectedKeyId,
@ -122,5 +119,4 @@ define(function(require) {
});
});
});
});

View File

@ -1,14 +1,12 @@
define(function(require) {
'use strict';
'use strict';
var DeviceStorageDAO = require('js/dao/devicestorage-dao'),
Auth = require('js/bo/auth'),
cfg = require('js/app-config').config,
UpdateHandler = require('js/util/update/update-handler'),
config = require('js/app-config').config,
expect = chai.expect;
var DeviceStorageDAO = require('../../src/js/dao/devicestorage-dao'),
Auth = require('../../src/js/bo/auth'),
cfg = require('../../src/js/app-config').config,
UpdateHandler = require('../../src/js/util/update/update-handler'),
config = require('../../src/js/app-config').config;
describe('UpdateHandler', function() {
describe('UpdateHandler', function() {
var updateHandler, appConfigStorageStub, authStub, userStorageStub, origDbVersion;
chai.Assertion.includeStack = true;
@ -470,5 +468,4 @@ define(function(require) {
});
});
});
});
});

View File

@ -1,16 +1,13 @@
define(function(require) {
'use strict';
'use strict';
var expect = chai.expect,
angular = require('angular'),
mocks = require('angularMocks'),
WriteCtrl = require('js/controller/write'),
EmailDAO = require('js/dao/email-dao'),
OutboxBO = require('js/bo/outbox'),
KeychainDAO = require('js/dao/keychain-dao'),
appController = require('js/app-controller');
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');
describe('Write controller unit test', function() {
describe('Write controller unit test', function() {
var ctrl, scope,
origEmailDao, origOutbox, origKeychain,
emailDaoMock, keychainMock, outboxMock, emailAddress, realname;
@ -397,5 +394,4 @@ define(function(require) {
});
});
});
});