mirror of https://github.com/moparisthebest/mail
commit
5d09e32320
|
@ -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
|
||||
|
|
34
.jshintrc
34
.jshintrc
|
@ -18,31 +18,29 @@
|
|||
"unused": true,
|
||||
|
||||
"predef": [
|
||||
"self",
|
||||
"importScripts",
|
||||
"console",
|
||||
"Notification",
|
||||
"importScripts",
|
||||
"process",
|
||||
"Event",
|
||||
"QUnit",
|
||||
"test",
|
||||
"asyncTest",
|
||||
"ok",
|
||||
"equal",
|
||||
"deepEqual",
|
||||
"start",
|
||||
"process",
|
||||
"chrome",
|
||||
"requirejs",
|
||||
"define",
|
||||
"self",
|
||||
"Notification",
|
||||
"Event",
|
||||
"sinon",
|
||||
"mocha",
|
||||
"chai",
|
||||
"expect",
|
||||
"describe",
|
||||
"it",
|
||||
"chai",
|
||||
"sinon",
|
||||
"mocha",
|
||||
"before",
|
||||
"beforeEach",
|
||||
"after",
|
||||
"afterEach"
|
||||
"afterEach",
|
||||
"FastClick",
|
||||
"angular",
|
||||
"forge",
|
||||
"Lawnchair",
|
||||
"_",
|
||||
"openpgp"
|
||||
],
|
||||
|
||||
"globals": {
|
||||
|
|
356
Gruntfile.js
356
Gruntfile.js
|
@ -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']);
|
||||
|
||||
//
|
||||
|
@ -335,4 +497,4 @@ module.exports = function(grunt) {
|
|||
grunt.registerTask('release-stable', ['dist', 'manifest-stable', 'compress']);
|
||||
grunt.registerTask('default', ['release-dev']);
|
||||
|
||||
};
|
||||
};
|
||||
|
|
33
package.json
33
package.json
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -1,223 +1,217 @@
|
|||
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 {
|
||||
var manifest = chrome.runtime.getManifest();
|
||||
// get key server base url
|
||||
cloudUrl = _.find(manifest.permissions, function(permission) {
|
||||
return typeof permission === 'string' && permission.indexOf('https://keys') === 0;
|
||||
});
|
||||
// remove last '/' from url due to required syntax in manifest
|
||||
cloudUrl = cloudUrl.substring(0, cloudUrl.length - 1);
|
||||
// get keychain server base url
|
||||
keychainUrl = _.find(manifest.permissions, function(permission) {
|
||||
return typeof permission === 'string' && permission.indexOf('https://keychain') === 0;
|
||||
});
|
||||
// remove last '/' from url due to required syntax in manifest
|
||||
keychainUrl = keychainUrl.substring(0, keychainUrl.length - 1);
|
||||
// get client ID for OAuth requests
|
||||
clientId = manifest.oauth2.client_id;
|
||||
// get the app version
|
||||
appVersion = manifest.version;
|
||||
} catch (e) {}
|
||||
// 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) {
|
||||
return typeof permission === 'string' && permission.indexOf('https://keys') === 0;
|
||||
});
|
||||
// remove last '/' from url due to required syntax in manifest
|
||||
cloudUrl = cloudUrl.substring(0, cloudUrl.length - 1);
|
||||
// get keychain server base url
|
||||
keychainUrl = _.find(manifest.permissions, function(permission) {
|
||||
return typeof permission === 'string' && permission.indexOf('https://keychain') === 0;
|
||||
});
|
||||
// remove last '/' from url due to required syntax in manifest
|
||||
keychainUrl = keychainUrl.substring(0, keychainUrl.length - 1);
|
||||
// get client ID for OAuth requests
|
||||
clientId = manifest.oauth2.client_id;
|
||||
// get the app version
|
||||
appVersion = manifest.version;
|
||||
} catch (e) {}
|
||||
|
||||
/**
|
||||
* Global app configurations
|
||||
*/
|
||||
app.config = {
|
||||
cloudUrl: cloudUrl || 'https://keys.whiteout.io',
|
||||
privkeyServerUrl: keychainUrl || 'https://keychain.whiteout.io',
|
||||
adminUrl: 'https://admin-node.whiteout.io',
|
||||
wmailDomain: 'wmail.io',
|
||||
serverPrivateKeyId: 'EE342F0DDBB0F3BE',
|
||||
symKeySize: 256,
|
||||
symIvSize: 96,
|
||||
asymKeySize: 2048,
|
||||
workerPath: 'js',
|
||||
reconnectInterval: 10000,
|
||||
wmail: {
|
||||
imap: {
|
||||
host: 'imap.wmail.io',
|
||||
port: 993,
|
||||
secure: true,
|
||||
ca: '-----BEGIN CERTIFICATE-----\r\nMIIGiTCCBXGgAwIBAgIDAn03MA0GCSqGSIb3DQEBBQUAMIGMMQswCQYDVQQGEwJJ\r\nTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0\r\nYWwgQ2VydGlmaWNhdGUgU2lnbmluZzE4MDYGA1UEAxMvU3RhcnRDb20gQ2xhc3Mg\r\nMiBQcmltYXJ5IEludGVybWVkaWF0ZSBTZXJ2ZXIgQ0EwHhcNMTQwOTIxMTQ0ODQ0\r\nWhcNMTYwOTIxMTE0MzIxWjCBmTEZMBcGA1UEDRMQQ1NIeEdsdlg4Z3lRQ2c3TzEL\r\nMAkGA1UEBhMCREUxDzANBgNVBAgTBkJheWVybjEPMA0GA1UEBxMGTXVuaWNoMRUw\r\nEwYDVQQKEwxUYW5rcmVkIEhhc2UxEzARBgNVBAMUCioud21haWwuaW8xITAfBgkq\r\nhkiG9w0BCQEWEndlYm1hc3RlckB3bWFpbC5pbzCCASIwDQYJKoZIhvcNAQEBBQAD\r\nggEPADCCAQoCggEBAMkbMzFfZLYqfWG8yw2HvuNiFs5ajBFMrjG3fobePE674mPd\r\niFtXqbl3ydQ+umTtbJ6bztgxB3KgrL3lhp6IkD4VxB8YQJoYGhU6YH7FhP4QMm8l\r\ncnFLUZXEbcpcCg1tjL6+vvoTMUWEbV/zNtF/oiJ4AIOKwf0zUMZkTu1FCNrOrvpj\r\n6SAkOdBGzLTOAP5vxP43PfpZPZ4dLL2Be7ENYKXqPs0jSlUWpdT9l4AZG8rHKa2d\r\nccWvRoAsCfvpzGDoMYEx9+a1F1XjNZfzo7yJQcHdaE3Mj1eNqbjZe0+Vmact1S/3\r\nsubdcDVkFSfUMsAB5rH0D5mEhy7sMpt1WWCvOF8CAwEAAaOCAuMwggLfMAkGA1Ud\r\nEwQCMAAwCwYDVR0PBAQDAgOoMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD\r\nATAdBgNVHQ4EFgQUv+9x4PISm5Roq4pdNoaFJeIZFfUwHwYDVR0jBBgwFoAUEdsj\r\nRf1UzGpxb4SKA9e+9wEvJoYwHwYDVR0RBBgwFoIKKi53bWFpbC5pb4IId21haWwu\r\naW8wggFWBgNVHSAEggFNMIIBSTAIBgZngQwBAgIwggE7BgsrBgEEAYG1NwECAzCC\r\nASowLgYIKwYBBQUHAgEWImh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5w\r\nZGYwgfcGCCsGAQUFBwICMIHqMCcWIFN0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0\r\naG9yaXR5MAMCAQEagb5UaGlzIGNlcnRpZmljYXRlIHdhcyBpc3N1ZWQgYWNjb3Jk\r\naW5nIHRvIHRoZSBDbGFzcyAyIFZhbGlkYXRpb24gcmVxdWlyZW1lbnRzIG9mIHRo\r\nZSBTdGFydENvbSBDQSBwb2xpY3ksIHJlbGlhbmNlIG9ubHkgZm9yIHRoZSBpbnRl\r\nbmRlZCBwdXJwb3NlIGluIGNvbXBsaWFuY2Ugb2YgdGhlIHJlbHlpbmcgcGFydHkg\r\nb2JsaWdhdGlvbnMuMDUGA1UdHwQuMCwwKqAooCaGJGh0dHA6Ly9jcmwuc3RhcnRz\r\nc2wuY29tL2NydDItY3JsLmNybDCBjgYIKwYBBQUHAQEEgYEwfzA5BggrBgEFBQcw\r\nAYYtaHR0cDovL29jc3Auc3RhcnRzc2wuY29tL3N1Yi9jbGFzczIvc2VydmVyL2Nh\r\nMEIGCCsGAQUFBzAChjZodHRwOi8vYWlhLnN0YXJ0c3NsLmNvbS9jZXJ0cy9zdWIu\r\nY2xhc3MyLnNlcnZlci5jYS5jcnQwIwYDVR0SBBwwGoYYaHR0cDovL3d3dy5zdGFy\r\ndHNzbC5jb20vMA0GCSqGSIb3DQEBBQUAA4IBAQDEDbCBvdfs/lEsEJk0xGB6OghO\r\ngUAHplmWJEXoHb0h7p2QAIcs/QKorMDi35bu/J2vbVRIP7i5wBTZLIHBaf03mqAR\r\nZxYFy/ymyROVmmKl1x1/ry1aumomfU86UN8hCkvJc+40V2KC1lCZcjqPWufECjpo\r\n37QXFA+te/rVyVgvoMhIq+zOBZEK0/2mYGCwlpEQo3HElCQIwV9upChOHnQ2jZyF\r\nCmoUyv0JNr8dkh6H7+KTV6FzWaC+b6Liier9bpfEq/zDAp41GR+L/pdbaliSDtRg\r\nfPhAyCZqwsXid6HgIyTBqxUpGDGRiuygqghmqlFfppZuAqz02wrNceDHH7Up\r\n-----END CERTIFICATE-----\r\n',
|
||||
pinned: true,
|
||||
ignoreTLS: false
|
||||
/**
|
||||
* Global app configurations
|
||||
*/
|
||||
exports.config = {
|
||||
cloudUrl: cloudUrl || 'https://keys.whiteout.io',
|
||||
privkeyServerUrl: keychainUrl || 'https://keychain.whiteout.io',
|
||||
adminUrl: 'https://admin-node.whiteout.io',
|
||||
wmailDomain: 'wmail.io',
|
||||
serverPrivateKeyId: 'EE342F0DDBB0F3BE',
|
||||
symKeySize: 256,
|
||||
symIvSize: 96,
|
||||
asymKeySize: 2048,
|
||||
workerPath: 'js',
|
||||
reconnectInterval: 10000,
|
||||
wmail: {
|
||||
imap: {
|
||||
host: 'imap.wmail.io',
|
||||
port: 993,
|
||||
secure: true,
|
||||
ca: '-----BEGIN CERTIFICATE-----\r\nMIIGiTCCBXGgAwIBAgIDAn03MA0GCSqGSIb3DQEBBQUAMIGMMQswCQYDVQQGEwJJ\r\nTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0\r\nYWwgQ2VydGlmaWNhdGUgU2lnbmluZzE4MDYGA1UEAxMvU3RhcnRDb20gQ2xhc3Mg\r\nMiBQcmltYXJ5IEludGVybWVkaWF0ZSBTZXJ2ZXIgQ0EwHhcNMTQwOTIxMTQ0ODQ0\r\nWhcNMTYwOTIxMTE0MzIxWjCBmTEZMBcGA1UEDRMQQ1NIeEdsdlg4Z3lRQ2c3TzEL\r\nMAkGA1UEBhMCREUxDzANBgNVBAgTBkJheWVybjEPMA0GA1UEBxMGTXVuaWNoMRUw\r\nEwYDVQQKEwxUYW5rcmVkIEhhc2UxEzARBgNVBAMUCioud21haWwuaW8xITAfBgkq\r\nhkiG9w0BCQEWEndlYm1hc3RlckB3bWFpbC5pbzCCASIwDQYJKoZIhvcNAQEBBQAD\r\nggEPADCCAQoCggEBAMkbMzFfZLYqfWG8yw2HvuNiFs5ajBFMrjG3fobePE674mPd\r\niFtXqbl3ydQ+umTtbJ6bztgxB3KgrL3lhp6IkD4VxB8YQJoYGhU6YH7FhP4QMm8l\r\ncnFLUZXEbcpcCg1tjL6+vvoTMUWEbV/zNtF/oiJ4AIOKwf0zUMZkTu1FCNrOrvpj\r\n6SAkOdBGzLTOAP5vxP43PfpZPZ4dLL2Be7ENYKXqPs0jSlUWpdT9l4AZG8rHKa2d\r\nccWvRoAsCfvpzGDoMYEx9+a1F1XjNZfzo7yJQcHdaE3Mj1eNqbjZe0+Vmact1S/3\r\nsubdcDVkFSfUMsAB5rH0D5mEhy7sMpt1WWCvOF8CAwEAAaOCAuMwggLfMAkGA1Ud\r\nEwQCMAAwCwYDVR0PBAQDAgOoMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD\r\nATAdBgNVHQ4EFgQUv+9x4PISm5Roq4pdNoaFJeIZFfUwHwYDVR0jBBgwFoAUEdsj\r\nRf1UzGpxb4SKA9e+9wEvJoYwHwYDVR0RBBgwFoIKKi53bWFpbC5pb4IId21haWwu\r\naW8wggFWBgNVHSAEggFNMIIBSTAIBgZngQwBAgIwggE7BgsrBgEEAYG1NwECAzCC\r\nASowLgYIKwYBBQUHAgEWImh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5w\r\nZGYwgfcGCCsGAQUFBwICMIHqMCcWIFN0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0\r\naG9yaXR5MAMCAQEagb5UaGlzIGNlcnRpZmljYXRlIHdhcyBpc3N1ZWQgYWNjb3Jk\r\naW5nIHRvIHRoZSBDbGFzcyAyIFZhbGlkYXRpb24gcmVxdWlyZW1lbnRzIG9mIHRo\r\nZSBTdGFydENvbSBDQSBwb2xpY3ksIHJlbGlhbmNlIG9ubHkgZm9yIHRoZSBpbnRl\r\nbmRlZCBwdXJwb3NlIGluIGNvbXBsaWFuY2Ugb2YgdGhlIHJlbHlpbmcgcGFydHkg\r\nb2JsaWdhdGlvbnMuMDUGA1UdHwQuMCwwKqAooCaGJGh0dHA6Ly9jcmwuc3RhcnRz\r\nc2wuY29tL2NydDItY3JsLmNybDCBjgYIKwYBBQUHAQEEgYEwfzA5BggrBgEFBQcw\r\nAYYtaHR0cDovL29jc3Auc3RhcnRzc2wuY29tL3N1Yi9jbGFzczIvc2VydmVyL2Nh\r\nMEIGCCsGAQUFBzAChjZodHRwOi8vYWlhLnN0YXJ0c3NsLmNvbS9jZXJ0cy9zdWIu\r\nY2xhc3MyLnNlcnZlci5jYS5jcnQwIwYDVR0SBBwwGoYYaHR0cDovL3d3dy5zdGFy\r\ndHNzbC5jb20vMA0GCSqGSIb3DQEBBQUAA4IBAQDEDbCBvdfs/lEsEJk0xGB6OghO\r\ngUAHplmWJEXoHb0h7p2QAIcs/QKorMDi35bu/J2vbVRIP7i5wBTZLIHBaf03mqAR\r\nZxYFy/ymyROVmmKl1x1/ry1aumomfU86UN8hCkvJc+40V2KC1lCZcjqPWufECjpo\r\n37QXFA+te/rVyVgvoMhIq+zOBZEK0/2mYGCwlpEQo3HElCQIwV9upChOHnQ2jZyF\r\nCmoUyv0JNr8dkh6H7+KTV6FzWaC+b6Liier9bpfEq/zDAp41GR+L/pdbaliSDtRg\r\nfPhAyCZqwsXid6HgIyTBqxUpGDGRiuygqghmqlFfppZuAqz02wrNceDHH7Up\r\n-----END CERTIFICATE-----\r\n',
|
||||
pinned: true,
|
||||
ignoreTLS: false
|
||||
|
||||
},
|
||||
smtp: {
|
||||
host: 'smtp.wmail.io',
|
||||
port: 465,
|
||||
secure: true,
|
||||
ca: '-----BEGIN CERTIFICATE-----\r\nMIIGiTCCBXGgAwIBAgIDAn03MA0GCSqGSIb3DQEBBQUAMIGMMQswCQYDVQQGEwJJ\r\nTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0\r\nYWwgQ2VydGlmaWNhdGUgU2lnbmluZzE4MDYGA1UEAxMvU3RhcnRDb20gQ2xhc3Mg\r\nMiBQcmltYXJ5IEludGVybWVkaWF0ZSBTZXJ2ZXIgQ0EwHhcNMTQwOTIxMTQ0ODQ0\r\nWhcNMTYwOTIxMTE0MzIxWjCBmTEZMBcGA1UEDRMQQ1NIeEdsdlg4Z3lRQ2c3TzEL\r\nMAkGA1UEBhMCREUxDzANBgNVBAgTBkJheWVybjEPMA0GA1UEBxMGTXVuaWNoMRUw\r\nEwYDVQQKEwxUYW5rcmVkIEhhc2UxEzARBgNVBAMUCioud21haWwuaW8xITAfBgkq\r\nhkiG9w0BCQEWEndlYm1hc3RlckB3bWFpbC5pbzCCASIwDQYJKoZIhvcNAQEBBQAD\r\nggEPADCCAQoCggEBAMkbMzFfZLYqfWG8yw2HvuNiFs5ajBFMrjG3fobePE674mPd\r\niFtXqbl3ydQ+umTtbJ6bztgxB3KgrL3lhp6IkD4VxB8YQJoYGhU6YH7FhP4QMm8l\r\ncnFLUZXEbcpcCg1tjL6+vvoTMUWEbV/zNtF/oiJ4AIOKwf0zUMZkTu1FCNrOrvpj\r\n6SAkOdBGzLTOAP5vxP43PfpZPZ4dLL2Be7ENYKXqPs0jSlUWpdT9l4AZG8rHKa2d\r\nccWvRoAsCfvpzGDoMYEx9+a1F1XjNZfzo7yJQcHdaE3Mj1eNqbjZe0+Vmact1S/3\r\nsubdcDVkFSfUMsAB5rH0D5mEhy7sMpt1WWCvOF8CAwEAAaOCAuMwggLfMAkGA1Ud\r\nEwQCMAAwCwYDVR0PBAQDAgOoMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD\r\nATAdBgNVHQ4EFgQUv+9x4PISm5Roq4pdNoaFJeIZFfUwHwYDVR0jBBgwFoAUEdsj\r\nRf1UzGpxb4SKA9e+9wEvJoYwHwYDVR0RBBgwFoIKKi53bWFpbC5pb4IId21haWwu\r\naW8wggFWBgNVHSAEggFNMIIBSTAIBgZngQwBAgIwggE7BgsrBgEEAYG1NwECAzCC\r\nASowLgYIKwYBBQUHAgEWImh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5w\r\nZGYwgfcGCCsGAQUFBwICMIHqMCcWIFN0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0\r\naG9yaXR5MAMCAQEagb5UaGlzIGNlcnRpZmljYXRlIHdhcyBpc3N1ZWQgYWNjb3Jk\r\naW5nIHRvIHRoZSBDbGFzcyAyIFZhbGlkYXRpb24gcmVxdWlyZW1lbnRzIG9mIHRo\r\nZSBTdGFydENvbSBDQSBwb2xpY3ksIHJlbGlhbmNlIG9ubHkgZm9yIHRoZSBpbnRl\r\nbmRlZCBwdXJwb3NlIGluIGNvbXBsaWFuY2Ugb2YgdGhlIHJlbHlpbmcgcGFydHkg\r\nb2JsaWdhdGlvbnMuMDUGA1UdHwQuMCwwKqAooCaGJGh0dHA6Ly9jcmwuc3RhcnRz\r\nc2wuY29tL2NydDItY3JsLmNybDCBjgYIKwYBBQUHAQEEgYEwfzA5BggrBgEFBQcw\r\nAYYtaHR0cDovL29jc3Auc3RhcnRzc2wuY29tL3N1Yi9jbGFzczIvc2VydmVyL2Nh\r\nMEIGCCsGAQUFBzAChjZodHRwOi8vYWlhLnN0YXJ0c3NsLmNvbS9jZXJ0cy9zdWIu\r\nY2xhc3MyLnNlcnZlci5jYS5jcnQwIwYDVR0SBBwwGoYYaHR0cDovL3d3dy5zdGFy\r\ndHNzbC5jb20vMA0GCSqGSIb3DQEBBQUAA4IBAQDEDbCBvdfs/lEsEJk0xGB6OghO\r\ngUAHplmWJEXoHb0h7p2QAIcs/QKorMDi35bu/J2vbVRIP7i5wBTZLIHBaf03mqAR\r\nZxYFy/ymyROVmmKl1x1/ry1aumomfU86UN8hCkvJc+40V2KC1lCZcjqPWufECjpo\r\n37QXFA+te/rVyVgvoMhIq+zOBZEK0/2mYGCwlpEQo3HElCQIwV9upChOHnQ2jZyF\r\nCmoUyv0JNr8dkh6H7+KTV6FzWaC+b6Liier9bpfEq/zDAp41GR+L/pdbaliSDtRg\r\nfPhAyCZqwsXid6HgIyTBqxUpGDGRiuygqghmqlFfppZuAqz02wrNceDHH7Up\r\n-----END CERTIFICATE-----\r\n',
|
||||
pinned: true,
|
||||
ignoreTLS: false
|
||||
}
|
||||
},
|
||||
gmail: {
|
||||
clientId: clientId || '440907777130.apps.googleusercontent.com',
|
||||
imap: {
|
||||
host: 'imap.gmail.com',
|
||||
port: 993,
|
||||
secure: true,
|
||||
ca: '-----BEGIN CERTIFICATE-----\r\nMIIEBDCCAuygAwIBAgIDAjppMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT\r\nMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i\r\nYWwgQ0EwHhcNMTMwNDA1MTUxNTU1WhcNMTUwNDA0MTUxNTU1WjBJMQswCQYDVQQG\r\nEwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzElMCMGA1UEAxMcR29vZ2xlIEludGVy\r\nbmV0IEF1dGhvcml0eSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\r\nAJwqBHdc2FCROgajguDYUEi8iT/xGXAaiEZ+4I/F8YnOIe5a/mENtzJEiaB0C1NP\r\nVaTOgmKV7utZX8bhBYASxF6UP7xbSDj0U/ck5vuR6RXEz/RTDfRK/J9U3n2+oGtv\r\nh8DQUB8oMANA2ghzUWx//zo8pzcGjr1LEQTrfSTe5vn8MXH7lNVg8y5Kr0LSy+rE\r\nahqyzFPdFUuLH8gZYR/Nnag+YyuENWllhMgZxUYi+FOVvuOAShDGKuy6lyARxzmZ\r\nEASg8GF6lSWMTlJ14rbtCMoU/M4iarNOz0YDl5cDfsCx3nuvRTPPuj5xt970JSXC\r\nDTWJnZ37DhF5iR43xa+OcmkCAwEAAaOB+zCB+DAfBgNVHSMEGDAWgBTAephojYn7\r\nqwVkDBF9qn1luMrMTjAdBgNVHQ4EFgQUSt0GFhu89mi1dvWBtrtiGrpagS8wEgYD\r\nVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwOgYDVR0fBDMwMTAvoC2g\r\nK4YpaHR0cDovL2NybC5nZW90cnVzdC5jb20vY3Jscy9ndGdsb2JhbC5jcmwwPQYI\r\nKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwOi8vZ3RnbG9iYWwtb2NzcC5n\r\nZW90cnVzdC5jb20wFwYDVR0gBBAwDjAMBgorBgEEAdZ5AgUBMA0GCSqGSIb3DQEB\r\nBQUAA4IBAQA21waAESetKhSbOHezI6B1WLuxfoNCunLaHtiONgaX4PCVOzf9G0JY\r\n/iLIa704XtE7JW4S615ndkZAkNoUyHgN7ZVm2o6Gb4ChulYylYbc3GrKBIxbf/a/\r\nzG+FA1jDaFETzf3I93k9mTXwVqO94FntT0QJo544evZG0R0SnU++0ED8Vf4GXjza\r\nHFa9llF7b1cq26KqltyMdMKVvvBulRP/F/A8rLIQjcxz++iPAsbw+zOzlTvjwsto\r\nWHPbqCRiOwY1nQ2pM714A5AuTHhdUDqB1O6gyHA43LL5Z/qHQF1hwFGPa4NrzQU6\r\nyuGnBXj8ytqU0CwIPX4WecigUCAkVDNx\r\n-----END CERTIFICATE-----',
|
||||
pinned: true,
|
||||
ignoreTLS: false
|
||||
},
|
||||
smtp: {
|
||||
host: 'smtp.gmail.com',
|
||||
port: 465,
|
||||
secure: true,
|
||||
ca: '-----BEGIN CERTIFICATE-----\r\nMIIEBDCCAuygAwIBAgIDAjppMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT\r\nMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i\r\nYWwgQ0EwHhcNMTMwNDA1MTUxNTU1WhcNMTUwNDA0MTUxNTU1WjBJMQswCQYDVQQG\r\nEwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzElMCMGA1UEAxMcR29vZ2xlIEludGVy\r\nbmV0IEF1dGhvcml0eSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\r\nAJwqBHdc2FCROgajguDYUEi8iT/xGXAaiEZ+4I/F8YnOIe5a/mENtzJEiaB0C1NP\r\nVaTOgmKV7utZX8bhBYASxF6UP7xbSDj0U/ck5vuR6RXEz/RTDfRK/J9U3n2+oGtv\r\nh8DQUB8oMANA2ghzUWx//zo8pzcGjr1LEQTrfSTe5vn8MXH7lNVg8y5Kr0LSy+rE\r\nahqyzFPdFUuLH8gZYR/Nnag+YyuENWllhMgZxUYi+FOVvuOAShDGKuy6lyARxzmZ\r\nEASg8GF6lSWMTlJ14rbtCMoU/M4iarNOz0YDl5cDfsCx3nuvRTPPuj5xt970JSXC\r\nDTWJnZ37DhF5iR43xa+OcmkCAwEAAaOB+zCB+DAfBgNVHSMEGDAWgBTAephojYn7\r\nqwVkDBF9qn1luMrMTjAdBgNVHQ4EFgQUSt0GFhu89mi1dvWBtrtiGrpagS8wEgYD\r\nVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwOgYDVR0fBDMwMTAvoC2g\r\nK4YpaHR0cDovL2NybC5nZW90cnVzdC5jb20vY3Jscy9ndGdsb2JhbC5jcmwwPQYI\r\nKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwOi8vZ3RnbG9iYWwtb2NzcC5n\r\nZW90cnVzdC5jb20wFwYDVR0gBBAwDjAMBgorBgEEAdZ5AgUBMA0GCSqGSIb3DQEB\r\nBQUAA4IBAQA21waAESetKhSbOHezI6B1WLuxfoNCunLaHtiONgaX4PCVOzf9G0JY\r\n/iLIa704XtE7JW4S615ndkZAkNoUyHgN7ZVm2o6Gb4ChulYylYbc3GrKBIxbf/a/\r\nzG+FA1jDaFETzf3I93k9mTXwVqO94FntT0QJo544evZG0R0SnU++0ED8Vf4GXjza\r\nHFa9llF7b1cq26KqltyMdMKVvvBulRP/F/A8rLIQjcxz++iPAsbw+zOzlTvjwsto\r\nWHPbqCRiOwY1nQ2pM714A5AuTHhdUDqB1O6gyHA43LL5Z/qHQF1hwFGPa4NrzQU6\r\nyuGnBXj8ytqU0CwIPX4WecigUCAkVDNx\r\n-----END CERTIFICATE-----',
|
||||
pinned: true,
|
||||
ignoreTLS: false
|
||||
},
|
||||
ignoreUploadOnSent: true
|
||||
smtp: {
|
||||
host: 'smtp.wmail.io',
|
||||
port: 465,
|
||||
secure: true,
|
||||
ca: '-----BEGIN CERTIFICATE-----\r\nMIIGiTCCBXGgAwIBAgIDAn03MA0GCSqGSIb3DQEBBQUAMIGMMQswCQYDVQQGEwJJ\r\nTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0\r\nYWwgQ2VydGlmaWNhdGUgU2lnbmluZzE4MDYGA1UEAxMvU3RhcnRDb20gQ2xhc3Mg\r\nMiBQcmltYXJ5IEludGVybWVkaWF0ZSBTZXJ2ZXIgQ0EwHhcNMTQwOTIxMTQ0ODQ0\r\nWhcNMTYwOTIxMTE0MzIxWjCBmTEZMBcGA1UEDRMQQ1NIeEdsdlg4Z3lRQ2c3TzEL\r\nMAkGA1UEBhMCREUxDzANBgNVBAgTBkJheWVybjEPMA0GA1UEBxMGTXVuaWNoMRUw\r\nEwYDVQQKEwxUYW5rcmVkIEhhc2UxEzARBgNVBAMUCioud21haWwuaW8xITAfBgkq\r\nhkiG9w0BCQEWEndlYm1hc3RlckB3bWFpbC5pbzCCASIwDQYJKoZIhvcNAQEBBQAD\r\nggEPADCCAQoCggEBAMkbMzFfZLYqfWG8yw2HvuNiFs5ajBFMrjG3fobePE674mPd\r\niFtXqbl3ydQ+umTtbJ6bztgxB3KgrL3lhp6IkD4VxB8YQJoYGhU6YH7FhP4QMm8l\r\ncnFLUZXEbcpcCg1tjL6+vvoTMUWEbV/zNtF/oiJ4AIOKwf0zUMZkTu1FCNrOrvpj\r\n6SAkOdBGzLTOAP5vxP43PfpZPZ4dLL2Be7ENYKXqPs0jSlUWpdT9l4AZG8rHKa2d\r\nccWvRoAsCfvpzGDoMYEx9+a1F1XjNZfzo7yJQcHdaE3Mj1eNqbjZe0+Vmact1S/3\r\nsubdcDVkFSfUMsAB5rH0D5mEhy7sMpt1WWCvOF8CAwEAAaOCAuMwggLfMAkGA1Ud\r\nEwQCMAAwCwYDVR0PBAQDAgOoMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD\r\nATAdBgNVHQ4EFgQUv+9x4PISm5Roq4pdNoaFJeIZFfUwHwYDVR0jBBgwFoAUEdsj\r\nRf1UzGpxb4SKA9e+9wEvJoYwHwYDVR0RBBgwFoIKKi53bWFpbC5pb4IId21haWwu\r\naW8wggFWBgNVHSAEggFNMIIBSTAIBgZngQwBAgIwggE7BgsrBgEEAYG1NwECAzCC\r\nASowLgYIKwYBBQUHAgEWImh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5w\r\nZGYwgfcGCCsGAQUFBwICMIHqMCcWIFN0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0\r\naG9yaXR5MAMCAQEagb5UaGlzIGNlcnRpZmljYXRlIHdhcyBpc3N1ZWQgYWNjb3Jk\r\naW5nIHRvIHRoZSBDbGFzcyAyIFZhbGlkYXRpb24gcmVxdWlyZW1lbnRzIG9mIHRo\r\nZSBTdGFydENvbSBDQSBwb2xpY3ksIHJlbGlhbmNlIG9ubHkgZm9yIHRoZSBpbnRl\r\nbmRlZCBwdXJwb3NlIGluIGNvbXBsaWFuY2Ugb2YgdGhlIHJlbHlpbmcgcGFydHkg\r\nb2JsaWdhdGlvbnMuMDUGA1UdHwQuMCwwKqAooCaGJGh0dHA6Ly9jcmwuc3RhcnRz\r\nc2wuY29tL2NydDItY3JsLmNybDCBjgYIKwYBBQUHAQEEgYEwfzA5BggrBgEFBQcw\r\nAYYtaHR0cDovL29jc3Auc3RhcnRzc2wuY29tL3N1Yi9jbGFzczIvc2VydmVyL2Nh\r\nMEIGCCsGAQUFBzAChjZodHRwOi8vYWlhLnN0YXJ0c3NsLmNvbS9jZXJ0cy9zdWIu\r\nY2xhc3MyLnNlcnZlci5jYS5jcnQwIwYDVR0SBBwwGoYYaHR0cDovL3d3dy5zdGFy\r\ndHNzbC5jb20vMA0GCSqGSIb3DQEBBQUAA4IBAQDEDbCBvdfs/lEsEJk0xGB6OghO\r\ngUAHplmWJEXoHb0h7p2QAIcs/QKorMDi35bu/J2vbVRIP7i5wBTZLIHBaf03mqAR\r\nZxYFy/ymyROVmmKl1x1/ry1aumomfU86UN8hCkvJc+40V2KC1lCZcjqPWufECjpo\r\n37QXFA+te/rVyVgvoMhIq+zOBZEK0/2mYGCwlpEQo3HElCQIwV9upChOHnQ2jZyF\r\nCmoUyv0JNr8dkh6H7+KTV6FzWaC+b6Liier9bpfEq/zDAp41GR+L/pdbaliSDtRg\r\nfPhAyCZqwsXid6HgIyTBqxUpGDGRiuygqghmqlFfppZuAqz02wrNceDHH7Up\r\n-----END CERTIFICATE-----\r\n',
|
||||
pinned: true,
|
||||
ignoreTLS: false
|
||||
}
|
||||
},
|
||||
gmail: {
|
||||
clientId: clientId || '440907777130.apps.googleusercontent.com',
|
||||
imap: {
|
||||
host: 'imap.gmail.com',
|
||||
port: 993,
|
||||
secure: true,
|
||||
ca: '-----BEGIN CERTIFICATE-----\r\nMIIEBDCCAuygAwIBAgIDAjppMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT\r\nMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i\r\nYWwgQ0EwHhcNMTMwNDA1MTUxNTU1WhcNMTUwNDA0MTUxNTU1WjBJMQswCQYDVQQG\r\nEwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzElMCMGA1UEAxMcR29vZ2xlIEludGVy\r\nbmV0IEF1dGhvcml0eSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\r\nAJwqBHdc2FCROgajguDYUEi8iT/xGXAaiEZ+4I/F8YnOIe5a/mENtzJEiaB0C1NP\r\nVaTOgmKV7utZX8bhBYASxF6UP7xbSDj0U/ck5vuR6RXEz/RTDfRK/J9U3n2+oGtv\r\nh8DQUB8oMANA2ghzUWx//zo8pzcGjr1LEQTrfSTe5vn8MXH7lNVg8y5Kr0LSy+rE\r\nahqyzFPdFUuLH8gZYR/Nnag+YyuENWllhMgZxUYi+FOVvuOAShDGKuy6lyARxzmZ\r\nEASg8GF6lSWMTlJ14rbtCMoU/M4iarNOz0YDl5cDfsCx3nuvRTPPuj5xt970JSXC\r\nDTWJnZ37DhF5iR43xa+OcmkCAwEAAaOB+zCB+DAfBgNVHSMEGDAWgBTAephojYn7\r\nqwVkDBF9qn1luMrMTjAdBgNVHQ4EFgQUSt0GFhu89mi1dvWBtrtiGrpagS8wEgYD\r\nVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwOgYDVR0fBDMwMTAvoC2g\r\nK4YpaHR0cDovL2NybC5nZW90cnVzdC5jb20vY3Jscy9ndGdsb2JhbC5jcmwwPQYI\r\nKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwOi8vZ3RnbG9iYWwtb2NzcC5n\r\nZW90cnVzdC5jb20wFwYDVR0gBBAwDjAMBgorBgEEAdZ5AgUBMA0GCSqGSIb3DQEB\r\nBQUAA4IBAQA21waAESetKhSbOHezI6B1WLuxfoNCunLaHtiONgaX4PCVOzf9G0JY\r\n/iLIa704XtE7JW4S615ndkZAkNoUyHgN7ZVm2o6Gb4ChulYylYbc3GrKBIxbf/a/\r\nzG+FA1jDaFETzf3I93k9mTXwVqO94FntT0QJo544evZG0R0SnU++0ED8Vf4GXjza\r\nHFa9llF7b1cq26KqltyMdMKVvvBulRP/F/A8rLIQjcxz++iPAsbw+zOzlTvjwsto\r\nWHPbqCRiOwY1nQ2pM714A5AuTHhdUDqB1O6gyHA43LL5Z/qHQF1hwFGPa4NrzQU6\r\nyuGnBXj8ytqU0CwIPX4WecigUCAkVDNx\r\n-----END CERTIFICATE-----',
|
||||
pinned: true,
|
||||
ignoreTLS: false
|
||||
},
|
||||
yahoo: {
|
||||
imap: {
|
||||
host: 'imap.mail.yahoo.com',
|
||||
port: 993,
|
||||
secure: true,
|
||||
ca: '-----BEGIN CERTIFICATE-----\r\nMIIFUjCCBDqgAwIBAgIQdThnkckQvgouzHOsQA7ctTANBgkqhkiG9w0BAQUFADCB\r\ntTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL\r\nExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2Ug\r\nYXQgaHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYSAoYykxMDEvMC0GA1UEAxMm\r\nVmVyaVNpZ24gQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzMwHhcNMTQwNDIy\r\nMDAwMDAwWhcNMTUwNDIzMjM1OTU5WjCBjDELMAkGA1UEBhMCVVMxEzARBgNVBAgT\r\nCkNhbGlmb3JuaWExEjAQBgNVBAcUCVN1bm55dmFsZTETMBEGA1UEChQKWWFob28g\r\nSW5jLjEfMB0GA1UECxQWSW5mb3JtYXRpb24gVGVjaG5vbG9neTEeMBwGA1UEAxQV\r\nKi5pbWFwLm1haWwueWFob28uY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\r\nCgKCAQEAw36HN2sgtMNQ0TZlqGgfqInK/UT6y3ZgqDdmFRU9D5D5675hfxcwJoS+\r\nb0hIn/UixbZDpSLhNkjAkOTAbFEFWar7628D2dU5WtCUlFMiwg2TA0Un8B9EbUi5\r\nwDrqzXDyABVnBVR5I2eKwr5cuB9ldjxAabcCyqQhVKdH0+IskRpUrvxAb84uQtJg\r\nJyNieOZAWdxg9fkubk1YKw/MgJHnaY8P4lUlYY8fIY39d6gW6My8oT0IersrH1X1\r\n/oCmqUIGM1PawXBvvpPKYdI4fCH75/UaEQ41BFSUn1NsinFYZUPlVcBCOvLFEOQi\r\nuU+4Tjybq3x7NNhd3uBxfm4jo4h5zQIDAQABo4IBgzCCAX8wNQYDVR0RBC4wLIIV\r\nKi5pbWFwLm1haWwueWFob28uY29tghNpbWFwLm1haWwueWFob28uY29tMAkGA1Ud\r\nEwQCMAAwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEF\r\nBQcDAjBlBgNVHSAEXjBcMFoGCmCGSAGG+EUBBzYwTDAjBggrBgEFBQcCARYXaHR0\r\ncHM6Ly9kLnN5bWNiLmNvbS9jcHMwJQYIKwYBBQUHAgIwGRoXaHR0cHM6Ly9kLnN5\r\nbWNiLmNvbS9ycGEwHwYDVR0jBBgwFoAUDURcFlNEwYJ+HSCrJfQBY9i+eaUwKwYD\r\nVR0fBCQwIjAgoB6gHIYaaHR0cDovL3NkLnN5bWNiLmNvbS9zZC5jcmwwVwYIKwYB\r\nBQUHAQEESzBJMB8GCCsGAQUFBzABhhNodHRwOi8vc2Quc3ltY2QuY29tMCYGCCsG\r\nAQUFBzAChhpodHRwOi8vc2Quc3ltY2IuY29tL3NkLmNydDANBgkqhkiG9w0BAQUF\r\nAAOCAQEAVxsglXJTtBTCoTwOd6j0iJQ+P9cxFVqHcmbshEfEQBlPwr4Sp9tLJ4kj\r\nfVi0XorWU6e6e57dtYtxpcPz+6WNSNKT0B0IBOTUTIBwSLJMHxEZI6gSS/fo1agt\r\n81B06rB8Rhn4yHwyDO/9uRvXbNYiEgpa5e6gIpXY6h6p1HscQMcuROaUA9ETvGd8\r\nDKG4XSZE7QAF9iB9WSLa/IQUD4sGMDaMp2q4XkoWZTnyL1bEDKwUvw9Z17PxVmrF\r\n8c7S5HTNU+1kyZw2LJRu3SgtsYXSWA88WFiKUPuqU+EBXmbrwLAwLAJ6mVc2bGFC\r\ng5fLGbtTscaARBlb1u3Iee2Fd419jg==\r\n-----END CERTIFICATE-----',
|
||||
pinned: true,
|
||||
ignoreTLS: false
|
||||
},
|
||||
smtp: {
|
||||
host: 'smtp.mail.yahoo.com',
|
||||
port: 465,
|
||||
secure: true,
|
||||
ca: '-----BEGIN CERTIFICATE-----\r\nMIIHITCCBgmgAwIBAgIQBlVSvxlwsqw8Kc8eVV5EKTANBgkqhkiG9w0BAQUFADBm\r\nMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\r\nd3cuZGlnaWNlcnQuY29tMSUwIwYDVQQDExxEaWdpQ2VydCBIaWdoIEFzc3VyYW5j\r\nZSBDQS0zMB4XDTE0MDYwNTAwMDAwMFoXDTE1MDYyMzEyMDAwMFowYjELMAkGA1UE\r\nBhMCVVMxCzAJBgNVBAgTAkNBMRIwEAYDVQQHEwlTdW5ueXZhbGUxFDASBgNVBAoM\r\nC1lhaG9vISBJbmMuMRwwGgYDVQQDExNzbXRwLm1haWwueWFob28uY29tMIIBIjAN\r\nBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA339OSxTIuW6NVqKwYHFBbg4/tmEx\r\nMYpzohtObPaWgvLTsYdce+WlQFBU5rxaCMUtVsVAGJWs5uQRDVHXRLPnOWe+foqZ\r\n5ItgjYIQc1XeMv6BZEeaF3Bum5ehUyLB3y48cjbWxma1QlkZ8XAK0f9AS7ySWAen\r\no5OXJlMFAqXGtKMAhY46dzSY0wjgdrvgiomFRy0iQKV1DxGsXoXSMEszlUTQoNQ3\r\nWTsiA3O//cdWE06wWeA3/90cb7QkU3KflSoyfi878BQGpPR1L+rLNsqnc8QuucbD\r\nz4Q++rxGqgg4QYrOtmZfAn96TXhPWCwKld6FN+f3uV5ITOBFx44M5v1ILQIDAQAB\r\no4IDzTCCA8kwHwYDVR0jBBgwFoAUUOpzidsp+xCPnuUBINTeeZlIg/cwHQYDVR0O\r\nBBYEFEhOpSFQjO/QrSNfVwyInZUhyzhsMIICJAYDVR0RBIICGzCCAheCE3NtdHAu\r\nbWFpbC55YWhvby5jb22CFnNtdHAubWFpbC55YWhvby5jb20uYXKCFnNtdHAubWFp\r\nbC55YWhvby5jb20uYXWCFnNtdHAubWFpbC55YWhvby5jb20uYnKCFnNtdHAubWFp\r\nbC55YWhvby5jb20uY26CFnNtdHAubWFpbC55YWhvby5jb20uaGuCFnNtdHAubWFp\r\nbC55YWhvby5jb20ubXmCFnNtdHAubWFpbC55YWhvby5jb20ucGiCFnNtdHAubWFp\r\nbC55YWhvby5jb20uc2eCFnNtdHAubWFpbC55YWhvby5jb20udHeCFnNtdHAubWFp\r\nbC55YWhvby5jb20udm6CFXNtdHAubWFpbC55YWhvby5jby5pZIIVc210cC5tYWls\r\nLnlhaG9vLmNvLmlughVzbXRwLm1haWwueWFob28uY28ua3KCFXNtdHAubWFpbC55\r\nYWhvby5jby50aIIVc210cC5tYWlsLnlhaG9vLmNvLnVrghJzbXRwLm1haWwueWFo\r\nb28uY2GCEnNtdHAubWFpbC55YWhvby5jboISc210cC5tYWlsLnlhaG9vLmRlghJz\r\nbXRwLm1haWwueWFob28uZXOCEnNtdHAubWFpbC55YWhvby5mcoISc210cC5tYWls\r\nLnlhaG9vLml0gg9zbXRwLnk3bWFpbC5jb22CFHNtdHAuY29ycmVvLnlhaG9vLmVz\r\nMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw\r\nYQYDVR0fBFowWDAqoCigJoYkaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL2NhMy1n\r\nMjguY3JsMCqgKKAmhiRodHRwOi8vY3JsNC5kaWdpY2VydC5jb20vY2EzLWcyOC5j\r\ncmwwQgYDVR0gBDswOTA3BglghkgBhv1sAQEwKjAoBggrBgEFBQcCARYcaHR0cHM6\r\nLy93d3cuZGlnaWNlcnQuY29tL0NQUzB7BggrBgEFBQcBAQRvMG0wJAYIKwYBBQUH\r\nMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBFBggrBgEFBQcwAoY5aHR0cDov\r\nL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0SGlnaEFzc3VyYW5jZUNBLTMu\r\nY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQEFBQADggEBABshjUND4hADHGfe\r\ncV9XGXcr9h3u7mT5kgdcgGFhcrFAlwkzt0NUCbuN0X8DWHE72Tpb3zRE25CmUUJe\r\nktBbq9PQb4b5/Wt1htAEw0qUs3BsUbqejK9OHJ/11Jn2ek4+SLJuYlijzc7KM3F/\r\nyz7ZTZtKR0PglkXfqbpvWYGabYpfL2FRLoJ7alTLsMJcFxbSLYcAIMxufj7RyTBJ\r\nbKgRJl4wmP4+Zc2Q1p59mENY0u5HqVAAOmWc0jNb0/31+tRr5f6EgXxK++7TQOpF\r\n0TOaFsXlzRlpKfmIbzVr2nfwghV5/bRZj96TK3g1OoOz4C8ksK4INHnUdTAqZ18M\r\nvHpnJw4=\r\n-----END CERTIFICATE-----',
|
||||
pinned: true,
|
||||
ignoreTLS: false
|
||||
}
|
||||
smtp: {
|
||||
host: 'smtp.gmail.com',
|
||||
port: 465,
|
||||
secure: true,
|
||||
ca: '-----BEGIN CERTIFICATE-----\r\nMIIEBDCCAuygAwIBAgIDAjppMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT\r\nMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i\r\nYWwgQ0EwHhcNMTMwNDA1MTUxNTU1WhcNMTUwNDA0MTUxNTU1WjBJMQswCQYDVQQG\r\nEwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzElMCMGA1UEAxMcR29vZ2xlIEludGVy\r\nbmV0IEF1dGhvcml0eSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\r\nAJwqBHdc2FCROgajguDYUEi8iT/xGXAaiEZ+4I/F8YnOIe5a/mENtzJEiaB0C1NP\r\nVaTOgmKV7utZX8bhBYASxF6UP7xbSDj0U/ck5vuR6RXEz/RTDfRK/J9U3n2+oGtv\r\nh8DQUB8oMANA2ghzUWx//zo8pzcGjr1LEQTrfSTe5vn8MXH7lNVg8y5Kr0LSy+rE\r\nahqyzFPdFUuLH8gZYR/Nnag+YyuENWllhMgZxUYi+FOVvuOAShDGKuy6lyARxzmZ\r\nEASg8GF6lSWMTlJ14rbtCMoU/M4iarNOz0YDl5cDfsCx3nuvRTPPuj5xt970JSXC\r\nDTWJnZ37DhF5iR43xa+OcmkCAwEAAaOB+zCB+DAfBgNVHSMEGDAWgBTAephojYn7\r\nqwVkDBF9qn1luMrMTjAdBgNVHQ4EFgQUSt0GFhu89mi1dvWBtrtiGrpagS8wEgYD\r\nVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwOgYDVR0fBDMwMTAvoC2g\r\nK4YpaHR0cDovL2NybC5nZW90cnVzdC5jb20vY3Jscy9ndGdsb2JhbC5jcmwwPQYI\r\nKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwOi8vZ3RnbG9iYWwtb2NzcC5n\r\nZW90cnVzdC5jb20wFwYDVR0gBBAwDjAMBgorBgEEAdZ5AgUBMA0GCSqGSIb3DQEB\r\nBQUAA4IBAQA21waAESetKhSbOHezI6B1WLuxfoNCunLaHtiONgaX4PCVOzf9G0JY\r\n/iLIa704XtE7JW4S615ndkZAkNoUyHgN7ZVm2o6Gb4ChulYylYbc3GrKBIxbf/a/\r\nzG+FA1jDaFETzf3I93k9mTXwVqO94FntT0QJo544evZG0R0SnU++0ED8Vf4GXjza\r\nHFa9llF7b1cq26KqltyMdMKVvvBulRP/F/A8rLIQjcxz++iPAsbw+zOzlTvjwsto\r\nWHPbqCRiOwY1nQ2pM714A5AuTHhdUDqB1O6gyHA43LL5Z/qHQF1hwFGPa4NrzQU6\r\nyuGnBXj8ytqU0CwIPX4WecigUCAkVDNx\r\n-----END CERTIFICATE-----',
|
||||
pinned: true,
|
||||
ignoreTLS: false
|
||||
},
|
||||
tonline: {
|
||||
imap: {
|
||||
host: 'secureimap.t-online.de',
|
||||
port: 993,
|
||||
secure: true,
|
||||
ca: '-----BEGIN CERTIFICATE-----\r\nMIIGmzCCBYOgAwIBAgIIIbZ3foy9DqgwDQYJKoZIhvcNAQEFBQAwcTELMAkGA1UE\r\nBhMCREUxHDAaBgNVBAoTE0RldXRzY2hlIFRlbGVrb20gQUcxHzAdBgNVBAsTFlQt\r\nVGVsZVNlYyBUcnVzdCBDZW50ZXIxIzAhBgNVBAMTGkRldXRzY2hlIFRlbGVrb20g\r\nUm9vdCBDQSAyMB4XDTEzMDMwMTEzNTgyOVoXDTE5MDcwOTIzNTkwMFowgckxCzAJ\r\nBgNVBAYTAkRFMSUwIwYDVQQKExxULVN5c3RlbXMgSW50ZXJuYXRpb25hbCBHbWJI\r\nMR8wHQYDVQQLExZULVN5c3RlbXMgVHJ1c3QgQ2VudGVyMQwwCgYDVQQIEwNOUlcx\r\nDjAMBgNVBBETBTU3MjUwMRAwDgYDVQQHEwdOZXRwaGVuMSAwHgYDVQQJExdVbnRl\r\ncmUgSW5kdXN0cmllc3RyLiAyMDEgMB4GA1UEAxMXVGVsZVNlYyBTZXJ2ZXJQYXNz\r\nIERFLTEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCwg+9QiuYAxX9/\r\n4F9XRZrS1o0q+aa9L/5/K3vc+RqPpliiZ24vTkJc1JDpXrbWXS25uT3yzHukQrhI\r\nq0AbcRqNEAeFi5EhUiEM/vtb4BYGHdqfXQ3etgYYcCtJ43NAHaSgsyQ9kyGV2lmM\r\nwkeAX3qZ2CGE9/cR6w+bOogHArBdk2JaHG09myNZDytr6oUbWLjLd/qhC9YzyZSX\r\nbZgE/kh5L5Y6P9paw2pDdn7+Ni4pXzlmoj1k43uiz+h2ibe3DO9dKMZAaEKeyG1O\r\ng0f0r53M8O+8Bm2sXtWelrAgrfFlISgWzO1hkNs12rWpr4c5Ygde/behx9OQmPwp\r\nmS+e3WvTAgMBAAGjggLcMIIC2DAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFGJP\r\nE842Z4TNGfygTxmL7xVUATIcMB8GA1UdIwQYMBaAFDHDeRu69VPXF+CJei0XbAqz\r\nK50zMBIGA1UdEwEB/wQIMAYBAf8CAQAwWQYDVR0gBFIwUDBEBgkrBgEEAb1HDQIw\r\nNzA1BggrBgEFBQcCARYpaHR0cDovL3d3dy50ZWxlc2VjLmRlL3NlcnZlcnBhc3Mv\r\nY3BzLmh0bWwwCAYGZ4EMAQICMIHvBgNVHR8EgecwgeQwOqA4oDaGNGh0dHA6Ly9j\r\ncmwuc2VydmVycGFzcy50ZWxlc2VjLmRlL3JsL0RUX1JPT1RfQ0FfMi5jcmwwgaWg\r\ngaKggZ+GgZxsZGFwOi8vbGRhcC5zZXJ2ZXJwYXNzLnRlbGVzZWMuZGUvQ049RGV1\r\ndHNjaGUlMjBUZWxla29tJTIwUm9vdCUyMENBJTIwMixPVT1ULVRlbGVTZWMlMjBU\r\ncnVzdCUyMENlbnRlcixPPURldXRzY2hlJTIwVGVsZWtvbSUyMEFHLEM9REU/QXV0\r\naG9yaXR5UmV2b2NhdGlvbkxpc3QwggEjBggrBgEFBQcBAQSCARUwggERMCoGCCsG\r\nAQUFBzABhh5odHRwOi8vb2NzcDAyLnRlbGVzZWMuZGUvb2NzcHIwQQYIKwYBBQUH\r\nMAKGNWh0dHA6Ly9jcmwuc2VydmVycGFzcy50ZWxlc2VjLmRlL2NydC9EVF9ST09U\r\nX0NBXzIuY2VyMIGfBggrBgEFBQcwAoaBkmxkYXA6Ly9sZGFwLnNlcnZlcnBhc3Mu\r\ndGVsZXNlYy5kZS9DTj1EZXV0c2NoZSUyMFRlbGVrb20lMjBSb290JTIwQ0ElMjAy\r\nLE9VPVQtVGVsZVNlYyUyMFRydXN0JTIwQ2VudGVyLE89RGV1dHNjaGUlMjBUZWxl\r\na29tJTIwQUcsQz1ERT9jQUNlcnRpZmljYXRlMA0GCSqGSIb3DQEBBQUAA4IBAQBO\r\nE04qoEkEc9ad+WwSurVYfcDdjGvpqrtbI89woXDsWLQTMhA7D7jVuls90SJns0vc\r\nK9qoYkEGt0/ZlawLe2lyNWtueHfUf+dgleUunwHYLxuj3jQ2ERzQLVLrswjecRpX\r\nvGAGej89WpGQ9PMq27WGNC5WCmzVC9rk5naFgacsbwKwyjU0LoBArtAQnAAlpHDw\r\nPenv1Pe7MhUkCK0LqdTvkI/AHFzPYg/l5E3j8lQQ8hiKx8U6wf9xVKECLA2RlRqY\r\nUX2rpjQNxnvEq/mEQv3x3mLOEFJ3TAKI+soDgOOi0OG8+ywhm6S+7Z9lTlJ+BcD6\r\noy1MNKd4CQbltHLMTFUH\r\n-----END CERTIFICATE-----',
|
||||
pinned: true,
|
||||
ignoreTLS: false
|
||||
},
|
||||
smtp: {
|
||||
host: 'securesmtp.t-online.de',
|
||||
port: 465,
|
||||
secure: true,
|
||||
ca: '-----BEGIN CERTIFICATE-----\r\nMIIGmzCCBYOgAwIBAgIIIbZ3foy9DqgwDQYJKoZIhvcNAQEFBQAwcTELMAkGA1UE\r\nBhMCREUxHDAaBgNVBAoTE0RldXRzY2hlIFRlbGVrb20gQUcxHzAdBgNVBAsTFlQt\r\nVGVsZVNlYyBUcnVzdCBDZW50ZXIxIzAhBgNVBAMTGkRldXRzY2hlIFRlbGVrb20g\r\nUm9vdCBDQSAyMB4XDTEzMDMwMTEzNTgyOVoXDTE5MDcwOTIzNTkwMFowgckxCzAJ\r\nBgNVBAYTAkRFMSUwIwYDVQQKExxULVN5c3RlbXMgSW50ZXJuYXRpb25hbCBHbWJI\r\nMR8wHQYDVQQLExZULVN5c3RlbXMgVHJ1c3QgQ2VudGVyMQwwCgYDVQQIEwNOUlcx\r\nDjAMBgNVBBETBTU3MjUwMRAwDgYDVQQHEwdOZXRwaGVuMSAwHgYDVQQJExdVbnRl\r\ncmUgSW5kdXN0cmllc3RyLiAyMDEgMB4GA1UEAxMXVGVsZVNlYyBTZXJ2ZXJQYXNz\r\nIERFLTEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCwg+9QiuYAxX9/\r\n4F9XRZrS1o0q+aa9L/5/K3vc+RqPpliiZ24vTkJc1JDpXrbWXS25uT3yzHukQrhI\r\nq0AbcRqNEAeFi5EhUiEM/vtb4BYGHdqfXQ3etgYYcCtJ43NAHaSgsyQ9kyGV2lmM\r\nwkeAX3qZ2CGE9/cR6w+bOogHArBdk2JaHG09myNZDytr6oUbWLjLd/qhC9YzyZSX\r\nbZgE/kh5L5Y6P9paw2pDdn7+Ni4pXzlmoj1k43uiz+h2ibe3DO9dKMZAaEKeyG1O\r\ng0f0r53M8O+8Bm2sXtWelrAgrfFlISgWzO1hkNs12rWpr4c5Ygde/behx9OQmPwp\r\nmS+e3WvTAgMBAAGjggLcMIIC2DAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFGJP\r\nE842Z4TNGfygTxmL7xVUATIcMB8GA1UdIwQYMBaAFDHDeRu69VPXF+CJei0XbAqz\r\nK50zMBIGA1UdEwEB/wQIMAYBAf8CAQAwWQYDVR0gBFIwUDBEBgkrBgEEAb1HDQIw\r\nNzA1BggrBgEFBQcCARYpaHR0cDovL3d3dy50ZWxlc2VjLmRlL3NlcnZlcnBhc3Mv\r\nY3BzLmh0bWwwCAYGZ4EMAQICMIHvBgNVHR8EgecwgeQwOqA4oDaGNGh0dHA6Ly9j\r\ncmwuc2VydmVycGFzcy50ZWxlc2VjLmRlL3JsL0RUX1JPT1RfQ0FfMi5jcmwwgaWg\r\ngaKggZ+GgZxsZGFwOi8vbGRhcC5zZXJ2ZXJwYXNzLnRlbGVzZWMuZGUvQ049RGV1\r\ndHNjaGUlMjBUZWxla29tJTIwUm9vdCUyMENBJTIwMixPVT1ULVRlbGVTZWMlMjBU\r\ncnVzdCUyMENlbnRlcixPPURldXRzY2hlJTIwVGVsZWtvbSUyMEFHLEM9REU/QXV0\r\naG9yaXR5UmV2b2NhdGlvbkxpc3QwggEjBggrBgEFBQcBAQSCARUwggERMCoGCCsG\r\nAQUFBzABhh5odHRwOi8vb2NzcDAyLnRlbGVzZWMuZGUvb2NzcHIwQQYIKwYBBQUH\r\nMAKGNWh0dHA6Ly9jcmwuc2VydmVycGFzcy50ZWxlc2VjLmRlL2NydC9EVF9ST09U\r\nX0NBXzIuY2VyMIGfBggrBgEFBQcwAoaBkmxkYXA6Ly9sZGFwLnNlcnZlcnBhc3Mu\r\ndGVsZXNlYy5kZS9DTj1EZXV0c2NoZSUyMFRlbGVrb20lMjBSb290JTIwQ0ElMjAy\r\nLE9VPVQtVGVsZVNlYyUyMFRydXN0JTIwQ2VudGVyLE89RGV1dHNjaGUlMjBUZWxl\r\na29tJTIwQUcsQz1ERT9jQUNlcnRpZmljYXRlMA0GCSqGSIb3DQEBBQUAA4IBAQBO\r\nE04qoEkEc9ad+WwSurVYfcDdjGvpqrtbI89woXDsWLQTMhA7D7jVuls90SJns0vc\r\nK9qoYkEGt0/ZlawLe2lyNWtueHfUf+dgleUunwHYLxuj3jQ2ERzQLVLrswjecRpX\r\nvGAGej89WpGQ9PMq27WGNC5WCmzVC9rk5naFgacsbwKwyjU0LoBArtAQnAAlpHDw\r\nPenv1Pe7MhUkCK0LqdTvkI/AHFzPYg/l5E3j8lQQ8hiKx8U6wf9xVKECLA2RlRqY\r\nUX2rpjQNxnvEq/mEQv3x3mLOEFJ3TAKI+soDgOOi0OG8+ywhm6S+7Z9lTlJ+BcD6\r\noy1MNKd4CQbltHLMTFUH\r\n-----END CERTIFICATE-----',
|
||||
pinned: true,
|
||||
ignoreTLS: false
|
||||
}
|
||||
ignoreUploadOnSent: true
|
||||
},
|
||||
yahoo: {
|
||||
imap: {
|
||||
host: 'imap.mail.yahoo.com',
|
||||
port: 993,
|
||||
secure: true,
|
||||
ca: '-----BEGIN CERTIFICATE-----\r\nMIIFUjCCBDqgAwIBAgIQdThnkckQvgouzHOsQA7ctTANBgkqhkiG9w0BAQUFADCB\r\ntTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL\r\nExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2Ug\r\nYXQgaHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYSAoYykxMDEvMC0GA1UEAxMm\r\nVmVyaVNpZ24gQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzMwHhcNMTQwNDIy\r\nMDAwMDAwWhcNMTUwNDIzMjM1OTU5WjCBjDELMAkGA1UEBhMCVVMxEzARBgNVBAgT\r\nCkNhbGlmb3JuaWExEjAQBgNVBAcUCVN1bm55dmFsZTETMBEGA1UEChQKWWFob28g\r\nSW5jLjEfMB0GA1UECxQWSW5mb3JtYXRpb24gVGVjaG5vbG9neTEeMBwGA1UEAxQV\r\nKi5pbWFwLm1haWwueWFob28uY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\r\nCgKCAQEAw36HN2sgtMNQ0TZlqGgfqInK/UT6y3ZgqDdmFRU9D5D5675hfxcwJoS+\r\nb0hIn/UixbZDpSLhNkjAkOTAbFEFWar7628D2dU5WtCUlFMiwg2TA0Un8B9EbUi5\r\nwDrqzXDyABVnBVR5I2eKwr5cuB9ldjxAabcCyqQhVKdH0+IskRpUrvxAb84uQtJg\r\nJyNieOZAWdxg9fkubk1YKw/MgJHnaY8P4lUlYY8fIY39d6gW6My8oT0IersrH1X1\r\n/oCmqUIGM1PawXBvvpPKYdI4fCH75/UaEQ41BFSUn1NsinFYZUPlVcBCOvLFEOQi\r\nuU+4Tjybq3x7NNhd3uBxfm4jo4h5zQIDAQABo4IBgzCCAX8wNQYDVR0RBC4wLIIV\r\nKi5pbWFwLm1haWwueWFob28uY29tghNpbWFwLm1haWwueWFob28uY29tMAkGA1Ud\r\nEwQCMAAwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEF\r\nBQcDAjBlBgNVHSAEXjBcMFoGCmCGSAGG+EUBBzYwTDAjBggrBgEFBQcCARYXaHR0\r\ncHM6Ly9kLnN5bWNiLmNvbS9jcHMwJQYIKwYBBQUHAgIwGRoXaHR0cHM6Ly9kLnN5\r\nbWNiLmNvbS9ycGEwHwYDVR0jBBgwFoAUDURcFlNEwYJ+HSCrJfQBY9i+eaUwKwYD\r\nVR0fBCQwIjAgoB6gHIYaaHR0cDovL3NkLnN5bWNiLmNvbS9zZC5jcmwwVwYIKwYB\r\nBQUHAQEESzBJMB8GCCsGAQUFBzABhhNodHRwOi8vc2Quc3ltY2QuY29tMCYGCCsG\r\nAQUFBzAChhpodHRwOi8vc2Quc3ltY2IuY29tL3NkLmNydDANBgkqhkiG9w0BAQUF\r\nAAOCAQEAVxsglXJTtBTCoTwOd6j0iJQ+P9cxFVqHcmbshEfEQBlPwr4Sp9tLJ4kj\r\nfVi0XorWU6e6e57dtYtxpcPz+6WNSNKT0B0IBOTUTIBwSLJMHxEZI6gSS/fo1agt\r\n81B06rB8Rhn4yHwyDO/9uRvXbNYiEgpa5e6gIpXY6h6p1HscQMcuROaUA9ETvGd8\r\nDKG4XSZE7QAF9iB9WSLa/IQUD4sGMDaMp2q4XkoWZTnyL1bEDKwUvw9Z17PxVmrF\r\n8c7S5HTNU+1kyZw2LJRu3SgtsYXSWA88WFiKUPuqU+EBXmbrwLAwLAJ6mVc2bGFC\r\ng5fLGbtTscaARBlb1u3Iee2Fd419jg==\r\n-----END CERTIFICATE-----',
|
||||
pinned: true,
|
||||
ignoreTLS: false
|
||||
},
|
||||
outlook: {
|
||||
imap: {
|
||||
host: 'imap-mail.outlook.com',
|
||||
port: 993,
|
||||
secure: true,
|
||||
ca: '-----BEGIN CERTIFICATE-----\r\nMIIFQjCCBCqgAwIBAgISESHl0vjrML7zKmGlv42YL75vMA0GCSqGSIb3DQEBBQUA\r\nMF0xCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMTMwMQYD\r\nVQQDEypHbG9iYWxTaWduIE9yZ2FuaXphdGlvbiBWYWxpZGF0aW9uIENBIC0gRzIw\r\nHhcNMTMwNDI0MjAzNTA5WhcNMTYwNDI0MjAzNTA5WjBsMQswCQYDVQQGEwJVUzET\r\nMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV\r\nTWljcm9zb2Z0IENvcnBvcmF0aW9uMRYwFAYDVQQDDA0qLmhvdG1haWwuY29tMIIB\r\nIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAumSiBWrzHZf6WFP5a/j4+K7D\r\n1izLoYKj5Omll0pdxKvKcBRDf+iaIkCbSOPNpx2uWGZdwNwkabYCQavaBf2ebwmS\r\nS8i1CJpHflO+k0qYd5WUi7sSsZ3+6RaCMdLoDIPGyYMQuy7TFtVO7LSt5+qscyyi\r\nET8c3lE2aj/XW13UZvRrV65ZJvMjUtwaDnIcAxGeasYoebLsKdqHQ2uTr4PmNwCc\r\nviGVFSOzkGAoC0PfyqKB2xUWy3Kc5zRI2xvUW8Jb2b/9Ze3g55pIUzKsjpglkQTm\r\nedVPSYYPGNz6Kl/ZshBXdBAk398q1JkSmUaTMa2hJgBbcC+73ax40AJDGJlz+QID\r\nAQABo4IB6zCCAecwDgYDVR0PAQH/BAQDAgWgMEkGA1UdIARCMEAwPgYGZ4EMAQIC\r\nMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8vd3d3Lmdsb2JhbHNpZ24uY29tL3JlcG9z\r\naXRvcnkvMEAGA1UdEQQ5MDeCDSouaG90bWFpbC5jb22CCioubGl2ZS5jb22CDSou\r\nb3V0bG9vay5jb22CC2hvdG1haWwuY29tMAkGA1UdEwQCMAAwHQYDVR0lBBYwFAYI\r\nKwYBBQUHAwEGCCsGAQUFBwMCMEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly9jcmwu\r\nZ2xvYmFsc2lnbi5jb20vZ3MvZ3Nvcmdhbml6YXRpb252YWxnMi5jcmwwgZYGCCsG\r\nAQUFBwEBBIGJMIGGMEcGCCsGAQUFBzAChjtodHRwOi8vc2VjdXJlLmdsb2JhbHNp\r\nZ24uY29tL2NhY2VydC9nc29yZ2FuaXphdGlvbnZhbGcyLmNydDA7BggrBgEFBQcw\r\nAYYvaHR0cDovL29jc3AyLmdsb2JhbHNpZ24uY29tL2dzb3JnYW5pemF0aW9udmFs\r\nZzIwHQYDVR0OBBYEFHbgHqTLsXDt7uMRyE62rnDEfLn9MB8GA1UdIwQYMBaAFF1G\r\nso3ES3Qcu+31c7Y6tziPdZ5+MA0GCSqGSIb3DQEBBQUAA4IBAQByy1+3N6ZRVooI\r\nxqw8Ng+UFz0g7UHkbPEnvTu1uxJ2AojFuP/P1PAk+/6uMRvpPlWg/5uqmOIWxKxJ\r\nLo6xSbkDf4LN+KYwes3XSuPyziZ4QbPnehHhZ0377iiA8fpRJADg9NWKCRHh5aAd\r\ne9QvJUW/GgYkBN+F4yYc2jIjR3Rehv4JYOKS3iXO9OoHsDS2CcCFaS2imgQVfYLg\r\nslBwT/A08PCOhW5huiluSmih7x5Qf7sFDv8jineu6ehKzi8pKnOq4k8G4QiWn38Y\r\nCeiBkkwFOwj7T3M/ITiiSS9DHDGeokj16eBi83Zx3YYiJ9YZvnQ+4GvqJ5eJJ6pR\r\nKKvemr+m\r\n-----END CERTIFICATE-----',
|
||||
pinned: true,
|
||||
ignoreTLS: false
|
||||
},
|
||||
smtp: {
|
||||
host: 'smtp-mail.outlook.com',
|
||||
port: 587,
|
||||
secure: false,
|
||||
ca: '-----BEGIN CERTIFICATE-----\r\nMIIFQjCCBCqgAwIBAgISESHl0vjrML7zKmGlv42YL75vMA0GCSqGSIb3DQEBBQUA\r\nMF0xCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMTMwMQYD\r\nVQQDEypHbG9iYWxTaWduIE9yZ2FuaXphdGlvbiBWYWxpZGF0aW9uIENBIC0gRzIw\r\nHhcNMTMwNDI0MjAzNTA5WhcNMTYwNDI0MjAzNTA5WjBsMQswCQYDVQQGEwJVUzET\r\nMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV\r\nTWljcm9zb2Z0IENvcnBvcmF0aW9uMRYwFAYDVQQDDA0qLmhvdG1haWwuY29tMIIB\r\nIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAumSiBWrzHZf6WFP5a/j4+K7D\r\n1izLoYKj5Omll0pdxKvKcBRDf+iaIkCbSOPNpx2uWGZdwNwkabYCQavaBf2ebwmS\r\nS8i1CJpHflO+k0qYd5WUi7sSsZ3+6RaCMdLoDIPGyYMQuy7TFtVO7LSt5+qscyyi\r\nET8c3lE2aj/XW13UZvRrV65ZJvMjUtwaDnIcAxGeasYoebLsKdqHQ2uTr4PmNwCc\r\nviGVFSOzkGAoC0PfyqKB2xUWy3Kc5zRI2xvUW8Jb2b/9Ze3g55pIUzKsjpglkQTm\r\nedVPSYYPGNz6Kl/ZshBXdBAk398q1JkSmUaTMa2hJgBbcC+73ax40AJDGJlz+QID\r\nAQABo4IB6zCCAecwDgYDVR0PAQH/BAQDAgWgMEkGA1UdIARCMEAwPgYGZ4EMAQIC\r\nMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8vd3d3Lmdsb2JhbHNpZ24uY29tL3JlcG9z\r\naXRvcnkvMEAGA1UdEQQ5MDeCDSouaG90bWFpbC5jb22CCioubGl2ZS5jb22CDSou\r\nb3V0bG9vay5jb22CC2hvdG1haWwuY29tMAkGA1UdEwQCMAAwHQYDVR0lBBYwFAYI\r\nKwYBBQUHAwEGCCsGAQUFBwMCMEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly9jcmwu\r\nZ2xvYmFsc2lnbi5jb20vZ3MvZ3Nvcmdhbml6YXRpb252YWxnMi5jcmwwgZYGCCsG\r\nAQUFBwEBBIGJMIGGMEcGCCsGAQUFBzAChjtodHRwOi8vc2VjdXJlLmdsb2JhbHNp\r\nZ24uY29tL2NhY2VydC9nc29yZ2FuaXphdGlvbnZhbGcyLmNydDA7BggrBgEFBQcw\r\nAYYvaHR0cDovL29jc3AyLmdsb2JhbHNpZ24uY29tL2dzb3JnYW5pemF0aW9udmFs\r\nZzIwHQYDVR0OBBYEFHbgHqTLsXDt7uMRyE62rnDEfLn9MB8GA1UdIwQYMBaAFF1G\r\nso3ES3Qcu+31c7Y6tziPdZ5+MA0GCSqGSIb3DQEBBQUAA4IBAQByy1+3N6ZRVooI\r\nxqw8Ng+UFz0g7UHkbPEnvTu1uxJ2AojFuP/P1PAk+/6uMRvpPlWg/5uqmOIWxKxJ\r\nLo6xSbkDf4LN+KYwes3XSuPyziZ4QbPnehHhZ0377iiA8fpRJADg9NWKCRHh5aAd\r\ne9QvJUW/GgYkBN+F4yYc2jIjR3Rehv4JYOKS3iXO9OoHsDS2CcCFaS2imgQVfYLg\r\nslBwT/A08PCOhW5huiluSmih7x5Qf7sFDv8jineu6ehKzi8pKnOq4k8G4QiWn38Y\r\nCeiBkkwFOwj7T3M/ITiiSS9DHDGeokj16eBi83Zx3YYiJ9YZvnQ+4GvqJ5eJJ6pR\r\nKKvemr+m\r\n-----END CERTIFICATE-----',
|
||||
pinned: true,
|
||||
ignoreTLS: false
|
||||
}
|
||||
smtp: {
|
||||
host: 'smtp.mail.yahoo.com',
|
||||
port: 465,
|
||||
secure: true,
|
||||
ca: '-----BEGIN CERTIFICATE-----\r\nMIIHITCCBgmgAwIBAgIQBlVSvxlwsqw8Kc8eVV5EKTANBgkqhkiG9w0BAQUFADBm\r\nMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\r\nd3cuZGlnaWNlcnQuY29tMSUwIwYDVQQDExxEaWdpQ2VydCBIaWdoIEFzc3VyYW5j\r\nZSBDQS0zMB4XDTE0MDYwNTAwMDAwMFoXDTE1MDYyMzEyMDAwMFowYjELMAkGA1UE\r\nBhMCVVMxCzAJBgNVBAgTAkNBMRIwEAYDVQQHEwlTdW5ueXZhbGUxFDASBgNVBAoM\r\nC1lhaG9vISBJbmMuMRwwGgYDVQQDExNzbXRwLm1haWwueWFob28uY29tMIIBIjAN\r\nBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA339OSxTIuW6NVqKwYHFBbg4/tmEx\r\nMYpzohtObPaWgvLTsYdce+WlQFBU5rxaCMUtVsVAGJWs5uQRDVHXRLPnOWe+foqZ\r\n5ItgjYIQc1XeMv6BZEeaF3Bum5ehUyLB3y48cjbWxma1QlkZ8XAK0f9AS7ySWAen\r\no5OXJlMFAqXGtKMAhY46dzSY0wjgdrvgiomFRy0iQKV1DxGsXoXSMEszlUTQoNQ3\r\nWTsiA3O//cdWE06wWeA3/90cb7QkU3KflSoyfi878BQGpPR1L+rLNsqnc8QuucbD\r\nz4Q++rxGqgg4QYrOtmZfAn96TXhPWCwKld6FN+f3uV5ITOBFx44M5v1ILQIDAQAB\r\no4IDzTCCA8kwHwYDVR0jBBgwFoAUUOpzidsp+xCPnuUBINTeeZlIg/cwHQYDVR0O\r\nBBYEFEhOpSFQjO/QrSNfVwyInZUhyzhsMIICJAYDVR0RBIICGzCCAheCE3NtdHAu\r\nbWFpbC55YWhvby5jb22CFnNtdHAubWFpbC55YWhvby5jb20uYXKCFnNtdHAubWFp\r\nbC55YWhvby5jb20uYXWCFnNtdHAubWFpbC55YWhvby5jb20uYnKCFnNtdHAubWFp\r\nbC55YWhvby5jb20uY26CFnNtdHAubWFpbC55YWhvby5jb20uaGuCFnNtdHAubWFp\r\nbC55YWhvby5jb20ubXmCFnNtdHAubWFpbC55YWhvby5jb20ucGiCFnNtdHAubWFp\r\nbC55YWhvby5jb20uc2eCFnNtdHAubWFpbC55YWhvby5jb20udHeCFnNtdHAubWFp\r\nbC55YWhvby5jb20udm6CFXNtdHAubWFpbC55YWhvby5jby5pZIIVc210cC5tYWls\r\nLnlhaG9vLmNvLmlughVzbXRwLm1haWwueWFob28uY28ua3KCFXNtdHAubWFpbC55\r\nYWhvby5jby50aIIVc210cC5tYWlsLnlhaG9vLmNvLnVrghJzbXRwLm1haWwueWFo\r\nb28uY2GCEnNtdHAubWFpbC55YWhvby5jboISc210cC5tYWlsLnlhaG9vLmRlghJz\r\nbXRwLm1haWwueWFob28uZXOCEnNtdHAubWFpbC55YWhvby5mcoISc210cC5tYWls\r\nLnlhaG9vLml0gg9zbXRwLnk3bWFpbC5jb22CFHNtdHAuY29ycmVvLnlhaG9vLmVz\r\nMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw\r\nYQYDVR0fBFowWDAqoCigJoYkaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL2NhMy1n\r\nMjguY3JsMCqgKKAmhiRodHRwOi8vY3JsNC5kaWdpY2VydC5jb20vY2EzLWcyOC5j\r\ncmwwQgYDVR0gBDswOTA3BglghkgBhv1sAQEwKjAoBggrBgEFBQcCARYcaHR0cHM6\r\nLy93d3cuZGlnaWNlcnQuY29tL0NQUzB7BggrBgEFBQcBAQRvMG0wJAYIKwYBBQUH\r\nMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBFBggrBgEFBQcwAoY5aHR0cDov\r\nL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0SGlnaEFzc3VyYW5jZUNBLTMu\r\nY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQEFBQADggEBABshjUND4hADHGfe\r\ncV9XGXcr9h3u7mT5kgdcgGFhcrFAlwkzt0NUCbuN0X8DWHE72Tpb3zRE25CmUUJe\r\nktBbq9PQb4b5/Wt1htAEw0qUs3BsUbqejK9OHJ/11Jn2ek4+SLJuYlijzc7KM3F/\r\nyz7ZTZtKR0PglkXfqbpvWYGabYpfL2FRLoJ7alTLsMJcFxbSLYcAIMxufj7RyTBJ\r\nbKgRJl4wmP4+Zc2Q1p59mENY0u5HqVAAOmWc0jNb0/31+tRr5f6EgXxK++7TQOpF\r\n0TOaFsXlzRlpKfmIbzVr2nfwghV5/bRZj96TK3g1OoOz4C8ksK4INHnUdTAqZ18M\r\nvHpnJw4=\r\n-----END CERTIFICATE-----',
|
||||
pinned: true,
|
||||
ignoreTLS: false
|
||||
}
|
||||
},
|
||||
tonline: {
|
||||
imap: {
|
||||
host: 'secureimap.t-online.de',
|
||||
port: 993,
|
||||
secure: true,
|
||||
ca: '-----BEGIN CERTIFICATE-----\r\nMIIGmzCCBYOgAwIBAgIIIbZ3foy9DqgwDQYJKoZIhvcNAQEFBQAwcTELMAkGA1UE\r\nBhMCREUxHDAaBgNVBAoTE0RldXRzY2hlIFRlbGVrb20gQUcxHzAdBgNVBAsTFlQt\r\nVGVsZVNlYyBUcnVzdCBDZW50ZXIxIzAhBgNVBAMTGkRldXRzY2hlIFRlbGVrb20g\r\nUm9vdCBDQSAyMB4XDTEzMDMwMTEzNTgyOVoXDTE5MDcwOTIzNTkwMFowgckxCzAJ\r\nBgNVBAYTAkRFMSUwIwYDVQQKExxULVN5c3RlbXMgSW50ZXJuYXRpb25hbCBHbWJI\r\nMR8wHQYDVQQLExZULVN5c3RlbXMgVHJ1c3QgQ2VudGVyMQwwCgYDVQQIEwNOUlcx\r\nDjAMBgNVBBETBTU3MjUwMRAwDgYDVQQHEwdOZXRwaGVuMSAwHgYDVQQJExdVbnRl\r\ncmUgSW5kdXN0cmllc3RyLiAyMDEgMB4GA1UEAxMXVGVsZVNlYyBTZXJ2ZXJQYXNz\r\nIERFLTEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCwg+9QiuYAxX9/\r\n4F9XRZrS1o0q+aa9L/5/K3vc+RqPpliiZ24vTkJc1JDpXrbWXS25uT3yzHukQrhI\r\nq0AbcRqNEAeFi5EhUiEM/vtb4BYGHdqfXQ3etgYYcCtJ43NAHaSgsyQ9kyGV2lmM\r\nwkeAX3qZ2CGE9/cR6w+bOogHArBdk2JaHG09myNZDytr6oUbWLjLd/qhC9YzyZSX\r\nbZgE/kh5L5Y6P9paw2pDdn7+Ni4pXzlmoj1k43uiz+h2ibe3DO9dKMZAaEKeyG1O\r\ng0f0r53M8O+8Bm2sXtWelrAgrfFlISgWzO1hkNs12rWpr4c5Ygde/behx9OQmPwp\r\nmS+e3WvTAgMBAAGjggLcMIIC2DAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFGJP\r\nE842Z4TNGfygTxmL7xVUATIcMB8GA1UdIwQYMBaAFDHDeRu69VPXF+CJei0XbAqz\r\nK50zMBIGA1UdEwEB/wQIMAYBAf8CAQAwWQYDVR0gBFIwUDBEBgkrBgEEAb1HDQIw\r\nNzA1BggrBgEFBQcCARYpaHR0cDovL3d3dy50ZWxlc2VjLmRlL3NlcnZlcnBhc3Mv\r\nY3BzLmh0bWwwCAYGZ4EMAQICMIHvBgNVHR8EgecwgeQwOqA4oDaGNGh0dHA6Ly9j\r\ncmwuc2VydmVycGFzcy50ZWxlc2VjLmRlL3JsL0RUX1JPT1RfQ0FfMi5jcmwwgaWg\r\ngaKggZ+GgZxsZGFwOi8vbGRhcC5zZXJ2ZXJwYXNzLnRlbGVzZWMuZGUvQ049RGV1\r\ndHNjaGUlMjBUZWxla29tJTIwUm9vdCUyMENBJTIwMixPVT1ULVRlbGVTZWMlMjBU\r\ncnVzdCUyMENlbnRlcixPPURldXRzY2hlJTIwVGVsZWtvbSUyMEFHLEM9REU/QXV0\r\naG9yaXR5UmV2b2NhdGlvbkxpc3QwggEjBggrBgEFBQcBAQSCARUwggERMCoGCCsG\r\nAQUFBzABhh5odHRwOi8vb2NzcDAyLnRlbGVzZWMuZGUvb2NzcHIwQQYIKwYBBQUH\r\nMAKGNWh0dHA6Ly9jcmwuc2VydmVycGFzcy50ZWxlc2VjLmRlL2NydC9EVF9ST09U\r\nX0NBXzIuY2VyMIGfBggrBgEFBQcwAoaBkmxkYXA6Ly9sZGFwLnNlcnZlcnBhc3Mu\r\ndGVsZXNlYy5kZS9DTj1EZXV0c2NoZSUyMFRlbGVrb20lMjBSb290JTIwQ0ElMjAy\r\nLE9VPVQtVGVsZVNlYyUyMFRydXN0JTIwQ2VudGVyLE89RGV1dHNjaGUlMjBUZWxl\r\na29tJTIwQUcsQz1ERT9jQUNlcnRpZmljYXRlMA0GCSqGSIb3DQEBBQUAA4IBAQBO\r\nE04qoEkEc9ad+WwSurVYfcDdjGvpqrtbI89woXDsWLQTMhA7D7jVuls90SJns0vc\r\nK9qoYkEGt0/ZlawLe2lyNWtueHfUf+dgleUunwHYLxuj3jQ2ERzQLVLrswjecRpX\r\nvGAGej89WpGQ9PMq27WGNC5WCmzVC9rk5naFgacsbwKwyjU0LoBArtAQnAAlpHDw\r\nPenv1Pe7MhUkCK0LqdTvkI/AHFzPYg/l5E3j8lQQ8hiKx8U6wf9xVKECLA2RlRqY\r\nUX2rpjQNxnvEq/mEQv3x3mLOEFJ3TAKI+soDgOOi0OG8+ywhm6S+7Z9lTlJ+BcD6\r\noy1MNKd4CQbltHLMTFUH\r\n-----END CERTIFICATE-----',
|
||||
pinned: true,
|
||||
ignoreTLS: false
|
||||
},
|
||||
gmx: {
|
||||
imap: {
|
||||
host: 'imap.gmx.net',
|
||||
port: 993,
|
||||
secure: true,
|
||||
ca: '-----BEGIN CERTIFICATE-----\r\nMIIHUDCCBjigAwIBAgIIM52e2Oc5J8kwDQYJKoZIhvcNAQEFBQAwgckxCzAJBgNV\r\nBAYTAkRFMSUwIwYDVQQKExxULVN5c3RlbXMgSW50ZXJuYXRpb25hbCBHbWJIMR8w\r\nHQYDVQQLExZULVN5c3RlbXMgVHJ1c3QgQ2VudGVyMQwwCgYDVQQIEwNOUlcxDjAM\r\nBgNVBBETBTU3MjUwMRAwDgYDVQQHEwdOZXRwaGVuMSAwHgYDVQQJExdVbnRlcmUg\r\nSW5kdXN0cmllc3RyLiAyMDEgMB4GA1UEAxMXVGVsZVNlYyBTZXJ2ZXJQYXNzIERF\r\nLTEwHhcNMTMxMTEyMTAxNzMxWhcNMTYxMTE3MjM1OTU5WjCBnTELMAkGA1UEBhMC\r\nREUxHjAcBgNVBAoMFTEmMSBNYWlsICYgTWVkaWEgR21iSDEdMBsGA1UECBMUUmhp\r\nbmVsYW5kLVBhbGF0aW5hdGUxEjAQBgNVBAcTCU1vbnRhYmF1cjEkMCIGCSqGSIb3\r\nDQEJARYVc2VydmVyLWNlcnRzQDF1bmQxLmRlMRUwEwYDVQQDEwxpbWFwLmdteC5u\r\nZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDHP3QN1ztX5m8LaC9m\r\nt2nrcLBEfxb61FQCieXW4OV/D5HEy7vgQIAeS+pg2/9ClawprQFbLUa46owUcJuq\r\no2SaefsYqjRhIP/tPeyGRJQmfnyCRRoHIYno57/bz2qmHjzvkqEUMvNGVVej7BJv\r\nGukNKPXYicodkRodF3EicTPnGitGeXOsAxyPuCPIsBPAMuuuBzKenS7nj+yseSGe\r\nkM7/kjtNgbjDyBXi2BvoUS/U+Gm9p1lq0ymT9fhGj/k9/Qz312GHYxfZB1bhL0pk\r\nadF25P8fTIy5tKMWqJfgCF+eaOwgkYecxeCcrt2E3D8ThLvmkBRrXsTT51ZzD7sp\r\n7hPpAgMBAAGjggNkMIIDYDAfBgNVHSMEGDAWgBRiTxPONmeEzRn8oE8Zi+8VVAEy\r\nHDAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMB\r\nMB0GA1UdDgQWBBRfb2gYyD6V+oBN2Ii5WQj0AddemjBZBgNVHSAEUjBQMEQGCSsG\r\nAQQBvUcNAjA3MDUGCCsGAQUFBwIBFilodHRwOi8vd3d3LnRlbGVzZWMuZGUvc2Vy\r\ndmVycGFzcy9jcHMuaHRtbDAIBgZngQwBAgIwggEhBgNVHR8EggEYMIIBFDBFoEOg\r\nQYY/aHR0cDovL2NybC5zZXJ2ZXJwYXNzLnRlbGVzZWMuZGUvcmwvVGVsZVNlY19T\r\nZXJ2ZXJQYXNzX0RFLTEuY3JsMIHKoIHHoIHEhoHBbGRhcDovL2xkYXAuc2VydmVy\r\ncGFzcy50ZWxlc2VjLmRlL2NuPVRlbGVTZWMlMjBTZXJ2ZXJQYXNzJTIwREUtMSxv\r\ndT1ULVN5c3RlbXMlMjBUcnVzdCUyMENlbnRlcixvPVQtU3lzdGVtcyUyMEludGVy\r\nbmF0aW9uYWwlMjBHbWJILGM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9ubGlzdD9i\r\nYXNlP2NlcnRpZmljYXRlUmV2b2NhdGlvbmxpc3Q9KjCCATkGCCsGAQUFBwEBBIIB\r\nKzCCAScwMwYIKwYBBQUHMAGGJ2h0dHA6Ly9vY3NwLnNlcnZlcnBhc3MudGVsZXNl\r\nYy5kZS9vY3NwcjBMBggrBgEFBQcwAoZAaHR0cDovL2NybC5zZXJ2ZXJwYXNzLnRl\r\nbGVzZWMuZGUvY3J0L1RlbGVTZWNfU2VydmVyUGFzc19ERS0xLmNlcjCBoQYIKwYB\r\nBQUHMAKGgZRsZGFwOi8vbGRhcC5zZXJ2ZXJwYXNzLnRlbGVzZWMuZGUvY249VGVs\r\nZVNlYyUyMFNlcnZlclBhc3MlMjBERS0xLG91PVQtU3lzdGVtcyUyMFRydXN0JTIw\r\nQ2VudGVyLG89VC1TeXN0ZW1zJTIwSW50ZXJuYXRpb25hbCUyMEdtYkgsYz1kZT9j\r\nQUNlcnRpZmljYXRlMAwGA1UdEwEB/wQCMAAwJAYDVR0RBB0wG4IMaW1hcC5nbXgu\r\nbmV0ggtpbWFwLmdteC5kZTANBgkqhkiG9w0BAQUFAAOCAQEADl442s2ouynDNEzl\r\nBPzhCxjp47TmKzgzrCGh1AH3+UOkw54mYU65UaY9cJ51jgOu+pjNB4KxVfcqyG5O\r\nJcowS09ZtZ+khb1OkzXAAbEqNj0oyH/1K550d5Ir5VbXF0ZdVKVTzEFRyjvUt5NJ\r\n/b6Q2bbY/sPm8QiK9SPaYkO5/3J1KB0u7PkqqfOhZ9UJE6mIN66T2bpFmAwle3zL\r\n6+L/VwYSgUNs/w2l6xWlYP3pFFaT12TO43Q057I3vP7yCt3QE/VLDpdQOQ3H0oy2\r\nJQGPJLFby9MPYNkmEmwjh4e95TAY4ZkZMe08ix0J1Smy8DwjTz6SXqYC+EC/PQlh\r\nE0UHxA==\r\n-----END CERTIFICATE-----',
|
||||
pinned: true,
|
||||
ignoreTLS: false
|
||||
},
|
||||
smtp: {
|
||||
host: 'mail.gmx.net',
|
||||
port: 587,
|
||||
secure: false,
|
||||
ca: '-----BEGIN CERTIFICATE-----\r\nMIIHbDCCBlSgAwIBAgIJAJ74Ek9UaA0TMA0GCSqGSIb3DQEBBQUAMIHJMQswCQYD\r\nVQQGEwJERTElMCMGA1UEChMcVC1TeXN0ZW1zIEludGVybmF0aW9uYWwgR21iSDEf\r\nMB0GA1UECxMWVC1TeXN0ZW1zIFRydXN0IENlbnRlcjEMMAoGA1UECBMDTlJXMQ4w\r\nDAYDVQQREwU1NzI1MDEQMA4GA1UEBxMHTmV0cGhlbjEgMB4GA1UECRMXVW50ZXJl\r\nIEluZHVzdHJpZXN0ci4gMjAxIDAeBgNVBAMTF1RlbGVTZWMgU2VydmVyUGFzcyBE\r\nRS0xMB4XDTEzMTExMjEwMTY0N1oXDTE2MTExNzIzNTk1OVowgZ0xCzAJBgNVBAYT\r\nAkRFMR4wHAYDVQQKDBUxJjEgTWFpbCAmIE1lZGlhIEdtYkgxHTAbBgNVBAgTFFJo\r\naW5lbGFuZC1QYWxhdGluYXRlMRIwEAYDVQQHEwlNb250YWJhdXIxJDAiBgkqhkiG\r\n9w0BCQEWFXNlcnZlci1jZXJ0c0AxdW5kMS5kZTEVMBMGA1UEAxMMbWFpbC5nbXgu\r\nbmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsEUYppbJXUpQELIL\r\nETAFUwreDfSmbyi9gJPUqquogROHEoRV+bBFZ2YCiHgLU8AggcLmM74HAQMFt8F5\r\neS2OyuEar3E3tNW5XFiI1QblTtY3B7o1coKyq/i+tV3b1jGq4+1aJeeD3UpcraBm\r\nk2XuQgQ6WAeY+XCYyI/OzZ56ZjQ17LHMMI0ayu7SSV+VBpG9loC9E5A82iVtZsqo\r\nSaUZpZf/xICpUgNJ+RlalSsoE6FdCywE/gEEmkpAUh+Mv0WvHITk6aqtMFWex8tE\r\n8cwJGQbmJGt9x8BExbhMY6uQ+HsnsDCPCMVh5JGKx7/e+qyxtHYGChOeI16m2+MT\r\nz47jSQIDAQABo4IDfzCCA3swHwYDVR0jBBgwFoAUYk8TzjZnhM0Z/KBPGYvvFVQB\r\nMhwwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD\r\nATAdBgNVHQ4EFgQUH8mLfxsd5FEh9M+3t3rP6qLiFTEwWQYDVR0gBFIwUDBEBgkr\r\nBgEEAb1HDQIwNzA1BggrBgEFBQcCARYpaHR0cDovL3d3dy50ZWxlc2VjLmRlL3Nl\r\ncnZlcnBhc3MvY3BzLmh0bWwwCAYGZ4EMAQICMIIBIQYDVR0fBIIBGDCCARQwRaBD\r\noEGGP2h0dHA6Ly9jcmwuc2VydmVycGFzcy50ZWxlc2VjLmRlL3JsL1RlbGVTZWNf\r\nU2VydmVyUGFzc19ERS0xLmNybDCByqCBx6CBxIaBwWxkYXA6Ly9sZGFwLnNlcnZl\r\ncnBhc3MudGVsZXNlYy5kZS9jbj1UZWxlU2VjJTIwU2VydmVyUGFzcyUyMERFLTEs\r\nb3U9VC1TeXN0ZW1zJTIwVHJ1c3QlMjBDZW50ZXIsbz1ULVN5c3RlbXMlMjBJbnRl\r\ncm5hdGlvbmFsJTIwR21iSCxjPWRlP2NlcnRpZmljYXRlUmV2b2NhdGlvbmxpc3Q/\r\nYmFzZT9jZXJ0aWZpY2F0ZVJldm9jYXRpb25saXN0PSowggE5BggrBgEFBQcBAQSC\r\nASswggEnMDMGCCsGAQUFBzABhidodHRwOi8vb2NzcC5zZXJ2ZXJwYXNzLnRlbGVz\r\nZWMuZGUvb2NzcHIwTAYIKwYBBQUHMAKGQGh0dHA6Ly9jcmwuc2VydmVycGFzcy50\r\nZWxlc2VjLmRlL2NydC9UZWxlU2VjX1NlcnZlclBhc3NfREUtMS5jZXIwgaEGCCsG\r\nAQUFBzAChoGUbGRhcDovL2xkYXAuc2VydmVycGFzcy50ZWxlc2VjLmRlL2NuPVRl\r\nbGVTZWMlMjBTZXJ2ZXJQYXNzJTIwREUtMSxvdT1ULVN5c3RlbXMlMjBUcnVzdCUy\r\nMENlbnRlcixvPVQtU3lzdGVtcyUyMEludGVybmF0aW9uYWwlMjBHbWJILGM9ZGU/\r\nY0FDZXJ0aWZpY2F0ZTAMBgNVHRMBAf8EAjAAMD8GA1UdEQQ4MDaCDG1haWwuZ214\r\nLm5ldIILbWFpbC5nbXguZGWCDHNtdHAuZ214Lm5ldIILc210cC5nbXguZGUwDQYJ\r\nKoZIhvcNAQEFBQADggEBAJ5SKsBXoOTO0ztsFh8RVd/iu6sijEMpKVF+/cs74TZN\r\nK1QWIz8Ay1f3SUPWXTvkmvayUXWGtxZfYoRIrx3feWBdUozJxe0wz1O3tDQOQgRX\r\nnkcwk7nibTUmW2rk5AIwV0jzOHtIbAGjSdc6my0543e7dVkhcuoCOC+g2NmD+pHF\r\ni1KpmqTLecDlFvprIZUJ23AT1uTnuXYg/tFHeDn3ga00Gce82xZQJelRirJs01SR\r\nDGEBaPXBjJVJNoAP/qEy3jQNRWo5TbSgwn3DTR81FJvltmf0c+zlt4fmcpjpr5ni\r\nFvK7L0rZQL5MOGHPpgRIukEBZCowYr3OYpZYBEaB94I=\r\n-----END CERTIFICATE-----',
|
||||
pinned: true,
|
||||
ignoreTLS: false
|
||||
}
|
||||
smtp: {
|
||||
host: 'securesmtp.t-online.de',
|
||||
port: 465,
|
||||
secure: true,
|
||||
ca: '-----BEGIN CERTIFICATE-----\r\nMIIGmzCCBYOgAwIBAgIIIbZ3foy9DqgwDQYJKoZIhvcNAQEFBQAwcTELMAkGA1UE\r\nBhMCREUxHDAaBgNVBAoTE0RldXRzY2hlIFRlbGVrb20gQUcxHzAdBgNVBAsTFlQt\r\nVGVsZVNlYyBUcnVzdCBDZW50ZXIxIzAhBgNVBAMTGkRldXRzY2hlIFRlbGVrb20g\r\nUm9vdCBDQSAyMB4XDTEzMDMwMTEzNTgyOVoXDTE5MDcwOTIzNTkwMFowgckxCzAJ\r\nBgNVBAYTAkRFMSUwIwYDVQQKExxULVN5c3RlbXMgSW50ZXJuYXRpb25hbCBHbWJI\r\nMR8wHQYDVQQLExZULVN5c3RlbXMgVHJ1c3QgQ2VudGVyMQwwCgYDVQQIEwNOUlcx\r\nDjAMBgNVBBETBTU3MjUwMRAwDgYDVQQHEwdOZXRwaGVuMSAwHgYDVQQJExdVbnRl\r\ncmUgSW5kdXN0cmllc3RyLiAyMDEgMB4GA1UEAxMXVGVsZVNlYyBTZXJ2ZXJQYXNz\r\nIERFLTEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCwg+9QiuYAxX9/\r\n4F9XRZrS1o0q+aa9L/5/K3vc+RqPpliiZ24vTkJc1JDpXrbWXS25uT3yzHukQrhI\r\nq0AbcRqNEAeFi5EhUiEM/vtb4BYGHdqfXQ3etgYYcCtJ43NAHaSgsyQ9kyGV2lmM\r\nwkeAX3qZ2CGE9/cR6w+bOogHArBdk2JaHG09myNZDytr6oUbWLjLd/qhC9YzyZSX\r\nbZgE/kh5L5Y6P9paw2pDdn7+Ni4pXzlmoj1k43uiz+h2ibe3DO9dKMZAaEKeyG1O\r\ng0f0r53M8O+8Bm2sXtWelrAgrfFlISgWzO1hkNs12rWpr4c5Ygde/behx9OQmPwp\r\nmS+e3WvTAgMBAAGjggLcMIIC2DAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFGJP\r\nE842Z4TNGfygTxmL7xVUATIcMB8GA1UdIwQYMBaAFDHDeRu69VPXF+CJei0XbAqz\r\nK50zMBIGA1UdEwEB/wQIMAYBAf8CAQAwWQYDVR0gBFIwUDBEBgkrBgEEAb1HDQIw\r\nNzA1BggrBgEFBQcCARYpaHR0cDovL3d3dy50ZWxlc2VjLmRlL3NlcnZlcnBhc3Mv\r\nY3BzLmh0bWwwCAYGZ4EMAQICMIHvBgNVHR8EgecwgeQwOqA4oDaGNGh0dHA6Ly9j\r\ncmwuc2VydmVycGFzcy50ZWxlc2VjLmRlL3JsL0RUX1JPT1RfQ0FfMi5jcmwwgaWg\r\ngaKggZ+GgZxsZGFwOi8vbGRhcC5zZXJ2ZXJwYXNzLnRlbGVzZWMuZGUvQ049RGV1\r\ndHNjaGUlMjBUZWxla29tJTIwUm9vdCUyMENBJTIwMixPVT1ULVRlbGVTZWMlMjBU\r\ncnVzdCUyMENlbnRlcixPPURldXRzY2hlJTIwVGVsZWtvbSUyMEFHLEM9REU/QXV0\r\naG9yaXR5UmV2b2NhdGlvbkxpc3QwggEjBggrBgEFBQcBAQSCARUwggERMCoGCCsG\r\nAQUFBzABhh5odHRwOi8vb2NzcDAyLnRlbGVzZWMuZGUvb2NzcHIwQQYIKwYBBQUH\r\nMAKGNWh0dHA6Ly9jcmwuc2VydmVycGFzcy50ZWxlc2VjLmRlL2NydC9EVF9ST09U\r\nX0NBXzIuY2VyMIGfBggrBgEFBQcwAoaBkmxkYXA6Ly9sZGFwLnNlcnZlcnBhc3Mu\r\ndGVsZXNlYy5kZS9DTj1EZXV0c2NoZSUyMFRlbGVrb20lMjBSb290JTIwQ0ElMjAy\r\nLE9VPVQtVGVsZVNlYyUyMFRydXN0JTIwQ2VudGVyLE89RGV1dHNjaGUlMjBUZWxl\r\na29tJTIwQUcsQz1ERT9jQUNlcnRpZmljYXRlMA0GCSqGSIb3DQEBBQUAA4IBAQBO\r\nE04qoEkEc9ad+WwSurVYfcDdjGvpqrtbI89woXDsWLQTMhA7D7jVuls90SJns0vc\r\nK9qoYkEGt0/ZlawLe2lyNWtueHfUf+dgleUunwHYLxuj3jQ2ERzQLVLrswjecRpX\r\nvGAGej89WpGQ9PMq27WGNC5WCmzVC9rk5naFgacsbwKwyjU0LoBArtAQnAAlpHDw\r\nPenv1Pe7MhUkCK0LqdTvkI/AHFzPYg/l5E3j8lQQ8hiKx8U6wf9xVKECLA2RlRqY\r\nUX2rpjQNxnvEq/mEQv3x3mLOEFJ3TAKI+soDgOOi0OG8+ywhm6S+7Z9lTlJ+BcD6\r\noy1MNKd4CQbltHLMTFUH\r\n-----END CERTIFICATE-----',
|
||||
pinned: true,
|
||||
ignoreTLS: false
|
||||
}
|
||||
},
|
||||
outlook: {
|
||||
imap: {
|
||||
host: 'imap-mail.outlook.com',
|
||||
port: 993,
|
||||
secure: true,
|
||||
ca: '-----BEGIN CERTIFICATE-----\r\nMIIFQjCCBCqgAwIBAgISESHl0vjrML7zKmGlv42YL75vMA0GCSqGSIb3DQEBBQUA\r\nMF0xCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMTMwMQYD\r\nVQQDEypHbG9iYWxTaWduIE9yZ2FuaXphdGlvbiBWYWxpZGF0aW9uIENBIC0gRzIw\r\nHhcNMTMwNDI0MjAzNTA5WhcNMTYwNDI0MjAzNTA5WjBsMQswCQYDVQQGEwJVUzET\r\nMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV\r\nTWljcm9zb2Z0IENvcnBvcmF0aW9uMRYwFAYDVQQDDA0qLmhvdG1haWwuY29tMIIB\r\nIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAumSiBWrzHZf6WFP5a/j4+K7D\r\n1izLoYKj5Omll0pdxKvKcBRDf+iaIkCbSOPNpx2uWGZdwNwkabYCQavaBf2ebwmS\r\nS8i1CJpHflO+k0qYd5WUi7sSsZ3+6RaCMdLoDIPGyYMQuy7TFtVO7LSt5+qscyyi\r\nET8c3lE2aj/XW13UZvRrV65ZJvMjUtwaDnIcAxGeasYoebLsKdqHQ2uTr4PmNwCc\r\nviGVFSOzkGAoC0PfyqKB2xUWy3Kc5zRI2xvUW8Jb2b/9Ze3g55pIUzKsjpglkQTm\r\nedVPSYYPGNz6Kl/ZshBXdBAk398q1JkSmUaTMa2hJgBbcC+73ax40AJDGJlz+QID\r\nAQABo4IB6zCCAecwDgYDVR0PAQH/BAQDAgWgMEkGA1UdIARCMEAwPgYGZ4EMAQIC\r\nMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8vd3d3Lmdsb2JhbHNpZ24uY29tL3JlcG9z\r\naXRvcnkvMEAGA1UdEQQ5MDeCDSouaG90bWFpbC5jb22CCioubGl2ZS5jb22CDSou\r\nb3V0bG9vay5jb22CC2hvdG1haWwuY29tMAkGA1UdEwQCMAAwHQYDVR0lBBYwFAYI\r\nKwYBBQUHAwEGCCsGAQUFBwMCMEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly9jcmwu\r\nZ2xvYmFsc2lnbi5jb20vZ3MvZ3Nvcmdhbml6YXRpb252YWxnMi5jcmwwgZYGCCsG\r\nAQUFBwEBBIGJMIGGMEcGCCsGAQUFBzAChjtodHRwOi8vc2VjdXJlLmdsb2JhbHNp\r\nZ24uY29tL2NhY2VydC9nc29yZ2FuaXphdGlvbnZhbGcyLmNydDA7BggrBgEFBQcw\r\nAYYvaHR0cDovL29jc3AyLmdsb2JhbHNpZ24uY29tL2dzb3JnYW5pemF0aW9udmFs\r\nZzIwHQYDVR0OBBYEFHbgHqTLsXDt7uMRyE62rnDEfLn9MB8GA1UdIwQYMBaAFF1G\r\nso3ES3Qcu+31c7Y6tziPdZ5+MA0GCSqGSIb3DQEBBQUAA4IBAQByy1+3N6ZRVooI\r\nxqw8Ng+UFz0g7UHkbPEnvTu1uxJ2AojFuP/P1PAk+/6uMRvpPlWg/5uqmOIWxKxJ\r\nLo6xSbkDf4LN+KYwes3XSuPyziZ4QbPnehHhZ0377iiA8fpRJADg9NWKCRHh5aAd\r\ne9QvJUW/GgYkBN+F4yYc2jIjR3Rehv4JYOKS3iXO9OoHsDS2CcCFaS2imgQVfYLg\r\nslBwT/A08PCOhW5huiluSmih7x5Qf7sFDv8jineu6ehKzi8pKnOq4k8G4QiWn38Y\r\nCeiBkkwFOwj7T3M/ITiiSS9DHDGeokj16eBi83Zx3YYiJ9YZvnQ+4GvqJ5eJJ6pR\r\nKKvemr+m\r\n-----END CERTIFICATE-----',
|
||||
pinned: true,
|
||||
ignoreTLS: false
|
||||
},
|
||||
webde: {
|
||||
imap: {
|
||||
host: 'imap.web.de',
|
||||
port: 993,
|
||||
secure: true,
|
||||
ca: '-----BEGIN CERTIFICATE-----\r\nMIIHUjCCBjqgAwIBAgIIHvqUOScyxYUwDQYJKoZIhvcNAQEFBQAwgckxCzAJBgNV\r\nBAYTAkRFMSUwIwYDVQQKExxULVN5c3RlbXMgSW50ZXJuYXRpb25hbCBHbWJIMR8w\r\nHQYDVQQLExZULVN5c3RlbXMgVHJ1c3QgQ2VudGVyMQwwCgYDVQQIEwNOUlcxDjAM\r\nBgNVBBETBTU3MjUwMRAwDgYDVQQHEwdOZXRwaGVuMSAwHgYDVQQJExdVbnRlcmUg\r\nSW5kdXN0cmllc3RyLiAyMDEgMB4GA1UEAxMXVGVsZVNlYyBTZXJ2ZXJQYXNzIERF\r\nLTEwHhcNMTMwODIxMDgzMTE2WhcNMTYwODI2MjM1OTU5WjCBrTELMAkGA1UEBhMC\r\nREUxHjAcBgNVBAoMFTEmMSBNYWlsICYgTWVkaWEgR21iSDEPMA0GA1UECxMGV0VC\r\nLkRFMR0wGwYDVQQIExRSaGluZWxhbmQtUGFsYXRpbmF0ZTESMBAGA1UEBxMJTW9u\r\ndGFiYXVyMSQwIgYJKoZIhvcNAQkBFhVzZXJ2ZXItY2VydHNAMXVuZDEuZGUxFDAS\r\nBgNVBAMTC2ltYXAud2ViLmRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC\r\nAQEA5dvpcnYczFs9GANTYN5N3mP6ATFVm7P4nYhRIQMj/YssSjmbrOPIEbe12JNC\r\nRO9CI7Z4Dnk42BM5TiH+QKGpcfnitofOv4gKPjwCMcPDHfY152/+YDaiyU/md2Hg\r\n/WrZ/50KwC8Sw1tZkYDXWB0zeJkfPdS2r4ATNrpMR0fYcG08+elz5T2SWNg+c1xL\r\nKFdCh33wZLkijOfW0HA164QjXaLPBjxa+GyZFY19ywOQ85KdFVVLmGUrLz8n4ZLc\r\n7K6KUuzYttnUuVxctFauQ4DRHE/CfUvvNHHgn5d+A2XS7jNUgmUb0gjzy3OtlTP3\r\ngM4ostVufmnlS7qqFDR7A3v4lQIDAQABo4IDVjCCA1IwHwYDVR0jBBgwFoAUYk8T\r\nzjZnhM0Z/KBPGYvvFVQBMhwwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsG\r\nAQUFBwMCBggrBgEFBQcDATAdBgNVHQ4EFgQUDH66P/Y4NlKGphPniIzmutEI9ocw\r\nWQYDVR0gBFIwUDBEBgkrBgEEAb1HDQIwNzA1BggrBgEFBQcCARYpaHR0cDovL3d3\r\ndy50ZWxlc2VjLmRlL3NlcnZlcnBhc3MvY3BzLmh0bWwwCAYGZ4EMAQICMIIBIQYD\r\nVR0fBIIBGDCCARQwRaBDoEGGP2h0dHA6Ly9jcmwuc2VydmVycGFzcy50ZWxlc2Vj\r\nLmRlL3JsL1RlbGVTZWNfU2VydmVyUGFzc19ERS0xLmNybDCByqCBx6CBxIaBwWxk\r\nYXA6Ly9sZGFwLnNlcnZlcnBhc3MudGVsZXNlYy5kZS9jbj1UZWxlU2VjJTIwU2Vy\r\ndmVyUGFzcyUyMERFLTEsb3U9VC1TeXN0ZW1zJTIwVHJ1c3QlMjBDZW50ZXIsbz1U\r\nLVN5c3RlbXMlMjBJbnRlcm5hdGlvbmFsJTIwR21iSCxjPWRlP2NlcnRpZmljYXRl\r\nUmV2b2NhdGlvbmxpc3Q/YmFzZT9jZXJ0aWZpY2F0ZVJldm9jYXRpb25saXN0PSow\r\nggE5BggrBgEFBQcBAQSCASswggEnMDMGCCsGAQUFBzABhidodHRwOi8vb2NzcC5z\r\nZXJ2ZXJwYXNzLnRlbGVzZWMuZGUvb2NzcHIwTAYIKwYBBQUHMAKGQGh0dHA6Ly9j\r\ncmwuc2VydmVycGFzcy50ZWxlc2VjLmRlL2NydC9UZWxlU2VjX1NlcnZlclBhc3Nf\r\nREUtMS5jZXIwgaEGCCsGAQUFBzAChoGUbGRhcDovL2xkYXAuc2VydmVycGFzcy50\r\nZWxlc2VjLmRlL2NuPVRlbGVTZWMlMjBTZXJ2ZXJQYXNzJTIwREUtMSxvdT1ULVN5\r\nc3RlbXMlMjBUcnVzdCUyMENlbnRlcixvPVQtU3lzdGVtcyUyMEludGVybmF0aW9u\r\nYWwlMjBHbWJILGM9ZGU/Y0FDZXJ0aWZpY2F0ZTAMBgNVHRMBAf8EAjAAMBYGA1Ud\r\nEQQPMA2CC2ltYXAud2ViLmRlMA0GCSqGSIb3DQEBBQUAA4IBAQAp7MQjjOWQ0M7N\r\n21GrPDfWSMR3eJnuMs37I9G2t9i99w7xKtmoVBPyYMORL1zRHn/DEguo4j5ua7CH\r\nrKLh9Sd1wDKqVWGFxvmP4f/mvEx5YVI68mg+M2VQf/h58IXTTZRbUcsv3HVeruI6\r\npgQNUsEQqRVJmrgT/iPd98RhhzBqef6Wfrt3Ns6N835egphxUVcVj/v/PqBCZKQb\r\nNl5QsoaHDqh3XC+Og2awOGvWHUzxUKEqkP5nsMs2YjfOtcQRtxdislQAfRktuUBA\r\nWMZAlvoVF/CIyFvcUH11KNur7HfK5PptvtdvLQaF2c+lN+LGP6D+nbTLqAo1v09o\r\nnZpHbQOt\r\n-----END CERTIFICATE-----',
|
||||
pinned: true,
|
||||
ignoreTLS: false
|
||||
},
|
||||
smtp: {
|
||||
host: 'smtp.web.de',
|
||||
port: 587,
|
||||
secure: false,
|
||||
ca: '-----BEGIN CERTIFICATE-----\r\nMIIHUzCCBjugAwIBAgIJAKaEwtMgQ2s5MA0GCSqGSIb3DQEBBQUAMIHJMQswCQYD\r\nVQQGEwJERTElMCMGA1UEChMcVC1TeXN0ZW1zIEludGVybmF0aW9uYWwgR21iSDEf\r\nMB0GA1UECxMWVC1TeXN0ZW1zIFRydXN0IENlbnRlcjEMMAoGA1UECBMDTlJXMQ4w\r\nDAYDVQQREwU1NzI1MDEQMA4GA1UEBxMHTmV0cGhlbjEgMB4GA1UECRMXVW50ZXJl\r\nIEluZHVzdHJpZXN0ci4gMjAxIDAeBgNVBAMTF1RlbGVTZWMgU2VydmVyUGFzcyBE\r\nRS0xMB4XDTEzMDgyMTA4MzU1MVoXDTE2MDgyNjIzNTk1OVowga0xCzAJBgNVBAYT\r\nAkRFMR4wHAYDVQQKDBUxJjEgTWFpbCAmIE1lZGlhIEdtYkgxDzANBgNVBAsTBldF\r\nQi5ERTEdMBsGA1UECBMUUmhpbmVsYW5kLVBhbGF0aW5hdGUxEjAQBgNVBAcTCU1v\r\nbnRhYmF1cjEkMCIGCSqGSIb3DQEJARYVc2VydmVyLWNlcnRzQDF1bmQxLmRlMRQw\r\nEgYDVQQDEwtzbXRwLndlYi5kZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\r\nggEBAL1WfyFqmifaEr6h9yntJKs33fnEVu0pQqS5RV9rnQU0E/cJYJ9RJOoTjhpE\r\nkT1LW8DyTM5vUzpgtFHQM0BOO6jOeZXUYNUaopK3yO4l/cpiitigtk9ZnWfKzbU3\r\n9hlhf+YwUUvoqjUA8I6fSu+VNPNlOBvamo18oHljXAPltL9oiwAxvTLNktBMy4T9\r\njxU1DeGoPbJKGwV7zIBQ2qUHuLkMvy5/H39t79Tih+zfzgfY/xIUfdmPNY9dK8ZY\r\nAtyF/RiUS199pd9dV4Vwh6JLvtNlWchBnKVhragLCGbkjnvUXmS1BPrclLR23s1v\r\nMehgOD2WhXl8PoqrOPb6y+lgMlECAwEAAaOCA1YwggNSMB8GA1UdIwQYMBaAFGJP\r\nE842Z4TNGfygTxmL7xVUATIcMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggr\r\nBgEFBQcDAgYIKwYBBQUHAwEwHQYDVR0OBBYEFAUuppozNCv2UzMV5p6CTpkEhsRS\r\nMFkGA1UdIARSMFAwRAYJKwYBBAG9Rw0CMDcwNQYIKwYBBQUHAgEWKWh0dHA6Ly93\r\nd3cudGVsZXNlYy5kZS9zZXJ2ZXJwYXNzL2Nwcy5odG1sMAgGBmeBDAECAjCCASEG\r\nA1UdHwSCARgwggEUMEWgQ6BBhj9odHRwOi8vY3JsLnNlcnZlcnBhc3MudGVsZXNl\r\nYy5kZS9ybC9UZWxlU2VjX1NlcnZlclBhc3NfREUtMS5jcmwwgcqggceggcSGgcFs\r\nZGFwOi8vbGRhcC5zZXJ2ZXJwYXNzLnRlbGVzZWMuZGUvY249VGVsZVNlYyUyMFNl\r\ncnZlclBhc3MlMjBERS0xLG91PVQtU3lzdGVtcyUyMFRydXN0JTIwQ2VudGVyLG89\r\nVC1TeXN0ZW1zJTIwSW50ZXJuYXRpb25hbCUyMEdtYkgsYz1kZT9jZXJ0aWZpY2F0\r\nZVJldm9jYXRpb25saXN0P2Jhc2U/Y2VydGlmaWNhdGVSZXZvY2F0aW9ubGlzdD0q\r\nMIIBOQYIKwYBBQUHAQEEggErMIIBJzAzBggrBgEFBQcwAYYnaHR0cDovL29jc3Au\r\nc2VydmVycGFzcy50ZWxlc2VjLmRlL29jc3ByMEwGCCsGAQUFBzAChkBodHRwOi8v\r\nY3JsLnNlcnZlcnBhc3MudGVsZXNlYy5kZS9jcnQvVGVsZVNlY19TZXJ2ZXJQYXNz\r\nX0RFLTEuY2VyMIGhBggrBgEFBQcwAoaBlGxkYXA6Ly9sZGFwLnNlcnZlcnBhc3Mu\r\ndGVsZXNlYy5kZS9jbj1UZWxlU2VjJTIwU2VydmVyUGFzcyUyMERFLTEsb3U9VC1T\r\neXN0ZW1zJTIwVHJ1c3QlMjBDZW50ZXIsbz1ULVN5c3RlbXMlMjBJbnRlcm5hdGlv\r\nbmFsJTIwR21iSCxjPWRlP2NBQ2VydGlmaWNhdGUwDAYDVR0TAQH/BAIwADAWBgNV\r\nHREEDzANggtzbXRwLndlYi5kZTANBgkqhkiG9w0BAQUFAAOCAQEAGFtJxVxWrURy\r\nFfR4UfmW+N1cZZx9sfC5jolV8LGje87DgbWdqu5TRL9FoQ1pwOTbM9mc3yWSoIbU\r\nx6E+rziJK+SFGMIy+Lt13P9M9Oc8JzVHoVEAgqlEeO6OYxrGE6SaSjZODJxPaEtU\r\nzEAKf0HVJQoGaU6fD+/8l3yhksAlsF/L85nP+KcZtoancOkJWE0GQMZp7pdLU0Ou\r\nFUQoAcqMIyHQfDqJ5iwvx/9C7jmy3Nvw9tXdPrdn2O7ywrnFeJuT2xiorZzg6ezn\r\nUlc/sMvd/LPX0f60pSQr9tkZgU4f8Jvx9EvPUCFTRXlXqkBIhgJhgCadZC+wKB1P\r\neQMS8rksXw==\r\n-----END CERTIFICATE-----',
|
||||
pinned: true,
|
||||
ignoreTLS: false
|
||||
}
|
||||
smtp: {
|
||||
host: 'smtp-mail.outlook.com',
|
||||
port: 587,
|
||||
secure: false,
|
||||
ca: '-----BEGIN CERTIFICATE-----\r\nMIIFQjCCBCqgAwIBAgISESHl0vjrML7zKmGlv42YL75vMA0GCSqGSIb3DQEBBQUA\r\nMF0xCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMTMwMQYD\r\nVQQDEypHbG9iYWxTaWduIE9yZ2FuaXphdGlvbiBWYWxpZGF0aW9uIENBIC0gRzIw\r\nHhcNMTMwNDI0MjAzNTA5WhcNMTYwNDI0MjAzNTA5WjBsMQswCQYDVQQGEwJVUzET\r\nMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV\r\nTWljcm9zb2Z0IENvcnBvcmF0aW9uMRYwFAYDVQQDDA0qLmhvdG1haWwuY29tMIIB\r\nIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAumSiBWrzHZf6WFP5a/j4+K7D\r\n1izLoYKj5Omll0pdxKvKcBRDf+iaIkCbSOPNpx2uWGZdwNwkabYCQavaBf2ebwmS\r\nS8i1CJpHflO+k0qYd5WUi7sSsZ3+6RaCMdLoDIPGyYMQuy7TFtVO7LSt5+qscyyi\r\nET8c3lE2aj/XW13UZvRrV65ZJvMjUtwaDnIcAxGeasYoebLsKdqHQ2uTr4PmNwCc\r\nviGVFSOzkGAoC0PfyqKB2xUWy3Kc5zRI2xvUW8Jb2b/9Ze3g55pIUzKsjpglkQTm\r\nedVPSYYPGNz6Kl/ZshBXdBAk398q1JkSmUaTMa2hJgBbcC+73ax40AJDGJlz+QID\r\nAQABo4IB6zCCAecwDgYDVR0PAQH/BAQDAgWgMEkGA1UdIARCMEAwPgYGZ4EMAQIC\r\nMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8vd3d3Lmdsb2JhbHNpZ24uY29tL3JlcG9z\r\naXRvcnkvMEAGA1UdEQQ5MDeCDSouaG90bWFpbC5jb22CCioubGl2ZS5jb22CDSou\r\nb3V0bG9vay5jb22CC2hvdG1haWwuY29tMAkGA1UdEwQCMAAwHQYDVR0lBBYwFAYI\r\nKwYBBQUHAwEGCCsGAQUFBwMCMEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly9jcmwu\r\nZ2xvYmFsc2lnbi5jb20vZ3MvZ3Nvcmdhbml6YXRpb252YWxnMi5jcmwwgZYGCCsG\r\nAQUFBwEBBIGJMIGGMEcGCCsGAQUFBzAChjtodHRwOi8vc2VjdXJlLmdsb2JhbHNp\r\nZ24uY29tL2NhY2VydC9nc29yZ2FuaXphdGlvbnZhbGcyLmNydDA7BggrBgEFBQcw\r\nAYYvaHR0cDovL29jc3AyLmdsb2JhbHNpZ24uY29tL2dzb3JnYW5pemF0aW9udmFs\r\nZzIwHQYDVR0OBBYEFHbgHqTLsXDt7uMRyE62rnDEfLn9MB8GA1UdIwQYMBaAFF1G\r\nso3ES3Qcu+31c7Y6tziPdZ5+MA0GCSqGSIb3DQEBBQUAA4IBAQByy1+3N6ZRVooI\r\nxqw8Ng+UFz0g7UHkbPEnvTu1uxJ2AojFuP/P1PAk+/6uMRvpPlWg/5uqmOIWxKxJ\r\nLo6xSbkDf4LN+KYwes3XSuPyziZ4QbPnehHhZ0377iiA8fpRJADg9NWKCRHh5aAd\r\ne9QvJUW/GgYkBN+F4yYc2jIjR3Rehv4JYOKS3iXO9OoHsDS2CcCFaS2imgQVfYLg\r\nslBwT/A08PCOhW5huiluSmih7x5Qf7sFDv8jineu6ehKzi8pKnOq4k8G4QiWn38Y\r\nCeiBkkwFOwj7T3M/ITiiSS9DHDGeokj16eBi83Zx3YYiJ9YZvnQ+4GvqJ5eJJ6pR\r\nKKvemr+m\r\n-----END CERTIFICATE-----',
|
||||
pinned: true,
|
||||
ignoreTLS: false
|
||||
}
|
||||
},
|
||||
gmx: {
|
||||
imap: {
|
||||
host: 'imap.gmx.net',
|
||||
port: 993,
|
||||
secure: true,
|
||||
ca: '-----BEGIN CERTIFICATE-----\r\nMIIHUDCCBjigAwIBAgIIM52e2Oc5J8kwDQYJKoZIhvcNAQEFBQAwgckxCzAJBgNV\r\nBAYTAkRFMSUwIwYDVQQKExxULVN5c3RlbXMgSW50ZXJuYXRpb25hbCBHbWJIMR8w\r\nHQYDVQQLExZULVN5c3RlbXMgVHJ1c3QgQ2VudGVyMQwwCgYDVQQIEwNOUlcxDjAM\r\nBgNVBBETBTU3MjUwMRAwDgYDVQQHEwdOZXRwaGVuMSAwHgYDVQQJExdVbnRlcmUg\r\nSW5kdXN0cmllc3RyLiAyMDEgMB4GA1UEAxMXVGVsZVNlYyBTZXJ2ZXJQYXNzIERF\r\nLTEwHhcNMTMxMTEyMTAxNzMxWhcNMTYxMTE3MjM1OTU5WjCBnTELMAkGA1UEBhMC\r\nREUxHjAcBgNVBAoMFTEmMSBNYWlsICYgTWVkaWEgR21iSDEdMBsGA1UECBMUUmhp\r\nbmVsYW5kLVBhbGF0aW5hdGUxEjAQBgNVBAcTCU1vbnRhYmF1cjEkMCIGCSqGSIb3\r\nDQEJARYVc2VydmVyLWNlcnRzQDF1bmQxLmRlMRUwEwYDVQQDEwxpbWFwLmdteC5u\r\nZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDHP3QN1ztX5m8LaC9m\r\nt2nrcLBEfxb61FQCieXW4OV/D5HEy7vgQIAeS+pg2/9ClawprQFbLUa46owUcJuq\r\no2SaefsYqjRhIP/tPeyGRJQmfnyCRRoHIYno57/bz2qmHjzvkqEUMvNGVVej7BJv\r\nGukNKPXYicodkRodF3EicTPnGitGeXOsAxyPuCPIsBPAMuuuBzKenS7nj+yseSGe\r\nkM7/kjtNgbjDyBXi2BvoUS/U+Gm9p1lq0ymT9fhGj/k9/Qz312GHYxfZB1bhL0pk\r\nadF25P8fTIy5tKMWqJfgCF+eaOwgkYecxeCcrt2E3D8ThLvmkBRrXsTT51ZzD7sp\r\n7hPpAgMBAAGjggNkMIIDYDAfBgNVHSMEGDAWgBRiTxPONmeEzRn8oE8Zi+8VVAEy\r\nHDAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMB\r\nMB0GA1UdDgQWBBRfb2gYyD6V+oBN2Ii5WQj0AddemjBZBgNVHSAEUjBQMEQGCSsG\r\nAQQBvUcNAjA3MDUGCCsGAQUFBwIBFilodHRwOi8vd3d3LnRlbGVzZWMuZGUvc2Vy\r\ndmVycGFzcy9jcHMuaHRtbDAIBgZngQwBAgIwggEhBgNVHR8EggEYMIIBFDBFoEOg\r\nQYY/aHR0cDovL2NybC5zZXJ2ZXJwYXNzLnRlbGVzZWMuZGUvcmwvVGVsZVNlY19T\r\nZXJ2ZXJQYXNzX0RFLTEuY3JsMIHKoIHHoIHEhoHBbGRhcDovL2xkYXAuc2VydmVy\r\ncGFzcy50ZWxlc2VjLmRlL2NuPVRlbGVTZWMlMjBTZXJ2ZXJQYXNzJTIwREUtMSxv\r\ndT1ULVN5c3RlbXMlMjBUcnVzdCUyMENlbnRlcixvPVQtU3lzdGVtcyUyMEludGVy\r\nbmF0aW9uYWwlMjBHbWJILGM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9ubGlzdD9i\r\nYXNlP2NlcnRpZmljYXRlUmV2b2NhdGlvbmxpc3Q9KjCCATkGCCsGAQUFBwEBBIIB\r\nKzCCAScwMwYIKwYBBQUHMAGGJ2h0dHA6Ly9vY3NwLnNlcnZlcnBhc3MudGVsZXNl\r\nYy5kZS9vY3NwcjBMBggrBgEFBQcwAoZAaHR0cDovL2NybC5zZXJ2ZXJwYXNzLnRl\r\nbGVzZWMuZGUvY3J0L1RlbGVTZWNfU2VydmVyUGFzc19ERS0xLmNlcjCBoQYIKwYB\r\nBQUHMAKGgZRsZGFwOi8vbGRhcC5zZXJ2ZXJwYXNzLnRlbGVzZWMuZGUvY249VGVs\r\nZVNlYyUyMFNlcnZlclBhc3MlMjBERS0xLG91PVQtU3lzdGVtcyUyMFRydXN0JTIw\r\nQ2VudGVyLG89VC1TeXN0ZW1zJTIwSW50ZXJuYXRpb25hbCUyMEdtYkgsYz1kZT9j\r\nQUNlcnRpZmljYXRlMAwGA1UdEwEB/wQCMAAwJAYDVR0RBB0wG4IMaW1hcC5nbXgu\r\nbmV0ggtpbWFwLmdteC5kZTANBgkqhkiG9w0BAQUFAAOCAQEADl442s2ouynDNEzl\r\nBPzhCxjp47TmKzgzrCGh1AH3+UOkw54mYU65UaY9cJ51jgOu+pjNB4KxVfcqyG5O\r\nJcowS09ZtZ+khb1OkzXAAbEqNj0oyH/1K550d5Ir5VbXF0ZdVKVTzEFRyjvUt5NJ\r\n/b6Q2bbY/sPm8QiK9SPaYkO5/3J1KB0u7PkqqfOhZ9UJE6mIN66T2bpFmAwle3zL\r\n6+L/VwYSgUNs/w2l6xWlYP3pFFaT12TO43Q057I3vP7yCt3QE/VLDpdQOQ3H0oy2\r\nJQGPJLFby9MPYNkmEmwjh4e95TAY4ZkZMe08ix0J1Smy8DwjTz6SXqYC+EC/PQlh\r\nE0UHxA==\r\n-----END CERTIFICATE-----',
|
||||
pinned: true,
|
||||
ignoreTLS: false
|
||||
},
|
||||
checkOutboxInterval: 5000,
|
||||
iconPath: '/img/icon-128-chrome.png',
|
||||
verificationUrl: '/verify/',
|
||||
verificationUuidLength: 36,
|
||||
dbVersion: 5,
|
||||
appVersion: appVersion,
|
||||
outboxMailboxPath: 'OUTBOX',
|
||||
outboxMailboxName: 'Outbox',
|
||||
outboxMailboxType: 'Outbox',
|
||||
connDocTimeout: 5000,
|
||||
imapUpdateBatchSize: 25
|
||||
};
|
||||
smtp: {
|
||||
host: 'mail.gmx.net',
|
||||
port: 587,
|
||||
secure: false,
|
||||
ca: '-----BEGIN CERTIFICATE-----\r\nMIIHbDCCBlSgAwIBAgIJAJ74Ek9UaA0TMA0GCSqGSIb3DQEBBQUAMIHJMQswCQYD\r\nVQQGEwJERTElMCMGA1UEChMcVC1TeXN0ZW1zIEludGVybmF0aW9uYWwgR21iSDEf\r\nMB0GA1UECxMWVC1TeXN0ZW1zIFRydXN0IENlbnRlcjEMMAoGA1UECBMDTlJXMQ4w\r\nDAYDVQQREwU1NzI1MDEQMA4GA1UEBxMHTmV0cGhlbjEgMB4GA1UECRMXVW50ZXJl\r\nIEluZHVzdHJpZXN0ci4gMjAxIDAeBgNVBAMTF1RlbGVTZWMgU2VydmVyUGFzcyBE\r\nRS0xMB4XDTEzMTExMjEwMTY0N1oXDTE2MTExNzIzNTk1OVowgZ0xCzAJBgNVBAYT\r\nAkRFMR4wHAYDVQQKDBUxJjEgTWFpbCAmIE1lZGlhIEdtYkgxHTAbBgNVBAgTFFJo\r\naW5lbGFuZC1QYWxhdGluYXRlMRIwEAYDVQQHEwlNb250YWJhdXIxJDAiBgkqhkiG\r\n9w0BCQEWFXNlcnZlci1jZXJ0c0AxdW5kMS5kZTEVMBMGA1UEAxMMbWFpbC5nbXgu\r\nbmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsEUYppbJXUpQELIL\r\nETAFUwreDfSmbyi9gJPUqquogROHEoRV+bBFZ2YCiHgLU8AggcLmM74HAQMFt8F5\r\neS2OyuEar3E3tNW5XFiI1QblTtY3B7o1coKyq/i+tV3b1jGq4+1aJeeD3UpcraBm\r\nk2XuQgQ6WAeY+XCYyI/OzZ56ZjQ17LHMMI0ayu7SSV+VBpG9loC9E5A82iVtZsqo\r\nSaUZpZf/xICpUgNJ+RlalSsoE6FdCywE/gEEmkpAUh+Mv0WvHITk6aqtMFWex8tE\r\n8cwJGQbmJGt9x8BExbhMY6uQ+HsnsDCPCMVh5JGKx7/e+qyxtHYGChOeI16m2+MT\r\nz47jSQIDAQABo4IDfzCCA3swHwYDVR0jBBgwFoAUYk8TzjZnhM0Z/KBPGYvvFVQB\r\nMhwwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD\r\nATAdBgNVHQ4EFgQUH8mLfxsd5FEh9M+3t3rP6qLiFTEwWQYDVR0gBFIwUDBEBgkr\r\nBgEEAb1HDQIwNzA1BggrBgEFBQcCARYpaHR0cDovL3d3dy50ZWxlc2VjLmRlL3Nl\r\ncnZlcnBhc3MvY3BzLmh0bWwwCAYGZ4EMAQICMIIBIQYDVR0fBIIBGDCCARQwRaBD\r\noEGGP2h0dHA6Ly9jcmwuc2VydmVycGFzcy50ZWxlc2VjLmRlL3JsL1RlbGVTZWNf\r\nU2VydmVyUGFzc19ERS0xLmNybDCByqCBx6CBxIaBwWxkYXA6Ly9sZGFwLnNlcnZl\r\ncnBhc3MudGVsZXNlYy5kZS9jbj1UZWxlU2VjJTIwU2VydmVyUGFzcyUyMERFLTEs\r\nb3U9VC1TeXN0ZW1zJTIwVHJ1c3QlMjBDZW50ZXIsbz1ULVN5c3RlbXMlMjBJbnRl\r\ncm5hdGlvbmFsJTIwR21iSCxjPWRlP2NlcnRpZmljYXRlUmV2b2NhdGlvbmxpc3Q/\r\nYmFzZT9jZXJ0aWZpY2F0ZVJldm9jYXRpb25saXN0PSowggE5BggrBgEFBQcBAQSC\r\nASswggEnMDMGCCsGAQUFBzABhidodHRwOi8vb2NzcC5zZXJ2ZXJwYXNzLnRlbGVz\r\nZWMuZGUvb2NzcHIwTAYIKwYBBQUHMAKGQGh0dHA6Ly9jcmwuc2VydmVycGFzcy50\r\nZWxlc2VjLmRlL2NydC9UZWxlU2VjX1NlcnZlclBhc3NfREUtMS5jZXIwgaEGCCsG\r\nAQUFBzAChoGUbGRhcDovL2xkYXAuc2VydmVycGFzcy50ZWxlc2VjLmRlL2NuPVRl\r\nbGVTZWMlMjBTZXJ2ZXJQYXNzJTIwREUtMSxvdT1ULVN5c3RlbXMlMjBUcnVzdCUy\r\nMENlbnRlcixvPVQtU3lzdGVtcyUyMEludGVybmF0aW9uYWwlMjBHbWJILGM9ZGU/\r\nY0FDZXJ0aWZpY2F0ZTAMBgNVHRMBAf8EAjAAMD8GA1UdEQQ4MDaCDG1haWwuZ214\r\nLm5ldIILbWFpbC5nbXguZGWCDHNtdHAuZ214Lm5ldIILc210cC5nbXguZGUwDQYJ\r\nKoZIhvcNAQEFBQADggEBAJ5SKsBXoOTO0ztsFh8RVd/iu6sijEMpKVF+/cs74TZN\r\nK1QWIz8Ay1f3SUPWXTvkmvayUXWGtxZfYoRIrx3feWBdUozJxe0wz1O3tDQOQgRX\r\nnkcwk7nibTUmW2rk5AIwV0jzOHtIbAGjSdc6my0543e7dVkhcuoCOC+g2NmD+pHF\r\ni1KpmqTLecDlFvprIZUJ23AT1uTnuXYg/tFHeDn3ga00Gce82xZQJelRirJs01SR\r\nDGEBaPXBjJVJNoAP/qEy3jQNRWo5TbSgwn3DTR81FJvltmf0c+zlt4fmcpjpr5ni\r\nFvK7L0rZQL5MOGHPpgRIukEBZCowYr3OYpZYBEaB94I=\r\n-----END CERTIFICATE-----',
|
||||
pinned: true,
|
||||
ignoreTLS: false
|
||||
}
|
||||
},
|
||||
webde: {
|
||||
imap: {
|
||||
host: 'imap.web.de',
|
||||
port: 993,
|
||||
secure: true,
|
||||
ca: '-----BEGIN CERTIFICATE-----\r\nMIIHUjCCBjqgAwIBAgIIHvqUOScyxYUwDQYJKoZIhvcNAQEFBQAwgckxCzAJBgNV\r\nBAYTAkRFMSUwIwYDVQQKExxULVN5c3RlbXMgSW50ZXJuYXRpb25hbCBHbWJIMR8w\r\nHQYDVQQLExZULVN5c3RlbXMgVHJ1c3QgQ2VudGVyMQwwCgYDVQQIEwNOUlcxDjAM\r\nBgNVBBETBTU3MjUwMRAwDgYDVQQHEwdOZXRwaGVuMSAwHgYDVQQJExdVbnRlcmUg\r\nSW5kdXN0cmllc3RyLiAyMDEgMB4GA1UEAxMXVGVsZVNlYyBTZXJ2ZXJQYXNzIERF\r\nLTEwHhcNMTMwODIxMDgzMTE2WhcNMTYwODI2MjM1OTU5WjCBrTELMAkGA1UEBhMC\r\nREUxHjAcBgNVBAoMFTEmMSBNYWlsICYgTWVkaWEgR21iSDEPMA0GA1UECxMGV0VC\r\nLkRFMR0wGwYDVQQIExRSaGluZWxhbmQtUGFsYXRpbmF0ZTESMBAGA1UEBxMJTW9u\r\ndGFiYXVyMSQwIgYJKoZIhvcNAQkBFhVzZXJ2ZXItY2VydHNAMXVuZDEuZGUxFDAS\r\nBgNVBAMTC2ltYXAud2ViLmRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC\r\nAQEA5dvpcnYczFs9GANTYN5N3mP6ATFVm7P4nYhRIQMj/YssSjmbrOPIEbe12JNC\r\nRO9CI7Z4Dnk42BM5TiH+QKGpcfnitofOv4gKPjwCMcPDHfY152/+YDaiyU/md2Hg\r\n/WrZ/50KwC8Sw1tZkYDXWB0zeJkfPdS2r4ATNrpMR0fYcG08+elz5T2SWNg+c1xL\r\nKFdCh33wZLkijOfW0HA164QjXaLPBjxa+GyZFY19ywOQ85KdFVVLmGUrLz8n4ZLc\r\n7K6KUuzYttnUuVxctFauQ4DRHE/CfUvvNHHgn5d+A2XS7jNUgmUb0gjzy3OtlTP3\r\ngM4ostVufmnlS7qqFDR7A3v4lQIDAQABo4IDVjCCA1IwHwYDVR0jBBgwFoAUYk8T\r\nzjZnhM0Z/KBPGYvvFVQBMhwwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsG\r\nAQUFBwMCBggrBgEFBQcDATAdBgNVHQ4EFgQUDH66P/Y4NlKGphPniIzmutEI9ocw\r\nWQYDVR0gBFIwUDBEBgkrBgEEAb1HDQIwNzA1BggrBgEFBQcCARYpaHR0cDovL3d3\r\ndy50ZWxlc2VjLmRlL3NlcnZlcnBhc3MvY3BzLmh0bWwwCAYGZ4EMAQICMIIBIQYD\r\nVR0fBIIBGDCCARQwRaBDoEGGP2h0dHA6Ly9jcmwuc2VydmVycGFzcy50ZWxlc2Vj\r\nLmRlL3JsL1RlbGVTZWNfU2VydmVyUGFzc19ERS0xLmNybDCByqCBx6CBxIaBwWxk\r\nYXA6Ly9sZGFwLnNlcnZlcnBhc3MudGVsZXNlYy5kZS9jbj1UZWxlU2VjJTIwU2Vy\r\ndmVyUGFzcyUyMERFLTEsb3U9VC1TeXN0ZW1zJTIwVHJ1c3QlMjBDZW50ZXIsbz1U\r\nLVN5c3RlbXMlMjBJbnRlcm5hdGlvbmFsJTIwR21iSCxjPWRlP2NlcnRpZmljYXRl\r\nUmV2b2NhdGlvbmxpc3Q/YmFzZT9jZXJ0aWZpY2F0ZVJldm9jYXRpb25saXN0PSow\r\nggE5BggrBgEFBQcBAQSCASswggEnMDMGCCsGAQUFBzABhidodHRwOi8vb2NzcC5z\r\nZXJ2ZXJwYXNzLnRlbGVzZWMuZGUvb2NzcHIwTAYIKwYBBQUHMAKGQGh0dHA6Ly9j\r\ncmwuc2VydmVycGFzcy50ZWxlc2VjLmRlL2NydC9UZWxlU2VjX1NlcnZlclBhc3Nf\r\nREUtMS5jZXIwgaEGCCsGAQUFBzAChoGUbGRhcDovL2xkYXAuc2VydmVycGFzcy50\r\nZWxlc2VjLmRlL2NuPVRlbGVTZWMlMjBTZXJ2ZXJQYXNzJTIwREUtMSxvdT1ULVN5\r\nc3RlbXMlMjBUcnVzdCUyMENlbnRlcixvPVQtU3lzdGVtcyUyMEludGVybmF0aW9u\r\nYWwlMjBHbWJILGM9ZGU/Y0FDZXJ0aWZpY2F0ZTAMBgNVHRMBAf8EAjAAMBYGA1Ud\r\nEQQPMA2CC2ltYXAud2ViLmRlMA0GCSqGSIb3DQEBBQUAA4IBAQAp7MQjjOWQ0M7N\r\n21GrPDfWSMR3eJnuMs37I9G2t9i99w7xKtmoVBPyYMORL1zRHn/DEguo4j5ua7CH\r\nrKLh9Sd1wDKqVWGFxvmP4f/mvEx5YVI68mg+M2VQf/h58IXTTZRbUcsv3HVeruI6\r\npgQNUsEQqRVJmrgT/iPd98RhhzBqef6Wfrt3Ns6N835egphxUVcVj/v/PqBCZKQb\r\nNl5QsoaHDqh3XC+Og2awOGvWHUzxUKEqkP5nsMs2YjfOtcQRtxdislQAfRktuUBA\r\nWMZAlvoVF/CIyFvcUH11KNur7HfK5PptvtdvLQaF2c+lN+LGP6D+nbTLqAo1v09o\r\nnZpHbQOt\r\n-----END CERTIFICATE-----',
|
||||
pinned: true,
|
||||
ignoreTLS: false
|
||||
},
|
||||
smtp: {
|
||||
host: 'smtp.web.de',
|
||||
port: 587,
|
||||
secure: false,
|
||||
ca: '-----BEGIN CERTIFICATE-----\r\nMIIHUzCCBjugAwIBAgIJAKaEwtMgQ2s5MA0GCSqGSIb3DQEBBQUAMIHJMQswCQYD\r\nVQQGEwJERTElMCMGA1UEChMcVC1TeXN0ZW1zIEludGVybmF0aW9uYWwgR21iSDEf\r\nMB0GA1UECxMWVC1TeXN0ZW1zIFRydXN0IENlbnRlcjEMMAoGA1UECBMDTlJXMQ4w\r\nDAYDVQQREwU1NzI1MDEQMA4GA1UEBxMHTmV0cGhlbjEgMB4GA1UECRMXVW50ZXJl\r\nIEluZHVzdHJpZXN0ci4gMjAxIDAeBgNVBAMTF1RlbGVTZWMgU2VydmVyUGFzcyBE\r\nRS0xMB4XDTEzMDgyMTA4MzU1MVoXDTE2MDgyNjIzNTk1OVowga0xCzAJBgNVBAYT\r\nAkRFMR4wHAYDVQQKDBUxJjEgTWFpbCAmIE1lZGlhIEdtYkgxDzANBgNVBAsTBldF\r\nQi5ERTEdMBsGA1UECBMUUmhpbmVsYW5kLVBhbGF0aW5hdGUxEjAQBgNVBAcTCU1v\r\nbnRhYmF1cjEkMCIGCSqGSIb3DQEJARYVc2VydmVyLWNlcnRzQDF1bmQxLmRlMRQw\r\nEgYDVQQDEwtzbXRwLndlYi5kZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\r\nggEBAL1WfyFqmifaEr6h9yntJKs33fnEVu0pQqS5RV9rnQU0E/cJYJ9RJOoTjhpE\r\nkT1LW8DyTM5vUzpgtFHQM0BOO6jOeZXUYNUaopK3yO4l/cpiitigtk9ZnWfKzbU3\r\n9hlhf+YwUUvoqjUA8I6fSu+VNPNlOBvamo18oHljXAPltL9oiwAxvTLNktBMy4T9\r\njxU1DeGoPbJKGwV7zIBQ2qUHuLkMvy5/H39t79Tih+zfzgfY/xIUfdmPNY9dK8ZY\r\nAtyF/RiUS199pd9dV4Vwh6JLvtNlWchBnKVhragLCGbkjnvUXmS1BPrclLR23s1v\r\nMehgOD2WhXl8PoqrOPb6y+lgMlECAwEAAaOCA1YwggNSMB8GA1UdIwQYMBaAFGJP\r\nE842Z4TNGfygTxmL7xVUATIcMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggr\r\nBgEFBQcDAgYIKwYBBQUHAwEwHQYDVR0OBBYEFAUuppozNCv2UzMV5p6CTpkEhsRS\r\nMFkGA1UdIARSMFAwRAYJKwYBBAG9Rw0CMDcwNQYIKwYBBQUHAgEWKWh0dHA6Ly93\r\nd3cudGVsZXNlYy5kZS9zZXJ2ZXJwYXNzL2Nwcy5odG1sMAgGBmeBDAECAjCCASEG\r\nA1UdHwSCARgwggEUMEWgQ6BBhj9odHRwOi8vY3JsLnNlcnZlcnBhc3MudGVsZXNl\r\nYy5kZS9ybC9UZWxlU2VjX1NlcnZlclBhc3NfREUtMS5jcmwwgcqggceggcSGgcFs\r\nZGFwOi8vbGRhcC5zZXJ2ZXJwYXNzLnRlbGVzZWMuZGUvY249VGVsZVNlYyUyMFNl\r\ncnZlclBhc3MlMjBERS0xLG91PVQtU3lzdGVtcyUyMFRydXN0JTIwQ2VudGVyLG89\r\nVC1TeXN0ZW1zJTIwSW50ZXJuYXRpb25hbCUyMEdtYkgsYz1kZT9jZXJ0aWZpY2F0\r\nZVJldm9jYXRpb25saXN0P2Jhc2U/Y2VydGlmaWNhdGVSZXZvY2F0aW9ubGlzdD0q\r\nMIIBOQYIKwYBBQUHAQEEggErMIIBJzAzBggrBgEFBQcwAYYnaHR0cDovL29jc3Au\r\nc2VydmVycGFzcy50ZWxlc2VjLmRlL29jc3ByMEwGCCsGAQUFBzAChkBodHRwOi8v\r\nY3JsLnNlcnZlcnBhc3MudGVsZXNlYy5kZS9jcnQvVGVsZVNlY19TZXJ2ZXJQYXNz\r\nX0RFLTEuY2VyMIGhBggrBgEFBQcwAoaBlGxkYXA6Ly9sZGFwLnNlcnZlcnBhc3Mu\r\ndGVsZXNlYy5kZS9jbj1UZWxlU2VjJTIwU2VydmVyUGFzcyUyMERFLTEsb3U9VC1T\r\neXN0ZW1zJTIwVHJ1c3QlMjBDZW50ZXIsbz1ULVN5c3RlbXMlMjBJbnRlcm5hdGlv\r\nbmFsJTIwR21iSCxjPWRlP2NBQ2VydGlmaWNhdGUwDAYDVR0TAQH/BAIwADAWBgNV\r\nHREEDzANggtzbXRwLndlYi5kZTANBgkqhkiG9w0BAQUFAAOCAQEAGFtJxVxWrURy\r\nFfR4UfmW+N1cZZx9sfC5jolV8LGje87DgbWdqu5TRL9FoQ1pwOTbM9mc3yWSoIbU\r\nx6E+rziJK+SFGMIy+Lt13P9M9Oc8JzVHoVEAgqlEeO6OYxrGE6SaSjZODJxPaEtU\r\nzEAKf0HVJQoGaU6fD+/8l3yhksAlsF/L85nP+KcZtoancOkJWE0GQMZp7pdLU0Ou\r\nFUQoAcqMIyHQfDqJ5iwvx/9C7jmy3Nvw9tXdPrdn2O7ywrnFeJuT2xiorZzg6ezn\r\nUlc/sMvd/LPX0f60pSQr9tkZgU4f8Jvx9EvPUCFTRXlXqkBIhgJhgCadZC+wKB1P\r\neQMS8rksXw==\r\n-----END CERTIFICATE-----',
|
||||
pinned: true,
|
||||
ignoreTLS: false
|
||||
}
|
||||
},
|
||||
checkOutboxInterval: 5000,
|
||||
iconPath: '/img/icon-128-chrome.png',
|
||||
verificationUrl: '/verify/',
|
||||
verificationUuidLength: 36,
|
||||
dbVersion: 5,
|
||||
appVersion: appVersion,
|
||||
outboxMailboxPath: 'OUTBOX',
|
||||
outboxMailboxName: 'Outbox',
|
||||
outboxMailboxType: 'Outbox',
|
||||
connDocTimeout: 5000,
|
||||
imapUpdateBatchSize: 25
|
||||
};
|
||||
|
||||
/**
|
||||
* Strings are maintained here
|
||||
*/
|
||||
app.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',
|
||||
signature: '\n\n\n--\nSent from Whiteout Mail - https://whiteout.io\n\nMy PGP key: ',
|
||||
webSite: 'http://whiteout.io',
|
||||
verificationSubject: '[whiteout] New public key uploaded',
|
||||
sendBtnClear: 'Send',
|
||||
sendBtnSecure: 'Send securely',
|
||||
updatePublicKeyTitle: 'Public Key Updated',
|
||||
updatePublicKeyMsgNewKey: '{0} updated his key and may not be able to read encrypted messages sent with his old key. Update the key?',
|
||||
updatePublicKeyMsgRemovedKey: '{0} revoked his key and may no longer be able to read encrypted messages. Remove the key?',
|
||||
updatePublicKeyPosBtn: 'Yes',
|
||||
updatePublicKeyNegBtn: 'No',
|
||||
outdatedCertificateTitle: 'Warning',
|
||||
outdatedCertificateMessage: 'The SSL certificate for the mail server {0} changed, the connection was refused.',
|
||||
updateCertificateTitle: 'Warning',
|
||||
updateCertificateMessage: 'The SSL certificate for the mail server {0} changed. Do you want to proceed?',
|
||||
updateCertificatePosBtn: 'Yes',
|
||||
updateCertificateNegBtn: 'No',
|
||||
certificateFaqLink: 'https://github.com/whiteout-io/mail-html5/wiki/FAQ#what-does-the-ssl-certificate-for-the-mail-server--changed-mean',
|
||||
bugReportTitle: 'Report a bug',
|
||||
bugReportSubject: '[Bug] I want to report a bug',
|
||||
bugReportBody: 'Steps to reproduce\n1. \n2. \n3. \n\nWhat happens?\n\n\nWhat do you expect to happen instead?\n\n\n\n== PLEASE DONT PUT ANY KEYS HERE! ==\n\n\n## Log\n\nBelow is the log. It includes your interactions with your email provider in an anonymized way from the point where you started the app for the last time. Any information provided by you will be used for the porpose of locating and fixing the bug you reported. It will be deleted subsequently. However, you can edit this log and/or remove log data in the event that something would show up.\n\n',
|
||||
supportAddress: 'mail.support@whiteout.io',
|
||||
connDocOffline: 'It appears that you are offline. Please retry when you are online.',
|
||||
connDocTlsWrongCert: 'A connection to {0} was rejected because the TLS certificate is invalid. Please have a look at the FAQ for information on how to fix this error.',
|
||||
connDocHostUnreachable: 'We could not establish a connection to {0}. Please check the server settings!',
|
||||
connDocHostTimeout: 'We could not establish a connection to {0} within {1} ms. Please check the server settings and encryption mode!',
|
||||
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;
|
||||
});
|
||||
/**
|
||||
* Strings are maintained here
|
||||
*/
|
||||
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',
|
||||
signature: '\n\n\n--\nSent from Whiteout Mail - https://whiteout.io\n\nMy PGP key: ',
|
||||
webSite: 'http://whiteout.io',
|
||||
verificationSubject: '[whiteout] New public key uploaded',
|
||||
sendBtnClear: 'Send',
|
||||
sendBtnSecure: 'Send securely',
|
||||
updatePublicKeyTitle: 'Public Key Updated',
|
||||
updatePublicKeyMsgNewKey: '{0} updated his key and may not be able to read encrypted messages sent with his old key. Update the key?',
|
||||
updatePublicKeyMsgRemovedKey: '{0} revoked his key and may no longer be able to read encrypted messages. Remove the key?',
|
||||
updatePublicKeyPosBtn: 'Yes',
|
||||
updatePublicKeyNegBtn: 'No',
|
||||
outdatedCertificateTitle: 'Warning',
|
||||
outdatedCertificateMessage: 'The SSL certificate for the mail server {0} changed, the connection was refused.',
|
||||
updateCertificateTitle: 'Warning',
|
||||
updateCertificateMessage: 'The SSL certificate for the mail server {0} changed. Do you want to proceed?',
|
||||
updateCertificatePosBtn: 'Yes',
|
||||
updateCertificateNegBtn: 'No',
|
||||
certificateFaqLink: 'https://github.com/whiteout-io/mail-html5/wiki/FAQ#what-does-the-ssl-certificate-for-the-mail-server--changed-mean',
|
||||
bugReportTitle: 'Report a bug',
|
||||
bugReportSubject: '[Bug] I want to report a bug',
|
||||
bugReportBody: 'Steps to reproduce\n1. \n2. \n3. \n\nWhat happens?\n\n\nWhat do you expect to happen instead?\n\n\n\n== PLEASE DONT PUT ANY KEYS HERE! ==\n\n\n## Log\n\nBelow is the log. It includes your interactions with your email provider in an anonymized way from the point where you started the app for the last time. Any information provided by you will be used for the porpose of locating and fixing the bug you reported. It will be deleted subsequently. However, you can edit this log and/or remove log data in the event that something would show up.\n\n',
|
||||
supportAddress: 'mail.support@whiteout.io',
|
||||
connDocOffline: 'It appears that you are offline. Please retry when you are online.',
|
||||
connDocTlsWrongCert: 'A connection to {0} was rejected because the TLS certificate is invalid. Please have a look at the FAQ for information on how to fix this error.',
|
||||
connDocHostUnreachable: 'We could not establish a connection to {0}. Please check the server settings!',
|
||||
connDocHostTimeout: 'We could not establish a connection to {0} within {1} ms. Please check the server settings and encryption mode!',
|
||||
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}'
|
||||
};
|
|
@ -1,271 +1,271 @@
|
|||
/**
|
||||
* The main application controller
|
||||
*/
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var axe = require('axe'),
|
||||
Auth = require('js/bo/auth'),
|
||||
PGP = require('js/crypto/pgp'),
|
||||
PgpMailer = require('pgpmailer'),
|
||||
OAuth = require('js/util/oauth'),
|
||||
PgpBuilder = require('pgpbuilder'),
|
||||
OutboxBO = require('js/bo/outbox'),
|
||||
mailreader = require('mailreader'),
|
||||
ImapClient = require('imap-client'),
|
||||
Crypto = require('js/crypto/crypto'),
|
||||
RestDAO = require('js/dao/rest-dao'),
|
||||
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'),
|
||||
config = appConfig.config,
|
||||
str = appConfig.string;
|
||||
'use strict';
|
||||
|
||||
var self = {};
|
||||
var axe = require('axe-logger'),
|
||||
Auth = require('./bo/auth'),
|
||||
PGP = require('./crypto/pgp'),
|
||||
PgpMailer = require('pgpmailer'),
|
||||
OAuth = require('./util/oauth'),
|
||||
PgpBuilder = require('pgpbuilder'),
|
||||
OutboxBO = require('./bo/outbox'),
|
||||
mailreader = require('mailreader'),
|
||||
ImapClient = require('imap-client'),
|
||||
Crypto = require('./crypto/crypto'),
|
||||
RestDAO = require('./dao/rest-dao'),
|
||||
appConfig = require('./app-config'),
|
||||
EmailDAO = require('./dao/email-dao'),
|
||||
AdminDao = require('./dao/admin-dao'),
|
||||
KeychainDAO = require('./dao/keychain-dao'),
|
||||
PublicKeyDAO = require('./dao/publickey-dao'),
|
||||
LawnchairDAO = require('./dao/lawnchair-dao'),
|
||||
PrivateKeyDAO = require('./dao/privatekey-dao'),
|
||||
InvitationDAO = require('./dao/invitation-dao'),
|
||||
DeviceStorageDAO = require('./dao/devicestorage-dao'),
|
||||
ConnectionDoctor = require('./util/connection-doctor'),
|
||||
UpdateHandler = require('./util/update/update-handler'),
|
||||
config = appConfig.config,
|
||||
str = appConfig.string;
|
||||
|
||||
/**
|
||||
* Start the application.
|
||||
*/
|
||||
self.start = function(options, callback) {
|
||||
if (self.started) {
|
||||
return callback();
|
||||
}
|
||||
var ctrl = {};
|
||||
|
||||
self.started = true;
|
||||
self.onError = options.onError;
|
||||
/**
|
||||
* Start the application.
|
||||
*/
|
||||
ctrl.start = function(options, callback) {
|
||||
if (ctrl.started) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
// are we running in a cordova app or in a browser environment?
|
||||
if (window.cordova) {
|
||||
// wait for 'deviceready' event to make sure plugins are loaded
|
||||
axe.debug('Assuming Cordova environment...');
|
||||
document.addEventListener("deviceready", onDeviceReady, false);
|
||||
} else {
|
||||
// No need to wait on events... just start the app
|
||||
axe.debug('Assuming Browser environment...');
|
||||
onDeviceReady();
|
||||
}
|
||||
ctrl.started = true;
|
||||
ctrl.onError = options.onError;
|
||||
|
||||
function onDeviceReady() {
|
||||
axe.debug('Starting app.');
|
||||
// are we running in a cordova app or in a browser environment?
|
||||
if (window.cordova) {
|
||||
// wait for 'deviceready' event to make sure plugins are loaded
|
||||
axe.debug('Assuming Cordova environment...');
|
||||
document.addEventListener("deviceready", onDeviceReady, false);
|
||||
} else {
|
||||
// No need to wait on events... just start the app
|
||||
axe.debug('Assuming Browser environment...');
|
||||
onDeviceReady();
|
||||
}
|
||||
|
||||
self.buildModules();
|
||||
function onDeviceReady() {
|
||||
axe.debug('Starting app.');
|
||||
|
||||
// Handle offline and online gracefully
|
||||
window.addEventListener('online', self.onConnect.bind(self, self.onError));
|
||||
window.addEventListener('offline', self.onDisconnect.bind(self));
|
||||
ctrl.buildModules();
|
||||
|
||||
self._appConfigStore.init('app-config', callback);
|
||||
}
|
||||
};
|
||||
// Handle offline and online gracefully
|
||||
window.addEventListener('online', ctrl.onConnect.bind(ctrl, ctrl.onError));
|
||||
window.addEventListener('offline', ctrl.onDisconnect.bind(ctrl));
|
||||
|
||||
/**
|
||||
* Initialize the dependency tree.
|
||||
*/
|
||||
self.buildModules = function() {
|
||||
var lawnchairDao, restDao, pubkeyDao, privkeyDao, crypto, emailDao, keychain, pgp, userStorage, pgpbuilder, oauth, appConfigStore, auth;
|
||||
ctrl._appConfigStore.init('app-config', callback);
|
||||
}
|
||||
};
|
||||
|
||||
// start the mailreader's worker thread
|
||||
mailreader.startWorker(config.workerPath + '/../lib/mailreader-parser-worker.js');
|
||||
/**
|
||||
* Initialize the dependency tree.
|
||||
*/
|
||||
ctrl.buildModules = function() {
|
||||
var lawnchairDao, restDao, pubkeyDao, privkeyDao, crypto, emailDao, keychain, pgp, userStorage, pgpbuilder, oauth, appConfigStore, auth;
|
||||
|
||||
// init objects and inject dependencies
|
||||
restDao = new RestDAO();
|
||||
lawnchairDao = new LawnchairDAO();
|
||||
pubkeyDao = new PublicKeyDAO(restDao);
|
||||
privkeyDao = new PrivateKeyDAO(new RestDAO(config.privkeyServerUrl));
|
||||
oauth = new OAuth(new RestDAO('https://www.googleapis.com'));
|
||||
// start the mailreader's worker thread
|
||||
mailreader.startWorker(config.workerPath + '/mailreader-parser-worker.min.js');
|
||||
|
||||
crypto = new Crypto();
|
||||
self._pgp = pgp = new PGP();
|
||||
self._keychain = keychain = new KeychainDAO(lawnchairDao, pubkeyDao, privkeyDao, crypto, pgp);
|
||||
keychain.requestPermissionForKeyUpdate = function(params, callback) {
|
||||
var message = params.newKey ? str.updatePublicKeyMsgNewKey : str.updatePublicKeyMsgRemovedKey;
|
||||
message = message.replace('{0}', params.userId);
|
||||
// init objects and inject dependencies
|
||||
restDao = new RestDAO();
|
||||
lawnchairDao = new LawnchairDAO();
|
||||
pubkeyDao = new PublicKeyDAO(restDao);
|
||||
privkeyDao = new PrivateKeyDAO(new RestDAO(config.privkeyServerUrl));
|
||||
oauth = new OAuth(new RestDAO('https://www.googleapis.com'));
|
||||
|
||||
self.onError({
|
||||
title: str.updatePublicKeyTitle,
|
||||
message: message,
|
||||
positiveBtnStr: str.updatePublicKeyPosBtn,
|
||||
negativeBtnStr: str.updatePublicKeyNegBtn,
|
||||
showNegativeBtn: true,
|
||||
callback: callback
|
||||
});
|
||||
};
|
||||
crypto = new Crypto();
|
||||
ctrl._pgp = pgp = new PGP();
|
||||
ctrl._keychain = keychain = new KeychainDAO(lawnchairDao, pubkeyDao, privkeyDao, crypto, pgp);
|
||||
keychain.requestPermissionForKeyUpdate = function(params, callback) {
|
||||
var message = params.newKey ? str.updatePublicKeyMsgNewKey : str.updatePublicKeyMsgRemovedKey;
|
||||
message = message.replace('{0}', params.userId);
|
||||
|
||||
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();
|
||||
|
||||
emailDao.onError = self.onError;
|
||||
};
|
||||
|
||||
/**
|
||||
* Calls runtime hooks to check if an app update is available.
|
||||
*/
|
||||
self.checkForUpdate = function() {
|
||||
self._updateHandler.checkForUpdate(self.onError);
|
||||
};
|
||||
|
||||
/**
|
||||
* Instanciate the mail email data access object and its dependencies. Login to imap on init.
|
||||
*/
|
||||
self.init = function(options, callback) {
|
||||
// init user's local database
|
||||
self._userStorage.init(options.emailAddress, function(err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// Migrate the databases if necessary
|
||||
self._updateHandler.update(onUpdate);
|
||||
});
|
||||
|
||||
function onUpdate(err) {
|
||||
if (err) {
|
||||
callback({
|
||||
errMsg: 'Update failed, please reinstall the app.',
|
||||
err: err
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// account information for the email dao
|
||||
var account = {
|
||||
realname: options.realname,
|
||||
emailAddress: options.emailAddress,
|
||||
asymKeySize: config.asymKeySize
|
||||
};
|
||||
|
||||
// init email dao
|
||||
self._emailDao.init({
|
||||
account: account
|
||||
}, function(err, keypair) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, keypair);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the user agent is online.
|
||||
*/
|
||||
self.isOnline = function() {
|
||||
return navigator.onLine;
|
||||
};
|
||||
|
||||
/**
|
||||
* Event handler that is called when the user agent goes offline.
|
||||
*/
|
||||
self.onDisconnect = function() {
|
||||
self._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;
|
||||
|
||||
// clear app config store
|
||||
self._auth.logout(function(err) {
|
||||
if (err) {
|
||||
self.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// delete instance of imap-client and pgp-mailer
|
||||
self._emailDao.onDisconnect(function(err) {
|
||||
if (err) {
|
||||
self.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// navigate to login
|
||||
window.location.href = '/';
|
||||
});
|
||||
ctrl.onError({
|
||||
title: str.updatePublicKeyTitle,
|
||||
message: message,
|
||||
positiveBtnStr: str.updatePublicKeyPosBtn,
|
||||
negativeBtnStr: str.updatePublicKeyNegBtn,
|
||||
showNegativeBtn: true,
|
||||
callback: callback
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
// prevent connection infinite loop
|
||||
callback();
|
||||
ctrl._appConfigStore = appConfigStore = new DeviceStorageDAO(new LawnchairDAO());
|
||||
ctrl._auth = auth = new Auth(appConfigStore, oauth, pgp);
|
||||
ctrl._userStorage = userStorage = new DeviceStorageDAO(lawnchairDao);
|
||||
ctrl._invitationDao = new InvitationDAO(restDao);
|
||||
ctrl._pgpbuilder = pgpbuilder = new PgpBuilder();
|
||||
ctrl._emailDao = emailDao = new EmailDAO(keychain, pgp, userStorage, pgpbuilder, mailreader);
|
||||
ctrl._outboxBo = new OutboxBO(emailDao, keychain, userStorage);
|
||||
ctrl._updateHandler = new UpdateHandler(appConfigStore, userStorage, auth);
|
||||
ctrl._adminDao = new AdminDao(new RestDAO(config.adminUrl));
|
||||
ctrl._doctor = new ConnectionDoctor();
|
||||
|
||||
emailDao.onError = ctrl.onError;
|
||||
};
|
||||
|
||||
/**
|
||||
* Calls runtime hooks to check if an app update is available.
|
||||
*/
|
||||
ctrl.checkForUpdate = function() {
|
||||
ctrl._updateHandler.checkForUpdate(ctrl.onError);
|
||||
};
|
||||
|
||||
/**
|
||||
* Instanciate the mail email data access object and its dependencies. Login to imap on init.
|
||||
*/
|
||||
ctrl.init = function(options, callback) {
|
||||
// init user's local database
|
||||
ctrl._userStorage.init(options.emailAddress, function(err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
self._auth.getCredentials(function(err, credentials) {
|
||||
// Migrate the databases if necessary
|
||||
ctrl._updateHandler.update(onUpdate);
|
||||
});
|
||||
|
||||
function onUpdate(err) {
|
||||
if (err) {
|
||||
callback({
|
||||
errMsg: 'Update failed, please reinstall the app.',
|
||||
err: err
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// account information for the email dao
|
||||
var account = {
|
||||
realname: options.realname,
|
||||
emailAddress: options.emailAddress,
|
||||
asymKeySize: config.asymKeySize
|
||||
};
|
||||
|
||||
// init email dao
|
||||
ctrl._emailDao.init({
|
||||
account: account
|
||||
}, function(err, keypair) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
initClients(credentials);
|
||||
callback(null, keypair);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function initClients(credentials) {
|
||||
// add the maximum update batch size for imap folders to the imap configuration
|
||||
credentials.imap.maxUpdateSize = config.imapUpdateBatchSize;
|
||||
/**
|
||||
* Check if the user agent is online.
|
||||
*/
|
||||
ctrl.isOnline = function() {
|
||||
return navigator.onLine;
|
||||
};
|
||||
|
||||
var pgpMailer = new PgpMailer(credentials.smtp, self._pgpbuilder);
|
||||
var imapClient = new ImapClient(credentials.imap);
|
||||
imapClient.onError = onConnectionError;
|
||||
pgpMailer.onError = onConnectionError;
|
||||
/**
|
||||
* Event handler that is called when the user agent goes offline.
|
||||
*/
|
||||
ctrl.onDisconnect = function() {
|
||||
ctrl._emailDao.onDisconnect();
|
||||
};
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
// connect to clients
|
||||
self._emailDao.onConnect({
|
||||
imapClient: imapClient,
|
||||
pgpMailer: pgpMailer
|
||||
}, callback);
|
||||
/**
|
||||
* Log the current user out by clear the app config store and deleting instances of imap-client and pgp-mailer.
|
||||
*/
|
||||
ctrl.logout = function() {
|
||||
// clear app config store
|
||||
ctrl._auth.logout(function(err) {
|
||||
if (err) {
|
||||
ctrl.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
function onConnectionError(error) {
|
||||
axe.debug('Connection error. Attempting reconnect in ' + config.reconnectInterval + ' ms. Error: ' + (error.errMsg || error.message) + (error.stack ? ('\n' + error.stack) : ''));
|
||||
// delete instance of imap-client and pgp-mailer
|
||||
ctrl._emailDao.onDisconnect(function(err) {
|
||||
if (err) {
|
||||
ctrl.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(function() {
|
||||
axe.debug('Reconnecting...');
|
||||
// re-init client modules on error
|
||||
self.onConnect(function(err) {
|
||||
if (err) {
|
||||
axe.error('Reconnect attempt failed! ' + (err.errMsg || err.message) + (err.stack ? ('\n' + err.stack) : ''));
|
||||
return;
|
||||
}
|
||||
// navigate to login
|
||||
window.location.href = '/';
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
axe.debug('Reconnect attempt complete.');
|
||||
});
|
||||
}, config.reconnectInterval);
|
||||
/**
|
||||
* Event that is called when the user agent goes online. This create new instances of the imap-client and pgp-mailer and connects to the mail server.
|
||||
*/
|
||||
ctrl.onConnect = function(callback) {
|
||||
if (!ctrl.isOnline() || !ctrl._emailDao || !ctrl._emailDao._account) {
|
||||
// prevent connection infinite loop
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
ctrl._auth.getCredentials(function(err, credentials) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
return self;
|
||||
});
|
||||
initClients(credentials);
|
||||
});
|
||||
|
||||
function initClients(credentials) {
|
||||
// add the maximum update batch size for imap folders to the imap configuration
|
||||
credentials.imap.maxUpdateSize = config.imapUpdateBatchSize;
|
||||
|
||||
// tls socket worker path for multithreaded tls in non-native tls environments
|
||||
credentials.imap.tlsWorkerPath = credentials.smtp.tlsWorkerPath = config.workerPath + '/tcp-socket-tls-worker.min.js';
|
||||
|
||||
var pgpMailer = new PgpMailer(credentials.smtp, ctrl._pgpbuilder);
|
||||
var imapClient = new ImapClient(credentials.imap);
|
||||
imapClient.onError = onConnectionError;
|
||||
pgpMailer.onError = onConnectionError;
|
||||
|
||||
// certificate update handling
|
||||
imapClient.onCert = ctrl._auth.handleCertificateUpdate.bind(ctrl._auth, 'imap', ctrl.onConnect, ctrl.onError);
|
||||
pgpMailer.onCert = ctrl._auth.handleCertificateUpdate.bind(ctrl._auth, 'smtp', ctrl.onConnect, ctrl.onError);
|
||||
|
||||
// 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
|
||||
ctrl._emailDao.ignoreUploadOnSent = !!(config[ctrl._auth.provider] && config[ctrl._auth.provider].ignoreUploadOnSent);
|
||||
|
||||
// connect to clients
|
||||
ctrl._emailDao.onConnect({
|
||||
imapClient: imapClient,
|
||||
pgpMailer: pgpMailer
|
||||
}, callback);
|
||||
}
|
||||
|
||||
function onConnectionError(error) {
|
||||
axe.debug('Connection error. Attempting reconnect in ' + config.reconnectInterval + ' ms. Error: ' + (error.errMsg || error.message) + (error.stack ? ('\n' + error.stack) : ''));
|
||||
|
||||
setTimeout(function() {
|
||||
axe.debug('Reconnecting...');
|
||||
// re-init client modules on error
|
||||
ctrl.onConnect(function(err) {
|
||||
if (err) {
|
||||
axe.error('Reconnect attempt failed! ' + (err.errMsg || err.message) + (err.stack ? ('\n' + err.stack) : ''));
|
||||
return;
|
||||
}
|
||||
|
||||
axe.debug('Reconnect attempt complete.');
|
||||
});
|
||||
}, config.reconnectInterval);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = ctrl;
|
||||
|
|
234
src/js/app.js
234
src/js/app.js
|
@ -14,148 +14,104 @@ 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',
|
||||
// init main angular module including dependencies
|
||||
var app = angular.module('mail', [
|
||||
'ngRoute',
|
||||
'ngAnimate',
|
||||
'navigation',
|
||||
'mail-list',
|
||||
'write',
|
||||
'read',
|
||||
'contacts',
|
||||
'login-new-device',
|
||||
'privatekey-upload',
|
||||
'popover',
|
||||
'infinite-scroll',
|
||||
'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', [
|
||||
'ngRoute',
|
||||
'ngAnimate',
|
||||
'navigation',
|
||||
'mail-list',
|
||||
'write',
|
||||
'read',
|
||||
'contacts',
|
||||
'login-new-device',
|
||||
'privatekey-upload',
|
||||
'popover',
|
||||
'infinite-scroll',
|
||||
'ngTagsInput'
|
||||
]);
|
||||
|
||||
// set router paths
|
||||
app.config(function($routeProvider) {
|
||||
$routeProvider.when('/add-account', {
|
||||
templateUrl: 'tpl/add-account.html',
|
||||
controller: AddAccountCtrl
|
||||
});
|
||||
$routeProvider.when('/login', {
|
||||
templateUrl: 'tpl/login.html',
|
||||
controller: LoginCtrl
|
||||
});
|
||||
$routeProvider.when('/login-set-credentials', {
|
||||
templateUrl: 'tpl/login-set-credentials.html',
|
||||
controller: LoginSetCredentialsCtrl
|
||||
});
|
||||
$routeProvider.when('/login-existing', {
|
||||
templateUrl: 'tpl/login-existing.html',
|
||||
controller: LoginExistingCtrl
|
||||
});
|
||||
$routeProvider.when('/login-initial', {
|
||||
templateUrl: 'tpl/login-initial.html',
|
||||
controller: LoginInitialCtrl
|
||||
});
|
||||
$routeProvider.when('/login-new-device', {
|
||||
templateUrl: 'tpl/login-new-device.html',
|
||||
controller: LoginNewDeviceCtrl
|
||||
});
|
||||
$routeProvider.when('/login-privatekey-download', {
|
||||
templateUrl: 'tpl/login-privatekey-download.html',
|
||||
controller: LoginPrivateKeyDownloadCtrl
|
||||
});
|
||||
$routeProvider.when('/desktop', {
|
||||
templateUrl: 'tpl/desktop.html',
|
||||
controller: NavigationCtrl
|
||||
});
|
||||
$routeProvider.otherwise({
|
||||
redirectTo: '/login'
|
||||
});
|
||||
// set router paths
|
||||
app.config(function($routeProvider) {
|
||||
$routeProvider.when('/add-account', {
|
||||
templateUrl: 'tpl/add-account.html',
|
||||
controller: AddAccountCtrl
|
||||
});
|
||||
|
||||
app.run(function($rootScope) {
|
||||
// global state... inherited to all child scopes
|
||||
$rootScope.state = {};
|
||||
|
||||
// attach global error handler
|
||||
errorUtil.attachHandler($rootScope);
|
||||
|
||||
// attach the back button handler to the root scope
|
||||
backButtonUtil.attachHandler($rootScope);
|
||||
|
||||
// attach fastclick
|
||||
FastClick.attach(document.body);
|
||||
$routeProvider.when('/login', {
|
||||
templateUrl: 'tpl/login.html',
|
||||
controller: LoginCtrl
|
||||
});
|
||||
|
||||
// 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']);
|
||||
$routeProvider.when('/login-set-credentials', {
|
||||
templateUrl: 'tpl/login-set-credentials.html',
|
||||
controller: LoginSetCredentialsCtrl
|
||||
});
|
||||
});
|
||||
$routeProvider.when('/login-existing', {
|
||||
templateUrl: 'tpl/login-existing.html',
|
||||
controller: LoginExistingCtrl
|
||||
});
|
||||
$routeProvider.when('/login-initial', {
|
||||
templateUrl: 'tpl/login-initial.html',
|
||||
controller: LoginInitialCtrl
|
||||
});
|
||||
$routeProvider.when('/login-new-device', {
|
||||
templateUrl: 'tpl/login-new-device.html',
|
||||
controller: LoginNewDeviceCtrl
|
||||
});
|
||||
$routeProvider.when('/login-privatekey-download', {
|
||||
templateUrl: 'tpl/login-privatekey-download.html',
|
||||
controller: LoginPrivateKeyDownloadCtrl
|
||||
});
|
||||
$routeProvider.when('/desktop', {
|
||||
templateUrl: 'tpl/desktop.html',
|
||||
controller: NavigationCtrl
|
||||
});
|
||||
$routeProvider.otherwise({
|
||||
redirectTo: '/login'
|
||||
});
|
||||
});
|
||||
|
||||
app.run(function($rootScope) {
|
||||
// global state... inherited to all child scopes
|
||||
$rootScope.state = {};
|
||||
|
||||
// attach global error handler
|
||||
errorUtil.attachHandler($rootScope);
|
||||
|
||||
// attach the back button handler to the root scope
|
||||
backButtonUtil.attachHandler($rootScope);
|
||||
|
||||
// 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);
|
|
@ -1,355 +1,210 @@
|
|||
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:
|
||||
* var auth = new Auth(...);
|
||||
* auth.setCredentials(...); // during the account setup
|
||||
* auth.getEmailAddress(...); // called from the login controller to determine if there is already a user present on the device
|
||||
* 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) {
|
||||
this._appConfigStore = appConfigStore;
|
||||
this._oauth = oauth;
|
||||
this._pgp = pgp;
|
||||
};
|
||||
/**
|
||||
* The Auth BO handles the rough edges and gaps between user/password authentication
|
||||
* and OAuth via Chrome Identity API.
|
||||
* Typical usage:
|
||||
* var auth = new Auth(...);
|
||||
* auth.setCredentials(...); // during the account setup
|
||||
* auth.getEmailAddress(...); // called from the login controller to determine if there is already a user present on the device
|
||||
* 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) {
|
||||
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.
|
||||
* 2 b) ... in a user/passwd setting, does not need to do additional work.
|
||||
* 3) Loads the intermediate certs from the configuration.
|
||||
*
|
||||
* @param {Function} callback(err, credentials)
|
||||
*/
|
||||
Auth.prototype.getCredentials = function(callback) {
|
||||
var self = this;
|
||||
|
||||
if (!self.provider || !self.emailAddress) {
|
||||
// we're not yet initialized, so let's load our stuff from disk
|
||||
self._loadCredentials(function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
chooseLogin();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
chooseLogin();
|
||||
|
||||
function chooseLogin() {
|
||||
if (self.provider === 'gmail' && !self.password) {
|
||||
// oauth login for gmail
|
||||
self.getOAuthToken(function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
done();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.passwordNeedsDecryption) {
|
||||
// decrypt password
|
||||
self._pgp.decrypt(self.password, undefined, function(err, cleartext) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
self.passwordNeedsDecryption = false;
|
||||
self.password = cleartext;
|
||||
|
||||
done();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
done();
|
||||
}
|
||||
|
||||
function done() {
|
||||
var credentials = {
|
||||
imap: {
|
||||
secure: self.imap.secure,
|
||||
port: self.imap.port,
|
||||
host: self.imap.host,
|
||||
ca: self.imap.ca,
|
||||
pinned: self.imap.pinned,
|
||||
auth: {
|
||||
user: self.username,
|
||||
xoauth2: self.oauthToken, // password or oauthToken is undefined
|
||||
pass: self.password
|
||||
}
|
||||
},
|
||||
smtp: {
|
||||
secure: self.smtp.secure,
|
||||
port: self.smtp.port,
|
||||
host: self.smtp.host,
|
||||
ca: self.smtp.ca,
|
||||
pinned: self.smtp.pinned,
|
||||
auth: {
|
||||
user: self.username,
|
||||
xoauth2: self.oauthToken,
|
||||
pass: self.password // password or oauthToken is undefined
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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.
|
||||
* @param {String} options.emailAddress The email address
|
||||
* @param {String} options.username The user name
|
||||
* @param {String} options.realname The user's real name
|
||||
* @param {String} options.password The password, only in user/passwd setting
|
||||
* @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) {
|
||||
this.credentialsDirty = true;
|
||||
this.provider = options.provider;
|
||||
this.emailAddress = options.emailAddress;
|
||||
this.username = options.username;
|
||||
this.realname = options.realname ? options.realname : '';
|
||||
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) {
|
||||
var self = this;
|
||||
|
||||
if (!self.credentialsDirty) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
// persist the provider
|
||||
self._appConfigStore.storeList([self.smtp], SMTP_DB_KEY, function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
self._appConfigStore.storeList([self.imap], IMAP_DB_KEY, function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
self._appConfigStore.storeList([self.provider], PROVIDER_DB_KEY, function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
self._appConfigStore.storeList([self.emailAddress], EMAIL_ADDR_DB_KEY, function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
self._appConfigStore.storeList([self.username], USERNAME_DB_KEY, function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
self._appConfigStore.storeList([self.realname], REALNAME_DB_KEY, function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if (!self.password) {
|
||||
self.credentialsDirty = false;
|
||||
return callback();
|
||||
}
|
||||
|
||||
if (self.passwordNeedsDecryption) {
|
||||
// password is not decrypted yet, so no need to re-encrypt it before storing...
|
||||
self._appConfigStore.storeList([self.password], PASSWD_DB_KEY, function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
self.credentialsDirty = false;
|
||||
callback();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
self._pgp.encrypt(self.password, undefined, function(err, ciphertext) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
self._appConfigStore.storeList([ciphertext], PASSWD_DB_KEY, function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
self.credentialsDirty = false;
|
||||
callback();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the email address. Loads it from disk, if necessary
|
||||
*/
|
||||
Auth.prototype.getEmailAddress = function(callback) {
|
||||
var self = this;
|
||||
|
||||
if (self.emailAddress) {
|
||||
return callback(null, {
|
||||
emailAddress: self.emailAddress,
|
||||
realname: self.realname
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 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.
|
||||
* 2 b) ... in a user/passwd setting, does not need to do additional work.
|
||||
* 3) Loads the intermediate certs from the configuration.
|
||||
*
|
||||
* @param {Function} callback(err, credentials)
|
||||
*/
|
||||
Auth.prototype.getCredentials = function(callback) {
|
||||
var self = this;
|
||||
|
||||
if (!self.provider || !self.emailAddress) {
|
||||
// we're not yet initialized, so let's load our stuff from disk
|
||||
self._loadCredentials(function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
callback(null, {
|
||||
emailAddress: self.emailAddress,
|
||||
realname: self.realname
|
||||
});
|
||||
chooseLogin();
|
||||
});
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* has one user logged in.
|
||||
* 1) try to read the email address from the configuration (see above)
|
||||
* 2) fetch the oauth token. if we already HAVE an email address at this point, we can spare
|
||||
* popping up the account picker on android! if not, the account picker will pop up. this
|
||||
* 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) {
|
||||
var self = this;
|
||||
chooseLogin();
|
||||
|
||||
if (self.oauthToken) {
|
||||
// removed cached token and get a new one
|
||||
self._oauth.refreshToken({
|
||||
emailAddress: self.emailAddress,
|
||||
oldToken: self.oauthToken
|
||||
}, onToken);
|
||||
} else {
|
||||
// get a fresh oauth token
|
||||
self._oauth.getOAuthToken(self.emailAddress, onToken);
|
||||
}
|
||||
|
||||
function onToken(err, oauthToken) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
// shortcut if the email address is already known
|
||||
if (self.emailAddress) {
|
||||
self.oauthToken = oauthToken;
|
||||
return callback();
|
||||
}
|
||||
|
||||
// query the email address
|
||||
self._oauth.queryEmailAddress(oauthToken, function(err, emailAddress) {
|
||||
function chooseLogin() {
|
||||
if (self.provider === 'gmail' && !self.password) {
|
||||
// oauth login for gmail
|
||||
self.getOAuthToken(function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
self.oauthToken = oauthToken;
|
||||
self.emailAddress = emailAddress;
|
||||
callback();
|
||||
done();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Loads email address, password, provider, ... from disk and sets them on `this`
|
||||
*/
|
||||
Auth.prototype._loadCredentials = function(callback) {
|
||||
var self = this;
|
||||
|
||||
if (self.initialized) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
loadFromDB(SMTP_DB_KEY, function(err, smtp) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
|
||||
loadFromDB(IMAP_DB_KEY, function(err, imap) {
|
||||
if (self.passwordNeedsDecryption) {
|
||||
// decrypt password
|
||||
self._pgp.decrypt(self.password, undefined, function(err, cleartext) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
self.passwordNeedsDecryption = false;
|
||||
self.password = cleartext;
|
||||
|
||||
loadFromDB(USERNAME_DB_KEY, function(err, username) {
|
||||
done();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
done();
|
||||
}
|
||||
|
||||
function done() {
|
||||
var credentials = {
|
||||
imap: {
|
||||
secure: self.imap.secure,
|
||||
port: self.imap.port,
|
||||
host: self.imap.host,
|
||||
ca: self.imap.ca,
|
||||
pinned: self.imap.pinned,
|
||||
auth: {
|
||||
user: self.username,
|
||||
xoauth2: self.oauthToken, // password or oauthToken is undefined
|
||||
pass: self.password
|
||||
}
|
||||
},
|
||||
smtp: {
|
||||
secure: self.smtp.secure,
|
||||
port: self.smtp.port,
|
||||
host: self.smtp.host,
|
||||
ca: self.smtp.ca,
|
||||
pinned: self.smtp.pinned,
|
||||
auth: {
|
||||
user: self.username,
|
||||
xoauth2: self.oauthToken,
|
||||
pass: self.password // password or oauthToken is undefined
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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.
|
||||
* @param {String} options.emailAddress The email address
|
||||
* @param {String} options.username The user name
|
||||
* @param {String} options.realname The user's real name
|
||||
* @param {String} options.password The password, only in user/passwd setting
|
||||
* @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) {
|
||||
this.credentialsDirty = true;
|
||||
this.provider = options.provider;
|
||||
this.emailAddress = options.emailAddress;
|
||||
this.username = options.username;
|
||||
this.realname = options.realname ? options.realname : '';
|
||||
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) {
|
||||
var self = this;
|
||||
|
||||
if (!self.credentialsDirty) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
// persist the provider
|
||||
self._appConfigStore.storeList([self.smtp], SMTP_DB_KEY, function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
self._appConfigStore.storeList([self.imap], IMAP_DB_KEY, function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
self._appConfigStore.storeList([self.provider], PROVIDER_DB_KEY, function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
self._appConfigStore.storeList([self.emailAddress], EMAIL_ADDR_DB_KEY, function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
|
||||
loadFromDB(REALNAME_DB_KEY, function(err, realname) {
|
||||
self._appConfigStore.storeList([self.username], USERNAME_DB_KEY, function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
|
||||
loadFromDB(EMAIL_ADDR_DB_KEY, function(err, emailAddress) {
|
||||
self._appConfigStore.storeList([self.realname], REALNAME_DB_KEY, function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
loadFromDB(PASSWD_DB_KEY, function(err, password) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
if (!self.password) {
|
||||
self.credentialsDirty = false;
|
||||
return callback();
|
||||
}
|
||||
|
||||
loadFromDB(PROVIDER_DB_KEY, function(err, provider) {
|
||||
if (self.passwordNeedsDecryption) {
|
||||
// password is not decrypted yet, so no need to re-encrypt it before storing...
|
||||
self._appConfigStore.storeList([self.password], PASSWD_DB_KEY, function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
self.emailAddress = emailAddress;
|
||||
self.password = password;
|
||||
self.passwordNeedsDecryption = !!password;
|
||||
self.provider = provider;
|
||||
self.username = username;
|
||||
self.realname = realname;
|
||||
self.smtp = smtp;
|
||||
self.imap = imap;
|
||||
self.initialized = true;
|
||||
self.credentialsDirty = false;
|
||||
callback();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
self._pgp.encrypt(self.password, undefined, function(err, ciphertext) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
self._appConfigStore.storeList([ciphertext], PASSWD_DB_KEY, function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
self.credentialsDirty = false;
|
||||
callback();
|
||||
});
|
||||
});
|
||||
|
@ -358,96 +213,239 @@ define(function(require) {
|
|||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
function loadFromDB(key, callback) {
|
||||
self._appConfigStore.listItems(key, 0, null, function(err, cachedItems) {
|
||||
callback(err, (!err && cachedItems && cachedItems[0]));
|
||||
});
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Returns the email address. Loads it from disk, if necessary
|
||||
*/
|
||||
Auth.prototype.getEmailAddress = function(callback) {
|
||||
var self = this;
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
var self = this;
|
||||
if (self.emailAddress) {
|
||||
return callback(null, {
|
||||
emailAddress: self.emailAddress,
|
||||
realname: self.realname
|
||||
});
|
||||
}
|
||||
|
||||
axe.debug('new ssl certificate received: ' + pemEncodedCert);
|
||||
|
||||
if (!self[component].ca) {
|
||||
// no previous ssl cert, trust on first use
|
||||
self[component].ca = pemEncodedCert;
|
||||
self.credentialsDirty = true;
|
||||
self.storeCredentials(callback);
|
||||
return;
|
||||
self._loadCredentials(function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if (self[component].ca === pemEncodedCert) {
|
||||
// ignore multiple successive tls handshakes, e.g. for gmail
|
||||
return;
|
||||
callback(null, {
|
||||
emailAddress: self.emailAddress,
|
||||
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
|
||||
* has one user logged in.
|
||||
* 1) try to read the email address from the configuration (see above)
|
||||
* 2) fetch the oauth token. if we already HAVE an email address at this point, we can spare
|
||||
* popping up the account picker on android! if not, the account picker will pop up. this
|
||||
* 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) {
|
||||
var self = this;
|
||||
|
||||
if (self.oauthToken) {
|
||||
// removed cached token and get a new one
|
||||
self._oauth.refreshToken({
|
||||
emailAddress: self.emailAddress,
|
||||
oldToken: self.oauthToken
|
||||
}, onToken);
|
||||
} else {
|
||||
// get a fresh oauth token
|
||||
self._oauth.getOAuthToken(self.emailAddress, onToken);
|
||||
}
|
||||
|
||||
function onToken(err, oauthToken) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if (self[component].ca && self[component].pinned) {
|
||||
// do not update the pinned certificates!
|
||||
callback({
|
||||
title: str.outdatedCertificateTitle,
|
||||
message: str.outdatedCertificateMessage.replace('{0}', self[component].host),
|
||||
faqLink: str.certificateFaqLink,
|
||||
});
|
||||
return;
|
||||
// shortcut if the email address is already known
|
||||
if (self.emailAddress) {
|
||||
self.oauthToken = oauthToken;
|
||||
return callback();
|
||||
}
|
||||
|
||||
// previous ssl cert known, does not match: query user and certificate
|
||||
callback({
|
||||
title: str.updateCertificateTitle,
|
||||
message: str.updateCertificateMessage.replace('{0}', self[component].host),
|
||||
positiveBtnStr: str.updateCertificatePosBtn,
|
||||
negativeBtnStr: str.updateCertificateNegBtn,
|
||||
showNegativeBtn: true,
|
||||
faqLink: str.certificateFaqLink,
|
||||
callback: function(granted) {
|
||||
if (!granted) {
|
||||
return;
|
||||
// query the email address
|
||||
self._oauth.queryEmailAddress(oauthToken, function(err, emailAddress) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
self.oauthToken = oauthToken;
|
||||
self.emailAddress = emailAddress;
|
||||
callback();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Loads email address, password, provider, ... from disk and sets them on `this`
|
||||
*/
|
||||
Auth.prototype._loadCredentials = function(callback) {
|
||||
var self = this;
|
||||
|
||||
if (self.initialized) {
|
||||
callback();
|
||||
}
|
||||
|
||||
loadFromDB(SMTP_DB_KEY, function(err, smtp) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
|
||||
loadFromDB(IMAP_DB_KEY, function(err, imap) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
|
||||
loadFromDB(USERNAME_DB_KEY, function(err, username) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
self[component].ca = pemEncodedCert;
|
||||
self.storeCredentials(function(err) {
|
||||
|
||||
loadFromDB(REALNAME_DB_KEY, function(err, realname) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
onConnect(callback);
|
||||
|
||||
loadFromDB(EMAIL_ADDR_DB_KEY, function(err, emailAddress) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
loadFromDB(PASSWD_DB_KEY, function(err, password) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
loadFromDB(PROVIDER_DB_KEY, function(err, provider) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
self.emailAddress = emailAddress;
|
||||
self.password = password;
|
||||
self.passwordNeedsDecryption = !!password;
|
||||
self.provider = provider;
|
||||
self.username = username;
|
||||
self.realname = realname;
|
||||
self.smtp = smtp;
|
||||
self.imap = imap;
|
||||
self.initialized = true;
|
||||
|
||||
callback();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Logout of the app by clearing the app config store and in memory credentials
|
||||
*/
|
||||
Auth.prototype.logout = function(callback) {
|
||||
var self = this;
|
||||
function loadFromDB(key, callback) {
|
||||
self._appConfigStore.listItems(key, 0, null, function(err, cachedItems) {
|
||||
callback(err, (!err && cachedItems && cachedItems[0]));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// clear app config db
|
||||
self._appConfigStore.clear(function(err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
/**
|
||||
* 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) {
|
||||
var self = this;
|
||||
|
||||
axe.debug('new ssl certificate received: ' + pemEncodedCert);
|
||||
|
||||
if (!self[component].ca) {
|
||||
// no previous ssl cert, trust on first use
|
||||
self[component].ca = pemEncodedCert;
|
||||
self.credentialsDirty = true;
|
||||
self.storeCredentials(callback);
|
||||
return;
|
||||
}
|
||||
|
||||
if (self[component].ca === pemEncodedCert) {
|
||||
// ignore multiple successive tls handshakes, e.g. for gmail
|
||||
return;
|
||||
}
|
||||
|
||||
if (self[component].ca && self[component].pinned) {
|
||||
// do not update the pinned certificates!
|
||||
callback({
|
||||
title: str.outdatedCertificateTitle,
|
||||
message: str.outdatedCertificateMessage.replace('{0}', self[component].host),
|
||||
faqLink: str.certificateFaqLink,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// previous ssl cert known, does not match: query user and certificate
|
||||
callback({
|
||||
title: str.updateCertificateTitle,
|
||||
message: str.updateCertificateMessage.replace('{0}', self[component].host),
|
||||
positiveBtnStr: str.updateCertificatePosBtn,
|
||||
negativeBtnStr: str.updateCertificateNegBtn,
|
||||
showNegativeBtn: true,
|
||||
faqLink: str.certificateFaqLink,
|
||||
callback: function(granted) {
|
||||
if (!granted) {
|
||||
return;
|
||||
}
|
||||
|
||||
// clear in memory cache
|
||||
self.setCredentials({});
|
||||
self.initialized = undefined;
|
||||
self.credentialsDirty = undefined;
|
||||
self.passwordNeedsDecryption = undefined;
|
||||
self[component].ca = pemEncodedCert;
|
||||
self.storeCredentials(function(err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
callback();
|
||||
});
|
||||
};
|
||||
onConnect(callback);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return Auth;
|
||||
});
|
||||
/**
|
||||
* Logout of the app by clearing the app config store and in memory credentials
|
||||
*/
|
||||
Auth.prototype.logout = function(callback) {
|
||||
var self = this;
|
||||
|
||||
// clear app config db
|
||||
self._appConfigStore.clear(function(err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// clear in memory cache
|
||||
self.setCredentials({});
|
||||
self.initialized = undefined;
|
||||
self.credentialsDirty = undefined;
|
||||
self.passwordNeedsDecryption = undefined;
|
||||
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = Auth;
|
|
@ -1,232 +1,229 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
'use strict';
|
||||
|
||||
var _ = require('underscore'),
|
||||
util = require('js/crypto/util'),
|
||||
config = require('js/app-config').config,
|
||||
outboxDb = 'email_OUTBOX';
|
||||
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) {
|
||||
/** @private */
|
||||
this._emailDao = emailDao;
|
||||
|
||||
/** @private */
|
||||
this._keychain = keychain;
|
||||
|
||||
/** @private */
|
||||
this._devicestorage = devicestorage;
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
/** @private */
|
||||
this._emailDao = emailDao;
|
||||
* Semaphore-esque flag to avoid 'concurrent' calls to _processOutbox when the timeout fires, but a call is still in process.
|
||||
* @private */
|
||||
this._outboxBusy = false;
|
||||
};
|
||||
|
||||
/** @private */
|
||||
this._keychain = keychain;
|
||||
/**
|
||||
* 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) {
|
||||
// remember global callback
|
||||
this._onUpdate = callback;
|
||||
// start periodic checking of outbox
|
||||
this._intervalId = setInterval(this._processOutbox.bind(this, this._onUpdate), config.checkOutboxInterval);
|
||||
};
|
||||
|
||||
/** @private */
|
||||
this._devicestorage = devicestorage;
|
||||
/**
|
||||
* Outbox stops the periodic checking of the local device storage for pending mails.
|
||||
*/
|
||||
OutboxBO.prototype.stopChecking = function() {
|
||||
if (!this._intervalId) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Semaphore-esque flag to avoid 'concurrent' calls to _processOutbox when the timeout fires, but a call is still in process.
|
||||
* @private */
|
||||
this._outboxBusy = false;
|
||||
};
|
||||
clearInterval(this._intervalId);
|
||||
delete this._intervalId;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
// remember global callback
|
||||
this._onUpdate = callback;
|
||||
// start periodic checking of outbox
|
||||
this._intervalId = setInterval(this._processOutbox.bind(this, this._onUpdate), config.checkOutboxInterval);
|
||||
};
|
||||
/**
|
||||
* 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) {
|
||||
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
|
||||
|
||||
/**
|
||||
* Outbox stops the periodic checking of the local device storage for pending mails.
|
||||
*/
|
||||
OutboxBO.prototype.stopChecking = function() {
|
||||
if (!this._intervalId) {
|
||||
return;
|
||||
}
|
||||
mail.publicKeysArmored = []; // gather the public keys
|
||||
mail.uid = mail.id = util.UUID(); // the mail needs a random id & uid for storage in the database
|
||||
|
||||
clearInterval(this._intervalId);
|
||||
delete this._intervalId;
|
||||
};
|
||||
// do not encrypt mails with a bcc recipient, due to a possible privacy leak
|
||||
if (mail.bcc.length > 0) {
|
||||
storeAndForward(mail);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
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
|
||||
checkRecipients(allReaders);
|
||||
|
||||
mail.publicKeysArmored = []; // gather the public keys
|
||||
mail.uid = mail.id = util.UUID(); // the mail needs a random id & uid for storage in the database
|
||||
// check if there are unregistered recipients
|
||||
function checkRecipients(recipients) {
|
||||
var after = _.after(recipients.length, function() {
|
||||
checkEncrypt();
|
||||
});
|
||||
|
||||
// do not encrypt mails with a bcc recipient, due to a possible privacy leak
|
||||
if (mail.bcc.length > 0) {
|
||||
// find out if there are unregistered users
|
||||
recipients.forEach(function(recipient) {
|
||||
self._keychain.getReceiverPublicKey(recipient.address, function(err, key) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// if a public key is available, add the recipient's key to the armored public keys,
|
||||
// otherwise remember the recipient as unregistered for later sending
|
||||
if (key) {
|
||||
mail.publicKeysArmored.push(key.publicKey);
|
||||
}
|
||||
|
||||
after();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function checkEncrypt() {
|
||||
// only encrypt if all recipients have public keys
|
||||
if (mail.publicKeysArmored.length < allReaders.length) {
|
||||
storeAndForward(mail);
|
||||
return;
|
||||
}
|
||||
|
||||
checkRecipients(allReaders);
|
||||
|
||||
// check if there are unregistered recipients
|
||||
function checkRecipients(recipients) {
|
||||
var after = _.after(recipients.length, function() {
|
||||
checkEncrypt();
|
||||
});
|
||||
|
||||
// find out if there are unregistered users
|
||||
recipients.forEach(function(recipient) {
|
||||
self._keychain.getReceiverPublicKey(recipient.address, function(err, key) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// if a public key is available, add the recipient's key to the armored public keys,
|
||||
// otherwise remember the recipient as unregistered for later sending
|
||||
if (key) {
|
||||
mail.publicKeysArmored.push(key.publicKey);
|
||||
}
|
||||
|
||||
after();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function checkEncrypt() {
|
||||
// only encrypt if all recipients have public keys
|
||||
if (mail.publicKeysArmored.length < allReaders.length) {
|
||||
storeAndForward(mail);
|
||||
// encrypts the body and attachments and persists the mail object
|
||||
self._emailDao.encrypt({
|
||||
mail: mail,
|
||||
publicKeysArmored: mail.publicKeysArmored
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// encrypts the body and attachments and persists the mail object
|
||||
self._emailDao.encrypt({
|
||||
mail: mail,
|
||||
publicKeysArmored: mail.publicKeysArmored
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
storeAndForward(mail);
|
||||
});
|
||||
}
|
||||
|
||||
storeAndForward(mail);
|
||||
});
|
||||
}
|
||||
function storeAndForward(mail) {
|
||||
// store in outbox
|
||||
self._devicestorage.storeList([mail], outboxDb, function(err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
function storeAndForward(mail) {
|
||||
// store in outbox
|
||||
self._devicestorage.storeList([mail], outboxDb, function(err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
// don't wait for next round
|
||||
self._processOutbox(self._onUpdate);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
callback();
|
||||
// don't wait for next round
|
||||
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) {
|
||||
var self = this,
|
||||
unsentMails = 0;
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
var self = this,
|
||||
unsentMails = 0;
|
||||
// also, if a _processOutbox call is still in progress, ignore it.
|
||||
if (self._outboxBusy) {
|
||||
return;
|
||||
}
|
||||
|
||||
// also, if a _processOutbox call is still in progress, ignore it.
|
||||
if (self._outboxBusy) {
|
||||
self._outboxBusy = true;
|
||||
|
||||
// get pending mails from the outbox
|
||||
self._devicestorage.listItems(outboxDb, 0, null, function(err, pendingMails) {
|
||||
// error, we're done here
|
||||
if (err) {
|
||||
self._outboxBusy = false;
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
self._outboxBusy = true;
|
||||
// if we're not online, don't even bother sending mails.
|
||||
if (!self._emailDao._account.online || _.isEmpty(pendingMails)) {
|
||||
self._outboxBusy = false;
|
||||
callback(null, pendingMails.length);
|
||||
return;
|
||||
}
|
||||
|
||||
// get pending mails from the outbox
|
||||
self._devicestorage.listItems(outboxDb, 0, null, function(err, pendingMails) {
|
||||
// error, we're done here
|
||||
// we're done after all the mails have been handled
|
||||
// update the outbox count...
|
||||
var after = _.after(pendingMails.length, function() {
|
||||
self._outboxBusy = false;
|
||||
callback(null, unsentMails);
|
||||
});
|
||||
|
||||
// send pending mails if possible
|
||||
pendingMails.forEach(function(mail) {
|
||||
send(mail, after);
|
||||
});
|
||||
});
|
||||
|
||||
// send the message
|
||||
function send(mail, done) {
|
||||
|
||||
// check is email is to be sent encrypted or as plaintex
|
||||
if (mail.encrypted === true) {
|
||||
// email was already encrypted before persisting in outbox, tell pgpmailer to send encrypted and not encrypt again
|
||||
self._emailDao.sendEncrypted({
|
||||
email: mail
|
||||
}, onSend);
|
||||
} else {
|
||||
// send email as plaintext
|
||||
self._emailDao.sendPlaintext({
|
||||
email: mail
|
||||
}, onSend);
|
||||
}
|
||||
|
||||
function onSend(err) {
|
||||
if (err) {
|
||||
self._outboxBusy = false;
|
||||
if (err.code === 42) {
|
||||
// offline try again later
|
||||
done();
|
||||
} else {
|
||||
self._outboxBusy = false;
|
||||
callback(err);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// remove the pending mail from the storage
|
||||
removeFromStorage(mail, done);
|
||||
|
||||
// fire sent notification
|
||||
if (typeof self.onSent === 'function') {
|
||||
self.onSent(mail);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// removes the mail object from disk after successfully sending it
|
||||
function removeFromStorage(mail, done) {
|
||||
self._devicestorage.removeList(outboxDb + '_' + mail.uid, function(err) {
|
||||
if (err) {
|
||||
self._outboxBusy = false;
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// if we're not online, don't even bother sending mails.
|
||||
if (!self._emailDao._account.online || _.isEmpty(pendingMails)) {
|
||||
self._outboxBusy = false;
|
||||
callback(null, pendingMails.length);
|
||||
return;
|
||||
}
|
||||
|
||||
// we're done after all the mails have been handled
|
||||
// update the outbox count...
|
||||
var after = _.after(pendingMails.length, function() {
|
||||
self._outboxBusy = false;
|
||||
callback(null, unsentMails);
|
||||
});
|
||||
|
||||
// send pending mails if possible
|
||||
pendingMails.forEach(function(mail) {
|
||||
send(mail, after);
|
||||
});
|
||||
done();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// send the message
|
||||
function send(mail, done) {
|
||||
|
||||
// check is email is to be sent encrypted or as plaintex
|
||||
if (mail.encrypted === true) {
|
||||
// email was already encrypted before persisting in outbox, tell pgpmailer to send encrypted and not encrypt again
|
||||
self._emailDao.sendEncrypted({
|
||||
email: mail
|
||||
}, onSend);
|
||||
} else {
|
||||
// send email as plaintext
|
||||
self._emailDao.sendPlaintext({
|
||||
email: mail
|
||||
}, onSend);
|
||||
}
|
||||
|
||||
function onSend(err) {
|
||||
if (err) {
|
||||
self._outboxBusy = false;
|
||||
if (err.code === 42) {
|
||||
// offline try again later
|
||||
done();
|
||||
} else {
|
||||
self._outboxBusy = false;
|
||||
callback(err);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// remove the pending mail from the storage
|
||||
removeFromStorage(mail, done);
|
||||
|
||||
// fire sent notification
|
||||
if (typeof self.onSent === 'function') {
|
||||
self.onSent(mail);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// removes the mail object from disk after successfully sending it
|
||||
function removeFromStorage(mail, done) {
|
||||
self._devicestorage.removeList(outboxDb + '_' + mail.uid, function(err) {
|
||||
if (err) {
|
||||
self._outboxBusy = false;
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
done();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return OutboxBO;
|
||||
});
|
||||
module.exports = OutboxBO;
|
|
@ -1,31 +1,29 @@
|
|||
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) {
|
||||
$scope.state.lightbox = (to) ? 'about' : undefined;
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// scope variables
|
||||
//
|
||||
|
||||
$scope.version = cfg.appVersion;
|
||||
$scope.date = new Date();
|
||||
|
||||
//
|
||||
// scope functions
|
||||
//
|
||||
$scope.state.about = {
|
||||
toggle: function(to) {
|
||||
$scope.state.lightbox = (to) ? 'about' : undefined;
|
||||
}
|
||||
};
|
||||
|
||||
return AboutCtrl;
|
||||
});
|
||||
//
|
||||
// scope variables
|
||||
//
|
||||
|
||||
$scope.version = cfg.appVersion;
|
||||
$scope.date = new Date();
|
||||
|
||||
//
|
||||
// scope functions
|
||||
//
|
||||
};
|
||||
|
||||
module.exports = AboutCtrl;
|
|
@ -1,61 +1,59 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
'use strict';
|
||||
|
||||
var appController = require('js/app-controller'),
|
||||
dl = require('js/util/download'),
|
||||
config = require('js/app-config').config,
|
||||
pgp, keychain, userId;
|
||||
var appController = require('../app-controller'),
|
||||
dl = require('../util/download'),
|
||||
config = require('../app-config').config,
|
||||
pgp, keychain, userId;
|
||||
|
||||
//
|
||||
// Controller
|
||||
//
|
||||
//
|
||||
// Controller
|
||||
//
|
||||
|
||||
var AccountCtrl = function($scope) {
|
||||
userId = appController._emailDao._account.emailAddress;
|
||||
keychain = appController._keychain;
|
||||
pgp = appController._pgp;
|
||||
var AccountCtrl = function($scope) {
|
||||
userId = appController._emailDao._account.emailAddress;
|
||||
keychain = appController._keychain;
|
||||
pgp = appController._pgp;
|
||||
|
||||
$scope.state.account = {
|
||||
toggle: function(to) {
|
||||
$scope.state.lightbox = (to) ? 'account' : undefined;
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// scope variables
|
||||
//
|
||||
|
||||
var keyParams = pgp.getKeyParams();
|
||||
|
||||
$scope.eMail = userId;
|
||||
$scope.keyId = keyParams._id.slice(8);
|
||||
var fpr = keyParams.fingerprint;
|
||||
$scope.fingerprint = fpr.slice(0, 4) + ' ' + fpr.slice(4, 8) + ' ' + fpr.slice(8, 12) + ' ' + fpr.slice(12, 16) + ' ' + fpr.slice(16, 20) + ' ' + fpr.slice(20, 24) + ' ' + fpr.slice(24, 28) + ' ' + fpr.slice(28, 32) + ' ' + fpr.slice(32, 36) + ' ' + fpr.slice(36);
|
||||
$scope.keysize = keyParams.bitSize;
|
||||
$scope.publicKeyUrl = config.cloudUrl + '/' + userId;
|
||||
|
||||
//
|
||||
// scope functions
|
||||
//
|
||||
|
||||
$scope.exportKeyFile = function() {
|
||||
keychain.getUserKeyPair(userId, function(err, keys) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
var keyId = keys.publicKey._id;
|
||||
var file = 'whiteout_mail_' + userId + '_' + keyId.substring(8, keyId.length);
|
||||
|
||||
dl.createDownload({
|
||||
content: keys.publicKey.publicKey + '\r\n' + keys.privateKey.encryptedKey,
|
||||
filename: file + '.asc',
|
||||
contentType: 'text/plain'
|
||||
});
|
||||
});
|
||||
};
|
||||
$scope.state.account = {
|
||||
toggle: function(to) {
|
||||
$scope.state.lightbox = (to) ? 'account' : undefined;
|
||||
}
|
||||
};
|
||||
|
||||
return AccountCtrl;
|
||||
});
|
||||
//
|
||||
// scope variables
|
||||
//
|
||||
|
||||
var keyParams = pgp.getKeyParams();
|
||||
|
||||
$scope.eMail = userId;
|
||||
$scope.keyId = keyParams._id.slice(8);
|
||||
var fpr = keyParams.fingerprint;
|
||||
$scope.fingerprint = fpr.slice(0, 4) + ' ' + fpr.slice(4, 8) + ' ' + fpr.slice(8, 12) + ' ' + fpr.slice(12, 16) + ' ' + fpr.slice(16, 20) + ' ' + fpr.slice(20, 24) + ' ' + fpr.slice(24, 28) + ' ' + fpr.slice(28, 32) + ' ' + fpr.slice(32, 36) + ' ' + fpr.slice(36);
|
||||
$scope.keysize = keyParams.bitSize;
|
||||
$scope.publicKeyUrl = config.cloudUrl + '/' + userId;
|
||||
|
||||
//
|
||||
// scope functions
|
||||
//
|
||||
|
||||
$scope.exportKeyFile = function() {
|
||||
keychain.getUserKeyPair(userId, function(err, keys) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
var keyId = keys.publicKey._id;
|
||||
var file = 'whiteout_mail_' + userId + '_' + keyId.substring(8, keyId.length);
|
||||
|
||||
dl.createDownload({
|
||||
content: keys.publicKey.publicKey + '\r\n' + keys.privateKey.encryptedKey,
|
||||
filename: file + '.asc',
|
||||
contentType: 'text/plain'
|
||||
});
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = AccountCtrl;
|
|
@ -1,120 +1,118 @@
|
|||
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) {
|
||||
if (!appCtrl._auth && !$routeParams.dev) {
|
||||
$location.path('/'); // init app
|
||||
var AddAccountCtrl = function($scope, $location, $routeParams) {
|
||||
if (!appCtrl._auth && !$routeParams.dev) {
|
||||
$location.path('/'); // init app
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.step = 1;
|
||||
|
||||
$scope.goTo = function(step) {
|
||||
$scope.step = step;
|
||||
};
|
||||
|
||||
$scope.createWhiteoutAccount = function() {
|
||||
if ($scope.form.$invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.step = 1;
|
||||
$scope.busy = true;
|
||||
$scope.errMsg = undefined; // reset error msg
|
||||
$scope.emailAddress = $scope.user + '@' + cfg.wmailDomain;
|
||||
|
||||
$scope.goTo = function(step) {
|
||||
$scope.step = step;
|
||||
};
|
||||
// call REST api
|
||||
appCtrl._adminDao.createUser({
|
||||
emailAddress: $scope.emailAddress,
|
||||
password: $scope.pass,
|
||||
phone: $scope.phone.replace(/\s+/g, ''), // remove spaces from the phone number
|
||||
betaCode: $scope.betaCode.toUpperCase()
|
||||
}, function(err) {
|
||||
$scope.busy = false;
|
||||
|
||||
$scope.createWhiteoutAccount = function() {
|
||||
if ($scope.form.$invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.busy = true;
|
||||
$scope.errMsg = undefined; // reset error msg
|
||||
$scope.emailAddress = $scope.user + '@' + cfg.wmailDomain;
|
||||
|
||||
// call REST api
|
||||
appCtrl._adminDao.createUser({
|
||||
emailAddress: $scope.emailAddress,
|
||||
password: $scope.pass,
|
||||
phone: $scope.phone.replace(/\s+/g, ''), // remove spaces from the phone number
|
||||
betaCode: $scope.betaCode.toUpperCase()
|
||||
}, function(err) {
|
||||
$scope.busy = false;
|
||||
|
||||
if (err) {
|
||||
$scope.errMsg = err.errMsg || err.message;
|
||||
$scope.$apply();
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.goTo(3);
|
||||
if (err) {
|
||||
$scope.errMsg = err.errMsg || err.message;
|
||||
$scope.$apply();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.validateUser = function() {
|
||||
if ($scope.formValidate.$invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.busyValidate = true;
|
||||
$scope.errMsgValidate = undefined; // reset error msg
|
||||
|
||||
// verify user to REST api
|
||||
appCtrl._adminDao.validateUser({
|
||||
emailAddress: $scope.emailAddress,
|
||||
token: $scope.token.toUpperCase()
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
$scope.busyValidate = false;
|
||||
$scope.errMsgValidate = err.errMsg || err.message;
|
||||
$scope.$apply();
|
||||
return;
|
||||
}
|
||||
|
||||
// proceed to login
|
||||
$scope.login();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.login = function() {
|
||||
// store credentials in memory
|
||||
appCtrl._auth.setCredentials({
|
||||
provider: 'wmail',
|
||||
emailAddress: $scope.emailAddress,
|
||||
username: $scope.emailAddress,
|
||||
realname: $scope.realname,
|
||||
password: $scope.pass,
|
||||
imap: cfg.wmail.imap,
|
||||
smtp: cfg.wmail.smtp
|
||||
});
|
||||
|
||||
// proceed to login and keygen
|
||||
$location.path('/login');
|
||||
$scope.goTo(3);
|
||||
$scope.$apply();
|
||||
};
|
||||
|
||||
$scope.connectToGoogle = function() {
|
||||
// test for oauth support
|
||||
if (appCtrl._auth._oauth.isSupported()) {
|
||||
// fetches the email address from the chrome identity api
|
||||
appCtrl._auth.getOAuthToken(function(err) {
|
||||
if (err) {
|
||||
return $scope.onError(err);
|
||||
}
|
||||
$location.path('/login-set-credentials').search({
|
||||
provider: 'gmail'
|
||||
});
|
||||
$scope.$apply();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// use normal user/password login
|
||||
$location.path('/login-set-credentials').search({
|
||||
provider: 'gmail'
|
||||
});
|
||||
};
|
||||
|
||||
$scope.connectTo = function(provider) {
|
||||
$location.path('/login-set-credentials').search({
|
||||
provider: provider
|
||||
});
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
return AddAccountCtrl;
|
||||
});
|
||||
$scope.validateUser = function() {
|
||||
if ($scope.formValidate.$invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.busyValidate = true;
|
||||
$scope.errMsgValidate = undefined; // reset error msg
|
||||
|
||||
// verify user to REST api
|
||||
appCtrl._adminDao.validateUser({
|
||||
emailAddress: $scope.emailAddress,
|
||||
token: $scope.token.toUpperCase()
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
$scope.busyValidate = false;
|
||||
$scope.errMsgValidate = err.errMsg || err.message;
|
||||
$scope.$apply();
|
||||
return;
|
||||
}
|
||||
|
||||
// proceed to login
|
||||
$scope.login();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.login = function() {
|
||||
// store credentials in memory
|
||||
appCtrl._auth.setCredentials({
|
||||
provider: 'wmail',
|
||||
emailAddress: $scope.emailAddress,
|
||||
username: $scope.emailAddress,
|
||||
realname: $scope.realname,
|
||||
password: $scope.pass,
|
||||
imap: cfg.wmail.imap,
|
||||
smtp: cfg.wmail.smtp
|
||||
});
|
||||
|
||||
// proceed to login and keygen
|
||||
$location.path('/login');
|
||||
$scope.$apply();
|
||||
};
|
||||
|
||||
$scope.connectToGoogle = function() {
|
||||
// test for oauth support
|
||||
if (appCtrl._auth._oauth.isSupported()) {
|
||||
// fetches the email address from the chrome identity api
|
||||
appCtrl._auth.getOAuthToken(function(err) {
|
||||
if (err) {
|
||||
return $scope.onError(err);
|
||||
}
|
||||
$location.path('/login-set-credentials').search({
|
||||
provider: 'gmail'
|
||||
});
|
||||
$scope.$apply();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// use normal user/password login
|
||||
$location.path('/login-set-credentials').search({
|
||||
provider: 'gmail'
|
||||
});
|
||||
};
|
||||
|
||||
$scope.connectTo = function(provider) {
|
||||
$location.path('/login-set-credentials').search({
|
||||
provider: provider
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = AddAccountCtrl;
|
|
@ -1,143 +1,139 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
'use strict';
|
||||
|
||||
var angular = require('angular'),
|
||||
_ = require('underscore'),
|
||||
appController = require('js/app-controller'),
|
||||
keychain, pgp;
|
||||
var appController = require('../app-controller'),
|
||||
keychain, pgp;
|
||||
|
||||
//
|
||||
// Controller
|
||||
//
|
||||
//
|
||||
// Controller
|
||||
//
|
||||
|
||||
var ContactsCtrl = function($scope) {
|
||||
keychain = appController._keychain,
|
||||
pgp = appController._pgp;
|
||||
var ContactsCtrl = function($scope) {
|
||||
keychain = appController._keychain,
|
||||
pgp = appController._pgp;
|
||||
|
||||
$scope.state.contacts = {
|
||||
toggle: function(to) {
|
||||
$scope.state.lightbox = (to) ? 'contacts' : undefined;
|
||||
$scope.state.contacts = {
|
||||
toggle: function(to) {
|
||||
$scope.state.lightbox = (to) ? 'contacts' : undefined;
|
||||
|
||||
$scope.listKeys();
|
||||
}
|
||||
};
|
||||
|
||||
// set default value so that the popover height is correct on init
|
||||
$scope.fingerprint = 'XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX';
|
||||
|
||||
//
|
||||
// scope functions
|
||||
//
|
||||
|
||||
$scope.listKeys = function() {
|
||||
keychain.listLocalPublicKeys(function(err, keys) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
keys.forEach(addParams);
|
||||
|
||||
$scope.keys = keys;
|
||||
$scope.$apply();
|
||||
|
||||
function addParams(key) {
|
||||
var params = pgp.getKeyParams(key.publicKey);
|
||||
_.extend(key, params);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.getFingerprint = function(key) {
|
||||
var fpr = key.fingerprint;
|
||||
var formatted = fpr.slice(0, 4) + ' ' + fpr.slice(4, 8) + ' ' + fpr.slice(8, 12) + ' ' + fpr.slice(12, 16) + ' ' + fpr.slice(16, 20) + ' ... ' + fpr.slice(20, 24) + ' ' + fpr.slice(24, 28) + ' ' + fpr.slice(28, 32) + ' ' + fpr.slice(32, 36) + ' ' + fpr.slice(36);
|
||||
|
||||
$scope.fingerprint = formatted;
|
||||
};
|
||||
|
||||
$scope.importKey = function(publicKeyArmored) {
|
||||
var keyParams, pubkey;
|
||||
|
||||
// verifiy public key string
|
||||
if (publicKeyArmored.indexOf('-----BEGIN PGP PUBLIC KEY BLOCK-----') < 0) {
|
||||
$scope.onError({
|
||||
showBugReporter: false,
|
||||
message: 'Invalid public key!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
keyParams = pgp.getKeyParams(publicKeyArmored);
|
||||
} catch (e) {
|
||||
$scope.onError(new Error('Error reading public key params!'));
|
||||
return;
|
||||
}
|
||||
|
||||
pubkey = {
|
||||
_id: keyParams._id,
|
||||
userId: keyParams.userId,
|
||||
userIds: keyParams.userIds,
|
||||
publicKey: publicKeyArmored,
|
||||
imported: true // mark manually imported keys
|
||||
};
|
||||
|
||||
keychain.saveLocalPublicKey(pubkey, function(err) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// update displayed keys
|
||||
$scope.listKeys();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.removeKey = function(key) {
|
||||
keychain.removeLocalPublicKey(key._id, function(err) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// update displayed keys
|
||||
$scope.listKeys();
|
||||
});
|
||||
};
|
||||
$scope.listKeys();
|
||||
}
|
||||
};
|
||||
|
||||
// set default value so that the popover height is correct on init
|
||||
$scope.fingerprint = 'XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX';
|
||||
|
||||
//
|
||||
// Directives
|
||||
// scope functions
|
||||
//
|
||||
|
||||
var ngModule = angular.module('contacts', []);
|
||||
|
||||
ngModule.directive('keyfileInput', function() {
|
||||
return function(scope, elm) {
|
||||
elm.on('change', function(e) {
|
||||
for (var i = 0; i < e.target.files.length; i++) {
|
||||
importKey(e.target.files.item(i));
|
||||
}
|
||||
});
|
||||
|
||||
function importKey(file) {
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
scope.importKey(e.target.result);
|
||||
};
|
||||
reader.readAsText(file);
|
||||
$scope.listKeys = function() {
|
||||
keychain.listLocalPublicKeys(function(err, keys) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
ngModule.directive('keyfileBtn', function() {
|
||||
return function(scope, elm) {
|
||||
elm.on('click touchstart', function(e) {
|
||||
e.preventDefault();
|
||||
document.querySelector('#keyfile-input').click();
|
||||
keys.forEach(addParams);
|
||||
|
||||
$scope.keys = keys;
|
||||
$scope.$apply();
|
||||
|
||||
function addParams(key) {
|
||||
var params = pgp.getKeyParams(key.publicKey);
|
||||
_.extend(key, params);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.getFingerprint = function(key) {
|
||||
var fpr = key.fingerprint;
|
||||
var formatted = fpr.slice(0, 4) + ' ' + fpr.slice(4, 8) + ' ' + fpr.slice(8, 12) + ' ' + fpr.slice(12, 16) + ' ' + fpr.slice(16, 20) + ' ... ' + fpr.slice(20, 24) + ' ' + fpr.slice(24, 28) + ' ' + fpr.slice(28, 32) + ' ' + fpr.slice(32, 36) + ' ' + fpr.slice(36);
|
||||
|
||||
$scope.fingerprint = formatted;
|
||||
};
|
||||
|
||||
$scope.importKey = function(publicKeyArmored) {
|
||||
var keyParams, pubkey;
|
||||
|
||||
// verifiy public key string
|
||||
if (publicKeyArmored.indexOf('-----BEGIN PGP PUBLIC KEY BLOCK-----') < 0) {
|
||||
$scope.onError({
|
||||
showBugReporter: false,
|
||||
message: 'Invalid public key!'
|
||||
});
|
||||
};
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
return ContactsCtrl;
|
||||
});
|
||||
try {
|
||||
keyParams = pgp.getKeyParams(publicKeyArmored);
|
||||
} catch (e) {
|
||||
$scope.onError(new Error('Error reading public key params!'));
|
||||
return;
|
||||
}
|
||||
|
||||
pubkey = {
|
||||
_id: keyParams._id,
|
||||
userId: keyParams.userId,
|
||||
userIds: keyParams.userIds,
|
||||
publicKey: publicKeyArmored,
|
||||
imported: true // mark manually imported keys
|
||||
};
|
||||
|
||||
keychain.saveLocalPublicKey(pubkey, function(err) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// update displayed keys
|
||||
$scope.listKeys();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.removeKey = function(key) {
|
||||
keychain.removeLocalPublicKey(key._id, function(err) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// update displayed keys
|
||||
$scope.listKeys();
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
//
|
||||
// Directives
|
||||
//
|
||||
|
||||
var ngModule = angular.module('contacts', []);
|
||||
|
||||
ngModule.directive('keyfileInput', function() {
|
||||
return function(scope, elm) {
|
||||
elm.on('change', function(e) {
|
||||
for (var i = 0; i < e.target.files.length; i++) {
|
||||
importKey(e.target.files.item(i));
|
||||
}
|
||||
});
|
||||
|
||||
function importKey(file) {
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
scope.importKey(e.target.result);
|
||||
};
|
||||
reader.readAsText(file);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
ngModule.directive('keyfileBtn', function() {
|
||||
return function(scope, elm) {
|
||||
elm.on('click touchstart', function(e) {
|
||||
e.preventDefault();
|
||||
document.querySelector('#keyfile-input').click();
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
module.exports = ContactsCtrl;
|
|
@ -1,16 +1,14 @@
|
|||
define(function() {
|
||||
'use strict';
|
||||
'use strict';
|
||||
|
||||
var DialogCtrl = function($scope) {
|
||||
$scope.confirm = function(ok) {
|
||||
$scope.state.dialog.open = false;
|
||||
var DialogCtrl = function($scope) {
|
||||
$scope.confirm = function(ok) {
|
||||
$scope.state.dialog.open = false;
|
||||
|
||||
if ($scope.state.dialog.callback) {
|
||||
$scope.state.dialog.callback(ok);
|
||||
}
|
||||
$scope.state.dialog.callback = undefined;
|
||||
};
|
||||
if ($scope.state.dialog.callback) {
|
||||
$scope.state.dialog.callback(ok);
|
||||
}
|
||||
$scope.state.dialog.callback = undefined;
|
||||
};
|
||||
};
|
||||
|
||||
return DialogCtrl;
|
||||
});
|
||||
module.exports = DialogCtrl;
|
|
@ -1,73 +1,71 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
'use strict';
|
||||
|
||||
var appController = require('js/app-controller');
|
||||
var appController = require('../app-controller');
|
||||
|
||||
var LoginExistingCtrl = function($scope, $location, $routeParams) {
|
||||
if (!appController._emailDao && !$routeParams.dev) {
|
||||
$location.path('/'); // init app
|
||||
var LoginExistingCtrl = function($scope, $location, $routeParams) {
|
||||
if (!appController._emailDao && !$routeParams.dev) {
|
||||
$location.path('/'); // init app
|
||||
return;
|
||||
}
|
||||
|
||||
var emailDao = appController._emailDao;
|
||||
|
||||
$scope.buttonEnabled = true;
|
||||
$scope.incorrect = false;
|
||||
|
||||
$scope.change = function() {
|
||||
$scope.incorrect = false;
|
||||
};
|
||||
|
||||
$scope.confirmPassphrase = function() {
|
||||
if (!$scope.passphrase) {
|
||||
return;
|
||||
}
|
||||
|
||||
var emailDao = appController._emailDao;
|
||||
|
||||
$scope.buttonEnabled = true;
|
||||
// disable button once loggin has started
|
||||
$scope.buttonEnabled = false;
|
||||
$scope.incorrect = false;
|
||||
|
||||
$scope.change = function() {
|
||||
$scope.incorrect = false;
|
||||
};
|
||||
|
||||
$scope.confirmPassphrase = function() {
|
||||
if (!$scope.passphrase) {
|
||||
return;
|
||||
}
|
||||
|
||||
// disable button once loggin has started
|
||||
$scope.buttonEnabled = false;
|
||||
$scope.incorrect = false;
|
||||
unlockCrypto();
|
||||
};
|
||||
|
||||
function unlockCrypto() {
|
||||
var userId = emailDao._account.emailAddress;
|
||||
emailDao._keychain.getUserKeyPair(userId, function(err, keypair) {
|
||||
if (err) {
|
||||
handleError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
emailDao.unlock({
|
||||
keypair: keypair,
|
||||
passphrase: $scope.passphrase
|
||||
}, onUnlock);
|
||||
});
|
||||
}
|
||||
|
||||
function onUnlock(err) {
|
||||
if (err) {
|
||||
$scope.incorrect = true;
|
||||
$scope.buttonEnabled = true;
|
||||
$scope.$apply();
|
||||
return;
|
||||
}
|
||||
|
||||
appController._auth.storeCredentials(function(err) {
|
||||
if (err) {
|
||||
return $scope.onError(err);
|
||||
}
|
||||
|
||||
$location.path('/desktop');
|
||||
$scope.$apply();
|
||||
});
|
||||
}
|
||||
|
||||
function handleError(err) {
|
||||
$scope.incorrect = true;
|
||||
$scope.buttonEnabled = true;
|
||||
$scope.onError(err);
|
||||
}
|
||||
unlockCrypto();
|
||||
};
|
||||
|
||||
return LoginExistingCtrl;
|
||||
});
|
||||
function unlockCrypto() {
|
||||
var userId = emailDao._account.emailAddress;
|
||||
emailDao._keychain.getUserKeyPair(userId, function(err, keypair) {
|
||||
if (err) {
|
||||
handleError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
emailDao.unlock({
|
||||
keypair: keypair,
|
||||
passphrase: $scope.passphrase
|
||||
}, onUnlock);
|
||||
});
|
||||
}
|
||||
|
||||
function onUnlock(err) {
|
||||
if (err) {
|
||||
$scope.incorrect = true;
|
||||
$scope.buttonEnabled = true;
|
||||
$scope.$apply();
|
||||
return;
|
||||
}
|
||||
|
||||
appController._auth.storeCredentials(function(err) {
|
||||
if (err) {
|
||||
return $scope.onError(err);
|
||||
}
|
||||
|
||||
$location.path('/desktop');
|
||||
$scope.$apply();
|
||||
});
|
||||
}
|
||||
|
||||
function handleError(err) {
|
||||
$scope.incorrect = true;
|
||||
$scope.buttonEnabled = true;
|
||||
$scope.onError(err);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = LoginExistingCtrl;
|
|
@ -1,121 +1,119 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
'use strict';
|
||||
|
||||
var appController = require('js/app-controller');
|
||||
var appController = require('../app-controller');
|
||||
|
||||
var LoginInitialCtrl = function($scope, $location, $routeParams) {
|
||||
if (!appController._emailDao && !$routeParams.dev) {
|
||||
$location.path('/'); // init app
|
||||
var LoginInitialCtrl = function($scope, $location, $routeParams) {
|
||||
if (!appController._emailDao && !$routeParams.dev) {
|
||||
$location.path('/'); // init app
|
||||
return;
|
||||
}
|
||||
|
||||
var emailDao = appController._emailDao,
|
||||
states, termsMsg = 'You must accept the Terms of Service to continue.';
|
||||
|
||||
states = {
|
||||
IDLE: 1,
|
||||
PROCESSING: 2,
|
||||
DONE: 3
|
||||
};
|
||||
$scope.state.ui = states.IDLE; // initial state
|
||||
|
||||
//
|
||||
// scope functions
|
||||
//
|
||||
|
||||
/**
|
||||
* Continue to key import screen
|
||||
*/
|
||||
$scope.importKey = function() {
|
||||
if (!$scope.state.agree) {
|
||||
$scope.onError({
|
||||
message: termsMsg
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var emailDao = appController._emailDao,
|
||||
states, termsMsg = 'You must accept the Terms of Service to continue.';
|
||||
|
||||
states = {
|
||||
IDLE: 1,
|
||||
PROCESSING: 2,
|
||||
DONE: 3
|
||||
};
|
||||
$scope.state.ui = states.IDLE; // initial state
|
||||
|
||||
//
|
||||
// scope functions
|
||||
//
|
||||
|
||||
/**
|
||||
* Continue to key import screen
|
||||
*/
|
||||
$scope.importKey = function() {
|
||||
if (!$scope.state.agree) {
|
||||
$scope.onError({
|
||||
message: termsMsg
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// sing up to newsletter
|
||||
$scope.signUpToNewsletter();
|
||||
// go to key import
|
||||
$location.path('/login-new-device');
|
||||
};
|
||||
|
||||
/**
|
||||
* Continue to keygen
|
||||
*/
|
||||
$scope.generateKey = function() {
|
||||
if (!$scope.state.agree) {
|
||||
$scope.onError({
|
||||
message: termsMsg
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// sing up to newsletter
|
||||
$scope.signUpToNewsletter();
|
||||
// go to set keygen screen
|
||||
$scope.setState(states.PROCESSING);
|
||||
|
||||
setTimeout(function() {
|
||||
emailDao.unlock({
|
||||
passphrase: undefined // generate key without passphrase
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
$scope.setState(states.IDLE);
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
appController._auth.storeCredentials(function(err) {
|
||||
if (err) {
|
||||
return $scope.onError(err);
|
||||
}
|
||||
|
||||
$location.path('/desktop');
|
||||
$scope.$apply();
|
||||
});
|
||||
});
|
||||
}, 500);
|
||||
};
|
||||
|
||||
/**
|
||||
* [signUpToNewsletter description]
|
||||
* @param {Function} callback (optional)
|
||||
*/
|
||||
$scope.signUpToNewsletter = function(callback) {
|
||||
if (!$scope.state.newsletter) {
|
||||
return;
|
||||
}
|
||||
|
||||
var address = emailDao._account.emailAddress;
|
||||
var uri = 'https://whiteout.us8.list-manage.com/subscribe/post?u=52ea5a9e1be9e1d194f184158&id=6538e8f09f';
|
||||
|
||||
var formData = new FormData();
|
||||
formData.append('EMAIL', address);
|
||||
formData.append('b_52ea5a9e1be9e1d194f184158_6538e8f09f', '');
|
||||
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('post', uri, true);
|
||||
|
||||
xhr.onload = function() {
|
||||
if (callback) {
|
||||
callback(null, xhr);
|
||||
}
|
||||
};
|
||||
|
||||
xhr.onerror = function(err) {
|
||||
if (callback) {
|
||||
callback(err);
|
||||
}
|
||||
};
|
||||
|
||||
xhr.send(formData);
|
||||
};
|
||||
|
||||
$scope.setState = function(state) {
|
||||
$scope.state.ui = state;
|
||||
};
|
||||
// sing up to newsletter
|
||||
$scope.signUpToNewsletter();
|
||||
// go to key import
|
||||
$location.path('/login-new-device');
|
||||
};
|
||||
|
||||
return LoginInitialCtrl;
|
||||
});
|
||||
/**
|
||||
* Continue to keygen
|
||||
*/
|
||||
$scope.generateKey = function() {
|
||||
if (!$scope.state.agree) {
|
||||
$scope.onError({
|
||||
message: termsMsg
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// sing up to newsletter
|
||||
$scope.signUpToNewsletter();
|
||||
// go to set keygen screen
|
||||
$scope.setState(states.PROCESSING);
|
||||
|
||||
setTimeout(function() {
|
||||
emailDao.unlock({
|
||||
passphrase: undefined // generate key without passphrase
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
$scope.setState(states.IDLE);
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
appController._auth.storeCredentials(function(err) {
|
||||
if (err) {
|
||||
return $scope.onError(err);
|
||||
}
|
||||
|
||||
$location.path('/desktop');
|
||||
$scope.$apply();
|
||||
});
|
||||
});
|
||||
}, 500);
|
||||
};
|
||||
|
||||
/**
|
||||
* [signUpToNewsletter description]
|
||||
* @param {Function} callback (optional)
|
||||
*/
|
||||
$scope.signUpToNewsletter = function(callback) {
|
||||
if (!$scope.state.newsletter) {
|
||||
return;
|
||||
}
|
||||
|
||||
var address = emailDao._account.emailAddress;
|
||||
var uri = 'https://whiteout.us8.list-manage.com/subscribe/post?u=52ea5a9e1be9e1d194f184158&id=6538e8f09f';
|
||||
|
||||
var formData = new FormData();
|
||||
formData.append('EMAIL', address);
|
||||
formData.append('b_52ea5a9e1be9e1d194f184158_6538e8f09f', '');
|
||||
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('post', uri, true);
|
||||
|
||||
xhr.onload = function() {
|
||||
if (callback) {
|
||||
callback(null, xhr);
|
||||
}
|
||||
};
|
||||
|
||||
xhr.onerror = function(err) {
|
||||
if (callback) {
|
||||
callback(err);
|
||||
}
|
||||
};
|
||||
|
||||
xhr.send(formData);
|
||||
};
|
||||
|
||||
$scope.setState = function(state) {
|
||||
$scope.state.ui = state;
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = LoginInitialCtrl;
|
|
@ -1,141 +1,138 @@
|
|||
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) {
|
||||
if (!appController._emailDao && !$routeParams.dev) {
|
||||
$location.path('/'); // init app
|
||||
return;
|
||||
}
|
||||
var LoginExistingCtrl = function($scope, $location, $routeParams) {
|
||||
if (!appController._emailDao && !$routeParams.dev) {
|
||||
$location.path('/'); // init app
|
||||
return;
|
||||
}
|
||||
|
||||
var emailDao = appController._emailDao,
|
||||
pgp = appController._pgp;
|
||||
var emailDao = appController._emailDao,
|
||||
pgp = appController._pgp;
|
||||
|
||||
$scope.incorrect = false;
|
||||
|
||||
$scope.confirmPassphrase = function() {
|
||||
$scope.incorrect = false;
|
||||
unlockCrypto();
|
||||
};
|
||||
|
||||
$scope.confirmPassphrase = function() {
|
||||
$scope.incorrect = false;
|
||||
unlockCrypto();
|
||||
};
|
||||
|
||||
function unlockCrypto() {
|
||||
var userId = emailDao._account.emailAddress;
|
||||
// check if user already has a public key on the key server
|
||||
emailDao._keychain.getUserKeyPair(userId, function(err, keypair) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
keypair = keypair || {};
|
||||
|
||||
// extract public key from private key block if missing in key file
|
||||
if (!$scope.key.publicKeyArmored || $scope.key.publicKeyArmored.indexOf('-----BEGIN PGP PUBLIC KEY BLOCK-----') < 0) {
|
||||
try {
|
||||
$scope.key.publicKeyArmored = pgp.extractPublicKey($scope.key.privateKeyArmored);
|
||||
} catch (e) {
|
||||
$scope.onError(new Error('Error parsing public key from private key!'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// parse keypair params
|
||||
var privKeyParams, pubKeyParams;
|
||||
try {
|
||||
privKeyParams = pgp.getKeyParams($scope.key.privateKeyArmored);
|
||||
pubKeyParams = pgp.getKeyParams($scope.key.publicKeyArmored);
|
||||
} catch (e) {
|
||||
$scope.onError(new Error('Error reading key params!'));
|
||||
return;
|
||||
}
|
||||
|
||||
// set parsed private key
|
||||
keypair.privateKey = {
|
||||
_id: privKeyParams._id,
|
||||
userId: userId,
|
||||
userIds: privKeyParams.userIds,
|
||||
encryptedKey: $scope.key.privateKeyArmored
|
||||
};
|
||||
|
||||
if (!keypair.publicKey) {
|
||||
// there is no public key on the key server yet... use parsed
|
||||
keypair.publicKey = {
|
||||
_id: pubKeyParams._id,
|
||||
userId: userId,
|
||||
userIds: pubKeyParams.userIds,
|
||||
publicKey: $scope.key.publicKeyArmored
|
||||
};
|
||||
}
|
||||
|
||||
// import and validate keypair
|
||||
emailDao.unlock({
|
||||
keypair: keypair,
|
||||
passphrase: $scope.passphrase
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
$scope.incorrect = true;
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
emailDao._keychain.putUserKeyPair(keypair, onUnlock);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function onUnlock(err) {
|
||||
function unlockCrypto() {
|
||||
var userId = emailDao._account.emailAddress;
|
||||
// check if user already has a public key on the key server
|
||||
emailDao._keychain.getUserKeyPair(userId, function(err, keypair) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
appController._auth.storeCredentials(function(err) {
|
||||
if (err) {
|
||||
return $scope.onError(err);
|
||||
keypair = keypair || {};
|
||||
|
||||
// extract public key from private key block if missing in key file
|
||||
if (!$scope.key.publicKeyArmored || $scope.key.publicKeyArmored.indexOf('-----BEGIN PGP PUBLIC KEY BLOCK-----') < 0) {
|
||||
try {
|
||||
$scope.key.publicKeyArmored = pgp.extractPublicKey($scope.key.privateKeyArmored);
|
||||
} catch (e) {
|
||||
$scope.onError(new Error('Error parsing public key from private key!'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$location.path('/desktop');
|
||||
$scope.$apply();
|
||||
});
|
||||
}
|
||||
};
|
||||
// parse keypair params
|
||||
var privKeyParams, pubKeyParams;
|
||||
try {
|
||||
privKeyParams = pgp.getKeyParams($scope.key.privateKeyArmored);
|
||||
pubKeyParams = pgp.getKeyParams($scope.key.publicKeyArmored);
|
||||
} catch (e) {
|
||||
$scope.onError(new Error('Error reading key params!'));
|
||||
return;
|
||||
}
|
||||
|
||||
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,
|
||||
reader = new FileReader();
|
||||
// set parsed private key
|
||||
keypair.privateKey = {
|
||||
_id: privKeyParams._id,
|
||||
userId: userId,
|
||||
userIds: privKeyParams.userIds,
|
||||
encryptedKey: $scope.key.privateKeyArmored
|
||||
};
|
||||
|
||||
if (files.length === 0) {
|
||||
if (!keypair.publicKey) {
|
||||
// there is no public key on the key server yet... use parsed
|
||||
keypair.publicKey = {
|
||||
_id: pubKeyParams._id,
|
||||
userId: userId,
|
||||
userIds: pubKeyParams.userIds,
|
||||
publicKey: $scope.key.publicKeyArmored
|
||||
};
|
||||
}
|
||||
|
||||
// import and validate keypair
|
||||
emailDao.unlock({
|
||||
keypair: keypair,
|
||||
passphrase: $scope.passphrase
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
$scope.incorrect = true;
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
reader.onload = function(e) {
|
||||
var rawKeys = e.target.result,
|
||||
index = rawKeys.indexOf('-----BEGIN PGP PRIVATE KEY BLOCK-----'),
|
||||
keyParts;
|
||||
|
||||
if (index === -1) {
|
||||
scope.onError(new Error('Error parsing private PGP key block!'));
|
||||
return;
|
||||
}
|
||||
|
||||
keyParts = {
|
||||
publicKeyArmored: rawKeys.substring(0, index).trim(),
|
||||
privateKeyArmored: rawKeys.substring(index, rawKeys.length).trim()
|
||||
};
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.key = keyParts;
|
||||
});
|
||||
};
|
||||
reader.readAsText(files[0]);
|
||||
emailDao._keychain.putUserKeyPair(keypair, onUnlock);
|
||||
});
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return LoginExistingCtrl;
|
||||
});
|
||||
function onUnlock(err) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
appController._auth.storeCredentials(function(err) {
|
||||
if (err) {
|
||||
return $scope.onError(err);
|
||||
}
|
||||
|
||||
$location.path('/desktop');
|
||||
$scope.$apply();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
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,
|
||||
reader = new FileReader();
|
||||
|
||||
if (files.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
reader.onload = function(e) {
|
||||
var rawKeys = e.target.result,
|
||||
index = rawKeys.indexOf('-----BEGIN PGP PRIVATE KEY BLOCK-----'),
|
||||
keyParts;
|
||||
|
||||
if (index === -1) {
|
||||
scope.onError(new Error('Error parsing private PGP key block!'));
|
||||
return;
|
||||
}
|
||||
|
||||
keyParts = {
|
||||
publicKeyArmored: rawKeys.substring(0, index).trim(),
|
||||
privateKeyArmored: rawKeys.substring(index, rawKeys.length).trim()
|
||||
};
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.key = keyParts;
|
||||
});
|
||||
};
|
||||
reader.readAsText(files[0]);
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
module.exports = LoginExistingCtrl;
|
|
@ -1,134 +1,132 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
'use strict';
|
||||
|
||||
var appController = require('js/app-controller');
|
||||
var appController = require('../app-controller');
|
||||
|
||||
var LoginPrivateKeyDownloadCtrl = function($scope, $location, $routeParams) {
|
||||
if (!appController._emailDao && !$routeParams.dev) {
|
||||
$location.path('/'); // init app
|
||||
var LoginPrivateKeyDownloadCtrl = function($scope, $location, $routeParams) {
|
||||
if (!appController._emailDao && !$routeParams.dev) {
|
||||
$location.path('/'); // init app
|
||||
return;
|
||||
}
|
||||
|
||||
var keychain = appController._keychain,
|
||||
emailDao = appController._emailDao,
|
||||
userId = emailDao._account.emailAddress;
|
||||
|
||||
$scope.step = 1;
|
||||
|
||||
$scope.handlePaste = function(event) {
|
||||
var evt = event;
|
||||
if (evt.originalEvent) {
|
||||
evt = evt.originalEvent;
|
||||
}
|
||||
|
||||
var value = evt.clipboardData.getData('text/plain');
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
|
||||
var keychain = appController._keychain,
|
||||
emailDao = appController._emailDao,
|
||||
userId = emailDao._account.emailAddress;
|
||||
|
||||
$scope.step = 1;
|
||||
|
||||
$scope.handlePaste = function(event) {
|
||||
var evt = event;
|
||||
if (evt.originalEvent) {
|
||||
evt = evt.originalEvent;
|
||||
}
|
||||
|
||||
var value = evt.clipboardData.getData('text/plain');
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
|
||||
value = value.replace(/-/g, '');
|
||||
$scope.code0 = value.slice(0, 4);
|
||||
$scope.code1 = value.slice(4, 8);
|
||||
$scope.code2 = value.slice(8, 12);
|
||||
$scope.code3 = value.slice(12, 16);
|
||||
$scope.code4 = value.slice(16, 20);
|
||||
$scope.code5 = value.slice(20, 24);
|
||||
};
|
||||
|
||||
$scope.verifyRecoveryToken = function(callback) {
|
||||
if (!$scope.recoveryToken) {
|
||||
$scope.onError(new Error('Please set the recovery token!'));
|
||||
return;
|
||||
}
|
||||
|
||||
keychain.getUserKeyPair(userId, function(err, keypair) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// remember for storage later
|
||||
$scope.cachedKeypair = keypair;
|
||||
|
||||
keychain.downloadPrivateKey({
|
||||
userId: userId,
|
||||
keyId: keypair.publicKey._id,
|
||||
recoveryToken: $scope.recoveryToken.toUpperCase()
|
||||
}, function(err, encryptedPrivateKey) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.encryptedPrivateKey = encryptedPrivateKey;
|
||||
callback();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.decryptAndStorePrivateKeyLocally = function() {
|
||||
var inputCode = '' + $scope.code0 + $scope.code1 + $scope.code2 + $scope.code3 + $scope.code4 + $scope.code5;
|
||||
|
||||
if (!inputCode) {
|
||||
$scope.onError(new Error('Please enter the keychain code!'));
|
||||
return;
|
||||
}
|
||||
|
||||
var options = $scope.encryptedPrivateKey;
|
||||
options.code = inputCode.toUpperCase();
|
||||
|
||||
keychain.decryptAndStorePrivateKeyLocally(options, function(err, privateKey) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// add private key to cached keypair object
|
||||
$scope.cachedKeypair.privateKey = privateKey;
|
||||
|
||||
// try empty passphrase
|
||||
emailDao.unlock({
|
||||
keypair: $scope.cachedKeypair,
|
||||
passphrase: undefined
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
// go to passphrase login screen
|
||||
$scope.goTo('/login-existing');
|
||||
return;
|
||||
}
|
||||
|
||||
// passphrase is corrent ... go to main app
|
||||
appController._auth.storeCredentials(function(err) {
|
||||
if (err) {
|
||||
return $scope.onError(err);
|
||||
}
|
||||
|
||||
$scope.goTo('/desktop');
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.goForward = function() {
|
||||
if ($scope.step === 1) {
|
||||
$scope.verifyRecoveryToken(function() {
|
||||
$scope.step++;
|
||||
$scope.$apply();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if ($scope.step === 2) {
|
||||
$scope.decryptAndStorePrivateKeyLocally();
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.goTo = function(location) {
|
||||
$location.path(location);
|
||||
$scope.$apply();
|
||||
};
|
||||
value = value.replace(/-/g, '');
|
||||
$scope.code0 = value.slice(0, 4);
|
||||
$scope.code1 = value.slice(4, 8);
|
||||
$scope.code2 = value.slice(8, 12);
|
||||
$scope.code3 = value.slice(12, 16);
|
||||
$scope.code4 = value.slice(16, 20);
|
||||
$scope.code5 = value.slice(20, 24);
|
||||
};
|
||||
|
||||
return LoginPrivateKeyDownloadCtrl;
|
||||
});
|
||||
$scope.verifyRecoveryToken = function(callback) {
|
||||
if (!$scope.recoveryToken) {
|
||||
$scope.onError(new Error('Please set the recovery token!'));
|
||||
return;
|
||||
}
|
||||
|
||||
keychain.getUserKeyPair(userId, function(err, keypair) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// remember for storage later
|
||||
$scope.cachedKeypair = keypair;
|
||||
|
||||
keychain.downloadPrivateKey({
|
||||
userId: userId,
|
||||
keyId: keypair.publicKey._id,
|
||||
recoveryToken: $scope.recoveryToken.toUpperCase()
|
||||
}, function(err, encryptedPrivateKey) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.encryptedPrivateKey = encryptedPrivateKey;
|
||||
callback();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.decryptAndStorePrivateKeyLocally = function() {
|
||||
var inputCode = '' + $scope.code0 + $scope.code1 + $scope.code2 + $scope.code3 + $scope.code4 + $scope.code5;
|
||||
|
||||
if (!inputCode) {
|
||||
$scope.onError(new Error('Please enter the keychain code!'));
|
||||
return;
|
||||
}
|
||||
|
||||
var options = $scope.encryptedPrivateKey;
|
||||
options.code = inputCode.toUpperCase();
|
||||
|
||||
keychain.decryptAndStorePrivateKeyLocally(options, function(err, privateKey) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// add private key to cached keypair object
|
||||
$scope.cachedKeypair.privateKey = privateKey;
|
||||
|
||||
// try empty passphrase
|
||||
emailDao.unlock({
|
||||
keypair: $scope.cachedKeypair,
|
||||
passphrase: undefined
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
// go to passphrase login screen
|
||||
$scope.goTo('/login-existing');
|
||||
return;
|
||||
}
|
||||
|
||||
// passphrase is corrent ... go to main app
|
||||
appController._auth.storeCredentials(function(err) {
|
||||
if (err) {
|
||||
return $scope.onError(err);
|
||||
}
|
||||
|
||||
$scope.goTo('/desktop');
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.goForward = function() {
|
||||
if ($scope.step === 1) {
|
||||
$scope.verifyRecoveryToken(function() {
|
||||
$scope.step++;
|
||||
$scope.$apply();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if ($scope.step === 2) {
|
||||
$scope.decryptAndStorePrivateKeyLocally();
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.goTo = function(location) {
|
||||
$location.path(location);
|
||||
$scope.$apply();
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = LoginPrivateKeyDownloadCtrl;
|
|
@ -1,120 +1,118 @@
|
|||
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) {
|
||||
if (!appCtrl._auth && !$routeParams.dev) {
|
||||
$location.path('/'); // init app
|
||||
return;
|
||||
var SetCredentialsCtrl = function($scope, $location, $routeParams) {
|
||||
if (!appCtrl._auth && !$routeParams.dev) {
|
||||
$location.path('/'); // init app
|
||||
return;
|
||||
}
|
||||
|
||||
var auth = appCtrl._auth;
|
||||
var doctor = appCtrl._doctor;
|
||||
|
||||
//
|
||||
// Presets and Settings
|
||||
//
|
||||
|
||||
var provider = $location.search().provider;
|
||||
$scope.hasProviderPreset = !!config[provider];
|
||||
$scope.useOAuth = !!auth.oauthToken;
|
||||
$scope.showDetails = (provider === 'custom');
|
||||
|
||||
if ($scope.useOAuth) {
|
||||
$scope.emailAddress = auth.emailAddress;
|
||||
}
|
||||
|
||||
if ($scope.hasProviderPreset) {
|
||||
// use non-editable presets
|
||||
|
||||
// SMTP config
|
||||
$scope.smtpHost = config[provider].smtp.host;
|
||||
$scope.smtpPort = config[provider].smtp.port;
|
||||
$scope.smtpCert = config[provider].smtp.ca;
|
||||
$scope.smtpPinned = config[provider].smtp.pinned;
|
||||
|
||||
// transport encryption method
|
||||
if (config[provider].smtp.secure && !config[provider].smtp.ignoreTLS) {
|
||||
$scope.smtpEncryption = ENCRYPTION_METHOD_TLS;
|
||||
} else if (!config[provider].smtp.secure && !config[provider].smtp.ignoreTLS) {
|
||||
$scope.smtpEncryption = ENCRYPTION_METHOD_STARTTLS;
|
||||
} else {
|
||||
$scope.smtpEncryption = ENCRYPTION_METHOD_NONE;
|
||||
}
|
||||
|
||||
var auth = appCtrl._auth;
|
||||
var doctor = appCtrl._doctor;
|
||||
// IMAP config
|
||||
$scope.imapHost = config[provider].imap.host;
|
||||
$scope.imapPort = config[provider].imap.port;
|
||||
$scope.imapCert = config[provider].imap.ca;
|
||||
$scope.imapPinned = config[provider].imap.pinned;
|
||||
|
||||
//
|
||||
// Presets and Settings
|
||||
//
|
||||
|
||||
var provider = $location.search().provider;
|
||||
$scope.hasProviderPreset = !!config[provider];
|
||||
$scope.useOAuth = !!auth.oauthToken;
|
||||
$scope.showDetails = (provider === 'custom');
|
||||
|
||||
if ($scope.useOAuth) {
|
||||
$scope.emailAddress = auth.emailAddress;
|
||||
// transport encryption method
|
||||
if (config[provider].imap.secure && !config[provider].imap.ignoreTLS) {
|
||||
$scope.imapEncryption = ENCRYPTION_METHOD_TLS;
|
||||
} else if (!config[provider].imap.secure && !config[provider].imap.ignoreTLS) {
|
||||
$scope.imapEncryption = ENCRYPTION_METHOD_STARTTLS;
|
||||
} else {
|
||||
$scope.imapEncryption = ENCRYPTION_METHOD_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
if ($scope.hasProviderPreset) {
|
||||
// use non-editable presets
|
||||
|
||||
// SMTP config
|
||||
$scope.smtpHost = config[provider].smtp.host;
|
||||
$scope.smtpPort = config[provider].smtp.port;
|
||||
$scope.smtpCert = config[provider].smtp.ca;
|
||||
$scope.smtpPinned = config[provider].smtp.pinned;
|
||||
$scope.test = function() {
|
||||
// parse the <select> dropdown lists
|
||||
var imapEncryption = parseInt($scope.imapEncryption, 10);
|
||||
var smtpEncryption = parseInt($scope.smtpEncryption, 10);
|
||||
|
||||
// transport encryption method
|
||||
if (config[provider].smtp.secure && !config[provider].smtp.ignoreTLS) {
|
||||
$scope.smtpEncryption = ENCRYPTION_METHOD_TLS;
|
||||
} else if (!config[provider].smtp.secure && !config[provider].smtp.ignoreTLS) {
|
||||
$scope.smtpEncryption = ENCRYPTION_METHOD_STARTTLS;
|
||||
} else {
|
||||
$scope.smtpEncryption = ENCRYPTION_METHOD_NONE;
|
||||
// build credentials object
|
||||
var credentials = {
|
||||
provider: provider,
|
||||
emailAddress: $scope.emailAddress,
|
||||
username: $scope.username || $scope.emailAddress,
|
||||
realname: $scope.realname,
|
||||
password: $scope.password,
|
||||
xoauth2: auth.oauthToken,
|
||||
imap: {
|
||||
host: $scope.imapHost.toLowerCase(),
|
||||
port: $scope.imapPort,
|
||||
secure: imapEncryption === ENCRYPTION_METHOD_TLS,
|
||||
ignoreTLS: imapEncryption === ENCRYPTION_METHOD_NONE,
|
||||
ca: $scope.imapCert,
|
||||
pinned: !!$scope.imapPinned
|
||||
},
|
||||
smtp: {
|
||||
host: $scope.smtpHost.toLowerCase(),
|
||||
port: $scope.smtpPort,
|
||||
secure: smtpEncryption === ENCRYPTION_METHOD_TLS,
|
||||
ignoreTLS: smtpEncryption === ENCRYPTION_METHOD_NONE,
|
||||
ca: $scope.smtpCert,
|
||||
pinned: !!$scope.smtpPinned
|
||||
}
|
||||
|
||||
// IMAP config
|
||||
$scope.imapHost = config[provider].imap.host;
|
||||
$scope.imapPort = config[provider].imap.port;
|
||||
$scope.imapCert = config[provider].imap.ca;
|
||||
$scope.imapPinned = config[provider].imap.pinned;
|
||||
|
||||
// transport encryption method
|
||||
if (config[provider].imap.secure && !config[provider].imap.ignoreTLS) {
|
||||
$scope.imapEncryption = ENCRYPTION_METHOD_TLS;
|
||||
} else if (!config[provider].imap.secure && !config[provider].imap.ignoreTLS) {
|
||||
$scope.imapEncryption = ENCRYPTION_METHOD_STARTTLS;
|
||||
} else {
|
||||
$scope.imapEncryption = ENCRYPTION_METHOD_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
$scope.test = function() {
|
||||
// parse the <select> dropdown lists
|
||||
var imapEncryption = parseInt($scope.imapEncryption, 10);
|
||||
var smtpEncryption = parseInt($scope.smtpEncryption, 10);
|
||||
|
||||
// build credentials object
|
||||
var credentials = {
|
||||
provider: provider,
|
||||
emailAddress: $scope.emailAddress,
|
||||
username: $scope.username || $scope.emailAddress,
|
||||
realname: $scope.realname,
|
||||
password: $scope.password,
|
||||
xoauth2: auth.oauthToken,
|
||||
imap: {
|
||||
host: $scope.imapHost.toLowerCase(),
|
||||
port: $scope.imapPort,
|
||||
secure: imapEncryption === ENCRYPTION_METHOD_TLS,
|
||||
ignoreTLS: imapEncryption === ENCRYPTION_METHOD_NONE,
|
||||
ca: $scope.imapCert,
|
||||
pinned: !!$scope.imapPinned
|
||||
},
|
||||
smtp: {
|
||||
host: $scope.smtpHost.toLowerCase(),
|
||||
port: $scope.smtpPort,
|
||||
secure: smtpEncryption === ENCRYPTION_METHOD_TLS,
|
||||
ignoreTLS: smtpEncryption === ENCRYPTION_METHOD_NONE,
|
||||
ca: $scope.smtpCert,
|
||||
pinned: !!$scope.smtpPinned
|
||||
}
|
||||
};
|
||||
|
||||
// use the credentials in the connection doctor
|
||||
doctor.configure(credentials);
|
||||
|
||||
// run connection doctor test suite
|
||||
$scope.busy = true;
|
||||
doctor.check(function(err) {
|
||||
if (err) {
|
||||
// display the error in the settings UI
|
||||
$scope.connectionError = err;
|
||||
} else {
|
||||
// persists the credentials and forwards to /login
|
||||
auth.setCredentials(credentials);
|
||||
$location.path('/login');
|
||||
}
|
||||
|
||||
$scope.busy = false;
|
||||
$scope.$apply();
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
return SetCredentialsCtrl;
|
||||
});
|
||||
// use the credentials in the connection doctor
|
||||
doctor.configure(credentials);
|
||||
|
||||
// run connection doctor test suite
|
||||
$scope.busy = true;
|
||||
doctor.check(function(err) {
|
||||
if (err) {
|
||||
// display the error in the settings UI
|
||||
$scope.connectionError = err;
|
||||
} else {
|
||||
// persists the credentials and forwards to /login
|
||||
auth.setCredentials(credentials);
|
||||
$location.path('/login');
|
||||
}
|
||||
|
||||
$scope.busy = false;
|
||||
$scope.$apply();
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = SetCredentialsCtrl;
|
|
@ -1,103 +1,101 @@
|
|||
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({
|
||||
onError: $scope.onError
|
||||
}, function(err) {
|
||||
// start main application controller
|
||||
appController.start({
|
||||
onError: $scope.onError
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// check for app update
|
||||
appController.checkForUpdate();
|
||||
|
||||
initializeUser();
|
||||
});
|
||||
|
||||
function initializeUser() {
|
||||
// get OAuth token from chrome
|
||||
appController._auth.getEmailAddress(function(err, info) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// check for app update
|
||||
appController.checkForUpdate();
|
||||
// check if account needs to be selected
|
||||
if (!info.emailAddress) {
|
||||
goTo('/add-account');
|
||||
return;
|
||||
}
|
||||
|
||||
initializeUser();
|
||||
});
|
||||
|
||||
function initializeUser() {
|
||||
// get OAuth token from chrome
|
||||
appController._auth.getEmailAddress(function(err, info) {
|
||||
// initiate controller by creating email dao
|
||||
appController.init({
|
||||
emailAddress: info.emailAddress,
|
||||
realname: info.realname
|
||||
}, function(err, availableKeys) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// check if account needs to be selected
|
||||
if (!info.emailAddress) {
|
||||
goTo('/add-account');
|
||||
redirect(availableKeys);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function redirect(availableKeys) {
|
||||
// redirect if needed
|
||||
if (typeof availableKeys === 'undefined') {
|
||||
// no public key available, start onboarding process
|
||||
goTo('/login-initial');
|
||||
|
||||
} else if (availableKeys && !availableKeys.privateKey) {
|
||||
// check if private key is synced
|
||||
appController._keychain.requestPrivateKeyDownload({
|
||||
userId: availableKeys.publicKey.userId,
|
||||
keyId: availableKeys.publicKey._id,
|
||||
}, function(err, privateKeySynced) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// initiate controller by creating email dao
|
||||
appController.init({
|
||||
emailAddress: info.emailAddress,
|
||||
realname: info.realname
|
||||
}, function(err, availableKeys) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
if (privateKeySynced) {
|
||||
// private key is synced, proceed to download
|
||||
goTo('/login-privatekey-download');
|
||||
return;
|
||||
}
|
||||
|
||||
redirect(availableKeys);
|
||||
});
|
||||
// no private key, import key file
|
||||
goTo('/login-new-device');
|
||||
});
|
||||
|
||||
} else {
|
||||
// public and private key available, try empty passphrase
|
||||
appController._emailDao.unlock({
|
||||
keypair: availableKeys,
|
||||
passphrase: undefined
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
goTo('/login-existing');
|
||||
return;
|
||||
}
|
||||
|
||||
goTo('/desktop');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function redirect(availableKeys) {
|
||||
// redirect if needed
|
||||
if (typeof availableKeys === 'undefined') {
|
||||
// no public key available, start onboarding process
|
||||
goTo('/login-initial');
|
||||
function goTo(location) {
|
||||
$scope.$apply(function() {
|
||||
$location.path(location);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
} else if (availableKeys && !availableKeys.privateKey) {
|
||||
// check if private key is synced
|
||||
appController._keychain.requestPrivateKeyDownload({
|
||||
userId: availableKeys.publicKey.userId,
|
||||
keyId: availableKeys.publicKey._id,
|
||||
}, function(err, privateKeySynced) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (privateKeySynced) {
|
||||
// private key is synced, proceed to download
|
||||
goTo('/login-privatekey-download');
|
||||
return;
|
||||
}
|
||||
|
||||
// no private key, import key file
|
||||
goTo('/login-new-device');
|
||||
});
|
||||
|
||||
} else {
|
||||
// public and private key available, try empty passphrase
|
||||
appController._emailDao.unlock({
|
||||
keypair: availableKeys,
|
||||
passphrase: undefined
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
goTo('/login-existing');
|
||||
return;
|
||||
}
|
||||
|
||||
goTo('/desktop');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function goTo(location) {
|
||||
$scope.$apply(function() {
|
||||
$location.path(location);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return LoginCtrl;
|
||||
});
|
||||
module.exports = LoginCtrl;
|
File diff suppressed because it is too large
Load Diff
|
@ -1,210 +1,206 @@
|
|||
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'),
|
||||
emailDao, outboxBo;
|
||||
var appController = require('../app-controller'),
|
||||
config = require('../app-config').config,
|
||||
notification = require('../util/notification'),
|
||||
backBtnHandler = require('../util/backbutton-handler'),
|
||||
emailDao, outboxBo;
|
||||
|
||||
//
|
||||
// Controller
|
||||
//
|
||||
|
||||
var NavigationCtrl = function($scope, $routeParams, $location) {
|
||||
if (!appController._emailDao && !$routeParams.dev) {
|
||||
$location.path('/'); // init app
|
||||
return;
|
||||
}
|
||||
|
||||
emailDao = appController._emailDao;
|
||||
outboxBo = appController._outboxBo;
|
||||
|
||||
//
|
||||
// Controller
|
||||
// scope functions
|
||||
//
|
||||
|
||||
var NavigationCtrl = function($scope, $routeParams, $location) {
|
||||
if (!appController._emailDao && !$routeParams.dev) {
|
||||
$location.path('/'); // init app
|
||||
return;
|
||||
}
|
||||
|
||||
emailDao = appController._emailDao;
|
||||
outboxBo = appController._outboxBo;
|
||||
|
||||
//
|
||||
// scope functions
|
||||
//
|
||||
|
||||
$scope.state.nav = {
|
||||
open: false,
|
||||
toggle: function(to) {
|
||||
this.open = to;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.openFolder = function(folder) {
|
||||
$scope.state.nav.currentFolder = folder;
|
||||
$scope.state.nav.toggle(false);
|
||||
};
|
||||
|
||||
$scope.onOutboxUpdate = function(err, count) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// update the outbox mail count
|
||||
var outbox = _.findWhere($scope.account.folders, {
|
||||
type: config.outboxMailboxType
|
||||
});
|
||||
outbox.count = count;
|
||||
$scope.$apply();
|
||||
|
||||
emailDao.refreshFolder({
|
||||
folder: outbox
|
||||
}, $scope.onError);
|
||||
};
|
||||
|
||||
$scope.logout = function() {
|
||||
$scope.onError({
|
||||
title: 'Logout',
|
||||
message: 'Are you sure you want to logout?',
|
||||
callback: function(confirm) {
|
||||
if (confirm) {
|
||||
appController.logout();
|
||||
}
|
||||
},
|
||||
sync: true
|
||||
});
|
||||
};
|
||||
|
||||
//
|
||||
// Start
|
||||
//
|
||||
|
||||
// handle back button
|
||||
backBtnHandler.start();
|
||||
// init folders
|
||||
initializeFolders();
|
||||
|
||||
// select inbox as the current folder on init
|
||||
if ($scope.account.folders && $scope.account.folders.length > 0) {
|
||||
$scope.openFolder($scope.account.folders[0]);
|
||||
}
|
||||
// connect imap/smtp clients on first startup
|
||||
appController.onConnect(function(err) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// select inbox if not yet selected
|
||||
if (!$scope.state.nav.currentFolder) {
|
||||
$scope.openFolder($scope.account.folders[0]);
|
||||
$scope.$apply();
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
// helper functions
|
||||
//
|
||||
|
||||
function initializeFolders() {
|
||||
// create dummy folder in dev environment only
|
||||
if ($routeParams.dev) {
|
||||
createDummyFolders();
|
||||
return;
|
||||
}
|
||||
|
||||
// get pointer to account/folder/message tree on root scope
|
||||
$scope.$root.account = emailDao._account;
|
||||
|
||||
// set notificatio handler for sent messages
|
||||
outboxBo.onSent = sentNotification;
|
||||
// start checking outbox periodically
|
||||
outboxBo.startChecking($scope.onOutboxUpdate);
|
||||
|
||||
}
|
||||
|
||||
function sentNotification(email) {
|
||||
notification.create({
|
||||
title: 'Message sent',
|
||||
message: email.subject,
|
||||
timeout: 2000
|
||||
}, function() {});
|
||||
}
|
||||
|
||||
|
||||
// attach dummy folders for development
|
||||
function createDummyFolders() {
|
||||
$scope.$root.account = {};
|
||||
$scope.account.folders = [{
|
||||
type: 'Inbox',
|
||||
count: 2,
|
||||
path: 'INBOX'
|
||||
}, {
|
||||
type: 'Sent',
|
||||
count: 0,
|
||||
path: 'SENT'
|
||||
}, {
|
||||
type: config.outboxMailboxType,
|
||||
count: 0,
|
||||
path: config.outboxMailboxPath
|
||||
}, {
|
||||
type: 'Drafts',
|
||||
count: 0,
|
||||
path: 'DRAFTS'
|
||||
}, {
|
||||
type: 'Trash',
|
||||
count: 0,
|
||||
path: 'TRASH'
|
||||
}];
|
||||
$scope.state.nav = {
|
||||
open: false,
|
||||
toggle: function(to) {
|
||||
this.open = to;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.openFolder = function(folder) {
|
||||
$scope.state.nav.currentFolder = folder;
|
||||
$scope.state.nav.toggle(false);
|
||||
};
|
||||
|
||||
$scope.onOutboxUpdate = function(err, count) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// update the outbox mail count
|
||||
var outbox = _.findWhere($scope.account.folders, {
|
||||
type: config.outboxMailboxType
|
||||
});
|
||||
outbox.count = count;
|
||||
$scope.$apply();
|
||||
|
||||
emailDao.refreshFolder({
|
||||
folder: outbox
|
||||
}, $scope.onError);
|
||||
};
|
||||
|
||||
$scope.logout = function() {
|
||||
$scope.onError({
|
||||
title: 'Logout',
|
||||
message: 'Are you sure you want to logout?',
|
||||
callback: function(confirm) {
|
||||
if (confirm) {
|
||||
appController.logout();
|
||||
}
|
||||
},
|
||||
sync: true
|
||||
});
|
||||
};
|
||||
|
||||
//
|
||||
// Directives
|
||||
// Start
|
||||
//
|
||||
|
||||
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
|
||||
if (!scope.state) {
|
||||
return;
|
||||
}
|
||||
// handle back button
|
||||
backBtnHandler.start();
|
||||
// init folders
|
||||
initializeFolders();
|
||||
|
||||
var modifier = e.ctrlKey || e.metaKey;
|
||||
// select inbox as the current folder on init
|
||||
if ($scope.account.folders && $scope.account.folders.length > 0) {
|
||||
$scope.openFolder($scope.account.folders[0]);
|
||||
}
|
||||
// connect imap/smtp clients on first startup
|
||||
appController.onConnect(function(err) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (modifier && e.keyCode === 78 && scope.state.lightbox !== 'write') {
|
||||
// n -> new mail
|
||||
e.preventDefault();
|
||||
scope.state.writer.write();
|
||||
scope.$apply();
|
||||
|
||||
} else if (modifier && e.keyCode === 70 && scope.state.lightbox !== 'write') {
|
||||
// f -> find
|
||||
e.preventDefault();
|
||||
scope.state.mailList.searching = true;
|
||||
$timeout(function() {
|
||||
scope.state.mailList.searching = false;
|
||||
}, 200);
|
||||
scope.$apply();
|
||||
|
||||
} else if (modifier && e.keyCode === 82 && scope.state.lightbox !== 'write' && scope.state.mailList.selected) {
|
||||
// r -> reply
|
||||
e.preventDefault();
|
||||
scope.state.writer.write(scope.state.mailList.selected);
|
||||
scope.$apply();
|
||||
|
||||
} else if (e.keyCode === 27 && scope.state.lightbox !== undefined) {
|
||||
// escape -> close current lightbox
|
||||
e.preventDefault();
|
||||
scope.state.lightbox = undefined;
|
||||
scope.$apply();
|
||||
|
||||
} else if (e.keyCode === 27 && scope.state.nav.open) {
|
||||
// escape -> close nav view
|
||||
e.preventDefault();
|
||||
scope.state.nav.toggle(false);
|
||||
scope.$apply();
|
||||
}
|
||||
|
||||
});
|
||||
};
|
||||
// select inbox if not yet selected
|
||||
if (!$scope.state.nav.currentFolder) {
|
||||
$scope.openFolder($scope.account.folders[0]);
|
||||
$scope.$apply();
|
||||
}
|
||||
});
|
||||
|
||||
return NavigationCtrl;
|
||||
});
|
||||
//
|
||||
// helper functions
|
||||
//
|
||||
|
||||
function initializeFolders() {
|
||||
// create dummy folder in dev environment only
|
||||
if ($routeParams.dev) {
|
||||
createDummyFolders();
|
||||
return;
|
||||
}
|
||||
|
||||
// get pointer to account/folder/message tree on root scope
|
||||
$scope.$root.account = emailDao._account;
|
||||
|
||||
// set notificatio handler for sent messages
|
||||
outboxBo.onSent = sentNotification;
|
||||
// start checking outbox periodically
|
||||
outboxBo.startChecking($scope.onOutboxUpdate);
|
||||
|
||||
}
|
||||
|
||||
function sentNotification(email) {
|
||||
notification.create({
|
||||
title: 'Message sent',
|
||||
message: email.subject,
|
||||
timeout: 2000
|
||||
}, function() {});
|
||||
}
|
||||
|
||||
|
||||
// attach dummy folders for development
|
||||
function createDummyFolders() {
|
||||
$scope.$root.account = {};
|
||||
$scope.account.folders = [{
|
||||
type: 'Inbox',
|
||||
count: 2,
|
||||
path: 'INBOX'
|
||||
}, {
|
||||
type: 'Sent',
|
||||
count: 0,
|
||||
path: 'SENT'
|
||||
}, {
|
||||
type: config.outboxMailboxType,
|
||||
count: 0,
|
||||
path: config.outboxMailboxPath
|
||||
}, {
|
||||
type: 'Drafts',
|
||||
count: 0,
|
||||
path: 'DRAFTS'
|
||||
}, {
|
||||
type: 'Trash',
|
||||
count: 0,
|
||||
path: 'TRASH'
|
||||
}];
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Directives
|
||||
//
|
||||
|
||||
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
|
||||
if (!scope.state) {
|
||||
return;
|
||||
}
|
||||
|
||||
var modifier = e.ctrlKey || e.metaKey;
|
||||
|
||||
if (modifier && e.keyCode === 78 && scope.state.lightbox !== 'write') {
|
||||
// n -> new mail
|
||||
e.preventDefault();
|
||||
scope.state.writer.write();
|
||||
scope.$apply();
|
||||
|
||||
} else if (modifier && e.keyCode === 70 && scope.state.lightbox !== 'write') {
|
||||
// f -> find
|
||||
e.preventDefault();
|
||||
scope.state.mailList.searching = true;
|
||||
$timeout(function() {
|
||||
scope.state.mailList.searching = false;
|
||||
}, 200);
|
||||
scope.$apply();
|
||||
|
||||
} else if (modifier && e.keyCode === 82 && scope.state.lightbox !== 'write' && scope.state.mailList.selected) {
|
||||
// r -> reply
|
||||
e.preventDefault();
|
||||
scope.state.writer.write(scope.state.mailList.selected);
|
||||
scope.$apply();
|
||||
|
||||
} else if (e.keyCode === 27 && scope.state.lightbox !== undefined) {
|
||||
// escape -> close current lightbox
|
||||
e.preventDefault();
|
||||
scope.state.lightbox = undefined;
|
||||
scope.$apply();
|
||||
|
||||
} else if (e.keyCode === 27 && scope.state.nav.open) {
|
||||
// escape -> close nav view
|
||||
e.preventDefault();
|
||||
scope.state.nav.toggle(false);
|
||||
scope.$apply();
|
||||
}
|
||||
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
module.exports = NavigationCtrl;
|
|
@ -1,47 +1,43 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
'use strict';
|
||||
|
||||
var angular = require('angular');
|
||||
//
|
||||
// Controller
|
||||
//
|
||||
|
||||
//
|
||||
// Controller
|
||||
//
|
||||
var PopoverCtrl = function($scope) {
|
||||
$scope.state.popover = {};
|
||||
};
|
||||
|
||||
var PopoverCtrl = function($scope) {
|
||||
$scope.state.popover = {};
|
||||
//
|
||||
// Directives
|
||||
//
|
||||
|
||||
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));
|
||||
|
||||
elm.on('mouseover', function() {
|
||||
// set popover position
|
||||
var top = elm[0].offsetTop;
|
||||
var left = elm[0].offsetLeft;
|
||||
var width = elm[0].offsetWidth;
|
||||
var height = elm[0].offsetHeight;
|
||||
|
||||
popover[0].style.transition = 'opacity 0.3s linear';
|
||||
popover[0].style.top = (top + height / 2 - popover[0].offsetHeight / 2) + 'px';
|
||||
popover[0].style.left = (left + width) + 'px';
|
||||
popover[0].style.opacity = '1';
|
||||
});
|
||||
|
||||
elm.on('mouseout', function() {
|
||||
popover[0].style.transition = 'opacity 0.3s linear, top 0.3s step-end, left 0.3s step-end';
|
||||
popover[0].style.opacity = '0';
|
||||
popover[0].style.top = '-9999px';
|
||||
popover[0].style.left = '-9999px';
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
//
|
||||
// Directives
|
||||
//
|
||||
|
||||
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));
|
||||
|
||||
elm.on('mouseover', function() {
|
||||
// set popover position
|
||||
var top = elm[0].offsetTop;
|
||||
var left = elm[0].offsetLeft;
|
||||
var width = elm[0].offsetWidth;
|
||||
var height = elm[0].offsetHeight;
|
||||
|
||||
popover[0].style.transition = 'opacity 0.3s linear';
|
||||
popover[0].style.top = (top + height / 2 - popover[0].offsetHeight / 2) + 'px';
|
||||
popover[0].style.left = (left + width) + 'px';
|
||||
popover[0].style.opacity = '1';
|
||||
});
|
||||
|
||||
elm.on('mouseout', function() {
|
||||
popover[0].style.transition = 'opacity 0.3s linear, top 0.3s step-end, left 0.3s step-end';
|
||||
popover[0].style.opacity = '0';
|
||||
popover[0].style.top = '-9999px';
|
||||
popover[0].style.left = '-9999px';
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
return PopoverCtrl;
|
||||
});
|
||||
module.exports = PopoverCtrl;
|
|
@ -1,204 +1,201 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
'use strict';
|
||||
|
||||
var angular = require('angular'),
|
||||
appController = require('js/app-controller'),
|
||||
util = require('js/crypto/util'),
|
||||
keychain, pgp;
|
||||
var appController = require('../app-controller'),
|
||||
util = require('crypto-lib').util,
|
||||
keychain, pgp;
|
||||
|
||||
var PrivateKeyUploadCtrl = function($scope) {
|
||||
keychain = appController._keychain;
|
||||
pgp = keychain._pgp;
|
||||
var PrivateKeyUploadCtrl = function($scope) {
|
||||
keychain = appController._keychain;
|
||||
pgp = keychain._pgp;
|
||||
|
||||
$scope.state.privateKeyUpload = {
|
||||
toggle: function(to) {
|
||||
// open lightbox
|
||||
$scope.state.lightbox = (to) ? 'privatekey-upload' : undefined;
|
||||
if (!to) {
|
||||
return;
|
||||
}
|
||||
|
||||
// show syncing status
|
||||
$scope.step = 4;
|
||||
// check if key is already synced
|
||||
$scope.checkServerForKey(function(privateKeySynced) {
|
||||
if (privateKeySynced) {
|
||||
// close lightbox
|
||||
$scope.state.lightbox = undefined;
|
||||
// show message
|
||||
$scope.onError({
|
||||
title: 'Info',
|
||||
message: 'Your PGP key has already been synced.'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// show sync ui if key is not synced
|
||||
$scope.displayUploadUi();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$scope.handlePaste = function(event) {
|
||||
var evt = event;
|
||||
if (evt.originalEvent) {
|
||||
evt = evt.originalEvent;
|
||||
}
|
||||
|
||||
var value = evt.clipboardData.getData('text/plain');
|
||||
if (!value) {
|
||||
$scope.state.privateKeyUpload = {
|
||||
toggle: function(to) {
|
||||
// open lightbox
|
||||
$scope.state.lightbox = (to) ? 'privatekey-upload' : undefined;
|
||||
if (!to) {
|
||||
return;
|
||||
}
|
||||
|
||||
value = value.replace(/-/g, '');
|
||||
$scope.code0 = value.slice(0, 4);
|
||||
$scope.code1 = value.slice(4, 8);
|
||||
$scope.code2 = value.slice(8, 12);
|
||||
$scope.code3 = value.slice(12, 16);
|
||||
$scope.code4 = value.slice(16, 20);
|
||||
$scope.code5 = value.slice(20, 24);
|
||||
};
|
||||
|
||||
$scope.checkServerForKey = function(callback) {
|
||||
var keyParams = pgp.getKeyParams();
|
||||
keychain.hasPrivateKey({
|
||||
userId: keyParams.userId,
|
||||
keyId: keyParams._id
|
||||
}, function(err, privateKeySynced) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// show syncing status
|
||||
$scope.step = 4;
|
||||
// check if key is already synced
|
||||
$scope.checkServerForKey(function(privateKeySynced) {
|
||||
if (privateKeySynced) {
|
||||
callback(privateKeySynced);
|
||||
// close lightbox
|
||||
$scope.state.lightbox = undefined;
|
||||
// show message
|
||||
$scope.onError({
|
||||
title: 'Info',
|
||||
message: 'Your PGP key has already been synced.'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
callback();
|
||||
// show sync ui if key is not synced
|
||||
$scope.displayUploadUi();
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
$scope.displayUploadUi = function() {
|
||||
// go to step 1
|
||||
$scope.step = 1;
|
||||
// generate new code for the user
|
||||
$scope.code = util.randomString(24);
|
||||
$scope.displayedCode = $scope.code.slice(0, 4) + '-' + $scope.code.slice(4, 8) + '-' + $scope.code.slice(8, 12) + '-' + $scope.code.slice(12, 16) + '-' + $scope.code.slice(16, 20) + '-' + $scope.code.slice(20, 24);
|
||||
$scope.handlePaste = function(event) {
|
||||
var evt = event;
|
||||
if (evt.originalEvent) {
|
||||
evt = evt.originalEvent;
|
||||
}
|
||||
|
||||
// clear input fields of any previous artifacts
|
||||
$scope.code0 = $scope.code1 = $scope.code2 = $scope.code3 = $scope.code4 = $scope.code5 = '';
|
||||
};
|
||||
var value = evt.clipboardData.getData('text/plain');
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.verifyCode = function() {
|
||||
var inputCode = '' + $scope.code0 + $scope.code1 + $scope.code2 + $scope.code3 + $scope.code4 + $scope.code5;
|
||||
value = value.replace(/-/g, '');
|
||||
$scope.code0 = value.slice(0, 4);
|
||||
$scope.code1 = value.slice(4, 8);
|
||||
$scope.code2 = value.slice(8, 12);
|
||||
$scope.code3 = value.slice(12, 16);
|
||||
$scope.code4 = value.slice(16, 20);
|
||||
$scope.code5 = value.slice(20, 24);
|
||||
};
|
||||
|
||||
if (inputCode.toUpperCase() !== $scope.code) {
|
||||
var err = new Error('The code does not match. Please go back and check the generated code.');
|
||||
err.sync = true;
|
||||
$scope.checkServerForKey = function(callback) {
|
||||
var keyParams = pgp.getKeyParams();
|
||||
keychain.hasPrivateKey({
|
||||
userId: keyParams.userId,
|
||||
keyId: keyParams._id
|
||||
}, function(err, privateKeySynced) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
if (privateKeySynced) {
|
||||
callback(privateKeySynced);
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.setDeviceName = function(callback) {
|
||||
keychain.setDeviceName($scope.deviceName, callback);
|
||||
};
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.encryptAndUploadKey = function(callback) {
|
||||
var userId = appController._emailDao._account.emailAddress;
|
||||
var code = $scope.code;
|
||||
$scope.displayUploadUi = function() {
|
||||
// go to step 1
|
||||
$scope.step = 1;
|
||||
// generate new code for the user
|
||||
$scope.code = util.randomString(24);
|
||||
$scope.displayedCode = $scope.code.slice(0, 4) + '-' + $scope.code.slice(4, 8) + '-' + $scope.code.slice(8, 12) + '-' + $scope.code.slice(12, 16) + '-' + $scope.code.slice(16, 20) + '-' + $scope.code.slice(20, 24);
|
||||
|
||||
// register device to keychain service
|
||||
keychain.registerDevice({
|
||||
userId: userId
|
||||
}, function(err) {
|
||||
// clear input fields of any previous artifacts
|
||||
$scope.code0 = $scope.code1 = $scope.code2 = $scope.code3 = $scope.code4 = $scope.code5 = '';
|
||||
};
|
||||
|
||||
$scope.verifyCode = function() {
|
||||
var inputCode = '' + $scope.code0 + $scope.code1 + $scope.code2 + $scope.code3 + $scope.code4 + $scope.code5;
|
||||
|
||||
if (inputCode.toUpperCase() !== $scope.code) {
|
||||
var err = new Error('The code does not match. Please go back and check the generated code.');
|
||||
err.sync = true;
|
||||
$scope.onError(err);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
$scope.setDeviceName = function(callback) {
|
||||
keychain.setDeviceName($scope.deviceName, callback);
|
||||
};
|
||||
|
||||
$scope.encryptAndUploadKey = function(callback) {
|
||||
var userId = appController._emailDao._account.emailAddress;
|
||||
var code = $scope.code;
|
||||
|
||||
// register device to keychain service
|
||||
keychain.registerDevice({
|
||||
userId: userId
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// encrypt private PGP key using code and upload
|
||||
keychain.uploadPrivateKey({
|
||||
userId: userId,
|
||||
code: code
|
||||
}, callback);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.goBack = function() {
|
||||
if ($scope.step > 1) {
|
||||
$scope.step--;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.goForward = function() {
|
||||
if ($scope.step < 2) {
|
||||
$scope.step++;
|
||||
return;
|
||||
}
|
||||
|
||||
if ($scope.step === 2 && $scope.verifyCode()) {
|
||||
$scope.step++;
|
||||
return;
|
||||
}
|
||||
|
||||
if ($scope.step === 3) {
|
||||
// set device name to local storage
|
||||
$scope.setDeviceName(function(err) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// encrypt private PGP key using code and upload
|
||||
keychain.uploadPrivateKey({
|
||||
userId: userId,
|
||||
code: code
|
||||
}, callback);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.goBack = function() {
|
||||
if ($scope.step > 1) {
|
||||
$scope.step--;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.goForward = function() {
|
||||
if ($scope.step < 2) {
|
||||
// show spinner
|
||||
$scope.step++;
|
||||
return;
|
||||
}
|
||||
$scope.$apply();
|
||||
|
||||
if ($scope.step === 2 && $scope.verifyCode()) {
|
||||
$scope.step++;
|
||||
return;
|
||||
}
|
||||
|
||||
if ($scope.step === 3) {
|
||||
// set device name to local storage
|
||||
$scope.setDeviceName(function(err) {
|
||||
// init key sync
|
||||
$scope.encryptAndUploadKey(function(err) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// show spinner
|
||||
$scope.step++;
|
||||
$scope.$apply();
|
||||
|
||||
// init key sync
|
||||
$scope.encryptAndUploadKey(function(err) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// close sync dialog
|
||||
$scope.state.privateKeyUpload.toggle(false);
|
||||
// show success message
|
||||
$scope.onError({
|
||||
title: 'Success',
|
||||
message: 'Whiteout Keychain setup successful!'
|
||||
});
|
||||
// close sync dialog
|
||||
$scope.state.privateKeyUpload.toggle(false);
|
||||
// show success message
|
||||
$scope.onError({
|
||||
title: 'Success',
|
||||
message: 'Whiteout Keychain setup successful!'
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Directives
|
||||
//
|
||||
};
|
||||
|
||||
var ngModule = angular.module('privatekey-upload', []);
|
||||
ngModule.directive('focusNext', function() {
|
||||
return {
|
||||
link: function(scope, element, attr) {
|
||||
var maxLen = element[0].maxLength;
|
||||
//
|
||||
// Directives
|
||||
//
|
||||
|
||||
scope.$watch(attr.ngModel, function(val) {
|
||||
if (val && val.length === maxLen) {
|
||||
var nextinput = element.next('input');
|
||||
if (nextinput.length) {
|
||||
nextinput[0].focus();
|
||||
}
|
||||
var ngModule = angular.module('privatekey-upload', []);
|
||||
ngModule.directive('focusNext', function() {
|
||||
return {
|
||||
link: function(scope, element, attr) {
|
||||
var maxLen = element[0].maxLength;
|
||||
|
||||
scope.$watch(attr.ngModel, function(val) {
|
||||
if (val && val.length === maxLen) {
|
||||
var nextinput = element.next('input');
|
||||
if (nextinput.length) {
|
||||
nextinput[0].focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
return PrivateKeyUploadCtrl;
|
||||
});
|
||||
module.exports = PrivateKeyUploadCtrl;
|
|
@ -1,218 +1,215 @@
|
|||
(function() {
|
||||
'use strict';
|
||||
'use strict';
|
||||
|
||||
// set listener for event from main window
|
||||
window.onmessage = function(e) {
|
||||
var html = '';
|
||||
// set listener for event from main window
|
||||
window.onmessage = function(e) {
|
||||
var html = '';
|
||||
|
||||
if (e.data.html) {
|
||||
// display html mail body
|
||||
html = '<div class="scale-body">' + e.data.html + '</div>';
|
||||
} else if (e.data.text) {
|
||||
// diplay text mail body by with colored conversation nodes
|
||||
html = renderNodes(parseConversation(e.data.text));
|
||||
}
|
||||
if (e.data.html) {
|
||||
// display html mail body
|
||||
html = '<div class="scale-body">' + e.data.html + '</div>';
|
||||
} else if (e.data.text) {
|
||||
// diplay text mail body by with colored conversation nodes
|
||||
html = renderNodes(parseConversation(e.data.text));
|
||||
}
|
||||
|
||||
// sanitize HTML content: https://github.com/cure53/DOMPurify
|
||||
html = window.DOMPurify.sanitize(html);
|
||||
// make links open in a new window
|
||||
html = html.replace(/<a /g, '<a target="_blank" ');
|
||||
// sanitize HTML content: https://github.com/cure53/DOMPurify
|
||||
html = window.DOMPurify.sanitize(html);
|
||||
// make links open in a new window
|
||||
html = html.replace(/<a /g, '<a target="_blank" ');
|
||||
|
||||
// remove sources where necessary
|
||||
if (e.data.removeImages) {
|
||||
html = html.replace(/(<img[^>]+\b)src=['"][^'">]+['"]/ig, function(match, prefix) {
|
||||
return prefix;
|
||||
// remove sources where necessary
|
||||
if (e.data.removeImages) {
|
||||
html = html.replace(/(<img[^>]+\b)src=['"][^'">]+['"]/ig, function(match, prefix) {
|
||||
return prefix;
|
||||
});
|
||||
}
|
||||
|
||||
document.body.innerHTML = html;
|
||||
|
||||
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) {
|
||||
var nodes;
|
||||
|
||||
function parseLines(body) {
|
||||
var lines = [];
|
||||
body.split('\n').forEach(parseLine);
|
||||
|
||||
function parseLine(line) {
|
||||
var regex = /^>*/;
|
||||
var result = regex.exec(line);
|
||||
|
||||
lines.push({
|
||||
text: line.replace(regex, '').trim(),
|
||||
level: (result && result.length > 0) ? result[0].length : 0
|
||||
});
|
||||
}
|
||||
|
||||
document.body.innerHTML = html;
|
||||
return lines;
|
||||
}
|
||||
|
||||
scaleToFit();
|
||||
function buildTextNodes(lines) {
|
||||
var i, j, root, currentLevel, currentNode, levelDelta;
|
||||
|
||||
root = new Node();
|
||||
currentLevel = 0;
|
||||
currentNode = root;
|
||||
|
||||
// iterate over text lines
|
||||
for (i = 0; i < lines.length; i++) {
|
||||
levelDelta = lines[i].level - currentLevel;
|
||||
|
||||
if (levelDelta === 0) {
|
||||
// we are at the desired node ... no traversal required
|
||||
} else if (levelDelta > 0) {
|
||||
// traverse to child node(s)
|
||||
for (j = 0; j < levelDelta; j++) {
|
||||
var newChild = new Node(currentNode);
|
||||
// create new child node
|
||||
currentNode.children.push(newChild);
|
||||
// go to last child node
|
||||
currentNode = newChild;
|
||||
// increase current level by one
|
||||
currentLevel++;
|
||||
}
|
||||
} else {
|
||||
// traverse to parent(s)
|
||||
for (j = levelDelta; j < 0; j++) {
|
||||
currentNode = currentNode.parent;
|
||||
currentLevel--;
|
||||
}
|
||||
}
|
||||
|
||||
// add text to the current node
|
||||
currentNode.addLine(lines[i].text);
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
function Node(parent) {
|
||||
this.parent = parent;
|
||||
this.children = [];
|
||||
}
|
||||
Node.prototype.addLine = function(lineText) {
|
||||
var c, l;
|
||||
|
||||
c = this.children;
|
||||
l = c.length;
|
||||
|
||||
// append text node to children if last child is not a text node
|
||||
if (l < 1 || typeof c[l - 1] !== 'string') {
|
||||
c[l] = '';
|
||||
l = c.length;
|
||||
}
|
||||
|
||||
// append line to last child (add newline between lines)
|
||||
c[l - 1] += lineText + '\n';
|
||||
};
|
||||
|
||||
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) {
|
||||
var nodes;
|
||||
|
||||
function parseLines(body) {
|
||||
var lines = [];
|
||||
body.split('\n').forEach(parseLine);
|
||||
|
||||
function parseLine(line) {
|
||||
var regex = /^>*/;
|
||||
var result = regex.exec(line);
|
||||
|
||||
lines.push({
|
||||
text: line.replace(regex, '').trim(),
|
||||
level: (result && result.length > 0) ? result[0].length : 0
|
||||
});
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
function buildTextNodes(lines) {
|
||||
var i, j, root, currentLevel, currentNode, levelDelta;
|
||||
|
||||
root = new Node();
|
||||
currentLevel = 0;
|
||||
currentNode = root;
|
||||
|
||||
// iterate over text lines
|
||||
for (i = 0; i < lines.length; i++) {
|
||||
levelDelta = lines[i].level - currentLevel;
|
||||
|
||||
if (levelDelta === 0) {
|
||||
// we are at the desired node ... no traversal required
|
||||
} else if (levelDelta > 0) {
|
||||
// traverse to child node(s)
|
||||
for (j = 0; j < levelDelta; j++) {
|
||||
var newChild = new Node(currentNode);
|
||||
// create new child node
|
||||
currentNode.children.push(newChild);
|
||||
// go to last child node
|
||||
currentNode = newChild;
|
||||
// increase current level by one
|
||||
currentLevel++;
|
||||
}
|
||||
} else {
|
||||
// traverse to parent(s)
|
||||
for (j = levelDelta; j < 0; j++) {
|
||||
currentNode = currentNode.parent;
|
||||
currentLevel--;
|
||||
}
|
||||
}
|
||||
|
||||
// add text to the current node
|
||||
currentNode.addLine(lines[i].text);
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
function Node(parent) {
|
||||
this.parent = parent;
|
||||
this.children = [];
|
||||
}
|
||||
Node.prototype.addLine = function(lineText) {
|
||||
var c, l;
|
||||
|
||||
c = this.children;
|
||||
l = c.length;
|
||||
|
||||
// append text node to children if last child is not a text node
|
||||
if (l < 1 || typeof c[l - 1] !== 'string') {
|
||||
c[l] = '';
|
||||
l = c.length;
|
||||
}
|
||||
|
||||
// append line to last child (add newline between lines)
|
||||
c[l - 1] += lineText + '\n';
|
||||
};
|
||||
|
||||
function removeParentReference(node) {
|
||||
if (!node.children) {
|
||||
// this is a text leaf ... terminate recursion
|
||||
return;
|
||||
}
|
||||
|
||||
// remove parent node to prevent infinite loop in JSON stringify
|
||||
delete node.parent;
|
||||
|
||||
for (var i = 0; i < node.children.length; i++) {
|
||||
if (typeof node.children[i] === 'string') {
|
||||
// remove trailing newline in string
|
||||
node.children[i] = node.children[i].replace(/\n$/, '');
|
||||
} else {
|
||||
// I used recursion ...
|
||||
removeParentReference(node.children[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nodes = buildTextNodes(parseLines(textBody.replace(/ >/g, '>')));
|
||||
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) {
|
||||
var body = '';
|
||||
|
||||
function render(node) {
|
||||
var i, html = '';
|
||||
if (!node.children) {
|
||||
// this is a text leaf
|
||||
var lines = node.split('\n');
|
||||
for (i = 0; i < lines.length; i++) {
|
||||
// replace all urls with anchors
|
||||
lines[i] = lines[i].replace(/(https?:\/\/[^\s]+)/g, createArchor);
|
||||
// wrap line into an element for easier styling
|
||||
html += '<div class="line';
|
||||
if (isLineEmpty(lines[i])) {
|
||||
html += ' empty-line';
|
||||
}
|
||||
html += '"><span>' + lines[i] + '</span><br></div>';
|
||||
}
|
||||
return html;
|
||||
}
|
||||
|
||||
for (i = 0; i < node.children.length; i++) {
|
||||
html += '<div class="prev-message">' + render(node.children[i]) + '</div>';
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
function createArchor(url) {
|
||||
return '<a href="' + url + '">' + url + '</a>';
|
||||
}
|
||||
|
||||
function isLineEmpty(line) {
|
||||
return line.replace(/>/g, '').trim().length === 0;
|
||||
}
|
||||
|
||||
for (var j = 0; j < root.children.length; j++) {
|
||||
// start by rendering the root nodes children
|
||||
body += render(root.children[j]);
|
||||
}
|
||||
|
||||
return '<div class="view-read-body">' + body + '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform scale content to fit iframe width
|
||||
*/
|
||||
function scaleToFit() {
|
||||
var view = document.getElementsByClassName('scale-body').item(0);
|
||||
if(!view) {
|
||||
function removeParentReference(node) {
|
||||
if (!node.children) {
|
||||
// this is a text leaf ... terminate recursion
|
||||
return;
|
||||
}
|
||||
|
||||
var parentWidth = view.parentNode.offsetWidth;
|
||||
var w = view.offsetWidth;
|
||||
var scale = '';
|
||||
// remove parent node to prevent infinite loop in JSON stringify
|
||||
delete node.parent;
|
||||
|
||||
if(w > parentWidth) {
|
||||
scale = parentWidth / w;
|
||||
scale = 'scale(' + scale + ',' + scale + ')';
|
||||
for (var i = 0; i < node.children.length; i++) {
|
||||
if (typeof node.children[i] === 'string') {
|
||||
// remove trailing newline in string
|
||||
node.children[i] = node.children[i].replace(/\n$/, '');
|
||||
} else {
|
||||
// I used recursion ...
|
||||
removeParentReference(node.children[i]);
|
||||
}
|
||||
}
|
||||
|
||||
view.style['-webkit-transform-origin'] = '0 0';
|
||||
view.style.transformOrigin = '0 0';
|
||||
view.style['-webkit-transform'] = scale;
|
||||
view.style.transform = scale;
|
||||
}
|
||||
|
||||
})();
|
||||
nodes = buildTextNodes(parseLines(textBody.replace(/ >/g, '>')));
|
||||
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) {
|
||||
var body = '';
|
||||
|
||||
function render(node) {
|
||||
var i, html = '';
|
||||
if (!node.children) {
|
||||
// this is a text leaf
|
||||
var lines = node.split('\n');
|
||||
for (i = 0; i < lines.length; i++) {
|
||||
// replace all urls with anchors
|
||||
lines[i] = lines[i].replace(/(https?:\/\/[^\s]+)/g, createArchor);
|
||||
// wrap line into an element for easier styling
|
||||
html += '<div class="line';
|
||||
if (isLineEmpty(lines[i])) {
|
||||
html += ' empty-line';
|
||||
}
|
||||
html += '"><span>' + lines[i] + '</span><br></div>';
|
||||
}
|
||||
return html;
|
||||
}
|
||||
|
||||
for (i = 0; i < node.children.length; i++) {
|
||||
html += '<div class="prev-message">' + render(node.children[i]) + '</div>';
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
function createArchor(url) {
|
||||
return '<a href="' + url + '">' + url + '</a>';
|
||||
}
|
||||
|
||||
function isLineEmpty(line) {
|
||||
return line.replace(/>/g, '').trim().length === 0;
|
||||
}
|
||||
|
||||
for (var j = 0; j < root.children.length; j++) {
|
||||
// start by rendering the root nodes children
|
||||
body += render(root.children[j]);
|
||||
}
|
||||
|
||||
return '<div class="view-read-body">' + body + '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform scale content to fit iframe width
|
||||
*/
|
||||
function scaleToFit() {
|
||||
var view = document.getElementsByClassName('scale-body').item(0);
|
||||
if (!view) {
|
||||
return;
|
||||
}
|
||||
|
||||
var parentWidth = view.parentNode.offsetWidth;
|
||||
var w = view.offsetWidth;
|
||||
var scale = '';
|
||||
|
||||
if (w > parentWidth) {
|
||||
scale = parentWidth / w;
|
||||
scale = 'scale(' + scale + ',' + scale + ')';
|
||||
}
|
||||
|
||||
view.style['-webkit-transform-origin'] = '0 0';
|
||||
view.style.transformOrigin = '0 0';
|
||||
view.style['-webkit-transform'] = scale;
|
||||
view.style.transform = scale;
|
||||
}
|
|
@ -1,286 +1,283 @@
|
|||
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,
|
||||
emailDao, invitationDao, outbox, pgp, keychain;
|
||||
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;
|
||||
outbox = appController._outboxBo;
|
||||
pgp = appController._pgp;
|
||||
keychain = appController._keychain;
|
||||
emailDao = appController._emailDao;
|
||||
invitationDao = appController._invitationDao;
|
||||
outbox = appController._outboxBo;
|
||||
pgp = appController._pgp;
|
||||
keychain = appController._keychain;
|
||||
|
||||
// set default value so that the popover height is correct on init
|
||||
$scope.keyId = 'No key found.';
|
||||
// set default value so that the popover height is correct on init
|
||||
$scope.keyId = 'No key found.';
|
||||
|
||||
$scope.state.read = {
|
||||
open: false,
|
||||
toggle: function(to) {
|
||||
this.open = to;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.getKeyId = function(address) {
|
||||
$scope.keyId = 'Searching...';
|
||||
keychain.getReceiverPublicKey(address, function(err, pubkey) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pubkey) {
|
||||
$scope.keyId = 'User has no key. Click to invite.';
|
||||
$scope.$apply();
|
||||
return;
|
||||
}
|
||||
|
||||
var fpr = pgp.getFingerprint(pubkey.publicKey);
|
||||
var formatted = fpr.slice(32);
|
||||
|
||||
$scope.keyId = 'PGP key: ' + formatted;
|
||||
$scope.$apply();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.$watch('state.mailList.selected', function(mail) {
|
||||
if (!mail) {
|
||||
return;
|
||||
}
|
||||
|
||||
// display sender security status
|
||||
mail.from.forEach(checkPublicKey);
|
||||
// display recipient security status
|
||||
mail.to.forEach(checkPublicKey);
|
||||
// display recipient security status
|
||||
Array.isArray(mail.cc) && mail.cc.forEach(checkPublicKey);
|
||||
});
|
||||
|
||||
function checkPublicKey(user) {
|
||||
user.secure = undefined;
|
||||
|
||||
if (!keychain) {
|
||||
return;
|
||||
}
|
||||
|
||||
keychain.getReceiverPublicKey(user.address, function(err, pubkey) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pubkey && pubkey.publicKey) {
|
||||
user.secure = true;
|
||||
} else {
|
||||
user.secure = false;
|
||||
}
|
||||
|
||||
$scope.$apply();
|
||||
});
|
||||
$scope.state.read = {
|
||||
open: false,
|
||||
toggle: function(to) {
|
||||
this.open = to;
|
||||
}
|
||||
|
||||
$scope.download = function(attachment) {
|
||||
// download file to disk if content is available
|
||||
if (attachment.content) {
|
||||
download.createDownload({
|
||||
content: attachment.content,
|
||||
filename: attachment.filename,
|
||||
contentType: attachment.mimeType
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var folder = $scope.state.nav.currentFolder;
|
||||
var email = $scope.state.mailList.selected;
|
||||
emailDao.getAttachment({
|
||||
folder: folder,
|
||||
uid: email.uid,
|
||||
attachment: attachment
|
||||
}, $scope.onError);
|
||||
};
|
||||
|
||||
$scope.invite = function(user) {
|
||||
// only invite non-pgp users
|
||||
if (user.secure) {
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.keyId = 'Sending invitation...';
|
||||
|
||||
var sender = emailDao._account.emailAddress,
|
||||
recipient = user.address;
|
||||
|
||||
invitationDao.invite({
|
||||
recipient: recipient,
|
||||
sender: sender
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
var invitationMail = {
|
||||
from: [{
|
||||
address: sender
|
||||
}],
|
||||
to: [{
|
||||
address: recipient
|
||||
}],
|
||||
cc: [],
|
||||
bcc: [],
|
||||
subject: str.invitationSubject,
|
||||
body: str.invitationMessage
|
||||
};
|
||||
|
||||
// send invitation mail
|
||||
outbox.put(invitationMail, $scope.onError);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
//
|
||||
// Directives
|
||||
//
|
||||
|
||||
var ngModule = angular.module('read', []);
|
||||
|
||||
ngModule.directive('replySelection', function() {
|
||||
return function(scope, elm) {
|
||||
var popover, visible;
|
||||
|
||||
popover = angular.element(document.querySelector('.reply-selection'));
|
||||
visible = false;
|
||||
|
||||
elm.on('touchstart click', appear);
|
||||
elm.parent().parent().on('touchstart click', disappear);
|
||||
popover.on('touchstart click', disappear);
|
||||
|
||||
function appear(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
visible = true;
|
||||
|
||||
// set popover position
|
||||
var top = elm[0].offsetTop;
|
||||
var left = elm[0].offsetLeft;
|
||||
var width = elm[0].offsetWidth;
|
||||
var height = elm[0].offsetHeight;
|
||||
|
||||
popover[0].style.transition = 'opacity 0.1s linear';
|
||||
popover[0].style.top = (top + height) + 'px';
|
||||
popover[0].style.left = (left + width / 2 - popover[0].offsetWidth / 2) + 'px';
|
||||
popover[0].style.opacity = '1';
|
||||
$scope.getKeyId = function(address) {
|
||||
$scope.keyId = 'Searching...';
|
||||
keychain.getReceiverPublicKey(address, function(err, pubkey) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
function disappear() {
|
||||
if (!visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
popover[0].style.transition = 'opacity 0.25s linear, top 0.25s step-end, left 0.25s step-end';
|
||||
popover[0].style.opacity = '0';
|
||||
popover[0].style.top = '-9999px';
|
||||
popover[0].style.left = '-9999px';
|
||||
visible = false;
|
||||
if (!pubkey) {
|
||||
$scope.keyId = 'User has no key. Click to invite.';
|
||||
$scope.$apply();
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
var fpr = pgp.getFingerprint(pubkey.publicKey);
|
||||
var formatted = fpr.slice(32);
|
||||
|
||||
$scope.keyId = 'PGP key: ' + formatted;
|
||||
$scope.$apply();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.$watch('state.mailList.selected', function(mail) {
|
||||
if (!mail) {
|
||||
return;
|
||||
}
|
||||
|
||||
// display sender security status
|
||||
mail.from.forEach(checkPublicKey);
|
||||
// display recipient security status
|
||||
mail.to.forEach(checkPublicKey);
|
||||
// display recipient security status
|
||||
Array.isArray(mail.cc) && mail.cc.forEach(checkPublicKey);
|
||||
});
|
||||
|
||||
ngModule.directive('frameLoad', function($timeout, $window) {
|
||||
return function(scope, elm) {
|
||||
var iframe = elm[0];
|
||||
function checkPublicKey(user) {
|
||||
user.secure = undefined;
|
||||
|
||||
scope.$watch('state.read.open', function(open) {
|
||||
if (open) {
|
||||
// trigger rendering of iframe
|
||||
// otherwise scale to fit would not compute correct dimensions on mobile
|
||||
displayText(scope.state.mailList.selected ? scope.state.mailList.selected.body : undefined);
|
||||
displayHtml(scope.state.mailList.selected ? scope.state.mailList.selected.html : undefined);
|
||||
}
|
||||
if (!keychain) {
|
||||
return;
|
||||
}
|
||||
|
||||
keychain.getReceiverPublicKey(user.address, function(err, pubkey) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pubkey && pubkey.publicKey) {
|
||||
user.secure = true;
|
||||
} else {
|
||||
user.secure = false;
|
||||
}
|
||||
|
||||
$scope.$apply();
|
||||
});
|
||||
}
|
||||
|
||||
$scope.download = function(attachment) {
|
||||
// download file to disk if content is available
|
||||
if (attachment.content) {
|
||||
download.createDownload({
|
||||
content: attachment.content,
|
||||
filename: attachment.filename,
|
||||
contentType: attachment.mimeType
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
$window.addEventListener('resize', scaleToFit);
|
||||
var folder = $scope.state.nav.currentFolder;
|
||||
var email = $scope.state.mailList.selected;
|
||||
emailDao.getAttachment({
|
||||
folder: folder,
|
||||
uid: email.uid,
|
||||
attachment: attachment
|
||||
}, $scope.onError);
|
||||
};
|
||||
|
||||
iframe.onload = function() {
|
||||
// set listeners
|
||||
scope.$watch('state.mailList.selected.body', displayText);
|
||||
scope.$watch('state.mailList.selected.html', displayHtml);
|
||||
// display initial message body
|
||||
scope.$apply();
|
||||
$scope.invite = function(user) {
|
||||
// only invite non-pgp users
|
||||
if (user.secure) {
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.keyId = 'Sending invitation...';
|
||||
|
||||
var sender = emailDao._account.emailAddress,
|
||||
recipient = user.address;
|
||||
|
||||
invitationDao.invite({
|
||||
recipient: recipient,
|
||||
sender: sender
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
var invitationMail = {
|
||||
from: [{
|
||||
address: sender
|
||||
}],
|
||||
to: [{
|
||||
address: recipient
|
||||
}],
|
||||
cc: [],
|
||||
bcc: [],
|
||||
subject: str.invitationSubject,
|
||||
body: str.invitationMessage
|
||||
};
|
||||
|
||||
function displayText(body) {
|
||||
var mail = scope.state.mailList.selected;
|
||||
if ((mail && mail.html) || (mail && mail.encrypted && !mail.decrypted)) {
|
||||
return;
|
||||
}
|
||||
// send invitation mail
|
||||
outbox.put(invitationMail, $scope.onError);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
// send text body for rendering in iframe
|
||||
iframe.contentWindow.postMessage({
|
||||
text: body
|
||||
}, '*');
|
||||
//
|
||||
// Directives
|
||||
//
|
||||
|
||||
$timeout(scaleToFit, 0);
|
||||
var ngModule = angular.module('read', []);
|
||||
|
||||
ngModule.directive('replySelection', function() {
|
||||
return function(scope, elm) {
|
||||
var popover, visible;
|
||||
|
||||
popover = angular.element(document.querySelector('.reply-selection'));
|
||||
visible = false;
|
||||
|
||||
elm.on('touchstart click', appear);
|
||||
elm.parent().parent().on('touchstart click', disappear);
|
||||
popover.on('touchstart click', disappear);
|
||||
|
||||
function appear(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
visible = true;
|
||||
|
||||
// set popover position
|
||||
var top = elm[0].offsetTop;
|
||||
var left = elm[0].offsetLeft;
|
||||
var width = elm[0].offsetWidth;
|
||||
var height = elm[0].offsetHeight;
|
||||
|
||||
popover[0].style.transition = 'opacity 0.1s linear';
|
||||
popover[0].style.top = (top + height) + 'px';
|
||||
popover[0].style.left = (left + width / 2 - popover[0].offsetWidth / 2) + 'px';
|
||||
popover[0].style.opacity = '1';
|
||||
}
|
||||
|
||||
function disappear() {
|
||||
if (!visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
function displayHtml(html) {
|
||||
if (!html) {
|
||||
return;
|
||||
}
|
||||
popover[0].style.transition = 'opacity 0.25s linear, top 0.25s step-end, left 0.25s step-end';
|
||||
popover[0].style.opacity = '0';
|
||||
popover[0].style.top = '-9999px';
|
||||
popover[0].style.left = '-9999px';
|
||||
visible = false;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// if there are image tags in the html?
|
||||
var hasImages = /<img[^>]+\bsrc=['"][^'">]+['"]/ig.test(html);
|
||||
scope.showImageButton = hasImages;
|
||||
ngModule.directive('frameLoad', function($timeout, $window) {
|
||||
return function(scope, elm) {
|
||||
var iframe = elm[0];
|
||||
|
||||
iframe.contentWindow.postMessage({
|
||||
html: html,
|
||||
removeImages: hasImages // avoids doing unnecessary work on the html
|
||||
}, '*');
|
||||
|
||||
// only add a scope function to reload the html if there are images
|
||||
if (hasImages) {
|
||||
// reload WITH images
|
||||
scope.displayImages = function() {
|
||||
scope.showImageButton = false;
|
||||
iframe.contentWindow.postMessage({
|
||||
html: html,
|
||||
removeImages: false
|
||||
}, '*');
|
||||
};
|
||||
}
|
||||
|
||||
$timeout(scaleToFit, 0);
|
||||
scope.$watch('state.read.open', function(open) {
|
||||
if (open) {
|
||||
// trigger rendering of iframe
|
||||
// otherwise scale to fit would not compute correct dimensions on mobile
|
||||
displayText(scope.state.mailList.selected ? scope.state.mailList.selected.body : undefined);
|
||||
displayHtml(scope.state.mailList.selected ? scope.state.mailList.selected.html : undefined);
|
||||
}
|
||||
});
|
||||
|
||||
// transform scale iframe (necessary on iOS) to fit container width
|
||||
function scaleToFit() {
|
||||
var parentWidth = elm.parent().width();
|
||||
var w = elm.width();
|
||||
var scale = '';
|
||||
$window.addEventListener('resize', scaleToFit);
|
||||
|
||||
if (w > parentWidth) {
|
||||
scale = parentWidth / w;
|
||||
scale = 'scale(' + scale + ',' + scale + ')';
|
||||
}
|
||||
|
||||
elm.css({
|
||||
'-webkit-transform-origin': '0 0',
|
||||
'transform-origin': '0 0',
|
||||
'-webkit-transform': scale,
|
||||
'transform': scale
|
||||
});
|
||||
}
|
||||
iframe.onload = function() {
|
||||
// set listeners
|
||||
scope.$watch('state.mailList.selected.body', displayText);
|
||||
scope.$watch('state.mailList.selected.html', displayHtml);
|
||||
// display initial message body
|
||||
scope.$apply();
|
||||
};
|
||||
});
|
||||
|
||||
return ReadCtrl;
|
||||
});
|
||||
function displayText(body) {
|
||||
var mail = scope.state.mailList.selected;
|
||||
if ((mail && mail.html) || (mail && mail.encrypted && !mail.decrypted)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// send text body for rendering in iframe
|
||||
iframe.contentWindow.postMessage({
|
||||
text: body
|
||||
}, '*');
|
||||
|
||||
$timeout(scaleToFit, 0);
|
||||
}
|
||||
|
||||
function displayHtml(html) {
|
||||
if (!html) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if there are image tags in the html?
|
||||
var hasImages = /<img[^>]+\bsrc=['"][^'">]+['"]/ig.test(html);
|
||||
scope.showImageButton = hasImages;
|
||||
|
||||
iframe.contentWindow.postMessage({
|
||||
html: html,
|
||||
removeImages: hasImages // avoids doing unnecessary work on the html
|
||||
}, '*');
|
||||
|
||||
// only add a scope function to reload the html if there are images
|
||||
if (hasImages) {
|
||||
// reload WITH images
|
||||
scope.displayImages = function() {
|
||||
scope.showImageButton = false;
|
||||
iframe.contentWindow.postMessage({
|
||||
html: html,
|
||||
removeImages: false
|
||||
}, '*');
|
||||
};
|
||||
}
|
||||
|
||||
$timeout(scaleToFit, 0);
|
||||
}
|
||||
|
||||
// transform scale iframe (necessary on iOS) to fit container width
|
||||
function scaleToFit() {
|
||||
var parentWidth = elm.parent().width();
|
||||
var w = elm.width();
|
||||
var scale = '';
|
||||
|
||||
if (w > parentWidth) {
|
||||
scale = parentWidth / w;
|
||||
scale = 'scale(' + scale + ',' + scale + ')';
|
||||
}
|
||||
|
||||
elm.css({
|
||||
'-webkit-transform-origin': '0 0',
|
||||
'transform-origin': '0 0',
|
||||
'-webkit-transform': scale,
|
||||
'transform': scale
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
module.exports = ReadCtrl;
|
|
@ -1,138 +1,136 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
'use strict';
|
||||
|
||||
var appController = require('js/app-controller'),
|
||||
pgp, keychain;
|
||||
var appController = require('../app-controller'),
|
||||
pgp, keychain;
|
||||
|
||||
//
|
||||
// Controller
|
||||
//
|
||||
//
|
||||
// Controller
|
||||
//
|
||||
|
||||
var SetPassphraseCtrl = function($scope) {
|
||||
keychain = appController._keychain;
|
||||
pgp = appController._pgp;
|
||||
var SetPassphraseCtrl = function($scope) {
|
||||
keychain = appController._keychain;
|
||||
pgp = appController._pgp;
|
||||
|
||||
$scope.state.setPassphrase = {
|
||||
toggle: function(to) {
|
||||
$scope.state.lightbox = (to) ? 'set-passphrase' : undefined;
|
||||
$scope.state.setPassphrase = {
|
||||
toggle: function(to) {
|
||||
$scope.state.lightbox = (to) ? 'set-passphrase' : undefined;
|
||||
|
||||
$scope.newPassphrase = undefined;
|
||||
$scope.oldPassphrase = undefined;
|
||||
$scope.confirmation = undefined;
|
||||
$scope.passphraseMsg = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// scope variables
|
||||
//
|
||||
|
||||
//
|
||||
// scope functions
|
||||
//
|
||||
|
||||
/*
|
||||
* Taken from jQuery validate.password plug-in 1.0
|
||||
* http://bassistance.de/jquery-plugins/jquery-plugin-validate.password/
|
||||
*
|
||||
* Copyright (c) 2009 Jörn Zaefferer
|
||||
*
|
||||
* Licensed under the MIT
|
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
*/
|
||||
$scope.checkPassphraseQuality = function() {
|
||||
var passphrase = $scope.newPassphrase;
|
||||
$scope.passphraseRating = 0;
|
||||
|
||||
var LOWER = /[a-z]/,
|
||||
UPPER = /[A-Z]/,
|
||||
DIGIT = /[0-9]/,
|
||||
DIGITS = /[0-9].*[0-9]/,
|
||||
SPECIAL = /[^a-zA-Z0-9]/,
|
||||
SAME = /^(.)\1+$/;
|
||||
|
||||
function uncapitalize(str) {
|
||||
return str.substring(0, 1).toLowerCase() + str.substring(1);
|
||||
}
|
||||
|
||||
if (!passphrase) {
|
||||
// no rating for empty passphrase
|
||||
$scope.passphraseMsg = '';
|
||||
return;
|
||||
}
|
||||
|
||||
if (passphrase.length < 8 || SAME.test(passphrase)) {
|
||||
$scope.passphraseMsg = 'Very weak';
|
||||
return;
|
||||
}
|
||||
|
||||
var lower = LOWER.test(passphrase),
|
||||
upper = UPPER.test(uncapitalize(passphrase)),
|
||||
digit = DIGIT.test(passphrase),
|
||||
digits = DIGITS.test(passphrase),
|
||||
special = SPECIAL.test(passphrase);
|
||||
|
||||
if (lower && upper && digit || lower && digits || upper && digits || special) {
|
||||
$scope.passphraseMsg = 'Strong';
|
||||
$scope.passphraseRating = 3;
|
||||
} else if (lower && upper || lower && digit || upper && digit) {
|
||||
$scope.passphraseMsg = 'Good';
|
||||
$scope.passphraseRating = 2;
|
||||
} else {
|
||||
$scope.passphraseMsg = 'Weak';
|
||||
$scope.passphraseRating = 1;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.setPassphrase = function() {
|
||||
var keyId = pgp.getKeyParams()._id;
|
||||
keychain.lookupPrivateKey(keyId, function(err, savedKey) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
pgp.changePassphrase({
|
||||
privateKeyArmored: savedKey.encryptedKey,
|
||||
oldPassphrase: $scope.oldPassphrase,
|
||||
newPassphrase: $scope.newPassphrase
|
||||
}, onPassphraseChanged);
|
||||
});
|
||||
};
|
||||
|
||||
function onPassphraseChanged(err, newPrivateKeyArmored) {
|
||||
if (err) {
|
||||
err.showBugReporter = false;
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// persist new armored key
|
||||
var keyParams = pgp.getKeyParams(newPrivateKeyArmored);
|
||||
var privateKey = {
|
||||
_id: keyParams._id,
|
||||
userId: keyParams.userId,
|
||||
userIds: keyParams.userIds,
|
||||
encryptedKey: newPrivateKeyArmored
|
||||
};
|
||||
|
||||
keychain.saveLocalPrivateKey(privateKey, onKeyPersisted);
|
||||
}
|
||||
|
||||
function onKeyPersisted(err) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.state.setPassphrase.toggle(false);
|
||||
$scope.$apply();
|
||||
$scope.onError({
|
||||
title: 'Success',
|
||||
message: 'Passphrase change complete.'
|
||||
});
|
||||
$scope.newPassphrase = undefined;
|
||||
$scope.oldPassphrase = undefined;
|
||||
$scope.confirmation = undefined;
|
||||
$scope.passphraseMsg = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
return SetPassphraseCtrl;
|
||||
});
|
||||
//
|
||||
// scope variables
|
||||
//
|
||||
|
||||
//
|
||||
// scope functions
|
||||
//
|
||||
|
||||
/*
|
||||
* Taken from jQuery validate.password plug-in 1.0
|
||||
* http://bassistance.de/jquery-plugins/jquery-plugin-validate.password/
|
||||
*
|
||||
* Copyright (c) 2009 Jörn Zaefferer
|
||||
*
|
||||
* Licensed under the MIT
|
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
*/
|
||||
$scope.checkPassphraseQuality = function() {
|
||||
var passphrase = $scope.newPassphrase;
|
||||
$scope.passphraseRating = 0;
|
||||
|
||||
var LOWER = /[a-z]/,
|
||||
UPPER = /[A-Z]/,
|
||||
DIGIT = /[0-9]/,
|
||||
DIGITS = /[0-9].*[0-9]/,
|
||||
SPECIAL = /[^a-zA-Z0-9]/,
|
||||
SAME = /^(.)\1+$/;
|
||||
|
||||
function uncapitalize(str) {
|
||||
return str.substring(0, 1).toLowerCase() + str.substring(1);
|
||||
}
|
||||
|
||||
if (!passphrase) {
|
||||
// no rating for empty passphrase
|
||||
$scope.passphraseMsg = '';
|
||||
return;
|
||||
}
|
||||
|
||||
if (passphrase.length < 8 || SAME.test(passphrase)) {
|
||||
$scope.passphraseMsg = 'Very weak';
|
||||
return;
|
||||
}
|
||||
|
||||
var lower = LOWER.test(passphrase),
|
||||
upper = UPPER.test(uncapitalize(passphrase)),
|
||||
digit = DIGIT.test(passphrase),
|
||||
digits = DIGITS.test(passphrase),
|
||||
special = SPECIAL.test(passphrase);
|
||||
|
||||
if (lower && upper && digit || lower && digits || upper && digits || special) {
|
||||
$scope.passphraseMsg = 'Strong';
|
||||
$scope.passphraseRating = 3;
|
||||
} else if (lower && upper || lower && digit || upper && digit) {
|
||||
$scope.passphraseMsg = 'Good';
|
||||
$scope.passphraseRating = 2;
|
||||
} else {
|
||||
$scope.passphraseMsg = 'Weak';
|
||||
$scope.passphraseRating = 1;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.setPassphrase = function() {
|
||||
var keyId = pgp.getKeyParams()._id;
|
||||
keychain.lookupPrivateKey(keyId, function(err, savedKey) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
pgp.changePassphrase({
|
||||
privateKeyArmored: savedKey.encryptedKey,
|
||||
oldPassphrase: $scope.oldPassphrase,
|
||||
newPassphrase: $scope.newPassphrase
|
||||
}, onPassphraseChanged);
|
||||
});
|
||||
};
|
||||
|
||||
function onPassphraseChanged(err, newPrivateKeyArmored) {
|
||||
if (err) {
|
||||
err.showBugReporter = false;
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// persist new armored key
|
||||
var keyParams = pgp.getKeyParams(newPrivateKeyArmored);
|
||||
var privateKey = {
|
||||
_id: keyParams._id,
|
||||
userId: keyParams.userId,
|
||||
userIds: keyParams.userIds,
|
||||
encryptedKey: newPrivateKeyArmored
|
||||
};
|
||||
|
||||
keychain.saveLocalPrivateKey(privateKey, onKeyPersisted);
|
||||
}
|
||||
|
||||
function onKeyPersisted(err) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.state.setPassphrase.toggle(false);
|
||||
$scope.$apply();
|
||||
$scope.onError({
|
||||
title: 'Success',
|
||||
message: 'Passphrase change complete.'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = SetPassphraseCtrl;
|
File diff suppressed because it is too large
Load Diff
|
@ -2,122 +2,119 @@
|
|||
* 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
|
||||
* @param {String} iv The base64 encoded IV
|
||||
* @param {Function} callback(error, ciphertext)
|
||||
* @return {String} The base64 encoded ciphertext
|
||||
*/
|
||||
Crypto.prototype.encrypt = function(plaintext, key, iv, callback) {
|
||||
var ct;
|
||||
/**
|
||||
* Encrypt plaintext using AES-GCM.
|
||||
* @param {String} plaintext The input string in UTF-16
|
||||
* @param {String} key The base64 encoded key
|
||||
* @param {String} iv The base64 encoded IV
|
||||
* @param {Function} callback(error, ciphertext)
|
||||
* @return {String} The base64 encoded ciphertext
|
||||
*/
|
||||
Crypto.prototype.encrypt = function(plaintext, key, iv, callback) {
|
||||
var ct;
|
||||
|
||||
try {
|
||||
ct = aes.encrypt(plaintext, key, iv);
|
||||
} catch (err) {
|
||||
callback(err);
|
||||
return;
|
||||
try {
|
||||
ct = aes.encrypt(plaintext, key, iv);
|
||||
} catch (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, ct);
|
||||
};
|
||||
|
||||
/**
|
||||
* Decrypt ciphertext suing AES-GCM
|
||||
* @param {String} ciphertext The base64 encoded ciphertext
|
||||
* @param {String} key The base64 encoded key
|
||||
* @param {String} iv The base64 encoded IV
|
||||
* @param {Function} callback(error, plaintext)
|
||||
* @return {String} The decrypted plaintext in UTF-16
|
||||
*/
|
||||
Crypto.prototype.decrypt = function(ciphertext, key, iv, callback) {
|
||||
var pt;
|
||||
|
||||
try {
|
||||
pt = aes.decrypt(ciphertext, key, iv);
|
||||
} catch (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, pt);
|
||||
};
|
||||
|
||||
/**
|
||||
* Do PBKDF2 key derivation in a WebWorker thread
|
||||
*/
|
||||
Crypto.prototype.deriveKey = function(password, salt, keySize, callback) {
|
||||
startWorker({
|
||||
script: config.workerPath + '/pbkdf2-worker.min.js',
|
||||
args: {
|
||||
password: password,
|
||||
salt: salt,
|
||||
keySize: keySize
|
||||
},
|
||||
callback: callback,
|
||||
noWorker: function() {
|
||||
return pbkdf2.getKey(password, salt, keySize);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
callback(null, ct);
|
||||
};
|
||||
//
|
||||
// helper functions
|
||||
//
|
||||
|
||||
/**
|
||||
* Decrypt ciphertext suing AES-GCM
|
||||
* @param {String} ciphertext The base64 encoded ciphertext
|
||||
* @param {String} key The base64 encoded key
|
||||
* @param {String} iv The base64 encoded IV
|
||||
* @param {Function} callback(error, plaintext)
|
||||
* @return {String} The decrypted plaintext in UTF-16
|
||||
*/
|
||||
Crypto.prototype.decrypt = function(ciphertext, key, iv, callback) {
|
||||
var pt;
|
||||
|
||||
try {
|
||||
pt = aes.decrypt(ciphertext, key, iv);
|
||||
} catch (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, pt);
|
||||
};
|
||||
|
||||
/**
|
||||
* Do PBKDF2 key derivation in a WebWorker thread
|
||||
*/
|
||||
Crypto.prototype.deriveKey = function(password, salt, keySize, callback) {
|
||||
startWorker({
|
||||
script: PBKDF2_WORKER,
|
||||
args: {
|
||||
password: password,
|
||||
salt: salt,
|
||||
keySize: keySize
|
||||
},
|
||||
callback: callback,
|
||||
noWorker: function() {
|
||||
return pbkdf2.getKey(password, salt, keySize);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
//
|
||||
// helper functions
|
||||
//
|
||||
|
||||
function startWorker(options) {
|
||||
// check for WebWorker support
|
||||
if (window.Worker) {
|
||||
// init webworker thread
|
||||
var worker = new Worker(config.workerPath + options.script);
|
||||
worker.onmessage = function(e) {
|
||||
if (e.data.err) {
|
||||
options.callback(e.data.err);
|
||||
return;
|
||||
}
|
||||
// return result from the worker
|
||||
options.callback(null, e.data);
|
||||
};
|
||||
worker.onerror = function(e) {
|
||||
// show error message in logger
|
||||
axe.error('Error handling web worker: Line ' + e.lineno + ' in ' + e.filename + ': ' + e.message);
|
||||
// return error
|
||||
options.callback({
|
||||
errMsg: (e.message) ? e.message : e
|
||||
});
|
||||
function startWorker(options) {
|
||||
// check for WebWorker support
|
||||
if (window.Worker) {
|
||||
// init webworker thread
|
||||
var worker = new Worker(options.script);
|
||||
worker.onmessage = function(e) {
|
||||
if (e.data.err) {
|
||||
options.callback(e.data.err);
|
||||
return;
|
||||
};
|
||||
// send data to the worker
|
||||
worker.postMessage(options.args);
|
||||
return;
|
||||
}
|
||||
|
||||
// no WebWorker support... do synchronous call
|
||||
var result;
|
||||
try {
|
||||
result = options.noWorker();
|
||||
} catch (e) {
|
||||
}
|
||||
// return result from the worker
|
||||
options.callback(null, e.data);
|
||||
};
|
||||
worker.onerror = function(e) {
|
||||
// show error message in logger
|
||||
axe.error('Error handling web worker: Line ' + e.lineno + ' in ' + e.filename + ': ' + e.message);
|
||||
// return error
|
||||
options.callback({
|
||||
errMsg: (e.message) ? e.message : e
|
||||
});
|
||||
return;
|
||||
}
|
||||
options.callback(null, result);
|
||||
};
|
||||
// send data to the worker
|
||||
worker.postMessage(options.args);
|
||||
return;
|
||||
}
|
||||
|
||||
return Crypto;
|
||||
});
|
||||
// no WebWorker support... do synchronous call
|
||||
var result;
|
||||
try {
|
||||
result = options.noWorker();
|
||||
} catch (e) {
|
||||
// return error
|
||||
options.callback({
|
||||
errMsg: (e.message) ? e.message : e
|
||||
});
|
||||
return;
|
||||
}
|
||||
options.callback(null, result);
|
||||
}
|
||||
|
||||
module.exports = Crypto;
|
|
@ -1,38 +1,25 @@
|
|||
(function() {
|
||||
'use strict';
|
||||
'use strict';
|
||||
|
||||
// import web worker dependencies
|
||||
importScripts('../../lib/require.js');
|
||||
importScripts('forge.min.js');
|
||||
|
||||
/**
|
||||
* 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'
|
||||
});
|
||||
var pbkdf2 = require('./pbkdf2');
|
||||
|
||||
require(['js/crypto/pbkdf2'], function(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) {
|
||||
var i = e.data,
|
||||
key = null;
|
||||
|
||||
var i = e.data,
|
||||
key = null;
|
||||
if (i.password && i.salt && i.keySize) {
|
||||
// start deriving key
|
||||
key = pbkdf2.getKey(i.password, i.salt, i.keySize);
|
||||
|
||||
if (i.password && i.salt && i.keySize) {
|
||||
// start deriving key
|
||||
key = pbkdf2.getKey(i.password, i.salt, i.keySize);
|
||||
} else {
|
||||
throw 'Not all arguments for web worker crypto are defined!';
|
||||
}
|
||||
|
||||
} else {
|
||||
throw 'Not all arguments for web worker crypto are defined!';
|
||||
}
|
||||
|
||||
// pass output back to main thread
|
||||
self.postMessage(key);
|
||||
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
}());
|
||||
// pass output back to main thread
|
||||
self.postMessage(key);
|
||||
};
|
|
@ -1,25 +1,24 @@
|
|||
/**
|
||||
* A Wrapper for Forge's PBKDF2 function
|
||||
*/
|
||||
define(['forge'], function(forge) {
|
||||
'use strict';
|
||||
|
||||
var self = {};
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
var saltUtf8 = forge.util.decode64(salt);
|
||||
var md = forge.md.sha256.create();
|
||||
var key = forge.pkcs5.pbkdf2(password, saltUtf8, 10000, keySize / 8, md);
|
||||
var pbkdf2 = {};
|
||||
|
||||
return forge.util.encode64(key);
|
||||
};
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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 self;
|
||||
});
|
||||
return forge.util.encode64(key);
|
||||
};
|
||||
|
||||
module.exports = pbkdf2;
|
|
@ -1,452 +1,450 @@
|
|||
/**
|
||||
* 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() {
|
||||
openpgp.config.prefer_hash_algorithm = openpgp.enums.hash.sha256;
|
||||
openpgp.initWorker(config.workerPath + '/../lib/openpgp/openpgp.worker.js');
|
||||
};
|
||||
var util = openpgp.util,
|
||||
config = require('../app-config').config;
|
||||
|
||||
/**
|
||||
* Generate a key pair for the user
|
||||
*/
|
||||
PGP.prototype.generateKeys = function(options, callback) {
|
||||
var userId, passphrase;
|
||||
var PGP = function() {
|
||||
openpgp.config.prefer_hash_algorithm = openpgp.enums.hash.sha256;
|
||||
openpgp.initWorker(config.workerPath + '/openpgp.worker.js');
|
||||
};
|
||||
|
||||
if (!util.emailRegEx.test(options.emailAddress) || !options.keySize) {
|
||||
callback(new Error('Crypto init failed. Not all options set!'));
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* Generate a key pair for the user
|
||||
*/
|
||||
PGP.prototype.generateKeys = function(options, callback) {
|
||||
var userId, passphrase;
|
||||
|
||||
// generate keypair
|
||||
userId = 'Whiteout User <' + options.emailAddress + '>';
|
||||
passphrase = (options.passphrase) ? options.passphrase : undefined;
|
||||
openpgp.generateKeyPair({
|
||||
keyType: 1, // (keytype 1=RSA)
|
||||
numBits: options.keySize,
|
||||
userId: userId,
|
||||
passphrase: passphrase
|
||||
}, onGenerated);
|
||||
if (!util.emailRegEx.test(options.emailAddress) || !options.keySize) {
|
||||
callback(new Error('Crypto init failed. Not all options set!'));
|
||||
return;
|
||||
}
|
||||
|
||||
function onGenerated(err, keys) {
|
||||
if (err) {
|
||||
callback(new Error('Keygeneration failed!'));
|
||||
return;
|
||||
}
|
||||
// generate keypair
|
||||
userId = 'Whiteout User <' + options.emailAddress + '>';
|
||||
passphrase = (options.passphrase) ? options.passphrase : undefined;
|
||||
openpgp.generateKeyPair({
|
||||
keyType: 1, // (keytype 1=RSA)
|
||||
numBits: options.keySize,
|
||||
userId: userId,
|
||||
passphrase: passphrase
|
||||
}, onGenerated);
|
||||
|
||||
callback(null, {
|
||||
keyId: keys.key.primaryKey.getKeyId().toHex().toUpperCase(),
|
||||
privateKeyArmored: keys.privateKeyArmored,
|
||||
publicKeyArmored: keys.publicKeyArmored
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Show a user's fingerprint
|
||||
*/
|
||||
PGP.prototype.getFingerprint = function(keyArmored) {
|
||||
function fingerprint(key) {
|
||||
return key.primaryKey.getFingerprint().toUpperCase();
|
||||
}
|
||||
|
||||
// process armored key input
|
||||
if (keyArmored) {
|
||||
return fingerprint(openpgp.key.readArmored(keyArmored).keys[0]);
|
||||
}
|
||||
|
||||
if (!this._publicKey) {
|
||||
throw new Error('No public key set for fingerprint generation!');
|
||||
}
|
||||
|
||||
// get local fingerpring
|
||||
return fingerprint(this._publicKey);
|
||||
};
|
||||
|
||||
/**
|
||||
* Show a user's key id.
|
||||
*/
|
||||
PGP.prototype.getKeyId = function(keyArmored) {
|
||||
var key, pubKeyId, privKeyId;
|
||||
|
||||
// process armored key input
|
||||
if (keyArmored) {
|
||||
key = openpgp.key.readArmored(keyArmored).keys[0];
|
||||
return key.primaryKey.getKeyId().toHex().toUpperCase();
|
||||
}
|
||||
|
||||
// check already imported keys
|
||||
if (!this._privateKey || !this._publicKey) {
|
||||
throw new Error('Cannot read key IDs... keys not set!');
|
||||
}
|
||||
|
||||
pubKeyId = this._publicKey.primaryKey.getKeyId().toHex().toUpperCase();
|
||||
privKeyId = this._privateKey.primaryKey.getKeyId().toHex().toUpperCase();
|
||||
|
||||
if (!pubKeyId || !privKeyId || pubKeyId !== privKeyId) {
|
||||
throw new Error('Key IDs do not match!');
|
||||
}
|
||||
|
||||
return pubKeyId;
|
||||
};
|
||||
|
||||
/**
|
||||
* Read all relevant params of an armored key.
|
||||
*/
|
||||
PGP.prototype.getKeyParams = function(keyArmored) {
|
||||
var key, packet, userIds;
|
||||
|
||||
// process armored key input
|
||||
if (keyArmored) {
|
||||
key = openpgp.key.readArmored(keyArmored).keys[0];
|
||||
} else if (this._publicKey) {
|
||||
key = this._publicKey;
|
||||
} else {
|
||||
throw new Error('Cannot read key params... keys not set!');
|
||||
}
|
||||
|
||||
packet = key.primaryKey;
|
||||
|
||||
// read user names and email addresses
|
||||
userIds = [];
|
||||
key.getUserIds().forEach(function(userId) {
|
||||
userIds.push({
|
||||
name: userId.split('<')[0].trim(),
|
||||
emailAddress: userId.split('<')[1].split('>')[0].trim()
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
_id: packet.getKeyId().toHex().toUpperCase(),
|
||||
userId: userIds[0].emailAddress, // the primary (first) email address of the key
|
||||
userIds: userIds, // a dictonary of all the key's name/address pairs
|
||||
fingerprint: packet.getFingerprint().toUpperCase(),
|
||||
algorithm: packet.algorithm,
|
||||
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) {
|
||||
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) {
|
||||
var pubKeyId, privKeyId, self = this;
|
||||
|
||||
// check options
|
||||
if (!options.privateKeyArmored || !options.publicKeyArmored) {
|
||||
callback(new Error('Importing keys failed. Not all options set!'));
|
||||
return;
|
||||
}
|
||||
|
||||
function resetKeys() {
|
||||
self._publicKey = undefined;
|
||||
self._privateKey = undefined;
|
||||
}
|
||||
|
||||
// read armored keys
|
||||
try {
|
||||
this._publicKey = openpgp.key.readArmored(options.publicKeyArmored).keys[0];
|
||||
this._privateKey = openpgp.key.readArmored(options.privateKeyArmored).keys[0];
|
||||
} catch (e) {
|
||||
resetKeys();
|
||||
callback(new Error('Importing keys failed. Parsing error!'));
|
||||
return;
|
||||
}
|
||||
|
||||
// decrypt private key with passphrase
|
||||
if (!this._privateKey.decrypt(options.passphrase)) {
|
||||
resetKeys();
|
||||
callback(new Error('Incorrect passphrase!'));
|
||||
return;
|
||||
}
|
||||
|
||||
// check if keys have the same id
|
||||
pubKeyId = this._publicKey.primaryKey.getKeyId().toHex();
|
||||
privKeyId = this._privateKey.primaryKey.getKeyId().toHex();
|
||||
if (!pubKeyId || !privKeyId || pubKeyId !== privKeyId) {
|
||||
resetKeys();
|
||||
callback(new Error('Key IDs dont match!'));
|
||||
return;
|
||||
}
|
||||
|
||||
callback();
|
||||
};
|
||||
|
||||
/**
|
||||
* Export the user's key pair
|
||||
*/
|
||||
PGP.prototype.exportKeys = function(callback) {
|
||||
if (!this._publicKey || !this._privateKey) {
|
||||
callback(new Error('Could not export keys!'));
|
||||
function onGenerated(err, keys) {
|
||||
if (err) {
|
||||
callback(new Error('Keygeneration failed!'));
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, {
|
||||
keyId: this._publicKey.primaryKey.getKeyId().toHex().toUpperCase(),
|
||||
privateKeyArmored: this._privateKey.armor(),
|
||||
publicKeyArmored: this._publicKey.armor()
|
||||
keyId: keys.key.primaryKey.getKeyId().toHex().toUpperCase(),
|
||||
privateKeyArmored: keys.privateKeyArmored,
|
||||
publicKeyArmored: keys.publicKeyArmored
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Change the passphrase of an ascii armored private key.
|
||||
*/
|
||||
PGP.prototype.changePassphrase = function(options, callback) {
|
||||
var privKey, packets, newPassphrase, newKeyArmored;
|
||||
|
||||
// set undefined instead of empty string as passphrase
|
||||
newPassphrase = (options.newPassphrase) ? options.newPassphrase : undefined;
|
||||
|
||||
if (!options.privateKeyArmored) {
|
||||
callback(new Error('Private key must be specified to change passphrase!'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.oldPassphrase === newPassphrase ||
|
||||
(!options.oldPassphrase && !newPassphrase)) {
|
||||
callback(new Error('New and old passphrase are the same!'));
|
||||
return;
|
||||
}
|
||||
|
||||
// read armored key
|
||||
try {
|
||||
privKey = openpgp.key.readArmored(options.privateKeyArmored).keys[0];
|
||||
} catch (e) {
|
||||
callback(new Error('Importing key failed. Parsing error!'));
|
||||
return;
|
||||
}
|
||||
|
||||
// decrypt private key with passphrase
|
||||
if (!privKey.decrypt(options.oldPassphrase)) {
|
||||
callback(new Error('Old passphrase incorrect!'));
|
||||
return;
|
||||
}
|
||||
|
||||
// encrypt key with new passphrase
|
||||
try {
|
||||
packets = privKey.getAllKeyPackets();
|
||||
for (var i = 0; i < packets.length; i++) {
|
||||
packets[i].encrypt(newPassphrase);
|
||||
}
|
||||
newKeyArmored = privKey.armor();
|
||||
} catch (e) {
|
||||
callback(new Error('Setting new passphrase failed!'));
|
||||
return;
|
||||
}
|
||||
|
||||
// check if new passphrase really works
|
||||
if (!privKey.decrypt(newPassphrase)) {
|
||||
callback(new Error('Decrypting key with new passphrase failed!'));
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, newKeyArmored);
|
||||
};
|
||||
|
||||
/**
|
||||
* Encrypt and sign a pgp message for a list of receivers
|
||||
*/
|
||||
PGP.prototype.encrypt = function(plaintext, publicKeysArmored, callback) {
|
||||
var publicKeys;
|
||||
|
||||
// check keys
|
||||
if (!this._privateKey) {
|
||||
callback(new Error('Error encrypting. Keys must be set!'));
|
||||
return;
|
||||
}
|
||||
|
||||
// parse armored public keys
|
||||
try {
|
||||
if (publicKeysArmored && publicKeysArmored.length) {
|
||||
publicKeys = [];
|
||||
publicKeysArmored.forEach(function(pubkeyArmored) {
|
||||
publicKeys = publicKeys.concat(openpgp.key.readArmored(pubkeyArmored).keys);
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
callback(new Error('Error encrypting plaintext!'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (publicKeys) {
|
||||
// encrypt and sign the plaintext
|
||||
openpgp.signAndEncryptMessage(publicKeys, this._privateKey, plaintext, callback);
|
||||
} else {
|
||||
// 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) {
|
||||
var publicKeys, message;
|
||||
|
||||
// check keys
|
||||
if (!this._privateKey) {
|
||||
callback(new Error('Error decrypting. Keys must be set!'));
|
||||
return;
|
||||
}
|
||||
|
||||
// read keys and ciphertext message
|
||||
try {
|
||||
if (publicKeyArmored) {
|
||||
// parse public keys if available ...
|
||||
publicKeys = openpgp.key.readArmored(publicKeyArmored).keys;
|
||||
} else {
|
||||
// use own public key to know if signatures are available
|
||||
publicKeys = [this._publicKey];
|
||||
}
|
||||
message = openpgp.message.readArmored(ciphertext);
|
||||
} catch (err) {
|
||||
callback(new Error('Error parsing encrypted PGP message!'));
|
||||
return;
|
||||
}
|
||||
|
||||
// decrypt and verify pgp message
|
||||
openpgp.decryptAndVerifyMessage(this._privateKey, publicKeys, message, onDecrypted);
|
||||
|
||||
function onDecrypted(err, decrypted) {
|
||||
if (err) {
|
||||
callback(new Error('Error decrypting and verifying PGP message!'));
|
||||
return;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
var publicKeys,
|
||||
message;
|
||||
|
||||
// check keys
|
||||
if (!this._privateKey) {
|
||||
callback(new Error('Error verifying signed PGP message. Keys must be set!'));
|
||||
return;
|
||||
}
|
||||
|
||||
// read keys and ciphertext message
|
||||
try {
|
||||
if (publicKeyArmored) {
|
||||
// parse public keys if available ...
|
||||
publicKeys = openpgp.key.readArmored(publicKeyArmored).keys;
|
||||
} else {
|
||||
// use own public key to know if signatures are available
|
||||
publicKeys = [this._publicKey];
|
||||
}
|
||||
message = openpgp.cleartext.readArmored(clearSignedText);
|
||||
} catch (err) {
|
||||
callback(new Error('Error verifying signed PGP message!'));
|
||||
return;
|
||||
}
|
||||
|
||||
openpgp.verifyClearSignedMessage(publicKeys, message, function(err, result) {
|
||||
if (err) {
|
||||
callback(new Error('Error verifying PGP message!'));
|
||||
return;
|
||||
}
|
||||
|
||||
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) {
|
||||
var publicKeys;
|
||||
|
||||
// check keys
|
||||
if (!this._privateKey) {
|
||||
callback(new Error('Error verifying signed PGP message. Keys must be set!'));
|
||||
return;
|
||||
}
|
||||
|
||||
// read keys and ciphertext message
|
||||
try {
|
||||
if (publicKeyArmored) {
|
||||
// parse public keys if available ...
|
||||
publicKeys = openpgp.key.readArmored(publicKeyArmored).keys;
|
||||
} else {
|
||||
// use own public key to know if signatures are available
|
||||
publicKeys = [this._publicKey];
|
||||
}
|
||||
} catch (err) {
|
||||
callback(new Error('Error verifying signed PGP message!'));
|
||||
return;
|
||||
}
|
||||
|
||||
var signatures;
|
||||
try {
|
||||
var msg = openpgp.message.readSignedContent(message, pgpSignature);
|
||||
signatures = msg.verify(publicKeys);
|
||||
} catch (err) {
|
||||
callback(new Error('Error verifying signed PGP message!'));
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, checkSignatureValidity(signatures));
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks signature validity
|
||||
* @param {Object} decrypted OpenPGP.js Signature array
|
||||
* @return {undefined|null|true|false}
|
||||
* If signatures array is empty (the message was not signed), returns undefined
|
||||
* If you're using the wrong public key, returns null.
|
||||
* If signatures are invalid, returns false.
|
||||
* If everything is in order, returns true
|
||||
*/
|
||||
function checkSignatureValidity(signatures) {
|
||||
if (!signatures.length) {
|
||||
// signatures array is empty (the message was not signed)
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < signatures.length; i++) {
|
||||
if (signatures[i].valid !== true) { // null | false
|
||||
// you're using the wrong public key or signatures are invalid
|
||||
return signatures[i].valid;
|
||||
}
|
||||
}
|
||||
|
||||
// everything is in order
|
||||
return true;
|
||||
/**
|
||||
* Show a user's fingerprint
|
||||
*/
|
||||
PGP.prototype.getFingerprint = function(keyArmored) {
|
||||
function fingerprint(key) {
|
||||
return key.primaryKey.getFingerprint().toUpperCase();
|
||||
}
|
||||
|
||||
return PGP;
|
||||
});
|
||||
// process armored key input
|
||||
if (keyArmored) {
|
||||
return fingerprint(openpgp.key.readArmored(keyArmored).keys[0]);
|
||||
}
|
||||
|
||||
if (!this._publicKey) {
|
||||
throw new Error('No public key set for fingerprint generation!');
|
||||
}
|
||||
|
||||
// get local fingerpring
|
||||
return fingerprint(this._publicKey);
|
||||
};
|
||||
|
||||
/**
|
||||
* Show a user's key id.
|
||||
*/
|
||||
PGP.prototype.getKeyId = function(keyArmored) {
|
||||
var key, pubKeyId, privKeyId;
|
||||
|
||||
// process armored key input
|
||||
if (keyArmored) {
|
||||
key = openpgp.key.readArmored(keyArmored).keys[0];
|
||||
return key.primaryKey.getKeyId().toHex().toUpperCase();
|
||||
}
|
||||
|
||||
// check already imported keys
|
||||
if (!this._privateKey || !this._publicKey) {
|
||||
throw new Error('Cannot read key IDs... keys not set!');
|
||||
}
|
||||
|
||||
pubKeyId = this._publicKey.primaryKey.getKeyId().toHex().toUpperCase();
|
||||
privKeyId = this._privateKey.primaryKey.getKeyId().toHex().toUpperCase();
|
||||
|
||||
if (!pubKeyId || !privKeyId || pubKeyId !== privKeyId) {
|
||||
throw new Error('Key IDs do not match!');
|
||||
}
|
||||
|
||||
return pubKeyId;
|
||||
};
|
||||
|
||||
/**
|
||||
* Read all relevant params of an armored key.
|
||||
*/
|
||||
PGP.prototype.getKeyParams = function(keyArmored) {
|
||||
var key, packet, userIds;
|
||||
|
||||
// process armored key input
|
||||
if (keyArmored) {
|
||||
key = openpgp.key.readArmored(keyArmored).keys[0];
|
||||
} else if (this._publicKey) {
|
||||
key = this._publicKey;
|
||||
} else {
|
||||
throw new Error('Cannot read key params... keys not set!');
|
||||
}
|
||||
|
||||
packet = key.primaryKey;
|
||||
|
||||
// read user names and email addresses
|
||||
userIds = [];
|
||||
key.getUserIds().forEach(function(userId) {
|
||||
userIds.push({
|
||||
name: userId.split('<')[0].trim(),
|
||||
emailAddress: userId.split('<')[1].split('>')[0].trim()
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
_id: packet.getKeyId().toHex().toUpperCase(),
|
||||
userId: userIds[0].emailAddress, // the primary (first) email address of the key
|
||||
userIds: userIds, // a dictonary of all the key's name/address pairs
|
||||
fingerprint: packet.getFingerprint().toUpperCase(),
|
||||
algorithm: packet.algorithm,
|
||||
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) {
|
||||
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) {
|
||||
var pubKeyId, privKeyId, self = this;
|
||||
|
||||
// check options
|
||||
if (!options.privateKeyArmored || !options.publicKeyArmored) {
|
||||
callback(new Error('Importing keys failed. Not all options set!'));
|
||||
return;
|
||||
}
|
||||
|
||||
function resetKeys() {
|
||||
self._publicKey = undefined;
|
||||
self._privateKey = undefined;
|
||||
}
|
||||
|
||||
// read armored keys
|
||||
try {
|
||||
this._publicKey = openpgp.key.readArmored(options.publicKeyArmored).keys[0];
|
||||
this._privateKey = openpgp.key.readArmored(options.privateKeyArmored).keys[0];
|
||||
} catch (e) {
|
||||
resetKeys();
|
||||
callback(new Error('Importing keys failed. Parsing error!'));
|
||||
return;
|
||||
}
|
||||
|
||||
// decrypt private key with passphrase
|
||||
if (!this._privateKey.decrypt(options.passphrase)) {
|
||||
resetKeys();
|
||||
callback(new Error('Incorrect passphrase!'));
|
||||
return;
|
||||
}
|
||||
|
||||
// check if keys have the same id
|
||||
pubKeyId = this._publicKey.primaryKey.getKeyId().toHex();
|
||||
privKeyId = this._privateKey.primaryKey.getKeyId().toHex();
|
||||
if (!pubKeyId || !privKeyId || pubKeyId !== privKeyId) {
|
||||
resetKeys();
|
||||
callback(new Error('Key IDs dont match!'));
|
||||
return;
|
||||
}
|
||||
|
||||
callback();
|
||||
};
|
||||
|
||||
/**
|
||||
* Export the user's key pair
|
||||
*/
|
||||
PGP.prototype.exportKeys = function(callback) {
|
||||
if (!this._publicKey || !this._privateKey) {
|
||||
callback(new Error('Could not export keys!'));
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, {
|
||||
keyId: this._publicKey.primaryKey.getKeyId().toHex().toUpperCase(),
|
||||
privateKeyArmored: this._privateKey.armor(),
|
||||
publicKeyArmored: this._publicKey.armor()
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Change the passphrase of an ascii armored private key.
|
||||
*/
|
||||
PGP.prototype.changePassphrase = function(options, callback) {
|
||||
var privKey, packets, newPassphrase, newKeyArmored;
|
||||
|
||||
// set undefined instead of empty string as passphrase
|
||||
newPassphrase = (options.newPassphrase) ? options.newPassphrase : undefined;
|
||||
|
||||
if (!options.privateKeyArmored) {
|
||||
callback(new Error('Private key must be specified to change passphrase!'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.oldPassphrase === newPassphrase ||
|
||||
(!options.oldPassphrase && !newPassphrase)) {
|
||||
callback(new Error('New and old passphrase are the same!'));
|
||||
return;
|
||||
}
|
||||
|
||||
// read armored key
|
||||
try {
|
||||
privKey = openpgp.key.readArmored(options.privateKeyArmored).keys[0];
|
||||
} catch (e) {
|
||||
callback(new Error('Importing key failed. Parsing error!'));
|
||||
return;
|
||||
}
|
||||
|
||||
// decrypt private key with passphrase
|
||||
if (!privKey.decrypt(options.oldPassphrase)) {
|
||||
callback(new Error('Old passphrase incorrect!'));
|
||||
return;
|
||||
}
|
||||
|
||||
// encrypt key with new passphrase
|
||||
try {
|
||||
packets = privKey.getAllKeyPackets();
|
||||
for (var i = 0; i < packets.length; i++) {
|
||||
packets[i].encrypt(newPassphrase);
|
||||
}
|
||||
newKeyArmored = privKey.armor();
|
||||
} catch (e) {
|
||||
callback(new Error('Setting new passphrase failed!'));
|
||||
return;
|
||||
}
|
||||
|
||||
// check if new passphrase really works
|
||||
if (!privKey.decrypt(newPassphrase)) {
|
||||
callback(new Error('Decrypting key with new passphrase failed!'));
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, newKeyArmored);
|
||||
};
|
||||
|
||||
/**
|
||||
* Encrypt and sign a pgp message for a list of receivers
|
||||
*/
|
||||
PGP.prototype.encrypt = function(plaintext, publicKeysArmored, callback) {
|
||||
var publicKeys;
|
||||
|
||||
// check keys
|
||||
if (!this._privateKey) {
|
||||
callback(new Error('Error encrypting. Keys must be set!'));
|
||||
return;
|
||||
}
|
||||
|
||||
// parse armored public keys
|
||||
try {
|
||||
if (publicKeysArmored && publicKeysArmored.length) {
|
||||
publicKeys = [];
|
||||
publicKeysArmored.forEach(function(pubkeyArmored) {
|
||||
publicKeys = publicKeys.concat(openpgp.key.readArmored(pubkeyArmored).keys);
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
callback(new Error('Error encrypting plaintext!'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (publicKeys) {
|
||||
// encrypt and sign the plaintext
|
||||
openpgp.signAndEncryptMessage(publicKeys, this._privateKey, plaintext, callback);
|
||||
} else {
|
||||
// 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) {
|
||||
var publicKeys, message;
|
||||
|
||||
// check keys
|
||||
if (!this._privateKey) {
|
||||
callback(new Error('Error decrypting. Keys must be set!'));
|
||||
return;
|
||||
}
|
||||
|
||||
// read keys and ciphertext message
|
||||
try {
|
||||
if (publicKeyArmored) {
|
||||
// parse public keys if available ...
|
||||
publicKeys = openpgp.key.readArmored(publicKeyArmored).keys;
|
||||
} else {
|
||||
// use own public key to know if signatures are available
|
||||
publicKeys = [this._publicKey];
|
||||
}
|
||||
message = openpgp.message.readArmored(ciphertext);
|
||||
} catch (err) {
|
||||
callback(new Error('Error parsing encrypted PGP message!'));
|
||||
return;
|
||||
}
|
||||
|
||||
// decrypt and verify pgp message
|
||||
openpgp.decryptAndVerifyMessage(this._privateKey, publicKeys, message, onDecrypted);
|
||||
|
||||
function onDecrypted(err, decrypted) {
|
||||
if (err) {
|
||||
callback(new Error('Error decrypting and verifying PGP message!'));
|
||||
return;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
var publicKeys,
|
||||
message;
|
||||
|
||||
// check keys
|
||||
if (!this._privateKey) {
|
||||
callback(new Error('Error verifying signed PGP message. Keys must be set!'));
|
||||
return;
|
||||
}
|
||||
|
||||
// read keys and ciphertext message
|
||||
try {
|
||||
if (publicKeyArmored) {
|
||||
// parse public keys if available ...
|
||||
publicKeys = openpgp.key.readArmored(publicKeyArmored).keys;
|
||||
} else {
|
||||
// use own public key to know if signatures are available
|
||||
publicKeys = [this._publicKey];
|
||||
}
|
||||
message = openpgp.cleartext.readArmored(clearSignedText);
|
||||
} catch (err) {
|
||||
callback(new Error('Error verifying signed PGP message!'));
|
||||
return;
|
||||
}
|
||||
|
||||
openpgp.verifyClearSignedMessage(publicKeys, message, function(err, result) {
|
||||
if (err) {
|
||||
callback(new Error('Error verifying PGP message!'));
|
||||
return;
|
||||
}
|
||||
|
||||
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) {
|
||||
var publicKeys;
|
||||
|
||||
// check keys
|
||||
if (!this._privateKey) {
|
||||
callback(new Error('Error verifying signed PGP message. Keys must be set!'));
|
||||
return;
|
||||
}
|
||||
|
||||
// read keys and ciphertext message
|
||||
try {
|
||||
if (publicKeyArmored) {
|
||||
// parse public keys if available ...
|
||||
publicKeys = openpgp.key.readArmored(publicKeyArmored).keys;
|
||||
} else {
|
||||
// use own public key to know if signatures are available
|
||||
publicKeys = [this._publicKey];
|
||||
}
|
||||
} catch (err) {
|
||||
callback(new Error('Error verifying signed PGP message!'));
|
||||
return;
|
||||
}
|
||||
|
||||
var signatures;
|
||||
try {
|
||||
var msg = openpgp.message.readSignedContent(message, pgpSignature);
|
||||
signatures = msg.verify(publicKeys);
|
||||
} catch (err) {
|
||||
callback(new Error('Error verifying signed PGP message!'));
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, checkSignatureValidity(signatures));
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks signature validity
|
||||
* @param {Object} decrypted OpenPGP.js Signature array
|
||||
* @return {undefined|null|true|false}
|
||||
* If signatures array is empty (the message was not signed), returns undefined
|
||||
* If you're using the wrong public key, returns null.
|
||||
* If signatures are invalid, returns false.
|
||||
* If everything is in order, returns true
|
||||
*/
|
||||
function checkSignatureValidity(signatures) {
|
||||
if (!signatures.length) {
|
||||
// signatures array is empty (the message was not signed)
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < signatures.length; i++) {
|
||||
if (signatures[i].valid !== true) { // null | false
|
||||
// you're using the wrong public key or signatures are invalid
|
||||
return signatures[i].valid;
|
||||
}
|
||||
}
|
||||
|
||||
// everything is in order
|
||||
return true;
|
||||
}
|
||||
|
||||
module.exports = PGP;
|
|
@ -1,63 +1,61 @@
|
|||
define(function() {
|
||||
'use strict';
|
||||
'use strict';
|
||||
|
||||
var AdminDAO = function(restDao) {
|
||||
this._restDao = 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) {
|
||||
var uri;
|
||||
/**
|
||||
* 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) {
|
||||
var uri;
|
||||
|
||||
if (!options.emailAddress || !options.password || !options.phone) {
|
||||
callback(new Error('Incomplete arguments!'));
|
||||
if (!options.emailAddress || !options.password || !options.phone) {
|
||||
callback(new Error('Incomplete arguments!'));
|
||||
return;
|
||||
}
|
||||
|
||||
uri = '/user';
|
||||
this._restDao.post(options, uri, function(err) {
|
||||
if (err && err.code === 409) {
|
||||
callback(new Error('User name is already taken!'));
|
||||
return;
|
||||
} else if (err) {
|
||||
callback(new Error('Error creating new user!'));
|
||||
return;
|
||||
}
|
||||
|
||||
uri = '/user';
|
||||
this._restDao.post(options, uri, function(err) {
|
||||
if (err && err.code === 409) {
|
||||
callback(new Error('User name is already taken!'));
|
||||
return;
|
||||
} else if (err) {
|
||||
callback(new Error('Error creating new user!'));
|
||||
return;
|
||||
}
|
||||
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) {
|
||||
var uri;
|
||||
|
||||
if (!options.emailAddress || !options.token) {
|
||||
callback(new Error('Incomplete arguments!'));
|
||||
return;
|
||||
}
|
||||
|
||||
uri = '/user/validate';
|
||||
this._restDao.post(options, uri, function(err) {
|
||||
if (!err || (err && err.code === 202)) {
|
||||
// success
|
||||
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) {
|
||||
var uri;
|
||||
|
||||
if (!options.emailAddress || !options.token) {
|
||||
callback(new Error('Incomplete arguments!'));
|
||||
return;
|
||||
} else {
|
||||
callback(new Error('Validation failed!'));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
uri = '/user/validate';
|
||||
this._restDao.post(options, uri, function(err) {
|
||||
if (!err || (err && err.code === 202)) {
|
||||
// success
|
||||
callback();
|
||||
} else {
|
||||
callback(new Error('Validation failed!'));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return AdminDAO;
|
||||
});
|
||||
module.exports = AdminDAO;
|
|
@ -1,97 +1,93 @@
|
|||
/**
|
||||
* 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) {
|
||||
this._localDbDao = localDbDao;
|
||||
};
|
||||
'use strict';
|
||||
|
||||
DeviceStorageDAO.prototype.init = function(emailAddress, callback) {
|
||||
this._localDbDao.init(emailAddress, callback);
|
||||
};
|
||||
var DeviceStorageDAO = function(localDbDao) {
|
||||
this._localDbDao = localDbDao;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
var key, items = [];
|
||||
DeviceStorageDAO.prototype.init = function(emailAddress, callback) {
|
||||
this._localDbDao.init(emailAddress, callback);
|
||||
};
|
||||
|
||||
// nothing to store
|
||||
if (!list || list.length === 0) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
// validate type
|
||||
if (!type) {
|
||||
callback({
|
||||
errMsg: 'Type is not set!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* 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) {
|
||||
var key, items = [];
|
||||
|
||||
// format items for batch storing in dao
|
||||
list.forEach(function(i) {
|
||||
key = createKey(i, type);
|
||||
|
||||
items.push({
|
||||
key: key,
|
||||
object: i
|
||||
});
|
||||
// nothing to store
|
||||
if (!list || list.length === 0) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
// validate type
|
||||
if (!type) {
|
||||
callback({
|
||||
errMsg: 'Type is not set!'
|
||||
});
|
||||
|
||||
this._localDbDao.batch(items, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Deletes items of a certain type from storage
|
||||
*/
|
||||
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) {
|
||||
// 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) {
|
||||
this._localDbDao.clear(callback);
|
||||
};
|
||||
|
||||
//
|
||||
// helper functions
|
||||
//
|
||||
|
||||
function createKey(i, type) {
|
||||
var key;
|
||||
|
||||
// put uid in key if available... for easy querying
|
||||
if (i.uid) {
|
||||
key = type + '_' + i.uid;
|
||||
} else if (i.id) {
|
||||
key = type + '_' + i.id;
|
||||
} else {
|
||||
key = type;
|
||||
}
|
||||
|
||||
return key;
|
||||
return;
|
||||
}
|
||||
|
||||
return DeviceStorageDAO;
|
||||
});
|
||||
// format items for batch storing in dao
|
||||
list.forEach(function(i) {
|
||||
key = createKey(i, type);
|
||||
|
||||
items.push({
|
||||
key: key,
|
||||
object: i
|
||||
});
|
||||
});
|
||||
|
||||
this._localDbDao.batch(items, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Deletes items of a certain type from storage
|
||||
*/
|
||||
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) {
|
||||
// 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) {
|
||||
this._localDbDao.clear(callback);
|
||||
};
|
||||
|
||||
//
|
||||
// helper functions
|
||||
//
|
||||
|
||||
function createKey(i, type) {
|
||||
var key;
|
||||
|
||||
// put uid in key if available... for easy querying
|
||||
if (i.uid) {
|
||||
key = type + '_' + i.uid;
|
||||
} else if (i.id) {
|
||||
key = type + '_' + i.id;
|
||||
} else {
|
||||
key = type;
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
module.exports = DeviceStorageDAO;
|
File diff suppressed because it is too large
Load Diff
|
@ -1,62 +1,60 @@
|
|||
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) {
|
||||
this._restDao = restDao;
|
||||
};
|
||||
/**
|
||||
* 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) {
|
||||
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) {
|
||||
if (typeof options !== 'object' || typeof options.recipient !== 'string' || typeof options.recipient !== 'string') {
|
||||
callback({
|
||||
errMsg: 'erroneous usage of api: incorrect parameters!'
|
||||
});
|
||||
/**
|
||||
* 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) {
|
||||
if (typeof options !== 'object' || typeof options.recipient !== 'string' || typeof options.recipient !== 'string') {
|
||||
callback({
|
||||
errMsg: 'erroneous usage of api: incorrect parameters!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var uri = '/invitation/recipient/' + options.recipient + '/sender/' + options.sender;
|
||||
this._restDao.put({}, uri, completed);
|
||||
|
||||
function completed(error, res, status) {
|
||||
if (error) {
|
||||
callback(error);
|
||||
return;
|
||||
}
|
||||
|
||||
var uri = '/invitation/recipient/' + options.recipient + '/sender/' + options.sender;
|
||||
this._restDao.put({}, uri, completed);
|
||||
|
||||
function completed(error, res, status) {
|
||||
if (error) {
|
||||
callback(error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (status === 201) {
|
||||
callback(null, InvitationDAO.INVITE_SUCCESS);
|
||||
return;
|
||||
} else if (status === 304) {
|
||||
callback(null, InvitationDAO.INVITE_PENDING);
|
||||
return;
|
||||
}
|
||||
|
||||
callback({
|
||||
errMsg: 'unexpected invitation state'
|
||||
});
|
||||
if (status === 201) {
|
||||
callback(null, InvitationDAO.INVITE_SUCCESS);
|
||||
return;
|
||||
} else if (status === 304) {
|
||||
callback(null, InvitationDAO.INVITE_PENDING);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
return InvitationDAO;
|
||||
});
|
||||
callback({
|
||||
errMsg: 'unexpected invitation state'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = InvitationDAO;
|
File diff suppressed because it is too large
Load Diff
|
@ -1,222 +1,216 @@
|
|||
/**
|
||||
* 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) {
|
||||
if (!dbName) {
|
||||
LawnchairDAO.prototype.init = function(dbName, callback) {
|
||||
if (!dbName) {
|
||||
callback({
|
||||
errMsg: 'Lawnchair DB name must be specified!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this._db = new Lawnchair({
|
||||
name: dbName
|
||||
}, function(lc) {
|
||||
if (!lc) {
|
||||
callback({
|
||||
errMsg: 'Lawnchair DB name must be specified!'
|
||||
errMsg: 'Lawnchair init failed!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this._db = new Lawnchair({
|
||||
name: dbName
|
||||
}, function(lc) {
|
||||
if (!lc) {
|
||||
callback({
|
||||
errMsg: 'Lawnchair init failed!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Create or update an object
|
||||
*/
|
||||
LawnchairDAO.prototype.persist = function(key, object, callback) {
|
||||
if (!key || !object) {
|
||||
callback({
|
||||
errMsg: 'Key and Object must be set!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this._db.save({
|
||||
key: key,
|
||||
object: object
|
||||
}, function(persisted) {
|
||||
if (persisted.key !== key) {
|
||||
callback({
|
||||
errMsg: 'Persisting failed!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Persist a bunch of items at once
|
||||
*/
|
||||
LawnchairDAO.prototype.batch = function(list, callback) {
|
||||
if (!(list instanceof Array)) {
|
||||
callback({
|
||||
errMsg: 'Input must be of type Array!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this._db.batch(list, function(res) {
|
||||
if (!res) {
|
||||
callback({
|
||||
errMsg: 'Persisting batch failed!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Read a single item by its key
|
||||
*/
|
||||
LawnchairDAO.prototype.read = function(key, callback) {
|
||||
if (!key) {
|
||||
callback({
|
||||
errMsg: 'Key must be specified!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this._db.get(key, function(o) {
|
||||
if (o) {
|
||||
callback(null, o.object);
|
||||
} else {
|
||||
callback();
|
||||
});
|
||||
};
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Create or update an object
|
||||
*/
|
||||
LawnchairDAO.prototype.persist = function(key, object, callback) {
|
||||
if (!key || !object) {
|
||||
callback({
|
||||
errMsg: 'Key and Object must be set!'
|
||||
});
|
||||
/**
|
||||
* 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) {
|
||||
var self = this,
|
||||
i, from, to,
|
||||
matchingKeys = [],
|
||||
intervalKeys = [],
|
||||
list = [];
|
||||
|
||||
// validate input
|
||||
if (!type || typeof offset === 'undefined' || typeof num === 'undefined') {
|
||||
callback({
|
||||
errMsg: 'Args not is not set!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// get all keys
|
||||
self._db.keys(function(keys) {
|
||||
|
||||
// check if key begins with type
|
||||
keys.forEach(function(key) {
|
||||
if (key.indexOf(type) === 0) {
|
||||
matchingKeys.push(key);
|
||||
}
|
||||
});
|
||||
|
||||
// sort keys
|
||||
matchingKeys.sort();
|
||||
|
||||
// set window of items to fetch
|
||||
// if num is null, list all items
|
||||
from = (num) ? matchingKeys.length - offset - num : 0;
|
||||
to = matchingKeys.length - 1 - offset;
|
||||
// filter items within requested interval
|
||||
for (i = 0; i < matchingKeys.length; i++) {
|
||||
if (i >= from && i <= to) {
|
||||
intervalKeys.push(matchingKeys[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// return if there are no matching keys
|
||||
if (intervalKeys.length === 0) {
|
||||
callback(null, list);
|
||||
return;
|
||||
}
|
||||
|
||||
this._db.save({
|
||||
key: key,
|
||||
object: object
|
||||
}, function(persisted) {
|
||||
if (persisted.key !== key) {
|
||||
callback({
|
||||
errMsg: 'Persisting failed!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
// fetch all items from data-store with matching key
|
||||
self._db.get(intervalKeys, function(intervalList) {
|
||||
intervalList.forEach(function(item) {
|
||||
list.push(item.object);
|
||||
});
|
||||
|
||||
// return only the interval between offset and num
|
||||
callback(null, list);
|
||||
});
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes an object liter from local storage by its key (delete)
|
||||
*/
|
||||
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) {
|
||||
var self = this,
|
||||
matchingKeys = [],
|
||||
after;
|
||||
|
||||
// validate type
|
||||
if (!type) {
|
||||
callback({
|
||||
errMsg: 'Type is not set!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// get all keys
|
||||
self._db.keys(function(keys) {
|
||||
// check if key begins with type
|
||||
keys.forEach(function(key) {
|
||||
if (key.indexOf(type) === 0) {
|
||||
matchingKeys.push(key);
|
||||
}
|
||||
});
|
||||
|
||||
if (matchingKeys.length < 1) {
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Persist a bunch of items at once
|
||||
*/
|
||||
LawnchairDAO.prototype.batch = function(list, callback) {
|
||||
if (!(list instanceof Array)) {
|
||||
callback({
|
||||
errMsg: 'Input must be of type Array!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this._db.batch(list, function(res) {
|
||||
if (!res) {
|
||||
callback({
|
||||
errMsg: 'Persisting batch failed!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
callback();
|
||||
// remove all matching keys
|
||||
after = _.after(matchingKeys.length, callback);
|
||||
_.each(matchingKeys, function(key) {
|
||||
self._db.remove(key, after);
|
||||
});
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Read a single item by its key
|
||||
*/
|
||||
LawnchairDAO.prototype.read = function(key, callback) {
|
||||
if (!key) {
|
||||
callback({
|
||||
errMsg: 'Key must be specified!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* Clears the whole local storage cache
|
||||
*/
|
||||
LawnchairDAO.prototype.clear = function(callback) {
|
||||
this._db.nuke(callback);
|
||||
};
|
||||
|
||||
this._db.get(key, function(o) {
|
||||
if (o) {
|
||||
callback(null, o.object);
|
||||
} else {
|
||||
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) {
|
||||
var self = this,
|
||||
i, from, to,
|
||||
matchingKeys = [],
|
||||
intervalKeys = [],
|
||||
list = [];
|
||||
|
||||
// validate input
|
||||
if (!type || typeof offset === 'undefined' || typeof num === 'undefined') {
|
||||
callback({
|
||||
errMsg: 'Args not is not set!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// get all keys
|
||||
self._db.keys(function(keys) {
|
||||
|
||||
// check if key begins with type
|
||||
keys.forEach(function(key) {
|
||||
if (key.indexOf(type) === 0) {
|
||||
matchingKeys.push(key);
|
||||
}
|
||||
});
|
||||
|
||||
// sort keys
|
||||
matchingKeys.sort();
|
||||
|
||||
// set window of items to fetch
|
||||
// if num is null, list all items
|
||||
from = (num) ? matchingKeys.length - offset - num : 0;
|
||||
to = matchingKeys.length - 1 - offset;
|
||||
// filter items within requested interval
|
||||
for (i = 0; i < matchingKeys.length; i++) {
|
||||
if (i >= from && i <= to) {
|
||||
intervalKeys.push(matchingKeys[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// return if there are no matching keys
|
||||
if (intervalKeys.length === 0) {
|
||||
callback(null, list);
|
||||
return;
|
||||
}
|
||||
|
||||
// fetch all items from data-store with matching key
|
||||
self._db.get(intervalKeys, function(intervalList) {
|
||||
intervalList.forEach(function(item) {
|
||||
list.push(item.object);
|
||||
});
|
||||
|
||||
// return only the interval between offset and num
|
||||
callback(null, list);
|
||||
});
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes an object liter from local storage by its key (delete)
|
||||
*/
|
||||
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) {
|
||||
var self = this,
|
||||
matchingKeys = [],
|
||||
after;
|
||||
|
||||
// validate type
|
||||
if (!type) {
|
||||
callback({
|
||||
errMsg: 'Type is not set!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// get all keys
|
||||
self._db.keys(function(keys) {
|
||||
// check if key begins with type
|
||||
keys.forEach(function(key) {
|
||||
if (key.indexOf(type) === 0) {
|
||||
matchingKeys.push(key);
|
||||
}
|
||||
});
|
||||
|
||||
if (matchingKeys.length < 1) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
// remove all matching keys
|
||||
after = _.after(matchingKeys.length, callback);
|
||||
_.each(matchingKeys, function(key) {
|
||||
self._db.remove(key, after);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Clears the whole local storage cache
|
||||
*/
|
||||
LawnchairDAO.prototype.clear = function(callback) {
|
||||
this._db.nuke(callback);
|
||||
};
|
||||
|
||||
return LawnchairDAO;
|
||||
});
|
||||
module.exports = LawnchairDAO;
|
|
@ -1,198 +1,196 @@
|
|||
define(function() {
|
||||
'use strict';
|
||||
'use strict';
|
||||
|
||||
var PrivateKeyDAO = function(restDao) {
|
||||
this._restDao = 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) {
|
||||
var uri;
|
||||
/**
|
||||
* 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) {
|
||||
var uri;
|
||||
|
||||
if (!options.userId || !options.deviceName) {
|
||||
callback(new Error('Incomplete arguments!'));
|
||||
if (!options.userId || !options.deviceName) {
|
||||
callback(new Error('Incomplete arguments!'));
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
* @param {String} options.encryptedDeviceSecret The base64 encoded encrypted device secret
|
||||
* @param {String} options.iv The iv used for encryption
|
||||
* @param {Function} callback(error)
|
||||
*/
|
||||
PrivateKeyDAO.prototype.uploadDeviceSecret = function(options, callback) {
|
||||
var uri;
|
||||
|
||||
if (!options.userId || !options.deviceName || !options.encryptedDeviceSecret || !options.iv) {
|
||||
callback(new Error('Incomplete arguments!'));
|
||||
return;
|
||||
}
|
||||
|
||||
uri = '/device/user/' + options.userId + '/devicename/' + options.deviceName;
|
||||
this._restDao.put(options, uri, callback);
|
||||
};
|
||||
|
||||
//
|
||||
// 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) {
|
||||
var uri;
|
||||
|
||||
if (!options.userId) {
|
||||
callback(new Error('Incomplete arguments!'));
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
* @param {String} options.encryptedDeviceSecret The server's base64 encoded deviceSecret encrypted using the authSessionKey
|
||||
* @param {String} options.iv The iv used for encryption
|
||||
* @param {Function} callback(error)
|
||||
*/
|
||||
PrivateKeyDAO.prototype.verifyAuthentication = function(options, callback) {
|
||||
var uri;
|
||||
|
||||
if (!options.userId || !options.sessionId || !options.encryptedChallenge || !options.encryptedDeviceSecret || !options.iv) {
|
||||
callback(new Error('Incomplete arguments!'));
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
* @param {String} options.encryptedPrivateKey The base64 encoded encrypted private PGP key
|
||||
* @param {String} options.sessionId The session id
|
||||
* @param {Function} callback(error)
|
||||
*/
|
||||
PrivateKeyDAO.prototype.upload = function(options, callback) {
|
||||
var uri;
|
||||
|
||||
if (!options._id || !options.userId || !options.encryptedPrivateKey || !options.sessionId || !options.salt || !options.iv) {
|
||||
callback(new Error('Incomplete arguments!'));
|
||||
return;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (!options.userId || !options.keyId) {
|
||||
callback(new Error('Incomplete arguments!'));
|
||||
return;
|
||||
}
|
||||
|
||||
this._restDao.get({
|
||||
uri: '/privatekey/user/' + options.userId + '/key/' + options.keyId + '?ignoreRecovery=true',
|
||||
}, function(err) {
|
||||
// 404: there is no encrypted private key on the server
|
||||
if (err && err.code !== 200) {
|
||||
callback(null, false);
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
* @param {String} options.encryptedDeviceSecret The base64 encoded encrypted device secret
|
||||
* @param {String} options.iv The iv used for encryption
|
||||
* @param {Function} callback(error)
|
||||
*/
|
||||
PrivateKeyDAO.prototype.uploadDeviceSecret = function(options, callback) {
|
||||
var uri;
|
||||
|
||||
if (!options.userId || !options.deviceName || !options.encryptedDeviceSecret || !options.iv) {
|
||||
callback(new Error('Incomplete arguments!'));
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
uri = '/device/user/' + options.userId + '/devicename/' + options.deviceName;
|
||||
this._restDao.put(options, uri, callback);
|
||||
};
|
||||
callback(null, true);
|
||||
});
|
||||
};
|
||||
|
||||
//
|
||||
// Private key functions
|
||||
//
|
||||
/**
|
||||
* 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) {
|
||||
if (!options.userId || !options.keyId) {
|
||||
callback(new Error('Incomplete arguments!'));
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
var uri;
|
||||
|
||||
if (!options.userId) {
|
||||
callback(new Error('Incomplete arguments!'));
|
||||
this._restDao.get({
|
||||
uri: '/privatekey/user/' + options.userId + '/key/' + options.keyId
|
||||
}, function(err) {
|
||||
// 404: there is no encrypted private key on the server
|
||||
if (err && err.code !== 200) {
|
||||
callback(null, false);
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
* @param {String} options.encryptedDeviceSecret The server's base64 encoded deviceSecret encrypted using the authSessionKey
|
||||
* @param {String} options.iv The iv used for encryption
|
||||
* @param {Function} callback(error)
|
||||
*/
|
||||
PrivateKeyDAO.prototype.verifyAuthentication = function(options, callback) {
|
||||
var uri;
|
||||
|
||||
if (!options.userId || !options.sessionId || !options.encryptedChallenge || !options.encryptedDeviceSecret || !options.iv) {
|
||||
callback(new Error('Incomplete arguments!'));
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
uri = '/auth/user/' + options.userId + '/session/' + options.sessionId;
|
||||
this._restDao.put(options, uri, callback);
|
||||
};
|
||||
callback(null, true);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param {String} options.encryptedPrivateKey The base64 encoded encrypted private PGP key
|
||||
* @param {String} options.sessionId The session id
|
||||
* @param {Function} callback(error)
|
||||
*/
|
||||
PrivateKeyDAO.prototype.upload = function(options, callback) {
|
||||
var uri;
|
||||
/**
|
||||
* 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
|
||||
* @param {String} options.recoveryToken The token proving the user own the email account
|
||||
* @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) {
|
||||
var uri;
|
||||
|
||||
if (!options._id || !options.userId || !options.encryptedPrivateKey || !options.sessionId || !options.salt || !options.iv) {
|
||||
callback(new Error('Incomplete arguments!'));
|
||||
return;
|
||||
}
|
||||
if (!options.userId || !options.keyId || !options.recoveryToken) {
|
||||
callback(new Error('Incomplete arguments!'));
|
||||
return;
|
||||
}
|
||||
|
||||
uri = '/privatekey/user/' + options.userId + '/session/' + options.sessionId;
|
||||
this._restDao.post(options, uri, callback);
|
||||
};
|
||||
uri = '/privatekey/user/' + options.userId + '/key/' + options.keyId + '/recovery/' + options.recoveryToken;
|
||||
this._restDao.get({
|
||||
uri: 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) {
|
||||
if (!options.userId || !options.keyId) {
|
||||
callback(new Error('Incomplete arguments!'));
|
||||
return;
|
||||
}
|
||||
|
||||
this._restDao.get({
|
||||
uri: '/privatekey/user/' + options.userId + '/key/' + options.keyId + '?ignoreRecovery=true',
|
||||
}, function(err) {
|
||||
// 404: there is no encrypted private key on the server
|
||||
if (err && err.code !== 200) {
|
||||
callback(null, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (!options.userId || !options.keyId) {
|
||||
callback(new Error('Incomplete arguments!'));
|
||||
return;
|
||||
}
|
||||
|
||||
this._restDao.get({
|
||||
uri: '/privatekey/user/' + options.userId + '/key/' + options.keyId
|
||||
}, function(err) {
|
||||
// 404: there is no encrypted private key on the server
|
||||
if (err && err.code !== 200) {
|
||||
callback(null, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
* @param {String} options.recoveryToken The token proving the user own the email account
|
||||
* @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) {
|
||||
var uri;
|
||||
|
||||
if (!options.userId || !options.keyId || !options.recoveryToken) {
|
||||
callback(new Error('Incomplete arguments!'));
|
||||
return;
|
||||
}
|
||||
|
||||
uri = '/privatekey/user/' + options.userId + '/key/' + options.keyId + '/recovery/' + options.recoveryToken;
|
||||
this._restDao.get({
|
||||
uri: uri
|
||||
}, callback);
|
||||
};
|
||||
|
||||
return PrivateKeyDAO;
|
||||
});
|
||||
module.exports = PrivateKeyDAO;
|
|
@ -1,105 +1,103 @@
|
|||
define(function() {
|
||||
'use strict';
|
||||
'use strict';
|
||||
|
||||
var PublicKeyDAO = function(restDao) {
|
||||
this._restDao = restDao;
|
||||
};
|
||||
var PublicKeyDAO = function(restDao) {
|
||||
this._restDao = restDao;
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify the public key behind the given uuid
|
||||
*/
|
||||
PublicKeyDAO.prototype.verify = function(uuid, callback) {
|
||||
var uri = '/verify/' + uuid;
|
||||
/**
|
||||
* Verify the public key behind the given uuid
|
||||
*/
|
||||
PublicKeyDAO.prototype.verify = function(uuid, callback) {
|
||||
var uri = '/verify/' + uuid;
|
||||
|
||||
this._restDao.get({
|
||||
uri: uri,
|
||||
type: 'text'
|
||||
}, function(err, res, status) {
|
||||
if (err && err.code === 400) {
|
||||
// there was an attempt to verify a non-existing public key
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
this._restDao.get({
|
||||
uri: uri,
|
||||
type: 'text'
|
||||
}, function(err, res, status) {
|
||||
if (err && err.code === 400) {
|
||||
// there was an attempt to verify a non-existing public key
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
callback(err, res, status);
|
||||
});
|
||||
};
|
||||
callback(err, res, status);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Find the user's corresponding public key
|
||||
*/
|
||||
PublicKeyDAO.prototype.get = function(keyId, callback) {
|
||||
var uri = '/publickey/key/' + keyId;
|
||||
/**
|
||||
* Find the user's corresponding public key
|
||||
*/
|
||||
PublicKeyDAO.prototype.get = function(keyId, callback) {
|
||||
var uri = '/publickey/key/' + keyId;
|
||||
|
||||
this._restDao.get({
|
||||
uri: uri
|
||||
}, function(err, key) {
|
||||
if (err && err.code === 404) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
this._restDao.get({
|
||||
uri: uri
|
||||
}, function(err, key) {
|
||||
if (err && err.code === 404) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, (key && key._id) ? key : undefined);
|
||||
});
|
||||
};
|
||||
callback(null, (key && key._id) ? key : undefined);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Find the user's corresponding public key by email
|
||||
*/
|
||||
PublicKeyDAO.prototype.getByUserId = function(userId, callback) {
|
||||
var uri = '/publickey/user/' + userId;
|
||||
/**
|
||||
* Find the user's corresponding public key by email
|
||||
*/
|
||||
PublicKeyDAO.prototype.getByUserId = function(userId, callback) {
|
||||
var uri = '/publickey/user/' + userId;
|
||||
|
||||
this._restDao.get({
|
||||
uri: uri
|
||||
}, function(err, keys) {
|
||||
// not found
|
||||
if (err && err.code === 404) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
this._restDao.get({
|
||||
uri: uri
|
||||
}, function(err, keys) {
|
||||
// not found
|
||||
if (err && err.code === 404) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!keys || keys.length < 1) {
|
||||
// 'No public key for that user!'
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
if (!keys || keys.length < 1) {
|
||||
// 'No public key for that user!'
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
if (keys.length > 1) {
|
||||
callback({
|
||||
errMsg: 'That user has multiple public keys!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (keys.length > 1) {
|
||||
callback({
|
||||
errMsg: 'That user has multiple public keys!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, keys[0]);
|
||||
});
|
||||
};
|
||||
callback(null, keys[0]);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Persist the user's publc key
|
||||
*/
|
||||
PublicKeyDAO.prototype.put = function(pubkey, callback) {
|
||||
var uri = '/publickey/user/' + pubkey.userId + '/key/' + pubkey._id;
|
||||
this._restDao.put(pubkey, uri, callback);
|
||||
};
|
||||
/**
|
||||
* Persist the user's publc key
|
||||
*/
|
||||
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) {
|
||||
var uri = '/publickey/key/' + keyId;
|
||||
this._restDao.remove(uri, callback);
|
||||
};
|
||||
/**
|
||||
* Delete the public key from the cloud storage service
|
||||
*/
|
||||
PublicKeyDAO.prototype.remove = function(keyId, callback) {
|
||||
var uri = '/publickey/key/' + keyId;
|
||||
this._restDao.remove(uri, callback);
|
||||
};
|
||||
|
||||
return PublicKeyDAO;
|
||||
});
|
||||
module.exports = PublicKeyDAO;
|
|
@ -1,123 +1,121 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
'use strict';
|
||||
|
||||
var config = require('js/app-config').config;
|
||||
var config = require('../app-config').config;
|
||||
|
||||
var RestDAO = function(baseUri) {
|
||||
if (baseUri) {
|
||||
this._baseUri = baseUri;
|
||||
} else {
|
||||
this._baseUri = config.cloudUrl;
|
||||
}
|
||||
};
|
||||
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) {
|
||||
options.method = 'GET';
|
||||
this._processRequest(options, callback);
|
||||
};
|
||||
/**
|
||||
* 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) {
|
||||
options.method = 'GET';
|
||||
this._processRequest(options, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* POST (create) request
|
||||
*/
|
||||
RestDAO.prototype.post = function(item, uri, callback) {
|
||||
this._processRequest({
|
||||
method: 'POST',
|
||||
payload: item,
|
||||
uri: uri
|
||||
}, callback);
|
||||
};
|
||||
/**
|
||||
* POST (create) request
|
||||
*/
|
||||
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) {
|
||||
this._processRequest({
|
||||
method: 'PUT',
|
||||
payload: item,
|
||||
uri: uri
|
||||
}, callback);
|
||||
};
|
||||
/**
|
||||
* PUT (update) request
|
||||
*/
|
||||
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) {
|
||||
this._processRequest({
|
||||
method: 'DELETE',
|
||||
uri: uri
|
||||
}, callback);
|
||||
};
|
||||
/**
|
||||
* DELETE (remove) request
|
||||
*/
|
||||
RestDAO.prototype.remove = function(uri, callback) {
|
||||
this._processRequest({
|
||||
method: 'DELETE',
|
||||
uri: uri
|
||||
}, callback);
|
||||
};
|
||||
|
||||
//
|
||||
// helper functions
|
||||
//
|
||||
//
|
||||
// helper functions
|
||||
//
|
||||
|
||||
RestDAO.prototype._processRequest = function(options, callback) {
|
||||
var xhr, format;
|
||||
RestDAO.prototype._processRequest = function(options, callback) {
|
||||
var xhr, format;
|
||||
|
||||
if (typeof options.uri === 'undefined') {
|
||||
callback({
|
||||
code: 400,
|
||||
errMsg: 'Bad Request! URI is a mandatory parameter.'
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (typeof options.uri === 'undefined') {
|
||||
callback({
|
||||
code: 400,
|
||||
errMsg: 'Bad Request! URI is a mandatory parameter.'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
options.type = options.type || 'json';
|
||||
options.type = options.type || 'json';
|
||||
|
||||
if (options.type === 'json') {
|
||||
format = 'application/json';
|
||||
} else if (options.type === 'xml') {
|
||||
format = 'application/xml';
|
||||
} else if (options.type === 'text') {
|
||||
format = 'text/plain';
|
||||
} else {
|
||||
callback({
|
||||
code: 400,
|
||||
errMsg: 'Bad Request! Unhandled data type.'
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (options.type === 'json') {
|
||||
format = 'application/json';
|
||||
} else if (options.type === 'xml') {
|
||||
format = 'application/xml';
|
||||
} else if (options.type === 'text') {
|
||||
format = 'text/plain';
|
||||
} else {
|
||||
callback({
|
||||
code: 400,
|
||||
errMsg: 'Bad Request! Unhandled data type.'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
xhr = new XMLHttpRequest();
|
||||
xhr.open(options.method, this._baseUri + options.uri);
|
||||
xhr.setRequestHeader('Accept', format);
|
||||
xhr.setRequestHeader('Content-Type', format);
|
||||
xhr = new XMLHttpRequest();
|
||||
xhr.open(options.method, this._baseUri + options.uri);
|
||||
xhr.setRequestHeader('Accept', format);
|
||||
xhr.setRequestHeader('Content-Type', format);
|
||||
|
||||
xhr.onload = function() {
|
||||
var res;
|
||||
xhr.onload = function() {
|
||||
var res;
|
||||
|
||||
if (xhr.readyState === 4 && (xhr.status === 200 || xhr.status === 201 || xhr.status === 304)) {
|
||||
if (options.type === 'json') {
|
||||
res = xhr.responseText ? JSON.parse(xhr.responseText) : xhr.responseText;
|
||||
} else {
|
||||
res = xhr.responseText;
|
||||
}
|
||||
|
||||
callback(null, res, xhr.status);
|
||||
return;
|
||||
if (xhr.readyState === 4 && (xhr.status === 200 || xhr.status === 201 || xhr.status === 304)) {
|
||||
if (options.type === 'json') {
|
||||
res = xhr.responseText ? JSON.parse(xhr.responseText) : xhr.responseText;
|
||||
} else {
|
||||
res = xhr.responseText;
|
||||
}
|
||||
|
||||
callback({
|
||||
code: xhr.status,
|
||||
errMsg: xhr.statusText
|
||||
});
|
||||
};
|
||||
callback(null, res, xhr.status);
|
||||
return;
|
||||
}
|
||||
|
||||
xhr.onerror = function() {
|
||||
callback({
|
||||
code: 42,
|
||||
errMsg: 'Error calling ' + options.method + ' on ' + options.uri
|
||||
});
|
||||
};
|
||||
|
||||
xhr.send(options.payload ? JSON.stringify(options.payload) : undefined);
|
||||
callback({
|
||||
code: xhr.status,
|
||||
errMsg: xhr.statusText
|
||||
});
|
||||
};
|
||||
|
||||
return RestDAO;
|
||||
});
|
||||
xhr.onerror = function() {
|
||||
callback({
|
||||
code: 42,
|
||||
errMsg: 'Error calling ' + options.method + ' on ' + options.uri
|
||||
});
|
||||
};
|
||||
|
||||
xhr.send(options.payload ? JSON.stringify(options.payload) : undefined);
|
||||
};
|
||||
|
||||
module.exports = RestDAO;
|
|
@ -1,57 +1,55 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
'use strict';
|
||||
|
||||
var axe = require('axe'),
|
||||
DEBUG_TAG = 'backbutton handler';
|
||||
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;
|
||||
* if the navigation is open, close it;
|
||||
* if there's nothing else open, shut down the app;
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
var backBtnHandler = {
|
||||
attachHandler: function(scope) {
|
||||
this.scope = scope;
|
||||
},
|
||||
start: function() {
|
||||
document.addEventListener("backbutton", handleBackButton, false);
|
||||
},
|
||||
stop: function() {
|
||||
document.removeEventListener("backbutton", handleBackButton, false);
|
||||
}
|
||||
};
|
||||
|
||||
function handleBackButton(event) {
|
||||
axe.debug(DEBUG_TAG, 'back button pressed');
|
||||
|
||||
// this disarms the default behavior which we NEVER want
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
if (backBtnHandler.scope.state.lightbox) {
|
||||
// closes the lightbox (error msgs, writer, ...)
|
||||
backBtnHandler.scope.state.lightbox = undefined;
|
||||
axe.debug(DEBUG_TAG, 'lightbox closed');
|
||||
backBtnHandler.scope.$apply();
|
||||
} else if (backBtnHandler.scope.state.read && backBtnHandler.scope.state.read.open) {
|
||||
// closes the reader
|
||||
backBtnHandler.scope.state.read.toggle(false);
|
||||
axe.debug(DEBUG_TAG, 'reader closed');
|
||||
backBtnHandler.scope.$apply();
|
||||
} else if (backBtnHandler.scope.state.nav && backBtnHandler.scope.state.nav.open) {
|
||||
// closes the navigation
|
||||
backBtnHandler.scope.state.nav.toggle(false);
|
||||
axe.debug(DEBUG_TAG, 'navigation closed');
|
||||
backBtnHandler.scope.$apply();
|
||||
} else {
|
||||
// exits the app
|
||||
navigator.app.exitApp();
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
* if the navigation is open, close it;
|
||||
* if there's nothing else open, shut down the app;
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
var backBtnHandler = {
|
||||
attachHandler: function(scope) {
|
||||
this.scope = scope;
|
||||
},
|
||||
start: function() {
|
||||
document.addEventListener("backbutton", handleBackButton, false);
|
||||
},
|
||||
stop: function() {
|
||||
document.removeEventListener("backbutton", handleBackButton, false);
|
||||
}
|
||||
};
|
||||
|
||||
return backBtnHandler;
|
||||
});
|
||||
function handleBackButton(event) {
|
||||
axe.debug(DEBUG_TAG, 'back button pressed');
|
||||
|
||||
// this disarms the default behavior which we NEVER want
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
if (backBtnHandler.scope.state.lightbox) {
|
||||
// closes the lightbox (error msgs, writer, ...)
|
||||
backBtnHandler.scope.state.lightbox = undefined;
|
||||
axe.debug(DEBUG_TAG, 'lightbox closed');
|
||||
backBtnHandler.scope.$apply();
|
||||
} else if (backBtnHandler.scope.state.read && backBtnHandler.scope.state.read.open) {
|
||||
// closes the reader
|
||||
backBtnHandler.scope.state.read.toggle(false);
|
||||
axe.debug(DEBUG_TAG, 'reader closed');
|
||||
backBtnHandler.scope.$apply();
|
||||
} else if (backBtnHandler.scope.state.nav && backBtnHandler.scope.state.nav.open) {
|
||||
// closes the navigation
|
||||
backBtnHandler.scope.state.nav.toggle(false);
|
||||
axe.debug(DEBUG_TAG, 'navigation closed');
|
||||
backBtnHandler.scope.$apply();
|
||||
} else {
|
||||
// exits the app
|
||||
navigator.app.exitApp();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = backBtnHandler;
|
|
@ -1,295 +1,298 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
'use strict';
|
||||
|
||||
var TCPSocket = require('tcp-socket'),
|
||||
appConfig = require('js/app-config'),
|
||||
cfg = appConfig.config,
|
||||
strings = appConfig.string,
|
||||
ImapClient = require('imap-client'),
|
||||
SmtpClient = require('smtpclient');
|
||||
var TCPSocket = require('tcp-socket'),
|
||||
appConfig = require('../app-config'),
|
||||
cfg = appConfig.config,
|
||||
strings = appConfig.string,
|
||||
ImapClient = require('imap-client'),
|
||||
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() {};
|
||||
/**
|
||||
* 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() {};
|
||||
|
||||
|
||||
//
|
||||
// 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';
|
||||
|
||||
/**
|
||||
* Configures the connection doctor
|
||||
*
|
||||
* @param {Object} credentials.imap IMAP configuration (host:string, port:number, secure:boolean, ignoreTLS:boolean)
|
||||
* @param {Object} credentials.smtp SMTP configuration (host:string, port:number, secure:boolean, ignoreTLS:boolean)
|
||||
* @param {String} credentials.username
|
||||
* @param {String} credentials.password
|
||||
*/
|
||||
ConnectionDoctor.prototype.configure = function(credentials) {
|
||||
this.credentials = credentials;
|
||||
//
|
||||
// Public API
|
||||
//
|
||||
|
||||
// internal members
|
||||
this._imap = new ImapClient({
|
||||
host: this.credentials.imap.host,
|
||||
port: this.credentials.imap.port,
|
||||
secure: this.credentials.imap.secure,
|
||||
ignoreTLS: this.credentials.imap.ignoreTLS,
|
||||
ca: this.credentials.imap.ca,
|
||||
auth: {
|
||||
user: this.credentials.username,
|
||||
pass: this.credentials.password,
|
||||
xoauth2: this.credentials.xoauth2
|
||||
}
|
||||
});
|
||||
/**
|
||||
* Configures the connection doctor
|
||||
*
|
||||
* @param {Object} credentials.imap IMAP configuration (host:string, port:number, secure:boolean, ignoreTLS:boolean)
|
||||
* @param {Object} credentials.smtp SMTP configuration (host:string, port:number, secure:boolean, ignoreTLS:boolean)
|
||||
* @param {String} credentials.username
|
||||
* @param {String} credentials.password
|
||||
*/
|
||||
ConnectionDoctor.prototype.configure = function(credentials) {
|
||||
this.credentials = credentials;
|
||||
|
||||
this._smtp = new SmtpClient(this.credentials.smtp.host, this.credentials.smtp.port, {
|
||||
useSecureTransport: this.credentials.smtp.secure,
|
||||
ignoreTLS: this.credentials.smtp.ignoreTLS,
|
||||
ca: this.credentials.smtp.ca,
|
||||
auth: {
|
||||
user: this.credentials.username,
|
||||
pass: this.credentials.password,
|
||||
xoauth2: this.credentials.xoauth2
|
||||
}
|
||||
});
|
||||
};
|
||||
// internal members
|
||||
this._imap = new ImapClient({
|
||||
host: this.credentials.imap.host,
|
||||
port: this.credentials.imap.port,
|
||||
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,
|
||||
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
|
||||
* 3) Login to the server
|
||||
* 4) Perform some basic commands (e.g. list folders)
|
||||
* 5) Exposes error codes
|
||||
*
|
||||
* @param {Function} callback(error) Invoked when the test suite passed, or with an error object if something went wrong
|
||||
*/
|
||||
ConnectionDoctor.prototype.check = function(callback) {
|
||||
var self = this;
|
||||
this._smtp = new SmtpClient(this.credentials.smtp.host, this.credentials.smtp.port, {
|
||||
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
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (!self.credentials) {
|
||||
return callback(new Error('You need to configure() the connection doctor first!'));
|
||||
/**
|
||||
* It conducts the following tests for IMAP and SMTP, respectively:
|
||||
* 1) Check if browser is online
|
||||
* 2) Connect to host:port via TCP/TLS
|
||||
* 3) Login to the server
|
||||
* 4) Perform some basic commands (e.g. list folders)
|
||||
* 5) Exposes error codes
|
||||
*
|
||||
* @param {Function} callback(error) Invoked when the test suite passed, or with an error object if something went wrong
|
||||
*/
|
||||
ConnectionDoctor.prototype.check = function(callback) {
|
||||
var self = this;
|
||||
|
||||
if (!self.credentials) {
|
||||
return callback(new Error('You need to configure() the connection doctor first!'));
|
||||
}
|
||||
|
||||
self._checkOnline(function(error) {
|
||||
if (error) {
|
||||
return callback(error);
|
||||
}
|
||||
|
||||
self._checkOnline(function(error) {
|
||||
self._checkReachable(self.credentials.imap, function(error) {
|
||||
if (error) {
|
||||
return callback(error);
|
||||
}
|
||||
|
||||
self._checkReachable(self.credentials.imap, function(error) {
|
||||
self._checkReachable(self.credentials.smtp, function(error) {
|
||||
if (error) {
|
||||
return callback(error);
|
||||
}
|
||||
|
||||
self._checkReachable(self.credentials.smtp, function(error) {
|
||||
self._checkImap(function(error) {
|
||||
if (error) {
|
||||
return callback(error);
|
||||
}
|
||||
|
||||
self._checkImap(function(error) {
|
||||
if (error) {
|
||||
return callback(error);
|
||||
}
|
||||
|
||||
self._checkSmtp(callback);
|
||||
});
|
||||
self._checkSmtp(callback);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// 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) {
|
||||
if (navigator.onLine) {
|
||||
callback();
|
||||
} else {
|
||||
callback(createError(OFFLINE, strings.connDocOffline));
|
||||
/**
|
||||
* 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) {
|
||||
if (navigator.onLine) {
|
||||
callback();
|
||||
} else {
|
||||
callback(createError(OFFLINE, strings.connDocOffline));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if a host is reachable via TCP
|
||||
*
|
||||
* @param {String} options.host
|
||||
* @param {Number} options.port
|
||||
* @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) {
|
||||
var socket,
|
||||
error, // remember the error message
|
||||
timeout, // remember the timeout object
|
||||
host = options.host + ':' + options.port,
|
||||
hasTimedOut = false; // prevents multiple callbacks
|
||||
|
||||
timeout = setTimeout(function() {
|
||||
hasTimedOut = true;
|
||||
callback(createError(HOST_TIMEOUT, strings.connDocHostTimeout.replace('{0}', host).replace('{1}', cfg.connDocTimeout)));
|
||||
}, cfg.connDocTimeout);
|
||||
|
||||
socket = TCPSocket.open(options.host, options.port, {
|
||||
binaryType: 'arraybuffer',
|
||||
useSecureTransport: options.secure,
|
||||
ca: options.ca,
|
||||
tlsWorkerPath: WORKER_PATH
|
||||
});
|
||||
|
||||
socket.ondata = function() {}; // we don't actually care about the data
|
||||
|
||||
// [WO-625] Mozilla forbids extensions to the TCPSocket object,
|
||||
// throws an exception when assigned unexpected callback functions.
|
||||
// The exception can be safely ignored since we need the callback
|
||||
// for the other shims
|
||||
try {
|
||||
socket.oncert = function() {
|
||||
if (options.ca) {
|
||||
// the certificate we already have is outdated
|
||||
error = createError(TLS_WRONG_CERT, strings.connDocTlsWrongCert.replace('{0}', host));
|
||||
}
|
||||
};
|
||||
} catch (e) {}
|
||||
|
||||
socket.onerror = function(e) {
|
||||
if (!error) {
|
||||
error = createError(HOST_UNREACHABLE, strings.connDocHostUnreachable.replace('{0}', host), e.data);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if a host is reachable via TCP
|
||||
*
|
||||
* @param {String} options.host
|
||||
* @param {Number} options.port
|
||||
* @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) {
|
||||
var socket,
|
||||
error, // remember the error message
|
||||
timeout, // remember the timeout object
|
||||
host = options.host + ':' + options.port,
|
||||
hasTimedOut = false; // prevents multiple callbacks
|
||||
|
||||
timeout = setTimeout(function() {
|
||||
hasTimedOut = true;
|
||||
callback(createError(HOST_TIMEOUT, strings.connDocHostTimeout.replace('{0}', host).replace('{1}', cfg.connDocTimeout)));
|
||||
}, cfg.connDocTimeout);
|
||||
|
||||
socket = TCPSocket.open(options.host, options.port, {
|
||||
binaryType: 'arraybuffer',
|
||||
useSecureTransport: options.secure,
|
||||
ca: options.ca
|
||||
});
|
||||
|
||||
socket.ondata = function() {}; // we don't actually care about the data
|
||||
|
||||
// [WO-625] Mozilla forbids extensions to the TCPSocket object,
|
||||
// throws an exception when assigned unexpected callback functions.
|
||||
// The exception can be safely ignored since we need the callback
|
||||
// for the other shims
|
||||
try {
|
||||
socket.oncert = function() {
|
||||
if (options.ca) {
|
||||
// the certificate we already have is outdated
|
||||
error = createError(TLS_WRONG_CERT, strings.connDocTlsWrongCert.replace('{0}', host));
|
||||
}
|
||||
};
|
||||
} catch (e) {}
|
||||
|
||||
socket.onerror = function(e) {
|
||||
if (!error) {
|
||||
error = createError(HOST_UNREACHABLE, strings.connDocHostUnreachable.replace('{0}', host), e.data);
|
||||
}
|
||||
};
|
||||
|
||||
socket.onopen = function() {
|
||||
socket.close();
|
||||
};
|
||||
|
||||
socket.onclose = function() {
|
||||
if (!hasTimedOut) {
|
||||
clearTimeout(timeout);
|
||||
callback(error);
|
||||
}
|
||||
};
|
||||
socket.onopen = function() {
|
||||
socket.close();
|
||||
};
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
var self = this,
|
||||
loggedIn = false,
|
||||
host = self.credentials.imap.host + ':' + self.credentials.imap.port;
|
||||
socket.onclose = function() {
|
||||
if (!hasTimedOut) {
|
||||
clearTimeout(timeout);
|
||||
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) {
|
||||
var self = this,
|
||||
loggedIn = false,
|
||||
host = self.credentials.imap.host + ':' + self.credentials.imap.port;
|
||||
|
||||
|
||||
self._imap.onCert = function(pemEncodedCert) {
|
||||
if (!self.credentials.imap.ca) {
|
||||
self.credentials.imap.ca = pemEncodedCert;
|
||||
self._imap.onCert = function(pemEncodedCert) {
|
||||
if (!self.credentials.imap.ca) {
|
||||
self.credentials.imap.ca = pemEncodedCert;
|
||||
}
|
||||
};
|
||||
|
||||
// login and logout do not use error objects in the callback, but rather invoke
|
||||
// the global onError handler, so we need to track if login was successful
|
||||
self._imap.onError = function(error) {
|
||||
if (!loggedIn) {
|
||||
callback(createError(AUTH_REJECTED, strings.connDocAuthRejected.replace('{0}', host), error));
|
||||
} else {
|
||||
callback(createError(GENERIC_ERROR, strings.connDocGenericError.replace('{0}', host).replace('{1}', error.message), error));
|
||||
}
|
||||
};
|
||||
|
||||
self._imap.login(function() {
|
||||
loggedIn = true;
|
||||
|
||||
self._imap.listWellKnownFolders(function(error, wellKnownFolders) {
|
||||
if (error) {
|
||||
return callback(createError(GENERIC_ERROR, strings.connDocGenericError.replace('{0}', host).replace('{1}', error.message), error));
|
||||
}
|
||||
};
|
||||
|
||||
// login and logout do not use error objects in the callback, but rather invoke
|
||||
// the global onError handler, so we need to track if login was successful
|
||||
self._imap.onError = function(error) {
|
||||
if (!loggedIn) {
|
||||
callback(createError(AUTH_REJECTED, strings.connDocAuthRejected.replace('{0}', host), error));
|
||||
} else {
|
||||
callback(createError(GENERIC_ERROR, strings.connDocGenericError.replace('{0}', host).replace('{1}', error.message), error));
|
||||
if (wellKnownFolders.Inbox.length === 0) {
|
||||
// the client needs at least an inbox folder to work properly
|
||||
return callback(createError(NO_INBOX, strings.connDocNoInbox.replace('{0}', host)));
|
||||
}
|
||||
};
|
||||
|
||||
self._imap.login(function() {
|
||||
loggedIn = true;
|
||||
|
||||
self._imap.listWellKnownFolders(function(error, wellKnownFolders) {
|
||||
if (error) {
|
||||
return callback(createError(GENERIC_ERROR, strings.connDocGenericError.replace('{0}', host).replace('{1}', error.message), error));
|
||||
}
|
||||
|
||||
if (wellKnownFolders.Inbox.length === 0) {
|
||||
// the client needs at least an inbox folder to work properly
|
||||
return callback(createError(NO_INBOX, strings.connDocNoInbox.replace('{0}', host)));
|
||||
}
|
||||
|
||||
self._imap.logout(function() {
|
||||
callback();
|
||||
});
|
||||
self._imap.logout(function() {
|
||||
callback();
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
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
|
||||
|
||||
self._smtp.oncert = function(pemEncodedCert) {
|
||||
if (!self.credentials.smtp.ca) {
|
||||
self.credentials.smtp.ca = pemEncodedCert;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
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
|
||||
|
||||
self._smtp.oncert = function(pemEncodedCert) {
|
||||
if (!self.credentials.smtp.ca) {
|
||||
self.credentials.smtp.ca = pemEncodedCert;
|
||||
}
|
||||
};
|
||||
|
||||
self._smtp.onerror = function(error) {
|
||||
if (error) {
|
||||
errored = true;
|
||||
callback(createError(AUTH_REJECTED, strings.connDocAuthRejected.replace('{0}', host), error));
|
||||
}
|
||||
};
|
||||
|
||||
self._smtp.onidle = function() {
|
||||
self._smtp.quit();
|
||||
};
|
||||
|
||||
self._smtp.onclose = function() {
|
||||
if (!errored) {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
self._smtp.connect();
|
||||
self._smtp.onerror = function(error) {
|
||||
if (error) {
|
||||
errored = true;
|
||||
callback(createError(AUTH_REJECTED, strings.connDocAuthRejected.replace('{0}', host), error));
|
||||
}
|
||||
};
|
||||
|
||||
self._smtp.onidle = function() {
|
||||
self._smtp.quit();
|
||||
};
|
||||
|
||||
//
|
||||
// Helper Functions
|
||||
//
|
||||
self._smtp.onclose = function() {
|
||||
if (!errored) {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
function createError(code, message, underlyingError) {
|
||||
var error = new Error(message);
|
||||
error.code = code;
|
||||
error.underlyingError = underlyingError;
|
||||
self._smtp.connect();
|
||||
};
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
return ConnectionDoctor;
|
||||
});
|
||||
//
|
||||
// Helper Functions
|
||||
//
|
||||
|
||||
function createError(code, message, underlyingError) {
|
||||
var error = new Error(message);
|
||||
error.code = code;
|
||||
error.underlyingError = underlyingError;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
module.exports = ConnectionDoctor;
|
||||
|
|
|
@ -1,60 +1,58 @@
|
|||
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) {
|
||||
var contentType = options.contentType || 'application/octet-stream';
|
||||
var filename = options.filename || 'file';
|
||||
var content = options.content;
|
||||
var a = document.createElement('a');
|
||||
var supportsBlob;
|
||||
dl.createDownload = function(options) {
|
||||
var contentType = options.contentType || 'application/octet-stream';
|
||||
var filename = options.filename || 'file';
|
||||
var content = options.content;
|
||||
var a = document.createElement('a');
|
||||
var supportsBlob;
|
||||
|
||||
try {
|
||||
supportsBlob = !!new Blob();
|
||||
} catch (e) {}
|
||||
try {
|
||||
supportsBlob = !!new Blob();
|
||||
} catch (e) {}
|
||||
|
||||
if (typeof a.download !== "undefined" && supportsBlob) {
|
||||
// ff 30+, chrome 27+ (android: 37+)
|
||||
document.body.appendChild(a);
|
||||
a.style = "display: none";
|
||||
a.href = window.URL.createObjectURL(new Blob([content], {
|
||||
type: contentType
|
||||
}));
|
||||
a.download = filename;
|
||||
a.click();
|
||||
setTimeout(function() {
|
||||
window.URL.revokeObjectURL(a.href);
|
||||
document.body.removeChild(a);
|
||||
}, 10); // arbitrary, just get it off the main thread
|
||||
} else if (window.navigator.msSaveBlob) {
|
||||
// ie 10+
|
||||
window.navigator.msSaveBlob(new Blob([content], {
|
||||
type: contentType
|
||||
}), filename);
|
||||
} else if (supportsBlob) {
|
||||
// safari actually makes no sense:
|
||||
// - you can't open a new window
|
||||
// - the file system api is dead
|
||||
// - download attribute doesn't work
|
||||
// - behaves randomly (opens a new tab or doesn't, downloads stuff or doesn't, ...)
|
||||
var url = window.URL.createObjectURL(new Blob([content], {
|
||||
type: contentType
|
||||
}));
|
||||
var newTab = window.open(url, "_blank");
|
||||
if (!newTab) {
|
||||
window.location.href = url;
|
||||
}
|
||||
} else {
|
||||
// anything else, where anything at all is better than nothing
|
||||
if (typeof content !== "string" && content.buffer) {
|
||||
content = util.arrBuf2BinStr(content.buffer);
|
||||
}
|
||||
window.open('data:' + contentType + ';base64,' + btoa(content), "_blank");
|
||||
if (typeof a.download !== "undefined" && supportsBlob) {
|
||||
// ff 30+, chrome 27+ (android: 37+)
|
||||
document.body.appendChild(a);
|
||||
a.style = "display: none";
|
||||
a.href = window.URL.createObjectURL(new Blob([content], {
|
||||
type: contentType
|
||||
}));
|
||||
a.download = filename;
|
||||
a.click();
|
||||
setTimeout(function() {
|
||||
window.URL.revokeObjectURL(a.href);
|
||||
document.body.removeChild(a);
|
||||
}, 10); // arbitrary, just get it off the main thread
|
||||
} else if (window.navigator.msSaveBlob) {
|
||||
// ie 10+
|
||||
window.navigator.msSaveBlob(new Blob([content], {
|
||||
type: contentType
|
||||
}), filename);
|
||||
} else if (supportsBlob) {
|
||||
// safari actually makes no sense:
|
||||
// - you can't open a new window
|
||||
// - the file system api is dead
|
||||
// - download attribute doesn't work
|
||||
// - behaves randomly (opens a new tab or doesn't, downloads stuff or doesn't, ...)
|
||||
var url = window.URL.createObjectURL(new Blob([content], {
|
||||
type: contentType
|
||||
}));
|
||||
var newTab = window.open(url, "_blank");
|
||||
if (!newTab) {
|
||||
window.location.href = url;
|
||||
}
|
||||
};
|
||||
} else {
|
||||
// anything else, where anything at all is better than nothing
|
||||
if (typeof content !== "string" && content.buffer) {
|
||||
content = util.arrBuf2BinStr(content.buffer);
|
||||
}
|
||||
window.open('data:' + contentType + ';base64,' + btoa(content), "_blank");
|
||||
}
|
||||
};
|
||||
|
||||
return dl;
|
||||
});
|
||||
module.exports = dl;
|
|
@ -1,35 +1,33 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
'use strict';
|
||||
|
||||
var axe = require('axe');
|
||||
var axe = require('axe-logger');
|
||||
|
||||
var er = {};
|
||||
er.attachHandler = function(scope) {
|
||||
scope.onError = function(options) {
|
||||
if (!options) {
|
||||
scope.$apply();
|
||||
return;
|
||||
}
|
||||
var er = {};
|
||||
er.attachHandler = function(scope) {
|
||||
scope.onError = function(options) {
|
||||
if (!options) {
|
||||
scope.$apply();
|
||||
return;
|
||||
}
|
||||
|
||||
axe.error((options.errMsg || options.message) + (options.stack ? ('\n' + options.stack) : ''));
|
||||
axe.error((options.errMsg || options.message) + (options.stack ? ('\n' + options.stack) : ''));
|
||||
|
||||
scope.state.dialog = {
|
||||
open: true,
|
||||
title: options.title || 'Error',
|
||||
message: options.errMsg || options.message,
|
||||
faqLink: options.faqLink,
|
||||
positiveBtnStr: options.positiveBtnStr || 'Ok',
|
||||
negativeBtnStr: options.negativeBtnStr || 'Cancel',
|
||||
showNegativeBtn: options.showNegativeBtn || false,
|
||||
showBugReporter: (typeof options.showBugReporter !== 'undefined' ? options.showBugReporter : !options.title), // if title is set, presume it's not an error by default
|
||||
callback: options.callback
|
||||
};
|
||||
// don't call apply for synchronous calls
|
||||
if (!options.sync) {
|
||||
scope.$apply();
|
||||
}
|
||||
scope.state.dialog = {
|
||||
open: true,
|
||||
title: options.title || 'Error',
|
||||
message: options.errMsg || options.message,
|
||||
faqLink: options.faqLink,
|
||||
positiveBtnStr: options.positiveBtnStr || 'Ok',
|
||||
negativeBtnStr: options.negativeBtnStr || 'Cancel',
|
||||
showNegativeBtn: options.showNegativeBtn || false,
|
||||
showBugReporter: (typeof options.showBugReporter !== 'undefined' ? options.showBugReporter : !options.title), // if title is set, presume it's not an error by default
|
||||
callback: options.callback
|
||||
};
|
||||
// don't call apply for synchronous calls
|
||||
if (!options.sync) {
|
||||
scope.$apply();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
return er;
|
||||
});
|
||||
module.exports = er;
|
|
@ -1,60 +1,58 @@
|
|||
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
|
||||
* @param {String} options.message The notification message
|
||||
* @param {Number} options.timeout (optional) Timeout when the notification is closed in milliseconds
|
||||
* @param {Function} options.onClick (optional) callback when the notification is clicked
|
||||
* @returns {Notification} A notification instance
|
||||
*/
|
||||
notif.create = function(options) {
|
||||
options.onClick = options.onClick || function() {};
|
||||
|
||||
if (!window.Notification) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a notification. Requests permission if not already granted
|
||||
*
|
||||
* @param {String} options.title The notification title
|
||||
* @param {String} options.message The notification message
|
||||
* @param {Number} options.timeout (optional) Timeout when the notification is closed in milliseconds
|
||||
* @param {Function} options.onClick (optional) callback when the notification is clicked
|
||||
* @returns {Notification} A notification instance
|
||||
*/
|
||||
self.create = function(options) {
|
||||
options.onClick = options.onClick || function() {};
|
||||
|
||||
if (!window.Notification) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self.hasPermission) {
|
||||
// don't wait until callback returns
|
||||
Notification.requestPermission(function(permission) {
|
||||
if (permission === "granted") {
|
||||
self.hasPermission = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var notification = new Notification(options.title, {
|
||||
body: options.message,
|
||||
icon: cfg.iconPath
|
||||
if (!notif.hasPermission) {
|
||||
// don't wait until callback returns
|
||||
Notification.requestPermission(function(permission) {
|
||||
if (permission === "granted") {
|
||||
notif.hasPermission = true;
|
||||
}
|
||||
});
|
||||
notification.onclick = function() {
|
||||
window.focus();
|
||||
options.onClick();
|
||||
};
|
||||
}
|
||||
|
||||
if (options.timeout > 0) {
|
||||
setTimeout(function() {
|
||||
notification.close();
|
||||
}, options.timeout);
|
||||
}
|
||||
|
||||
return notification;
|
||||
var notification = new Notification(options.title, {
|
||||
body: options.message,
|
||||
icon: cfg.iconPath
|
||||
});
|
||||
notification.onclick = function() {
|
||||
window.focus();
|
||||
options.onClick();
|
||||
};
|
||||
|
||||
self.close = function(notification) {
|
||||
notification.close();
|
||||
};
|
||||
if (options.timeout > 0) {
|
||||
setTimeout(function() {
|
||||
notification.close();
|
||||
}, options.timeout);
|
||||
}
|
||||
|
||||
return self;
|
||||
});
|
||||
return notification;
|
||||
};
|
||||
|
||||
notif.close = function(notification) {
|
||||
notification.close();
|
||||
};
|
||||
|
||||
module.exports = notif;
|
|
@ -1,101 +1,99 @@
|
|||
define(function() {
|
||||
'use strict';
|
||||
'use strict';
|
||||
|
||||
var OAuth = function(googleApi) {
|
||||
this._googleApi = googleApi;
|
||||
var OAuth = function(googleApi) {
|
||||
this._googleApi = googleApi;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if chrome.identity api is supported
|
||||
* @return {Boolean} If is supported
|
||||
*/
|
||||
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) {
|
||||
var idOptions = {
|
||||
interactive: true
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if chrome.identity api is supported
|
||||
* @return {Boolean} If is supported
|
||||
*/
|
||||
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) {
|
||||
var idOptions = {
|
||||
interactive: true
|
||||
};
|
||||
|
||||
// check which runtime the app is running under
|
||||
chrome.runtime.getPlatformInfo(function(platformInfo) {
|
||||
if (chrome.runtime.lastError || !platformInfo) {
|
||||
callback(new Error('Error getting chrome platform info!'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (emailAddress && platformInfo.os.indexOf('android') !== -1) {
|
||||
// set accountHint so that native Android account picker does not show up each time
|
||||
idOptions.accountHint = emailAddress;
|
||||
}
|
||||
|
||||
// get OAuth Token from chrome
|
||||
chrome.identity.getAuthToken(idOptions, function(token) {
|
||||
if (chrome.runtime.lastError || !token) {
|
||||
callback({
|
||||
errMsg: 'Error fetching an OAuth token for the user!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
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) {
|
||||
var self = this;
|
||||
|
||||
if (!options.oldToken) {
|
||||
callback(new Error('oldToken option not set!'));
|
||||
// check which runtime the app is running under
|
||||
chrome.runtime.getPlatformInfo(function(platformInfo) {
|
||||
if (chrome.runtime.lastError || !platformInfo) {
|
||||
callback(new Error('Error getting chrome platform info!'));
|
||||
return;
|
||||
}
|
||||
|
||||
// remove cached token
|
||||
chrome.identity.removeCachedAuthToken({
|
||||
token: options.oldToken
|
||||
}, 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) {
|
||||
if (!token) {
|
||||
callback({
|
||||
errMsg: 'Invalid OAuth token!'
|
||||
});
|
||||
return;
|
||||
if (emailAddress && platformInfo.os.indexOf('android') !== -1) {
|
||||
// set accountHint so that native Android account picker does not show up each time
|
||||
idOptions.accountHint = emailAddress;
|
||||
}
|
||||
|
||||
// fetch gmail user's email address from the Google Authorization Server
|
||||
this._googleApi.get({
|
||||
uri: '/oauth2/v3/userinfo?access_token=' + token
|
||||
}, function(err, info) {
|
||||
if (err || !info || !info.email) {
|
||||
// get OAuth Token from chrome
|
||||
chrome.identity.getAuthToken(idOptions, function(token) {
|
||||
if (chrome.runtime.lastError || !token) {
|
||||
callback({
|
||||
errMsg: 'Error looking up email address on google api!'
|
||||
errMsg: 'Error fetching an OAuth token for the user!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, info.email);
|
||||
callback(null, token);
|
||||
});
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
return OAuth;
|
||||
});
|
||||
/**
|
||||
* 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) {
|
||||
var self = this;
|
||||
|
||||
if (!options.oldToken) {
|
||||
callback(new Error('oldToken option not set!'));
|
||||
return;
|
||||
}
|
||||
|
||||
// remove cached token
|
||||
chrome.identity.removeCachedAuthToken({
|
||||
token: options.oldToken
|
||||
}, 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) {
|
||||
if (!token) {
|
||||
callback({
|
||||
errMsg: 'Invalid OAuth token!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// fetch gmail user's email address from the Google Authorization Server
|
||||
this._googleApi.get({
|
||||
uri: '/oauth2/v3/userinfo?access_token=' + token
|
||||
}, function(err, info) {
|
||||
if (err || !info || !info.email) {
|
||||
callback({
|
||||
errMsg: 'Error looking up email address on google api!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, info.email);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = OAuth;
|
|
@ -1,132 +1,130 @@
|
|||
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) {
|
||||
this._appConfigStorage = appConfigStorage;
|
||||
this._userStorage = userStorage;
|
||||
this._updateScripts = [updateV1, updateV2, updateV3, updateV4, updateV5];
|
||||
this._auth = auth;
|
||||
/**
|
||||
* Handles database migration
|
||||
*/
|
||||
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) {
|
||||
var self = this,
|
||||
currentVersion = 0,
|
||||
targetVersion = cfg.dbVersion,
|
||||
versionDbType = 'dbVersion';
|
||||
|
||||
self._appConfigStorage.listItems(versionDbType, 0, null, function(err, items) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// parse the database version number
|
||||
if (items && items.length > 0) {
|
||||
currentVersion = parseInt(items[0], 10);
|
||||
}
|
||||
|
||||
self._applyUpdate({
|
||||
currentVersion: currentVersion,
|
||||
targetVersion: targetVersion
|
||||
}, callback);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Schedules necessary updates and executes thom in order
|
||||
*/
|
||||
UpdateHandler.prototype._applyUpdate = function(options, callback) {
|
||||
var self = this,
|
||||
scriptOptions,
|
||||
queue = [];
|
||||
|
||||
if (options.currentVersion >= options.targetVersion) {
|
||||
// the current database version is up to date
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
scriptOptions = {
|
||||
appConfigStorage: self._appConfigStorage,
|
||||
userStorage: self._userStorage,
|
||||
auth: self._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) {
|
||||
var self = this,
|
||||
currentVersion = 0,
|
||||
targetVersion = cfg.dbVersion,
|
||||
versionDbType = 'dbVersion';
|
||||
// add all the necessary database updates to the queue
|
||||
for (var i = options.currentVersion; i < options.targetVersion; i++) {
|
||||
queue.push(self._updateScripts[i]);
|
||||
}
|
||||
|
||||
self._appConfigStorage.listItems(versionDbType, 0, null, function(err, items) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
// takes the next update from the queue and executes it
|
||||
function executeNextUpdate(err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// parse the database version number
|
||||
if (items && items.length > 0) {
|
||||
currentVersion = parseInt(items[0], 10);
|
||||
}
|
||||
|
||||
self._applyUpdate({
|
||||
currentVersion: currentVersion,
|
||||
targetVersion: targetVersion
|
||||
}, callback);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Schedules necessary updates and executes thom in order
|
||||
*/
|
||||
UpdateHandler.prototype._applyUpdate = function(options, callback) {
|
||||
var self = this,
|
||||
scriptOptions,
|
||||
queue = [];
|
||||
|
||||
if (options.currentVersion >= options.targetVersion) {
|
||||
// the current database version is up to date
|
||||
if (queue.length < 1) {
|
||||
// we're done
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
scriptOptions = {
|
||||
appConfigStorage: self._appConfigStorage,
|
||||
userStorage: self._userStorage,
|
||||
auth: self._auth
|
||||
};
|
||||
// process next update
|
||||
var script = queue.shift();
|
||||
script(scriptOptions, executeNextUpdate);
|
||||
}
|
||||
|
||||
// add all the necessary database updates to the queue
|
||||
for (var i = options.currentVersion; i < options.targetVersion; i++) {
|
||||
queue.push(self._updateScripts[i]);
|
||||
}
|
||||
executeNextUpdate();
|
||||
};
|
||||
|
||||
// takes the next update from the queue and executes it
|
||||
function executeNextUpdate(err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (queue.length < 1) {
|
||||
// we're done
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
// process next update
|
||||
var script = queue.shift();
|
||||
script(scriptOptions, executeNextUpdate);
|
||||
}
|
||||
|
||||
executeNextUpdate();
|
||||
};
|
||||
|
||||
/**
|
||||
* Check application version and update correspondingly
|
||||
*/
|
||||
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
|
||||
chrome.runtime.onUpdateAvailable.addListener(function(details) {
|
||||
axe.debug('New Chrome App update... requesting reload.');
|
||||
// Chrome downloaded a new app version
|
||||
dialog({
|
||||
title: 'Update available',
|
||||
message: 'A new version ' + details.version + ' of the app is available. Restart the app to update?',
|
||||
positiveBtnStr: 'Restart',
|
||||
negativeBtnStr: 'Not now',
|
||||
showNegativeBtn: true,
|
||||
callback: function(agree) {
|
||||
if (agree) {
|
||||
chrome.runtime.reload();
|
||||
}
|
||||
/**
|
||||
* Check application version and update correspondingly
|
||||
*/
|
||||
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
|
||||
chrome.runtime.onUpdateAvailable.addListener(function(details) {
|
||||
axe.debug('New Chrome App update... requesting reload.');
|
||||
// Chrome downloaded a new app version
|
||||
dialog({
|
||||
title: 'Update available',
|
||||
message: 'A new version ' + details.version + ' of the app is available. Restart the app to update?',
|
||||
positiveBtnStr: 'Restart',
|
||||
negativeBtnStr: 'Not now',
|
||||
showNegativeBtn: true,
|
||||
callback: function(agree) {
|
||||
if (agree) {
|
||||
chrome.runtime.reload();
|
||||
}
|
||||
});
|
||||
});
|
||||
chrome.runtime.requestUpdateCheck(function(status) {
|
||||
if (status === "update_found") {
|
||||
axe.debug("Update pending...");
|
||||
} else if (status === "no_update") {
|
||||
axe.debug("No update found.");
|
||||
} else if (status === "throttled") {
|
||||
axe.debug("Checking updates too frequently.");
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
chrome.runtime.requestUpdateCheck(function(status) {
|
||||
if (status === "update_found") {
|
||||
axe.debug("Update pending...");
|
||||
} else if (status === "no_update") {
|
||||
axe.debug("No update found.");
|
||||
} else if (status === "throttled") {
|
||||
axe.debug("Checking updates too frequently.");
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return UpdateHandler;
|
||||
});
|
||||
module.exports = UpdateHandler;
|
|
@ -1,29 +1,27 @@
|
|||
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) {
|
||||
var emailDbType = 'email_',
|
||||
versionDbType = 'dbVersion',
|
||||
postUpdateDbVersion = 1;
|
||||
/**
|
||||
* 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) {
|
||||
var emailDbType = 'email_',
|
||||
versionDbType = 'dbVersion',
|
||||
postUpdateDbVersion = 1;
|
||||
|
||||
// remove the emails
|
||||
options.userStorage.removeList(emailDbType, function(err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
// remove the emails
|
||||
options.userStorage.removeList(emailDbType, function(err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// update the database version to postUpdateDbVersion
|
||||
options.appConfigStorage.storeList([postUpdateDbVersion], versionDbType, callback);
|
||||
});
|
||||
}
|
||||
// update the database version to postUpdateDbVersion
|
||||
options.appConfigStorage.storeList([postUpdateDbVersion], versionDbType, callback);
|
||||
});
|
||||
}
|
||||
|
||||
return updateV1;
|
||||
});
|
||||
module.exports = updateV1;
|
|
@ -1,28 +1,26 @@
|
|||
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) {
|
||||
var emailDbType = 'email_',
|
||||
versionDbType = 'dbVersion',
|
||||
postUpdateDbVersion = 2;
|
||||
/**
|
||||
* 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) {
|
||||
var emailDbType = 'email_',
|
||||
versionDbType = 'dbVersion',
|
||||
postUpdateDbVersion = 2;
|
||||
|
||||
// remove the emails
|
||||
options.userStorage.removeList(emailDbType, function(err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
// remove the emails
|
||||
options.userStorage.removeList(emailDbType, function(err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// update the database version to postUpdateDbVersion
|
||||
options.appConfigStorage.storeList([postUpdateDbVersion], versionDbType, callback);
|
||||
});
|
||||
}
|
||||
// update the database version to postUpdateDbVersion
|
||||
options.appConfigStorage.storeList([postUpdateDbVersion], versionDbType, callback);
|
||||
});
|
||||
}
|
||||
|
||||
return updateV2;
|
||||
});
|
||||
module.exports = updateV2;
|
|
@ -1,28 +1,26 @@
|
|||
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) {
|
||||
var emailDbType = 'email_',
|
||||
versionDbType = 'dbVersion',
|
||||
postUpdateDbVersion = 3;
|
||||
/**
|
||||
* 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) {
|
||||
var emailDbType = 'email_',
|
||||
versionDbType = 'dbVersion',
|
||||
postUpdateDbVersion = 3;
|
||||
|
||||
// remove the emails
|
||||
options.userStorage.removeList(emailDbType, function(err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
// remove the emails
|
||||
options.userStorage.removeList(emailDbType, function(err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// update the database version to postUpdateDbVersion
|
||||
options.appConfigStorage.storeList([postUpdateDbVersion], versionDbType, callback);
|
||||
});
|
||||
}
|
||||
// update the database version to postUpdateDbVersion
|
||||
options.appConfigStorage.storeList([postUpdateDbVersion], versionDbType, callback);
|
||||
});
|
||||
}
|
||||
|
||||
return update;
|
||||
});
|
||||
module.exports = update;
|
|
@ -1,91 +1,89 @@
|
|||
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) {
|
||||
var VERSION_DB_TYPE = 'dbVersion',
|
||||
EMAIL_ADDR_DB_KEY = 'emailaddress',
|
||||
USERNAME_DB_KEY = 'username',
|
||||
PROVIDER_DB_KEY = 'provider',
|
||||
IMAP_DB_KEY = 'imap',
|
||||
SMTP_DB_KEY = 'smtp',
|
||||
REALNAME_DB_KEY = 'realname',
|
||||
POST_UPDATE_DB_VERSION = 4;
|
||||
/**
|
||||
* 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) {
|
||||
var VERSION_DB_TYPE = 'dbVersion',
|
||||
EMAIL_ADDR_DB_KEY = 'emailaddress',
|
||||
USERNAME_DB_KEY = 'username',
|
||||
PROVIDER_DB_KEY = 'provider',
|
||||
IMAP_DB_KEY = 'imap',
|
||||
SMTP_DB_KEY = 'smtp',
|
||||
REALNAME_DB_KEY = 'realname',
|
||||
POST_UPDATE_DB_VERSION = 4;
|
||||
|
||||
|
||||
var imap = config.gmail.imap,
|
||||
smtp = config.gmail.smtp;
|
||||
var imap = config.gmail.imap,
|
||||
smtp = config.gmail.smtp;
|
||||
|
||||
// load the email address (if existing)
|
||||
loadFromDB(EMAIL_ADDR_DB_KEY, function(err, emailAddress) {
|
||||
// load the email address (if existing)
|
||||
loadFromDB(EMAIL_ADDR_DB_KEY, function(err, emailAddress) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
// load the provider (if existing)
|
||||
loadFromDB(PROVIDER_DB_KEY, function(err, provider) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
// load the provider (if existing)
|
||||
loadFromDB(PROVIDER_DB_KEY, function(err, provider) {
|
||||
// if there is an email address without a provider, we need to add the missing provider entry
|
||||
// for any other situation, we're good.
|
||||
|
||||
if (!(emailAddress && !provider)) {
|
||||
// update the database version to POST_UPDATE_DB_VERSION
|
||||
return options.appConfigStorage.storeList([POST_UPDATE_DB_VERSION], VERSION_DB_TYPE, callback);
|
||||
}
|
||||
|
||||
// add the missing provider key
|
||||
options.appConfigStorage.storeList(['gmail'], PROVIDER_DB_KEY, function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
// if there is an email address without a provider, we need to add the missing provider entry
|
||||
// for any other situation, we're good.
|
||||
|
||||
if (!(emailAddress && !provider)) {
|
||||
// update the database version to POST_UPDATE_DB_VERSION
|
||||
return options.appConfigStorage.storeList([POST_UPDATE_DB_VERSION], VERSION_DB_TYPE, callback);
|
||||
}
|
||||
|
||||
// add the missing provider key
|
||||
options.appConfigStorage.storeList(['gmail'], PROVIDER_DB_KEY, function(err) {
|
||||
// add the missing user name key
|
||||
options.appConfigStorage.storeList([emailAddress], USERNAME_DB_KEY, function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
// add the missing user name key
|
||||
options.appConfigStorage.storeList([emailAddress], USERNAME_DB_KEY, function(err) {
|
||||
// add the missing imap host info key
|
||||
options.appConfigStorage.storeList([imap], IMAP_DB_KEY, function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
// add the missing imap host info key
|
||||
options.appConfigStorage.storeList([imap], IMAP_DB_KEY, function(err) {
|
||||
// add the missing empty real name
|
||||
options.appConfigStorage.storeList([''], REALNAME_DB_KEY, function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
// add the missing empty real name
|
||||
options.appConfigStorage.storeList([''], REALNAME_DB_KEY, function(err) {
|
||||
// add the missing smtp host info key
|
||||
options.appConfigStorage.storeList([smtp], SMTP_DB_KEY, function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
// add the missing smtp host info key
|
||||
options.appConfigStorage.storeList([smtp], SMTP_DB_KEY, function(err) {
|
||||
// reload the credentials
|
||||
options.auth.initialized = false;
|
||||
options.auth._loadCredentials(function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
// reload the credentials
|
||||
options.auth.initialized = false;
|
||||
options.auth._loadCredentials(function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
|
||||
// update the database version to POST_UPDATE_DB_VERSION
|
||||
options.appConfigStorage.storeList([POST_UPDATE_DB_VERSION], VERSION_DB_TYPE, callback);
|
||||
});
|
||||
// update the database version to POST_UPDATE_DB_VERSION
|
||||
options.appConfigStorage.storeList([POST_UPDATE_DB_VERSION], VERSION_DB_TYPE, callback);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -93,13 +91,13 @@ define(function(require) {
|
|||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function loadFromDB(key, callback) {
|
||||
options.appConfigStorage.listItems(key, 0, null, function(err, cachedItems) {
|
||||
callback(err, (!err && cachedItems && cachedItems[0]));
|
||||
});
|
||||
}
|
||||
function loadFromDB(key, callback) {
|
||||
options.appConfigStorage.listItems(key, 0, null, function(err, cachedItems) {
|
||||
callback(err, (!err && cachedItems && cachedItems[0]));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return update;
|
||||
});
|
||||
module.exports = update;
|
|
@ -1,56 +1,54 @@
|
|||
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) {
|
||||
/**
|
||||
* 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) {
|
||||
|
||||
// remove the emails
|
||||
options.userStorage.listItems(FOLDER_DB_TYPE, 0, null, function(err, stored) {
|
||||
// remove the emails
|
||||
options.userStorage.listItems(FOLDER_DB_TYPE, 0, null, function(err, stored) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
var folders = stored[0] || [];
|
||||
[FOLDER_TYPE_INBOX, FOLDER_TYPE_SENT, FOLDER_TYPE_DRAFTS, FOLDER_TYPE_TRASH].forEach(function(mbxType) {
|
||||
var foldersForType = folders.filter(function(mbx) {
|
||||
return mbx.type === mbxType;
|
||||
});
|
||||
|
||||
if (foldersForType.length <= 1) {
|
||||
return; // nothing to do here
|
||||
}
|
||||
|
||||
// remove duplicate folders
|
||||
for (var i = 1; i < foldersForType.length; i++) {
|
||||
folders.splice(folders.indexOf(foldersForType[i]), 1);
|
||||
}
|
||||
});
|
||||
|
||||
options.userStorage.storeList([folders], FOLDER_DB_TYPE, function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
var folders = stored[0] || [];
|
||||
[FOLDER_TYPE_INBOX, FOLDER_TYPE_SENT, FOLDER_TYPE_DRAFTS, FOLDER_TYPE_TRASH].forEach(function(mbxType) {
|
||||
var foldersForType = folders.filter(function(mbx) {
|
||||
return mbx.type === mbxType;
|
||||
});
|
||||
|
||||
if (foldersForType.length <= 1) {
|
||||
return; // nothing to do here
|
||||
}
|
||||
|
||||
// remove duplicate folders
|
||||
for (var i = 1; i < foldersForType.length; i++) {
|
||||
folders.splice(folders.indexOf(foldersForType[i]), 1);
|
||||
}
|
||||
});
|
||||
|
||||
options.userStorage.storeList([folders], FOLDER_DB_TYPE, function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
// update the database version to POST_UPDATE_DB_VERSION
|
||||
options.appConfigStorage.storeList([POST_UPDATE_DB_VERSION], VERSION_DB_TYPE, callback);
|
||||
});
|
||||
// update the database version to POST_UPDATE_DB_VERSION
|
||||
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
|
@ -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;
|
||||
}
|
||||
}());
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"name": "Whiteout Mail",
|
||||
"version": "0.0.1",
|
||||
"main": "index.html",
|
||||
"window": {
|
||||
"toolbar": false,
|
||||
"width": 1024,
|
||||
"height": 768
|
||||
}
|
||||
}
|
|
@ -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: '_'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}());
|
|
@ -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>
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
chrome.app.runtime.onLaunched.addListener(function() {
|
||||
chrome.app.window.create('integration/index.html', {
|
||||
'bounds': {
|
||||
'width': 1024,
|
||||
'height': 768
|
||||
}
|
||||
});
|
||||
});
|
File diff suppressed because one or more lines are too long
|
@ -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>
|
|
@ -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();
|
||||
}
|
||||
);
|
||||
}
|
|
@ -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';
|
|
@ -1,101 +1,97 @@
|
|||
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() {
|
||||
var scope, accountCtrl,
|
||||
dummyFingerprint, expectedFingerprint,
|
||||
dummyKeyId, expectedKeyId,
|
||||
emailAddress, keySize, pgpMock, keychainMock;
|
||||
describe('Account Controller unit test', function() {
|
||||
var scope, accountCtrl,
|
||||
dummyFingerprint, expectedFingerprint,
|
||||
dummyKeyId, expectedKeyId,
|
||||
emailAddress, keySize, pgpMock, keychainMock;
|
||||
|
||||
beforeEach(function() {
|
||||
appController._pgp = pgpMock = sinon.createStubInstance(PGP);
|
||||
appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
beforeEach(function() {
|
||||
appController._pgp = pgpMock = sinon.createStubInstance(PGP);
|
||||
appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
|
||||
dummyFingerprint = '3A2D39B4E1404190B8B949DE7D7E99036E712926';
|
||||
expectedFingerprint = '3A2D 39B4 E140 4190 B8B9 49DE 7D7E 9903 6E71 2926';
|
||||
dummyKeyId = '9FEB47936E712926';
|
||||
expectedKeyId = '6E712926';
|
||||
pgpMock.getFingerprint.returns(dummyFingerprint);
|
||||
pgpMock.getKeyId.returns(dummyKeyId);
|
||||
emailAddress = 'fred@foo.com';
|
||||
keySize = 1234;
|
||||
appController._emailDao = {
|
||||
_account: {
|
||||
emailAddress: emailAddress,
|
||||
asymKeySize: keySize
|
||||
}
|
||||
};
|
||||
pgpMock.getKeyParams.returns({
|
||||
_id: dummyKeyId,
|
||||
fingerprint: dummyFingerprint,
|
||||
userId: emailAddress,
|
||||
bitSize: keySize
|
||||
});
|
||||
|
||||
angular.module('accounttest', []);
|
||||
mocks.module('accounttest');
|
||||
mocks.inject(function($rootScope, $controller) {
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
accountCtrl = $controller(AccountCtrl, {
|
||||
$scope: scope
|
||||
});
|
||||
});
|
||||
dummyFingerprint = '3A2D39B4E1404190B8B949DE7D7E99036E712926';
|
||||
expectedFingerprint = '3A2D 39B4 E140 4190 B8B9 49DE 7D7E 9903 6E71 2926';
|
||||
dummyKeyId = '9FEB47936E712926';
|
||||
expectedKeyId = '6E712926';
|
||||
pgpMock.getFingerprint.returns(dummyFingerprint);
|
||||
pgpMock.getKeyId.returns(dummyKeyId);
|
||||
emailAddress = 'fred@foo.com';
|
||||
keySize = 1234;
|
||||
appController._emailDao = {
|
||||
_account: {
|
||||
emailAddress: emailAddress,
|
||||
asymKeySize: keySize
|
||||
}
|
||||
};
|
||||
pgpMock.getKeyParams.returns({
|
||||
_id: dummyKeyId,
|
||||
fingerprint: dummyFingerprint,
|
||||
userId: emailAddress,
|
||||
bitSize: keySize
|
||||
});
|
||||
|
||||
afterEach(function() {});
|
||||
|
||||
describe('scope variables', function() {
|
||||
it('should be set correctly', function() {
|
||||
expect(scope.eMail).to.equal(emailAddress);
|
||||
expect(scope.keyId).to.equal(expectedKeyId);
|
||||
expect(scope.fingerprint).to.equal(expectedFingerprint);
|
||||
expect(scope.keysize).to.equal(keySize);
|
||||
});
|
||||
});
|
||||
describe('export to key file', function() {
|
||||
it('should work', function() {
|
||||
var createDownloadMock = sinon.stub(dl, 'createDownload');
|
||||
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(null, {
|
||||
publicKey: {
|
||||
_id: dummyKeyId,
|
||||
publicKey: 'a'
|
||||
},
|
||||
privateKey: {
|
||||
encryptedKey: 'b'
|
||||
}
|
||||
});
|
||||
createDownloadMock.withArgs(sinon.match(function(arg) {
|
||||
return arg.content === 'a\r\nb' && arg.filename === 'whiteout_mail_' + emailAddress + '_' + expectedKeyId + '.asc' && arg.contentType === 'text/plain';
|
||||
})).returns();
|
||||
|
||||
scope.exportKeyFile();
|
||||
|
||||
expect(scope.state.lightbox).to.equal(undefined);
|
||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||
expect(dl.createDownload.calledOnce).to.be.true;
|
||||
dl.createDownload.restore();
|
||||
});
|
||||
|
||||
it('should not work when key export failed', function(done) {
|
||||
keychainMock.getUserKeyPair.yields(new Error('Boom!'));
|
||||
scope.onError = function(err) {
|
||||
expect(err.message).to.equal('Boom!');
|
||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.exportKeyFile();
|
||||
angular.module('accounttest', []);
|
||||
mocks.module('accounttest');
|
||||
mocks.inject(function($rootScope, $controller) {
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
accountCtrl = $controller(AccountCtrl, {
|
||||
$scope: scope
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {});
|
||||
|
||||
describe('scope variables', function() {
|
||||
it('should be set correctly', function() {
|
||||
expect(scope.eMail).to.equal(emailAddress);
|
||||
expect(scope.keyId).to.equal(expectedKeyId);
|
||||
expect(scope.fingerprint).to.equal(expectedFingerprint);
|
||||
expect(scope.keysize).to.equal(keySize);
|
||||
});
|
||||
});
|
||||
describe('export to key file', function() {
|
||||
it('should work', function() {
|
||||
var createDownloadMock = sinon.stub(dl, 'createDownload');
|
||||
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(null, {
|
||||
publicKey: {
|
||||
_id: dummyKeyId,
|
||||
publicKey: 'a'
|
||||
},
|
||||
privateKey: {
|
||||
encryptedKey: 'b'
|
||||
}
|
||||
});
|
||||
createDownloadMock.withArgs(sinon.match(function(arg) {
|
||||
return arg.content === 'a\r\nb' && arg.filename === 'whiteout_mail_' + emailAddress + '_' + expectedKeyId + '.asc' && arg.contentType === 'text/plain';
|
||||
})).returns();
|
||||
|
||||
scope.exportKeyFile();
|
||||
|
||||
expect(scope.state.lightbox).to.equal(undefined);
|
||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||
expect(dl.createDownload.calledOnce).to.be.true;
|
||||
dl.createDownload.restore();
|
||||
});
|
||||
|
||||
it('should not work when key export failed', function(done) {
|
||||
keychainMock.getUserKeyPair.yields(new Error('Boom!'));
|
||||
scope.onError = function(err) {
|
||||
expect(err.message).to.equal('Boom!');
|
||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.exportKeyFile();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,216 +1,212 @@
|
|||
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() {
|
||||
var scope, location, ctrl, authStub, origAuth, adminStub;
|
||||
describe('Add Account Controller unit test', function() {
|
||||
var scope, location, ctrl, authStub, origAuth, adminStub;
|
||||
|
||||
beforeEach(function() {
|
||||
// remember original module to restore later, then replace it
|
||||
origAuth = appController._auth;
|
||||
appController._auth = authStub = sinon.createStubInstance(Auth);
|
||||
appController._adminDao = adminStub = sinon.createStubInstance(AdminDao);
|
||||
beforeEach(function() {
|
||||
// remember original module to restore later, then replace it
|
||||
origAuth = appController._auth;
|
||||
appController._auth = authStub = sinon.createStubInstance(Auth);
|
||||
appController._adminDao = adminStub = sinon.createStubInstance(AdminDao);
|
||||
|
||||
angular.module('addaccounttest', []);
|
||||
mocks.module('addaccounttest');
|
||||
mocks.inject(function($controller, $rootScope, $location) {
|
||||
location = $location;
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
scope.form = {};
|
||||
scope.formValidate = {};
|
||||
angular.module('addaccounttest', []);
|
||||
mocks.module('addaccounttest');
|
||||
mocks.inject(function($controller, $rootScope, $location) {
|
||||
location = $location;
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
scope.form = {};
|
||||
scope.formValidate = {};
|
||||
|
||||
sinon.stub(location, 'path').returns(location);
|
||||
sinon.stub(location, 'search').returns(location);
|
||||
sinon.stub(scope, '$apply', function() {});
|
||||
sinon.stub(location, 'path').returns(location);
|
||||
sinon.stub(location, 'search').returns(location);
|
||||
sinon.stub(scope, '$apply', function() {});
|
||||
|
||||
ctrl = $controller(AddAccountCtrl, {
|
||||
$location: location,
|
||||
$scope: scope,
|
||||
$routeParams: {}
|
||||
});
|
||||
ctrl = $controller(AddAccountCtrl, {
|
||||
$location: location,
|
||||
$scope: scope,
|
||||
$routeParams: {}
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// restore the app controller module
|
||||
appController._auth = origAuth;
|
||||
|
||||
location.path.restore();
|
||||
location.search.restore();
|
||||
if (scope.$apply.restore) {
|
||||
scope.$apply.restore();
|
||||
}
|
||||
});
|
||||
|
||||
describe('createWhiteoutAccount', function() {
|
||||
it('should return early for invalid form', function() {
|
||||
scope.form.$invalid = true;
|
||||
scope.createWhiteoutAccount();
|
||||
expect(adminStub.createUser.called).to.be.false;
|
||||
});
|
||||
|
||||
it('should fail to error creating user', function(done) {
|
||||
scope.form.$invalid = false;
|
||||
scope.betaCode = 'asfd';
|
||||
scope.phone = '12345';
|
||||
adminStub.createUser.yieldsAsync(new Error('asdf'));
|
||||
|
||||
scope.$apply = function() {
|
||||
expect(scope.busy).to.be.false;
|
||||
expect(scope.errMsg).to.equal('asdf');
|
||||
expect(adminStub.createUser.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.createWhiteoutAccount();
|
||||
expect(scope.busy).to.be.true;
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
scope.form.$invalid = false;
|
||||
scope.betaCode = 'asfd';
|
||||
scope.phone = '12345';
|
||||
adminStub.createUser.yieldsAsync();
|
||||
|
||||
scope.$apply = function() {
|
||||
expect(scope.busy).to.be.false;
|
||||
expect(scope.errMsg).to.be.undefined;
|
||||
expect(scope.step).to.equal(3);
|
||||
expect(adminStub.createUser.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.createWhiteoutAccount();
|
||||
expect(scope.busy).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateUser', function() {
|
||||
it('should return early for invalid form', function() {
|
||||
scope.formValidate.$invalid = true;
|
||||
scope.validateUser();
|
||||
expect(adminStub.validateUser.called).to.be.false;
|
||||
});
|
||||
|
||||
it('should fail to error creating user', function(done) {
|
||||
scope.formValidate.$invalid = false;
|
||||
scope.token = 'asfd';
|
||||
adminStub.validateUser.yieldsAsync(new Error('asdf'));
|
||||
|
||||
scope.$apply = function() {
|
||||
expect(scope.busyValidate).to.be.false;
|
||||
expect(scope.errMsgValidate).to.equal('asdf');
|
||||
expect(adminStub.validateUser.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.validateUser();
|
||||
expect(scope.busyValidate).to.be.true;
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
scope.formValidate.$invalid = false;
|
||||
scope.token = 'asfd';
|
||||
adminStub.validateUser.yieldsAsync();
|
||||
|
||||
scope.login = function() {
|
||||
expect(scope.busyValidate).to.be.true;
|
||||
expect(scope.errMsgValidate).to.be.undefined;
|
||||
expect(adminStub.validateUser.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.validateUser();
|
||||
expect(scope.busyValidate).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('login', function() {
|
||||
it('should work', function() {
|
||||
scope.form.$invalid = false;
|
||||
authStub.setCredentials.returns();
|
||||
|
||||
scope.login();
|
||||
expect(authStub.setCredentials.calledOnce).to.be.true;
|
||||
expect(location.path.calledWith('/login')).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('connectToGoogle', function() {
|
||||
it('should forward to login', function() {
|
||||
authStub._oauth = {
|
||||
isSupported: function() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
authStub.getOAuthToken.yields();
|
||||
|
||||
scope.connectToGoogle();
|
||||
|
||||
expect(location.path.calledWith('/login-set-credentials')).to.be.true;
|
||||
expect(location.search.calledWith({
|
||||
provider: 'gmail'
|
||||
})).to.be.true;
|
||||
expect(authStub.getOAuthToken.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should not use oauth for gmail', function() {
|
||||
authStub._oauth = {
|
||||
isSupported: function() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
scope.connectToGoogle();
|
||||
|
||||
expect(location.path.calledWith('/login-set-credentials')).to.be.true;
|
||||
expect(location.search.calledWith({
|
||||
provider: 'gmail'
|
||||
})).to.be.true;
|
||||
expect(authStub.getOAuthToken.called).to.be.false;
|
||||
});
|
||||
|
||||
it('should not forward to login when oauth fails', function(done) {
|
||||
authStub._oauth = {
|
||||
isSupported: function() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
authStub.getOAuthToken.yields(new Error());
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(location.path.called).to.be.false;
|
||||
expect(location.search.called).to.be.false;
|
||||
|
||||
done();
|
||||
};
|
||||
|
||||
scope.connectToGoogle();
|
||||
});
|
||||
});
|
||||
|
||||
describe('connectTo', function() {
|
||||
it('should forward to login', function() {
|
||||
var provider = 'wmail';
|
||||
scope.connectTo(provider);
|
||||
|
||||
expect(location.path.calledWith('/login-set-credentials')).to.be.true;
|
||||
expect(location.search.calledWith({
|
||||
provider: provider
|
||||
})).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// restore the app controller module
|
||||
appController._auth = origAuth;
|
||||
|
||||
location.path.restore();
|
||||
location.search.restore();
|
||||
if (scope.$apply.restore) {
|
||||
scope.$apply.restore();
|
||||
}
|
||||
});
|
||||
|
||||
describe('createWhiteoutAccount', function() {
|
||||
it('should return early for invalid form', function() {
|
||||
scope.form.$invalid = true;
|
||||
scope.createWhiteoutAccount();
|
||||
expect(adminStub.createUser.called).to.be.false;
|
||||
});
|
||||
|
||||
it('should fail to error creating user', function(done) {
|
||||
scope.form.$invalid = false;
|
||||
scope.betaCode = 'asfd';
|
||||
scope.phone = '12345';
|
||||
adminStub.createUser.yieldsAsync(new Error('asdf'));
|
||||
|
||||
scope.$apply = function() {
|
||||
expect(scope.busy).to.be.false;
|
||||
expect(scope.errMsg).to.equal('asdf');
|
||||
expect(adminStub.createUser.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.createWhiteoutAccount();
|
||||
expect(scope.busy).to.be.true;
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
scope.form.$invalid = false;
|
||||
scope.betaCode = 'asfd';
|
||||
scope.phone = '12345';
|
||||
adminStub.createUser.yieldsAsync();
|
||||
|
||||
scope.$apply = function() {
|
||||
expect(scope.busy).to.be.false;
|
||||
expect(scope.errMsg).to.be.undefined;
|
||||
expect(scope.step).to.equal(3);
|
||||
expect(adminStub.createUser.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.createWhiteoutAccount();
|
||||
expect(scope.busy).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateUser', function() {
|
||||
it('should return early for invalid form', function() {
|
||||
scope.formValidate.$invalid = true;
|
||||
scope.validateUser();
|
||||
expect(adminStub.validateUser.called).to.be.false;
|
||||
});
|
||||
|
||||
it('should fail to error creating user', function(done) {
|
||||
scope.formValidate.$invalid = false;
|
||||
scope.token = 'asfd';
|
||||
adminStub.validateUser.yieldsAsync(new Error('asdf'));
|
||||
|
||||
scope.$apply = function() {
|
||||
expect(scope.busyValidate).to.be.false;
|
||||
expect(scope.errMsgValidate).to.equal('asdf');
|
||||
expect(adminStub.validateUser.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.validateUser();
|
||||
expect(scope.busyValidate).to.be.true;
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
scope.formValidate.$invalid = false;
|
||||
scope.token = 'asfd';
|
||||
adminStub.validateUser.yieldsAsync();
|
||||
|
||||
scope.login = function() {
|
||||
expect(scope.busyValidate).to.be.true;
|
||||
expect(scope.errMsgValidate).to.be.undefined;
|
||||
expect(adminStub.validateUser.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.validateUser();
|
||||
expect(scope.busyValidate).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('login', function() {
|
||||
it('should work', function() {
|
||||
scope.form.$invalid = false;
|
||||
authStub.setCredentials.returns();
|
||||
|
||||
scope.login();
|
||||
expect(authStub.setCredentials.calledOnce).to.be.true;
|
||||
expect(location.path.calledWith('/login')).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('connectToGoogle', function() {
|
||||
it('should forward to login', function() {
|
||||
authStub._oauth = {
|
||||
isSupported: function() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
authStub.getOAuthToken.yields();
|
||||
|
||||
scope.connectToGoogle();
|
||||
|
||||
expect(location.path.calledWith('/login-set-credentials')).to.be.true;
|
||||
expect(location.search.calledWith({
|
||||
provider: 'gmail'
|
||||
})).to.be.true;
|
||||
expect(authStub.getOAuthToken.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should not use oauth for gmail', function() {
|
||||
authStub._oauth = {
|
||||
isSupported: function() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
scope.connectToGoogle();
|
||||
|
||||
expect(location.path.calledWith('/login-set-credentials')).to.be.true;
|
||||
expect(location.search.calledWith({
|
||||
provider: 'gmail'
|
||||
})).to.be.true;
|
||||
expect(authStub.getOAuthToken.called).to.be.false;
|
||||
});
|
||||
|
||||
it('should not forward to login when oauth fails', function(done) {
|
||||
authStub._oauth = {
|
||||
isSupported: function() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
authStub.getOAuthToken.yields(new Error());
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(location.path.called).to.be.false;
|
||||
expect(location.search.called).to.be.false;
|
||||
|
||||
done();
|
||||
};
|
||||
|
||||
scope.connectToGoogle();
|
||||
});
|
||||
});
|
||||
|
||||
describe('connectTo', function() {
|
||||
it('should forward to login', function() {
|
||||
var provider = 'wmail';
|
||||
scope.connectTo(provider);
|
||||
|
||||
expect(location.path.calledWith('/login-set-credentials')).to.be.true;
|
||||
expect(location.search.calledWith({
|
||||
provider: provider
|
||||
})).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
});
|
|
@ -1,146 +1,142 @@
|
|||
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',
|
||||
password = 'secret';
|
||||
var adminDao, restDaoStub,
|
||||
emailAddress = 'test@example.com',
|
||||
password = 'secret';
|
||||
|
||||
beforeEach(function() {
|
||||
restDaoStub = sinon.createStubInstance(RestDAO);
|
||||
adminDao = new AdminDAO(restDaoStub);
|
||||
});
|
||||
beforeEach(function() {
|
||||
restDaoStub = sinon.createStubInstance(RestDAO);
|
||||
adminDao = new AdminDAO(restDaoStub);
|
||||
});
|
||||
|
||||
afterEach(function() {});
|
||||
afterEach(function() {});
|
||||
|
||||
describe('createUser', function() {
|
||||
it('should fail due to incomplete args', function(done) {
|
||||
var opt = {
|
||||
emailAddress: emailAddress
|
||||
};
|
||||
describe('createUser', function() {
|
||||
it('should fail due to incomplete args', function(done) {
|
||||
var opt = {
|
||||
emailAddress: emailAddress
|
||||
};
|
||||
|
||||
adminDao.createUser(opt, function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail if user already exists', function(done) {
|
||||
var opt = {
|
||||
emailAddress: emailAddress,
|
||||
password: password,
|
||||
phone: '12345'
|
||||
};
|
||||
|
||||
restDaoStub.post.withArgs(opt, '/user').yields({
|
||||
code: 409
|
||||
});
|
||||
|
||||
adminDao.createUser(opt, function(err) {
|
||||
expect(err.message).to.contain('already taken');
|
||||
expect(restDaoStub.post.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail due to unknown error', function(done) {
|
||||
var opt = {
|
||||
emailAddress: emailAddress,
|
||||
password: password,
|
||||
phone: '12345'
|
||||
};
|
||||
|
||||
restDaoStub.post.withArgs(opt, '/user').yields(new Error());
|
||||
|
||||
adminDao.createUser(opt, function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(restDaoStub.post.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
var opt = {
|
||||
emailAddress: emailAddress,
|
||||
password: password,
|
||||
phone: '12345'
|
||||
};
|
||||
|
||||
restDaoStub.post.withArgs(opt, '/user').yields();
|
||||
|
||||
adminDao.createUser(opt, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(restDaoStub.post.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
adminDao.createUser(opt, function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateUser', function() {
|
||||
it('should fail due to incomplete args', function(done) {
|
||||
var opt = {
|
||||
emailAddress: emailAddress
|
||||
};
|
||||
it('should fail if user already exists', function(done) {
|
||||
var opt = {
|
||||
emailAddress: emailAddress,
|
||||
password: password,
|
||||
phone: '12345'
|
||||
};
|
||||
|
||||
adminDao.validateUser(opt, function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
restDaoStub.post.withArgs(opt, '/user').yields({
|
||||
code: 409
|
||||
});
|
||||
|
||||
it('should fail due to error in rest api', function(done) {
|
||||
var opt = {
|
||||
emailAddress: emailAddress,
|
||||
token: 'H45Z6D'
|
||||
};
|
||||
|
||||
restDaoStub.post.withArgs(opt, '/user/validate').yields(new Error());
|
||||
|
||||
adminDao.validateUser(opt, function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(restDaoStub.post.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work with no error object', function(done) {
|
||||
var opt = {
|
||||
emailAddress: emailAddress,
|
||||
token: 'H45Z6D'
|
||||
};
|
||||
|
||||
restDaoStub.post.withArgs(opt, '/user/validate').yields();
|
||||
|
||||
adminDao.validateUser(opt, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(restDaoStub.post.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work with 202', function(done) {
|
||||
var opt = {
|
||||
emailAddress: emailAddress,
|
||||
token: 'H45Z6D'
|
||||
};
|
||||
|
||||
restDaoStub.post.withArgs(opt, '/user/validate').yields({
|
||||
code: 202
|
||||
});
|
||||
|
||||
adminDao.validateUser(opt, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(restDaoStub.post.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
adminDao.createUser(opt, function(err) {
|
||||
expect(err.message).to.contain('already taken');
|
||||
expect(restDaoStub.post.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail due to unknown error', function(done) {
|
||||
var opt = {
|
||||
emailAddress: emailAddress,
|
||||
password: password,
|
||||
phone: '12345'
|
||||
};
|
||||
|
||||
restDaoStub.post.withArgs(opt, '/user').yields(new Error());
|
||||
|
||||
adminDao.createUser(opt, function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(restDaoStub.post.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
var opt = {
|
||||
emailAddress: emailAddress,
|
||||
password: password,
|
||||
phone: '12345'
|
||||
};
|
||||
|
||||
restDaoStub.post.withArgs(opt, '/user').yields();
|
||||
|
||||
adminDao.createUser(opt, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(restDaoStub.post.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateUser', function() {
|
||||
it('should fail due to incomplete args', function(done) {
|
||||
var opt = {
|
||||
emailAddress: emailAddress
|
||||
};
|
||||
|
||||
adminDao.validateUser(opt, function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail due to error in rest api', function(done) {
|
||||
var opt = {
|
||||
emailAddress: emailAddress,
|
||||
token: 'H45Z6D'
|
||||
};
|
||||
|
||||
restDaoStub.post.withArgs(opt, '/user/validate').yields(new Error());
|
||||
|
||||
adminDao.validateUser(opt, function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(restDaoStub.post.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work with no error object', function(done) {
|
||||
var opt = {
|
||||
emailAddress: emailAddress,
|
||||
token: 'H45Z6D'
|
||||
};
|
||||
|
||||
restDaoStub.post.withArgs(opt, '/user/validate').yields();
|
||||
|
||||
adminDao.validateUser(opt, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(restDaoStub.post.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work with 202', function(done) {
|
||||
var opt = {
|
||||
emailAddress: emailAddress,
|
||||
token: 'H45Z6D'
|
||||
};
|
||||
|
||||
restDaoStub.post.withArgs(opt, '/user/validate').yields({
|
||||
code: 202
|
||||
});
|
||||
|
||||
adminDao.validateUser(opt, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(restDaoStub.post.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
|
@ -1,212 +1,209 @@
|
|||
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() {
|
||||
var emailDaoStub, outboxStub, updateHandlerStub, appConfigStoreStub, devicestorageStub, isOnlineStub, authStub;
|
||||
describe('App Controller unit tests', function() {
|
||||
var emailDaoStub, outboxStub, updateHandlerStub, appConfigStoreStub, devicestorageStub, isOnlineStub, authStub;
|
||||
|
||||
beforeEach(function() {
|
||||
controller._emailDao = emailDaoStub = sinon.createStubInstance(EmailDAO);
|
||||
controller._outboxBo = outboxStub = sinon.createStubInstance(OutboxBO);
|
||||
controller._appConfigStore = appConfigStoreStub = sinon.createStubInstance(DeviceStorageDAO);
|
||||
controller._userStorage = devicestorageStub = sinon.createStubInstance(DeviceStorageDAO);
|
||||
controller._updateHandler = updateHandlerStub = sinon.createStubInstance(UpdateHandler);
|
||||
controller._auth = authStub = sinon.createStubInstance(Auth);
|
||||
|
||||
isOnlineStub = sinon.stub(controller, 'isOnline');
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
isOnlineStub.restore();
|
||||
});
|
||||
|
||||
describe('buildModules', function() {
|
||||
it('should work', function() {
|
||||
controller.buildModules({
|
||||
onError: function() {}
|
||||
});
|
||||
expect(controller._appConfigStore).to.exist;
|
||||
expect(controller._auth).to.exist;
|
||||
expect(controller._userStorage).to.exist;
|
||||
expect(controller._invitationDao).to.exist;
|
||||
expect(controller._keychain).to.exist;
|
||||
expect(controller._pgp).to.exist;
|
||||
expect(controller._pgpbuilder).to.exist;
|
||||
expect(controller._emailDao).to.exist;
|
||||
expect(controller._outboxBo).to.exist;
|
||||
expect(controller._updateHandler).to.exist;
|
||||
});
|
||||
});
|
||||
|
||||
describe('start', function() {
|
||||
it('should not explode', function(done) {
|
||||
controller.start({
|
||||
onError: function() {}
|
||||
}, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('onDisconnect', function() {
|
||||
it('should work', function() {
|
||||
controller.onDisconnect();
|
||||
|
||||
expect(emailDaoStub.onDisconnect.calledOnce).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('logout', function() {
|
||||
it('should work', function(done) {
|
||||
authStub.logout.yields();
|
||||
emailDaoStub.onDisconnect.yields(new Error());
|
||||
|
||||
controller.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(authStub.logout.calledOnce).to.be.true;
|
||||
expect(emailDaoStub.onDisconnect.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
controller.logout();
|
||||
});
|
||||
});
|
||||
|
||||
describe('onConnect', function() {
|
||||
beforeEach(function() {
|
||||
controller._emailDao._account = {};
|
||||
});
|
||||
|
||||
it('should not connect if offline', function(done) {
|
||||
isOnlineStub.returns(false);
|
||||
|
||||
controller.onConnect(function(err) {
|
||||
expect(err).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not connect if account is not initialized', function(done) {
|
||||
controller._emailDao._account = null;
|
||||
|
||||
controller.onConnect(function(err) {
|
||||
expect(err).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail due to error in auth.getCredentials', function(done) {
|
||||
isOnlineStub.returns(true);
|
||||
authStub.getCredentials.yields(new Error());
|
||||
|
||||
controller.onConnect(function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(authStub.getCredentials.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
isOnlineStub.returns(true);
|
||||
authStub.getCredentials.yields(null, {
|
||||
emailAddress: 'asdf@example.com',
|
||||
oauthToken: 'token',
|
||||
sslCert: 'cert',
|
||||
imap: {},
|
||||
smtp: {}
|
||||
});
|
||||
emailDaoStub.onConnect.yields();
|
||||
|
||||
controller.onConnect(function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(authStub.getCredentials.calledOnce).to.be.true;
|
||||
expect(emailDaoStub.onConnect.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('init', function() {
|
||||
var onConnectStub, emailAddress;
|
||||
|
||||
beforeEach(function() {
|
||||
controller._emailDao = emailDaoStub = sinon.createStubInstance(EmailDAO);
|
||||
controller._outboxBo = outboxStub = sinon.createStubInstance(OutboxBO);
|
||||
controller._appConfigStore = appConfigStoreStub = sinon.createStubInstance(DeviceStorageDAO);
|
||||
controller._userStorage = devicestorageStub = sinon.createStubInstance(DeviceStorageDAO);
|
||||
controller._updateHandler = updateHandlerStub = sinon.createStubInstance(UpdateHandler);
|
||||
controller._auth = authStub = sinon.createStubInstance(Auth);
|
||||
emailAddress = 'alice@bob.com';
|
||||
|
||||
isOnlineStub = sinon.stub(controller, 'isOnline');
|
||||
// onConnect
|
||||
onConnectStub = sinon.stub(controller, 'onConnect');
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
isOnlineStub.restore();
|
||||
onConnectStub.restore();
|
||||
});
|
||||
|
||||
describe('buildModules', function() {
|
||||
it('should work', function() {
|
||||
controller.buildModules({
|
||||
onError: function() {}
|
||||
});
|
||||
expect(controller._appConfigStore).to.exist;
|
||||
expect(controller._auth).to.exist;
|
||||
expect(controller._userStorage).to.exist;
|
||||
expect(controller._invitationDao).to.exist;
|
||||
expect(controller._keychain).to.exist;
|
||||
expect(controller._pgp).to.exist;
|
||||
expect(controller._pgpbuilder).to.exist;
|
||||
expect(controller._emailDao).to.exist;
|
||||
expect(controller._outboxBo).to.exist;
|
||||
expect(controller._updateHandler).to.exist;
|
||||
it('should fail due to error in storage initialization', function(done) {
|
||||
devicestorageStub.init.withArgs(undefined).yields({});
|
||||
|
||||
controller.init({}, function(err, keypair) {
|
||||
expect(err).to.exist;
|
||||
expect(keypair).to.not.exist;
|
||||
expect(devicestorageStub.init.calledOnce).to.be.true;
|
||||
expect(updateHandlerStub.update.calledOnce).to.be.false;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('start', function() {
|
||||
it('should not explode', function(done) {
|
||||
controller.start({
|
||||
onError: function() {}
|
||||
}, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
done();
|
||||
});
|
||||
it('should fail due to error in update handler', function(done) {
|
||||
devicestorageStub.init.yields();
|
||||
updateHandlerStub.update.yields({});
|
||||
|
||||
controller.init({
|
||||
emailAddress: emailAddress
|
||||
}, function(err, keypair) {
|
||||
expect(err).to.exist;
|
||||
expect(keypair).to.not.exist;
|
||||
expect(updateHandlerStub.update.calledOnce).to.be.true;
|
||||
expect(devicestorageStub.init.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('onDisconnect', function() {
|
||||
it('should work', function() {
|
||||
controller.onDisconnect();
|
||||
it('should fail due to error in emailDao.init', function(done) {
|
||||
devicestorageStub.init.yields();
|
||||
updateHandlerStub.update.yields();
|
||||
emailDaoStub.init.yields({});
|
||||
|
||||
expect(emailDaoStub.onDisconnect.calledOnce).to.be.true;
|
||||
controller.init({
|
||||
emailAddress: emailAddress
|
||||
}, function(err, keypair) {
|
||||
expect(err).to.exist;
|
||||
expect(keypair).to.not.exist;
|
||||
expect(updateHandlerStub.update.calledOnce).to.be.true;
|
||||
expect(emailDaoStub.init.calledOnce).to.be.true;
|
||||
expect(devicestorageStub.init.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('logout', function() {
|
||||
it('should work', function(done) {
|
||||
authStub.logout.yields();
|
||||
emailDaoStub.onDisconnect.yields(new Error());
|
||||
it('should work and return a keypair', function(done) {
|
||||
devicestorageStub.init.withArgs(emailAddress).yields();
|
||||
emailDaoStub.init.yields(null, {});
|
||||
updateHandlerStub.update.yields();
|
||||
|
||||
controller.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(authStub.logout.calledOnce).to.be.true;
|
||||
expect(emailDaoStub.onDisconnect.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
controller.logout();
|
||||
});
|
||||
});
|
||||
|
||||
describe('onConnect', function() {
|
||||
beforeEach(function() {
|
||||
controller._emailDao._account = {};
|
||||
});
|
||||
|
||||
it('should not connect if offline', function(done) {
|
||||
isOnlineStub.returns(false);
|
||||
|
||||
controller.onConnect(function(err) {
|
||||
expect(err).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not connect if account is not initialized', function(done) {
|
||||
controller._emailDao._account = null;
|
||||
|
||||
controller.onConnect(function(err) {
|
||||
expect(err).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail due to error in auth.getCredentials', function(done) {
|
||||
isOnlineStub.returns(true);
|
||||
authStub.getCredentials.yields(new Error());
|
||||
|
||||
controller.onConnect(function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(authStub.getCredentials.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
isOnlineStub.returns(true);
|
||||
authStub.getCredentials.yields(null, {
|
||||
emailAddress: 'asdf@example.com',
|
||||
oauthToken: 'token',
|
||||
sslCert: 'cert',
|
||||
imap: {},
|
||||
smtp: {}
|
||||
});
|
||||
emailDaoStub.onConnect.yields();
|
||||
|
||||
controller.onConnect(function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(authStub.getCredentials.calledOnce).to.be.true;
|
||||
expect(emailDaoStub.onConnect.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('init', function() {
|
||||
var onConnectStub, emailAddress;
|
||||
|
||||
beforeEach(function() {
|
||||
emailAddress = 'alice@bob.com';
|
||||
|
||||
// onConnect
|
||||
onConnectStub = sinon.stub(controller, 'onConnect');
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
onConnectStub.restore();
|
||||
});
|
||||
|
||||
it('should fail due to error in storage initialization', function(done) {
|
||||
devicestorageStub.init.withArgs(undefined).yields({});
|
||||
|
||||
controller.init({}, function(err, keypair) {
|
||||
expect(err).to.exist;
|
||||
expect(keypair).to.not.exist;
|
||||
expect(devicestorageStub.init.calledOnce).to.be.true;
|
||||
expect(updateHandlerStub.update.calledOnce).to.be.false;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail due to error in update handler', function(done) {
|
||||
devicestorageStub.init.yields();
|
||||
updateHandlerStub.update.yields({});
|
||||
|
||||
controller.init({
|
||||
emailAddress: emailAddress
|
||||
}, function(err, keypair) {
|
||||
expect(err).to.exist;
|
||||
expect(keypair).to.not.exist;
|
||||
expect(updateHandlerStub.update.calledOnce).to.be.true;
|
||||
expect(devicestorageStub.init.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail due to error in emailDao.init', function(done) {
|
||||
devicestorageStub.init.yields();
|
||||
updateHandlerStub.update.yields();
|
||||
emailDaoStub.init.yields({});
|
||||
|
||||
controller.init({
|
||||
emailAddress: emailAddress
|
||||
}, function(err, keypair) {
|
||||
expect(err).to.exist;
|
||||
expect(keypair).to.not.exist;
|
||||
expect(updateHandlerStub.update.calledOnce).to.be.true;
|
||||
expect(emailDaoStub.init.calledOnce).to.be.true;
|
||||
expect(devicestorageStub.init.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work and return a keypair', function(done) {
|
||||
devicestorageStub.init.withArgs(emailAddress).yields();
|
||||
emailDaoStub.init.yields(null, {});
|
||||
updateHandlerStub.update.yields();
|
||||
|
||||
controller.init({
|
||||
emailAddress: emailAddress
|
||||
}, function(err, keypair) {
|
||||
expect(err).to.not.exist;
|
||||
expect(keypair).to.exist;
|
||||
expect(updateHandlerStub.update.calledOnce).to.be.true;
|
||||
expect(emailDaoStub.init.calledOnce).to.be.true;
|
||||
expect(devicestorageStub.init.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
controller.init({
|
||||
emailAddress: emailAddress
|
||||
}, function(err, keypair) {
|
||||
expect(err).to.not.exist;
|
||||
expect(keypair).to.exist;
|
||||
expect(updateHandlerStub.update.calledOnce).to.be.true;
|
||||
expect(emailDaoStub.init.calledOnce).to.be.true;
|
||||
expect(devicestorageStub.init.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,377 +1,374 @@
|
|||
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() {
|
||||
// Constancts
|
||||
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';
|
||||
// SUT
|
||||
var auth;
|
||||
describe('Auth unit tests', function() {
|
||||
// Constancts
|
||||
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';
|
||||
// SUT
|
||||
var auth;
|
||||
|
||||
// Dependencies
|
||||
var storageStub, oauthStub, pgpStub;
|
||||
// Dependencies
|
||||
var storageStub, oauthStub, pgpStub;
|
||||
|
||||
// test data
|
||||
var emailAddress = 'bla@blubb.com';
|
||||
var password = 'passwordpasswordpassword';
|
||||
var encryptedPassword = 'pgppasswordpgppassword';
|
||||
var oauthToken = 'tokentokentokentoken';
|
||||
var provider = 'gmail';
|
||||
var realname = 'Bla Blubb';
|
||||
var username = 'bla';
|
||||
var imap = {
|
||||
host: 'mail.blablubb.com',
|
||||
port: 123,
|
||||
secure: true,
|
||||
ca: 'PEMPEMPEMPEMPEMPEMPEMPEMPEMPEM'
|
||||
};
|
||||
var smtp = {
|
||||
host: 'mail.blablubb.com',
|
||||
port: 456,
|
||||
secure: true,
|
||||
ca: 'PEMPEMPEMPEMPEMPEMPEMPEMPEMPEM'
|
||||
};
|
||||
// test data
|
||||
var emailAddress = 'bla@blubb.com';
|
||||
var password = 'passwordpasswordpassword';
|
||||
var encryptedPassword = 'pgppasswordpgppassword';
|
||||
var oauthToken = 'tokentokentokentoken';
|
||||
var provider = 'gmail';
|
||||
var realname = 'Bla Blubb';
|
||||
var username = 'bla';
|
||||
var imap = {
|
||||
host: 'mail.blablubb.com',
|
||||
port: 123,
|
||||
secure: true,
|
||||
ca: 'PEMPEMPEMPEMPEMPEMPEMPEMPEMPEM'
|
||||
};
|
||||
var smtp = {
|
||||
host: 'mail.blablubb.com',
|
||||
port: 456,
|
||||
secure: true,
|
||||
ca: 'PEMPEMPEMPEMPEMPEMPEMPEMPEMPEM'
|
||||
};
|
||||
|
||||
beforeEach(function() {
|
||||
storageStub = sinon.createStubInstance(DeviceStorageDAO);
|
||||
oauthStub = sinon.createStubInstance(OAuth);
|
||||
pgpStub = sinon.createStubInstance(PGP);
|
||||
auth = new Auth(storageStub, oauthStub, pgpStub);
|
||||
beforeEach(function() {
|
||||
storageStub = sinon.createStubInstance(DeviceStorageDAO);
|
||||
oauthStub = sinon.createStubInstance(OAuth);
|
||||
pgpStub = sinon.createStubInstance(PGP);
|
||||
auth = new Auth(storageStub, oauthStub, pgpStub);
|
||||
});
|
||||
|
||||
describe('#getCredentials', function() {
|
||||
it('should load credentials and retrieve credentials from cfg', function(done) {
|
||||
storageStub.listItems.withArgs(EMAIL_ADDR_DB_KEY, 0, null).yieldsAsync(null, [emailAddress]);
|
||||
storageStub.listItems.withArgs(PASSWD_DB_KEY, 0, null).yieldsAsync(null, [encryptedPassword]);
|
||||
storageStub.listItems.withArgs(PROVIDER_DB_KEY, 0, null).yieldsAsync(null, [provider]);
|
||||
storageStub.listItems.withArgs(USERNAME_DB_KEY, 0, null).yieldsAsync(null, [username]);
|
||||
storageStub.listItems.withArgs(REALNAME_DB_KEY, 0, null).yieldsAsync(null, [realname]);
|
||||
storageStub.listItems.withArgs(IMAP_DB_KEY, 0, null).yieldsAsync(null, [imap]);
|
||||
storageStub.listItems.withArgs(SMTP_DB_KEY, 0, null).yieldsAsync(null, [smtp]);
|
||||
pgpStub.decrypt.withArgs(encryptedPassword, undefined).yields(null, password);
|
||||
|
||||
auth.getCredentials(function(err, cred) {
|
||||
expect(err).to.not.exist;
|
||||
|
||||
expect(auth.provider).to.equal(provider);
|
||||
expect(auth.emailAddress).to.equal(emailAddress);
|
||||
expect(auth.password).to.equal(password);
|
||||
|
||||
expect(cred.imap.host).to.equal(imap.host);
|
||||
expect(cred.imap.port).to.equal(imap.port);
|
||||
expect(cred.imap.secure).to.equal(imap.secure);
|
||||
expect(cred.imap.ca).to.equal(imap.ca);
|
||||
expect(cred.imap.auth.user).to.equal(username);
|
||||
expect(cred.imap.auth.pass).to.equal(password);
|
||||
|
||||
expect(cred.smtp.host).to.equal(smtp.host);
|
||||
expect(cred.smtp.port).to.equal(smtp.port);
|
||||
expect(cred.smtp.secure).to.equal(smtp.secure);
|
||||
expect(cred.smtp.ca).to.equal(smtp.ca);
|
||||
expect(cred.smtp.auth.user).to.equal(username);
|
||||
expect(cred.smtp.auth.pass).to.equal(password);
|
||||
|
||||
expect(storageStub.listItems.callCount).to.equal(7);
|
||||
expect(pgpStub.decrypt.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#setCredentials', function() {
|
||||
it('should set the credentials', function() {
|
||||
auth.setCredentials({
|
||||
provider: 'albhsvadlbvsdalbsadflb',
|
||||
emailAddress: emailAddress,
|
||||
username: username,
|
||||
realname: realname,
|
||||
password: password,
|
||||
imap: imap,
|
||||
smtp: smtp
|
||||
});
|
||||
|
||||
expect(auth.provider).to.equal('albhsvadlbvsdalbsadflb');
|
||||
expect(auth.emailAddress).to.equal(emailAddress);
|
||||
expect(auth.username).to.equal(username);
|
||||
expect(auth.realname).to.equal(realname);
|
||||
expect(auth.password).to.equal(password);
|
||||
expect(auth.smtp).to.equal(smtp);
|
||||
expect(auth.imap).to.equal(imap);
|
||||
expect(auth.credentialsDirty).to.be.true;
|
||||
});
|
||||
|
||||
describe('#getCredentials', function() {
|
||||
it('should load credentials and retrieve credentials from cfg', function(done) {
|
||||
storageStub.listItems.withArgs(EMAIL_ADDR_DB_KEY, 0, null).yieldsAsync(null, [emailAddress]);
|
||||
storageStub.listItems.withArgs(PASSWD_DB_KEY, 0, null).yieldsAsync(null, [encryptedPassword]);
|
||||
storageStub.listItems.withArgs(PROVIDER_DB_KEY, 0, null).yieldsAsync(null, [provider]);
|
||||
storageStub.listItems.withArgs(USERNAME_DB_KEY, 0, null).yieldsAsync(null, [username]);
|
||||
storageStub.listItems.withArgs(REALNAME_DB_KEY, 0, null).yieldsAsync(null, [realname]);
|
||||
storageStub.listItems.withArgs(IMAP_DB_KEY, 0, null).yieldsAsync(null, [imap]);
|
||||
storageStub.listItems.withArgs(SMTP_DB_KEY, 0, null).yieldsAsync(null, [smtp]);
|
||||
pgpStub.decrypt.withArgs(encryptedPassword, undefined).yields(null, password);
|
||||
});
|
||||
|
||||
auth.getCredentials(function(err, cred) {
|
||||
expect(err).to.not.exist;
|
||||
describe('#storeCredentials', function() {
|
||||
it('should persist ALL the things!', function(done) {
|
||||
auth.credentialsDirty = true;
|
||||
auth.emailAddress = emailAddress;
|
||||
auth.username = username;
|
||||
auth.realname = realname;
|
||||
auth.password = password;
|
||||
auth.smtp = smtp;
|
||||
auth.imap = imap;
|
||||
auth.provider = provider;
|
||||
|
||||
expect(auth.provider).to.equal(provider);
|
||||
expect(auth.emailAddress).to.equal(emailAddress);
|
||||
expect(auth.password).to.equal(password);
|
||||
storageStub.storeList.withArgs([encryptedPassword], PASSWD_DB_KEY).yieldsAsync();
|
||||
storageStub.storeList.withArgs([emailAddress], EMAIL_ADDR_DB_KEY).yieldsAsync();
|
||||
storageStub.storeList.withArgs([provider], PROVIDER_DB_KEY).yieldsAsync();
|
||||
storageStub.storeList.withArgs([username], USERNAME_DB_KEY).yieldsAsync();
|
||||
storageStub.storeList.withArgs([realname], REALNAME_DB_KEY).yieldsAsync();
|
||||
storageStub.storeList.withArgs([imap], IMAP_DB_KEY).yieldsAsync();
|
||||
storageStub.storeList.withArgs([smtp], SMTP_DB_KEY).yieldsAsync();
|
||||
pgpStub.encrypt.withArgs(password).yields(null, encryptedPassword);
|
||||
|
||||
expect(cred.imap.host).to.equal(imap.host);
|
||||
expect(cred.imap.port).to.equal(imap.port);
|
||||
expect(cred.imap.secure).to.equal(imap.secure);
|
||||
expect(cred.imap.ca).to.equal(imap.ca);
|
||||
expect(cred.imap.auth.user).to.equal(username);
|
||||
expect(cred.imap.auth.pass).to.equal(password);
|
||||
auth.storeCredentials(function(err) {
|
||||
expect(err).to.not.exist;
|
||||
|
||||
expect(cred.smtp.host).to.equal(smtp.host);
|
||||
expect(cred.smtp.port).to.equal(smtp.port);
|
||||
expect(cred.smtp.secure).to.equal(smtp.secure);
|
||||
expect(cred.smtp.ca).to.equal(smtp.ca);
|
||||
expect(cred.smtp.auth.user).to.equal(username);
|
||||
expect(cred.smtp.auth.pass).to.equal(password);
|
||||
expect(storageStub.storeList.callCount).to.equal(7);
|
||||
expect(pgpStub.encrypt.calledOnce).to.be.true;
|
||||
|
||||
expect(storageStub.listItems.callCount).to.equal(7);
|
||||
expect(pgpStub.decrypt.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
done();
|
||||
});
|
||||
describe('#getOAuthToken', function() {
|
||||
it('should refresh token with known email address', function(done) {
|
||||
auth.emailAddress = emailAddress;
|
||||
auth.oauthToken = 'oldToken';
|
||||
|
||||
oauthStub.refreshToken.withArgs({
|
||||
emailAddress: emailAddress,
|
||||
oldToken: 'oldToken'
|
||||
}).yieldsAsync(null, oauthToken);
|
||||
|
||||
auth.getOAuthToken(function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(auth.emailAddress).to.equal(emailAddress);
|
||||
expect(auth.oauthToken).to.equal(oauthToken);
|
||||
|
||||
expect(oauthStub.refreshToken.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('#setCredentials', function() {
|
||||
it('should set the credentials', function() {
|
||||
auth.setCredentials({
|
||||
provider: 'albhsvadlbvsdalbsadflb',
|
||||
emailAddress: emailAddress,
|
||||
username: username,
|
||||
realname: realname,
|
||||
password: password,
|
||||
imap: imap,
|
||||
smtp: smtp
|
||||
});
|
||||
it('should fetch token with known email address', function(done) {
|
||||
auth.emailAddress = emailAddress;
|
||||
oauthStub.getOAuthToken.withArgs(emailAddress).yieldsAsync(null, oauthToken);
|
||||
|
||||
expect(auth.provider).to.equal('albhsvadlbvsdalbsadflb');
|
||||
auth.getOAuthToken(function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(auth.emailAddress).to.equal(emailAddress);
|
||||
expect(auth.oauthToken).to.equal(oauthToken);
|
||||
|
||||
expect(oauthStub.getOAuthToken.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fetch token with unknown email address', function(done) {
|
||||
oauthStub.getOAuthToken.withArgs(undefined).yieldsAsync(null, oauthToken);
|
||||
oauthStub.queryEmailAddress.withArgs(oauthToken).yieldsAsync(null, emailAddress);
|
||||
|
||||
auth.getOAuthToken(function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(auth.emailAddress).to.equal(emailAddress);
|
||||
expect(auth.oauthToken).to.equal(oauthToken);
|
||||
|
||||
expect(oauthStub.getOAuthToken.calledOnce).to.be.true;
|
||||
expect(oauthStub.queryEmailAddress.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail when email address fetch fails', function(done) {
|
||||
oauthStub.getOAuthToken.yieldsAsync(null, oauthToken);
|
||||
oauthStub.queryEmailAddress.yieldsAsync(new Error());
|
||||
|
||||
auth.getOAuthToken(function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(auth.emailAddress).to.not.exist;
|
||||
expect(auth.oauthToken).to.not.exist;
|
||||
|
||||
expect(oauthStub.getOAuthToken.calledOnce).to.be.true;
|
||||
expect(oauthStub.queryEmailAddress.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail when oauth fetch fails', function(done) {
|
||||
oauthStub.getOAuthToken.yieldsAsync(new Error());
|
||||
|
||||
auth.getOAuthToken(function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(auth.emailAddress).to.not.exist;
|
||||
expect(auth.oauthToken).to.not.exist;
|
||||
|
||||
expect(oauthStub.getOAuthToken.calledOnce).to.be.true;
|
||||
expect(oauthStub.queryEmailAddress.called).to.be.false;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#_loadCredentials', function() {
|
||||
it('should work', function(done) {
|
||||
storageStub.listItems.withArgs(EMAIL_ADDR_DB_KEY, 0, null).yieldsAsync(null, [emailAddress]);
|
||||
storageStub.listItems.withArgs(PASSWD_DB_KEY, 0, null).yieldsAsync(null, [encryptedPassword]);
|
||||
storageStub.listItems.withArgs(PROVIDER_DB_KEY, 0, null).yieldsAsync(null, [provider]);
|
||||
storageStub.listItems.withArgs(USERNAME_DB_KEY, 0, null).yieldsAsync(null, [username]);
|
||||
storageStub.listItems.withArgs(REALNAME_DB_KEY, 0, null).yieldsAsync(null, [realname]);
|
||||
storageStub.listItems.withArgs(IMAP_DB_KEY, 0, null).yieldsAsync(null, [imap]);
|
||||
storageStub.listItems.withArgs(SMTP_DB_KEY, 0, null).yieldsAsync(null, [smtp]);
|
||||
|
||||
auth._loadCredentials(function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(auth.emailAddress).to.equal(emailAddress);
|
||||
expect(auth.password).to.equal(encryptedPassword);
|
||||
expect(auth.provider).to.equal(provider);
|
||||
expect(auth.imap).to.equal(imap);
|
||||
expect(auth.smtp).to.equal(smtp);
|
||||
expect(auth.username).to.equal(username);
|
||||
expect(auth.realname).to.equal(realname);
|
||||
expect(auth.password).to.equal(password);
|
||||
expect(auth.smtp).to.equal(smtp);
|
||||
expect(auth.imap).to.equal(imap);
|
||||
expect(auth.credentialsDirty).to.be.true;
|
||||
});
|
||||
|
||||
});
|
||||
expect(auth.passwordNeedsDecryption).to.be.true;
|
||||
|
||||
describe('#storeCredentials', function() {
|
||||
it('should persist ALL the things!', function(done) {
|
||||
auth.credentialsDirty = true;
|
||||
auth.emailAddress = emailAddress;
|
||||
auth.username = username;
|
||||
auth.realname = realname;
|
||||
auth.password = password;
|
||||
auth.smtp = smtp;
|
||||
auth.imap = imap;
|
||||
auth.provider = provider;
|
||||
expect(storageStub.listItems.callCount).to.equal(7);
|
||||
|
||||
storageStub.storeList.withArgs([encryptedPassword], PASSWD_DB_KEY).yieldsAsync();
|
||||
storageStub.storeList.withArgs([emailAddress], EMAIL_ADDR_DB_KEY).yieldsAsync();
|
||||
storageStub.storeList.withArgs([provider], PROVIDER_DB_KEY).yieldsAsync();
|
||||
storageStub.storeList.withArgs([username], USERNAME_DB_KEY).yieldsAsync();
|
||||
storageStub.storeList.withArgs([realname], REALNAME_DB_KEY).yieldsAsync();
|
||||
storageStub.storeList.withArgs([imap], IMAP_DB_KEY).yieldsAsync();
|
||||
storageStub.storeList.withArgs([smtp], SMTP_DB_KEY).yieldsAsync();
|
||||
pgpStub.encrypt.withArgs(password).yields(null, encryptedPassword);
|
||||
|
||||
auth.storeCredentials(function(err) {
|
||||
expect(err).to.not.exist;
|
||||
|
||||
expect(storageStub.storeList.callCount).to.equal(7);
|
||||
expect(pgpStub.encrypt.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getOAuthToken', function() {
|
||||
it('should refresh token with known email address', function(done) {
|
||||
auth.emailAddress = emailAddress;
|
||||
auth.oauthToken = 'oldToken';
|
||||
it('should fail', function(done) {
|
||||
storageStub.listItems.yieldsAsync(new Error());
|
||||
|
||||
oauthStub.refreshToken.withArgs({
|
||||
emailAddress: emailAddress,
|
||||
oldToken: 'oldToken'
|
||||
}).yieldsAsync(null, oauthToken);
|
||||
auth._loadCredentials(function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(auth.emailAddress).to.not.exist;
|
||||
expect(auth.password).to.not.exist;
|
||||
expect(auth.provider).to.not.exist;
|
||||
expect(auth.imap).to.not.exist;
|
||||
expect(auth.smtp).to.not.exist;
|
||||
expect(auth.username).to.not.exist;
|
||||
expect(auth.realname).to.not.exist;
|
||||
|
||||
auth.getOAuthToken(function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(auth.emailAddress).to.equal(emailAddress);
|
||||
expect(auth.oauthToken).to.equal(oauthToken);
|
||||
expect(storageStub.listItems.calledOnce).to.be.true;
|
||||
|
||||
expect(oauthStub.refreshToken.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fetch token with known email address', function(done) {
|
||||
auth.emailAddress = emailAddress;
|
||||
oauthStub.getOAuthToken.withArgs(emailAddress).yieldsAsync(null, oauthToken);
|
||||
|
||||
auth.getOAuthToken(function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(auth.emailAddress).to.equal(emailAddress);
|
||||
expect(auth.oauthToken).to.equal(oauthToken);
|
||||
|
||||
expect(oauthStub.getOAuthToken.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fetch token with unknown email address', function(done) {
|
||||
oauthStub.getOAuthToken.withArgs(undefined).yieldsAsync(null, oauthToken);
|
||||
oauthStub.queryEmailAddress.withArgs(oauthToken).yieldsAsync(null, emailAddress);
|
||||
|
||||
auth.getOAuthToken(function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(auth.emailAddress).to.equal(emailAddress);
|
||||
expect(auth.oauthToken).to.equal(oauthToken);
|
||||
|
||||
expect(oauthStub.getOAuthToken.calledOnce).to.be.true;
|
||||
expect(oauthStub.queryEmailAddress.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail when email address fetch fails', function(done) {
|
||||
oauthStub.getOAuthToken.yieldsAsync(null, oauthToken);
|
||||
oauthStub.queryEmailAddress.yieldsAsync(new Error());
|
||||
|
||||
auth.getOAuthToken(function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(auth.emailAddress).to.not.exist;
|
||||
expect(auth.oauthToken).to.not.exist;
|
||||
|
||||
expect(oauthStub.getOAuthToken.calledOnce).to.be.true;
|
||||
expect(oauthStub.queryEmailAddress.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail when oauth fetch fails', function(done) {
|
||||
oauthStub.getOAuthToken.yieldsAsync(new Error());
|
||||
|
||||
auth.getOAuthToken(function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(auth.emailAddress).to.not.exist;
|
||||
expect(auth.oauthToken).to.not.exist;
|
||||
|
||||
expect(oauthStub.getOAuthToken.calledOnce).to.be.true;
|
||||
expect(oauthStub.queryEmailAddress.called).to.be.false;
|
||||
|
||||
done();
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#_loadCredentials', function() {
|
||||
it('should work', function(done) {
|
||||
storageStub.listItems.withArgs(EMAIL_ADDR_DB_KEY, 0, null).yieldsAsync(null, [emailAddress]);
|
||||
storageStub.listItems.withArgs(PASSWD_DB_KEY, 0, null).yieldsAsync(null, [encryptedPassword]);
|
||||
storageStub.listItems.withArgs(PROVIDER_DB_KEY, 0, null).yieldsAsync(null, [provider]);
|
||||
storageStub.listItems.withArgs(USERNAME_DB_KEY, 0, null).yieldsAsync(null, [username]);
|
||||
storageStub.listItems.withArgs(REALNAME_DB_KEY, 0, null).yieldsAsync(null, [realname]);
|
||||
storageStub.listItems.withArgs(IMAP_DB_KEY, 0, null).yieldsAsync(null, [imap]);
|
||||
storageStub.listItems.withArgs(SMTP_DB_KEY, 0, null).yieldsAsync(null, [smtp]);
|
||||
describe('#handleCertificateUpdate', function() {
|
||||
var storeCredentialsStub;
|
||||
var dummyCert = 'cert';
|
||||
|
||||
auth._loadCredentials(function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(auth.emailAddress).to.equal(emailAddress);
|
||||
expect(auth.password).to.equal(encryptedPassword);
|
||||
expect(auth.provider).to.equal(provider);
|
||||
expect(auth.imap).to.equal(imap);
|
||||
expect(auth.smtp).to.equal(smtp);
|
||||
expect(auth.username).to.equal(username);
|
||||
expect(auth.realname).to.equal(realname);
|
||||
function onConnectDummy() {}
|
||||
|
||||
expect(auth.passwordNeedsDecryption).to.be.true;
|
||||
|
||||
expect(storageStub.listItems.callCount).to.equal(7);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail', function(done) {
|
||||
storageStub.listItems.yieldsAsync(new Error());
|
||||
|
||||
auth._loadCredentials(function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(auth.emailAddress).to.not.exist;
|
||||
expect(auth.password).to.not.exist;
|
||||
expect(auth.provider).to.not.exist;
|
||||
expect(auth.imap).to.not.exist;
|
||||
expect(auth.smtp).to.not.exist;
|
||||
expect(auth.username).to.not.exist;
|
||||
expect(auth.realname).to.not.exist;
|
||||
|
||||
expect(storageStub.listItems.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
beforeEach(function() {
|
||||
storeCredentialsStub = sinon.stub(auth, 'storeCredentials');
|
||||
});
|
||||
|
||||
describe('#handleCertificateUpdate', function() {
|
||||
var storeCredentialsStub;
|
||||
var dummyCert = 'cert';
|
||||
it('should work for Trust on first use', function(done) {
|
||||
auth.imap = {};
|
||||
storeCredentialsStub.yields();
|
||||
|
||||
function onConnectDummy() {}
|
||||
function callback(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(storeCredentialsStub.callCount).to.equal(1);
|
||||
done();
|
||||
}
|
||||
auth.handleCertificateUpdate('imap', onConnectDummy, callback, dummyCert);
|
||||
});
|
||||
|
||||
beforeEach(function() {
|
||||
storeCredentialsStub = sinon.stub(auth, 'storeCredentials');
|
||||
});
|
||||
it('should work for stored cert', function() {
|
||||
auth.imap = {
|
||||
ca: dummyCert
|
||||
};
|
||||
storeCredentialsStub.yields();
|
||||
|
||||
it('should work for Trust on first use', function(done) {
|
||||
auth.imap = {};
|
||||
storeCredentialsStub.yields();
|
||||
auth.handleCertificateUpdate('imap', onConnectDummy, onConnectDummy, dummyCert);
|
||||
expect(storeCredentialsStub.callCount).to.equal(0);
|
||||
});
|
||||
|
||||
function callback(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(storeCredentialsStub.callCount).to.equal(1);
|
||||
done();
|
||||
}
|
||||
auth.handleCertificateUpdate('imap', onConnectDummy, callback, dummyCert);
|
||||
});
|
||||
it('should work for pinned cert', function(done) {
|
||||
auth.imap = {
|
||||
ca: 'other',
|
||||
pinned: true
|
||||
};
|
||||
storeCredentialsStub.yields();
|
||||
|
||||
it('should work for stored cert', function() {
|
||||
auth.imap = {
|
||||
ca: dummyCert
|
||||
};
|
||||
storeCredentialsStub.yields();
|
||||
|
||||
auth.handleCertificateUpdate('imap', onConnectDummy, onConnectDummy, dummyCert);
|
||||
function callback(err) {
|
||||
expect(err).to.exist;
|
||||
expect(err.message).to.exist;
|
||||
expect(storeCredentialsStub.callCount).to.equal(0);
|
||||
});
|
||||
done();
|
||||
}
|
||||
auth.handleCertificateUpdate('imap', onConnectDummy, callback, dummyCert);
|
||||
});
|
||||
|
||||
it('should work for pinned cert', function(done) {
|
||||
auth.imap = {
|
||||
ca: 'other',
|
||||
pinned: true
|
||||
};
|
||||
storeCredentialsStub.yields();
|
||||
it('should work for updated cert', function(done) {
|
||||
auth.imap = {
|
||||
ca: 'other'
|
||||
};
|
||||
storeCredentialsStub.yields();
|
||||
|
||||
function callback(err) {
|
||||
function callback(err) {
|
||||
if (err && err.callback) {
|
||||
expect(err).to.exist;
|
||||
expect(err.message).to.exist;
|
||||
expect(storeCredentialsStub.callCount).to.equal(0);
|
||||
err.callback(true);
|
||||
} else {
|
||||
expect(storeCredentialsStub.callCount).to.equal(1);
|
||||
done();
|
||||
}
|
||||
auth.handleCertificateUpdate('imap', onConnectDummy, callback, dummyCert);
|
||||
});
|
||||
}
|
||||
|
||||
it('should work for updated cert', function(done) {
|
||||
auth.imap = {
|
||||
ca: 'other'
|
||||
};
|
||||
storeCredentialsStub.yields();
|
||||
function onConnect(callback) {
|
||||
callback();
|
||||
}
|
||||
|
||||
function callback(err) {
|
||||
if (err && err.callback) {
|
||||
expect(err).to.exist;
|
||||
expect(err.message).to.exist;
|
||||
expect(storeCredentialsStub.callCount).to.equal(0);
|
||||
err.callback(true);
|
||||
} else {
|
||||
expect(storeCredentialsStub.callCount).to.equal(1);
|
||||
done();
|
||||
}
|
||||
}
|
||||
auth.handleCertificateUpdate('imap', onConnect, callback, dummyCert);
|
||||
});
|
||||
});
|
||||
|
||||
function onConnect(callback) {
|
||||
callback();
|
||||
}
|
||||
describe('#logout', function() {
|
||||
it('should fail to to error in calling db clear', function(done) {
|
||||
storageStub.clear.yields(new Error());
|
||||
|
||||
auth.handleCertificateUpdate('imap', onConnect, callback, dummyCert);
|
||||
auth.logout(function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('#logout', function() {
|
||||
it('should fail to to error in calling db clear', function(done) {
|
||||
storageStub.clear.yields(new Error());
|
||||
it('should work', function(done) {
|
||||
storageStub.clear.yields();
|
||||
|
||||
auth.logout(function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
storageStub.clear.yields();
|
||||
|
||||
auth.logout(function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(auth.password).to.be.undefined;
|
||||
expect(auth.initialized).to.be.undefined;
|
||||
expect(auth.credentialsDirty).to.be.undefined;
|
||||
expect(auth.passwordNeedsDecryption).to.be.undefined;
|
||||
done();
|
||||
});
|
||||
auth.logout(function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(auth.password).to.be.undefined;
|
||||
expect(auth.initialized).to.be.undefined;
|
||||
expect(auth.credentialsDirty).to.be.undefined;
|
||||
expect(auth.passwordNeedsDecryption).to.be.undefined;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,67 +1,64 @@
|
|||
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() {
|
||||
chai.Assertion.includeStack = true;
|
||||
describe('Backbutton Handler', function() {
|
||||
chai.Assertion.includeStack = true;
|
||||
|
||||
var scope, event;
|
||||
var scope, event;
|
||||
|
||||
beforeEach(function() {
|
||||
scope = {
|
||||
state: {},
|
||||
$apply: function() {}
|
||||
};
|
||||
beforeEach(function() {
|
||||
scope = {
|
||||
state: {},
|
||||
$apply: function() {}
|
||||
};
|
||||
|
||||
event = new CustomEvent('backbutton');
|
||||
event = new CustomEvent('backbutton');
|
||||
|
||||
// this is a precondition for the test. throw an exception
|
||||
// if this would produce side effects
|
||||
expect(navigator.app).to.not.exist;
|
||||
navigator.app = {};
|
||||
// this is a precondition for the test. throw an exception
|
||||
// if this would produce side effects
|
||||
expect(navigator.app).to.not.exist;
|
||||
navigator.app = {};
|
||||
|
||||
btnHandler.attachHandler(scope);
|
||||
btnHandler.start();
|
||||
});
|
||||
btnHandler.attachHandler(scope);
|
||||
btnHandler.start();
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
btnHandler.stop();
|
||||
delete navigator.app;
|
||||
});
|
||||
afterEach(function() {
|
||||
btnHandler.stop();
|
||||
delete navigator.app;
|
||||
});
|
||||
|
||||
it('should close lightbox', function() {
|
||||
scope.state.lightbox = 'asd';
|
||||
document.dispatchEvent(event);
|
||||
expect(scope.state.lightbox).to.be.undefined;
|
||||
});
|
||||
it('should close lightbox', function() {
|
||||
scope.state.lightbox = 'asd';
|
||||
document.dispatchEvent(event);
|
||||
expect(scope.state.lightbox).to.be.undefined;
|
||||
});
|
||||
|
||||
it('should close reader', function() {
|
||||
scope.state.read = {
|
||||
open: true,
|
||||
toggle: function(state) {
|
||||
scope.state.read.open = state;
|
||||
}
|
||||
};
|
||||
document.dispatchEvent(event);
|
||||
expect(scope.state.read.open).to.be.false;
|
||||
});
|
||||
it('should close reader', function() {
|
||||
scope.state.read = {
|
||||
open: true,
|
||||
toggle: function(state) {
|
||||
scope.state.read.open = state;
|
||||
}
|
||||
};
|
||||
document.dispatchEvent(event);
|
||||
expect(scope.state.read.open).to.be.false;
|
||||
});
|
||||
|
||||
it('should close navigation', function() {
|
||||
scope.state.nav = {
|
||||
open: true,
|
||||
toggle: function(state) {
|
||||
scope.state.nav.open = state;
|
||||
}
|
||||
};
|
||||
document.dispatchEvent(event);
|
||||
expect(scope.state.nav.open).to.be.false;
|
||||
});
|
||||
it('should close navigation', function() {
|
||||
scope.state.nav = {
|
||||
open: true,
|
||||
toggle: function(state) {
|
||||
scope.state.nav.open = state;
|
||||
}
|
||||
};
|
||||
document.dispatchEvent(event);
|
||||
expect(scope.state.nav.open).to.be.false;
|
||||
});
|
||||
|
||||
it('should close app', function(done) {
|
||||
navigator.app.exitApp = done;
|
||||
document.dispatchEvent(event);
|
||||
});
|
||||
it('should close app', function(done) {
|
||||
navigator.app.exitApp = done;
|
||||
document.dispatchEvent(event);
|
||||
});
|
||||
});
|
|
@ -1,414 +1,415 @@
|
|||
define(function(require) {
|
||||
'use strict';
|
||||
'use strict';
|
||||
|
||||
var ConnectionDoctor = require('js/util/connection-doctor'),
|
||||
TCPSocket = require('tcp-socket'),
|
||||
ImapClient = require('imap-client'),
|
||||
SmtpClient = require('smtpclient'),
|
||||
cfg = require('js/app-config').config,
|
||||
expect = chai.expect;
|
||||
var TCPSocket = require('tcp-socket'),
|
||||
ImapClient = require('imap-client'),
|
||||
SmtpClient = require('wo-smtpclient'),
|
||||
ConnectionDoctor = require('../../src/js/util/connection-doctor'),
|
||||
cfg = require('../../src/js/app-config').config;
|
||||
|
||||
describe('Connection Doctor', function() {
|
||||
var doctor;
|
||||
var socketStub, imapStub, smtpStub, credentials;
|
||||
describe('Connection Doctor', function() {
|
||||
var doctor;
|
||||
var socketStub, imapStub, smtpStub, credentials, workerPath;
|
||||
|
||||
beforeEach(function() {
|
||||
//
|
||||
// Stubs
|
||||
//
|
||||
beforeEach(function() {
|
||||
//
|
||||
// Stubs
|
||||
//
|
||||
|
||||
// there is no socket shim for for this use case, use dummy object
|
||||
socketStub = {
|
||||
close: function() {
|
||||
this.onclose();
|
||||
// there is no socket shim for for this use case, use dummy object
|
||||
socketStub = {
|
||||
close: function() {
|
||||
this.onclose();
|
||||
}
|
||||
};
|
||||
|
||||
workerPath = 'js/tcp-socket-tls-worker.min.js';
|
||||
imapStub = sinon.createStubInstance(ImapClient);
|
||||
smtpStub = sinon.createStubInstance(SmtpClient);
|
||||
|
||||
//
|
||||
// Fixture
|
||||
//
|
||||
credentials = {
|
||||
imap: {
|
||||
host: 'asd',
|
||||
port: 1234,
|
||||
secure: true,
|
||||
ca: 'cert'
|
||||
},
|
||||
smtp: {
|
||||
host: 'qwe',
|
||||
port: 5678,
|
||||
secure: false,
|
||||
ca: 'cert'
|
||||
},
|
||||
username: 'username',
|
||||
password: 'password'
|
||||
};
|
||||
|
||||
sinon.stub(TCPSocket, 'open').returns(socketStub); // convenience constructors suck
|
||||
|
||||
//
|
||||
// Setup SUT
|
||||
//
|
||||
doctor = new ConnectionDoctor();
|
||||
doctor.configure(credentials);
|
||||
doctor._imap = imapStub;
|
||||
doctor._smtp = smtpStub;
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
TCPSocket.open.restore();
|
||||
});
|
||||
|
||||
describe('#_checkOnline', function() {
|
||||
it('should check if browser is online', function(done) {
|
||||
doctor._checkOnline(function(error) {
|
||||
if (navigator.onLine) {
|
||||
expect(error).to.not.exist;
|
||||
} else {
|
||||
expect(error).to.exist;
|
||||
expect(error.code).to.equal(ConnectionDoctor.OFFLINE);
|
||||
}
|
||||
};
|
||||
|
||||
imapStub = sinon.createStubInstance(ImapClient);
|
||||
smtpStub = sinon.createStubInstance(SmtpClient);
|
||||
|
||||
//
|
||||
// Fixture
|
||||
//
|
||||
credentials = {
|
||||
imap: {
|
||||
host: 'asd',
|
||||
port: 1234,
|
||||
secure: true,
|
||||
ca: 'cert'
|
||||
},
|
||||
smtp: {
|
||||
host: 'qwe',
|
||||
port: 5678,
|
||||
secure: false,
|
||||
ca: 'cert'
|
||||
},
|
||||
username: 'username',
|
||||
password: 'password'
|
||||
};
|
||||
|
||||
sinon.stub(TCPSocket, 'open').returns(socketStub); // convenience constructors suck
|
||||
|
||||
//
|
||||
// Setup SUT
|
||||
//
|
||||
doctor = new ConnectionDoctor();
|
||||
doctor.configure(credentials);
|
||||
doctor._imap = imapStub;
|
||||
doctor._smtp = smtpStub;
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
TCPSocket.open.restore();
|
||||
});
|
||||
|
||||
describe('#_checkOnline', function() {
|
||||
it('should check if browser is online', function(done) {
|
||||
doctor._checkOnline(function(error) {
|
||||
if (navigator.onLine) {
|
||||
expect(error).to.not.exist;
|
||||
} else {
|
||||
expect(error).to.exist;
|
||||
expect(error.code).to.equal(ConnectionDoctor.OFFLINE);
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#_checkReachable', function() {
|
||||
it('should be able to reach the host w/o cert', function(done) {
|
||||
credentials.imap.ca = undefined;
|
||||
|
||||
doctor._checkReachable(credentials.imap, function(error) {
|
||||
expect(error).to.not.exist;
|
||||
expect(TCPSocket.open.calledOnce).to.be.true;
|
||||
expect(TCPSocket.open.calledWith(credentials.imap.host, credentials.imap.port, {
|
||||
binaryType: 'arraybuffer',
|
||||
useSecureTransport: credentials.imap.secure,
|
||||
ca: credentials.imap.ca
|
||||
})).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
socketStub.oncert();
|
||||
socketStub.onopen();
|
||||
});
|
||||
|
||||
it('should catch Mozilla TCPSocket exception', function(done) {
|
||||
// Mozilla forbids extensions to the TCPSocket object
|
||||
Object.defineProperty(socketStub, 'oncert', {
|
||||
set: function() {
|
||||
throw 'Mozilla specific behavior';
|
||||
}
|
||||
});
|
||||
|
||||
doctor._checkReachable(credentials.imap, function(error) {
|
||||
expect(error).to.not.exist;
|
||||
expect(TCPSocket.open.calledOnce).to.be.true;
|
||||
expect(TCPSocket.open.calledWith(credentials.imap.host, credentials.imap.port, {
|
||||
binaryType: 'arraybuffer',
|
||||
useSecureTransport: credentials.imap.secure,
|
||||
ca: credentials.imap.ca
|
||||
})).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
socketStub.onopen();
|
||||
});
|
||||
|
||||
it('should fail w/ wrong cert', function(done) {
|
||||
doctor._checkReachable(credentials.imap, function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(error.code).to.equal(ConnectionDoctor.TLS_WRONG_CERT);
|
||||
expect(TCPSocket.open.calledOnce).to.be.true;
|
||||
expect(TCPSocket.open.calledWith(credentials.imap.host, credentials.imap.port, {
|
||||
binaryType: 'arraybuffer',
|
||||
useSecureTransport: credentials.imap.secure,
|
||||
ca: credentials.imap.ca
|
||||
})).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
socketStub.oncert();
|
||||
socketStub.onerror();
|
||||
socketStub.onclose();
|
||||
});
|
||||
|
||||
it('should fail w/ host unreachable', function(done) {
|
||||
doctor._checkReachable(credentials.imap, function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(error.code).to.equal(ConnectionDoctor.HOST_UNREACHABLE);
|
||||
expect(TCPSocket.open.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
socketStub.onerror({
|
||||
data: new Error()
|
||||
});
|
||||
socketStub.onclose();
|
||||
});
|
||||
|
||||
it('should fail w/ timeout', function(done) {
|
||||
var origTimeout = cfg.connDocTimeout; // remember timeout from the config to reset it on done
|
||||
cfg.connDocTimeout = 20; // set to 20ms for the test
|
||||
|
||||
doctor._checkReachable(credentials.imap, function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(error.code).to.equal(ConnectionDoctor.HOST_TIMEOUT);
|
||||
expect(TCPSocket.open.calledOnce).to.be.true;
|
||||
cfg.connDocTimeout = origTimeout;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#_checkImap', function() {
|
||||
it('should perform IMAP login, list folders, logout', function(done) {
|
||||
imapStub.login.yieldsAsync();
|
||||
imapStub.listWellKnownFolders.yieldsAsync(null, {
|
||||
Inbox: [{}]
|
||||
});
|
||||
imapStub.logout.yieldsAsync();
|
||||
|
||||
doctor._checkImap(function(error) {
|
||||
expect(error).to.not.exist;
|
||||
expect(imapStub.login.calledOnce).to.be.true;
|
||||
expect(imapStub.listWellKnownFolders.calledOnce).to.be.true;
|
||||
expect(imapStub.logout.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail w/ generic error on logout', function(done) {
|
||||
imapStub.login.yieldsAsync();
|
||||
imapStub.listWellKnownFolders.yieldsAsync(null, {
|
||||
Inbox: [{}]
|
||||
});
|
||||
|
||||
doctor._checkImap(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(error.code).to.equal(ConnectionDoctor.GENERIC_ERROR);
|
||||
expect(error.underlyingError).to.exist;
|
||||
expect(imapStub.login.calledOnce).to.be.true;
|
||||
expect(imapStub.listWellKnownFolders.calledOnce).to.be.true;
|
||||
expect(imapStub.logout.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
setTimeout(function() {
|
||||
// this error is thrown while we're waiting for the logout
|
||||
imapStub.onError(new Error());
|
||||
}, 50);
|
||||
});
|
||||
|
||||
it('should fail w/ generic error on inbox missing', function(done) {
|
||||
imapStub.login.yieldsAsync();
|
||||
imapStub.listWellKnownFolders.yieldsAsync(null, {
|
||||
Inbox: []
|
||||
});
|
||||
|
||||
doctor._checkImap(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(error.code).to.equal(ConnectionDoctor.NO_INBOX);
|
||||
expect(imapStub.login.calledOnce).to.be.true;
|
||||
expect(imapStub.listWellKnownFolders.calledOnce).to.be.true;
|
||||
expect(imapStub.logout.called).to.be.false;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail w/ generic error on listing folders fails', function(done) {
|
||||
imapStub.login.yieldsAsync();
|
||||
imapStub.listWellKnownFolders.yieldsAsync(new Error());
|
||||
|
||||
doctor._checkImap(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(error.code).to.equal(ConnectionDoctor.GENERIC_ERROR);
|
||||
expect(error.underlyingError).to.exist;
|
||||
expect(imapStub.login.calledOnce).to.be.true;
|
||||
expect(imapStub.listWellKnownFolders.calledOnce).to.be.true;
|
||||
expect(imapStub.logout.called).to.be.false;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail w/ auth rejected', function(done) {
|
||||
doctor._checkImap(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(error.code).to.equal(ConnectionDoctor.AUTH_REJECTED);
|
||||
expect(error.underlyingError).to.exist;
|
||||
expect(imapStub.login.calledOnce).to.be.true;
|
||||
expect(imapStub.listWellKnownFolders.called).to.be.false;
|
||||
expect(imapStub.logout.called).to.be.false;
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
setTimeout(function() {
|
||||
// this error is thrown while we're waiting for the login
|
||||
imapStub.onError(new Error());
|
||||
}, 50);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#_checkSmtp', function() {
|
||||
it('should perform SMTP login, logout', function(done) {
|
||||
doctor._checkSmtp(function(error) {
|
||||
expect(error).to.not.exist;
|
||||
expect(smtpStub.connect.calledOnce).to.be.true;
|
||||
expect(smtpStub.quit.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
smtpStub.onidle();
|
||||
smtpStub.onclose();
|
||||
});
|
||||
|
||||
it('should fail w/ auth rejected', function(done) {
|
||||
doctor._checkSmtp(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(error.code).to.equal(ConnectionDoctor.AUTH_REJECTED);
|
||||
expect(error.underlyingError).to.exist;
|
||||
expect(smtpStub.connect.calledOnce).to.be.true;
|
||||
expect(smtpStub.quit.called).to.be.false;
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
smtpStub.onerror(new Error());
|
||||
});
|
||||
});
|
||||
|
||||
describe('#check', function() {
|
||||
beforeEach(function() {
|
||||
sinon.stub(doctor, '_checkOnline');
|
||||
sinon.stub(doctor, '_checkReachable');
|
||||
sinon.stub(doctor, '_checkImap');
|
||||
sinon.stub(doctor, '_checkSmtp');
|
||||
});
|
||||
|
||||
it('should perform all tests', function(done) {
|
||||
doctor._checkOnline.yieldsAsync();
|
||||
doctor._checkReachable.withArgs(credentials.imap).yieldsAsync();
|
||||
doctor._checkReachable.withArgs(credentials.smtp).yieldsAsync();
|
||||
doctor._checkImap.yieldsAsync();
|
||||
doctor._checkSmtp.yieldsAsync();
|
||||
|
||||
doctor.check(function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(doctor._checkOnline.calledOnce).to.be.true;
|
||||
expect(doctor._checkReachable.calledTwice).to.be.true;
|
||||
expect(doctor._checkImap.calledOnce).to.be.true;
|
||||
expect(doctor._checkSmtp.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail for smtp', function(done) {
|
||||
doctor._checkOnline.yieldsAsync();
|
||||
doctor._checkReachable.withArgs(credentials.imap).yieldsAsync();
|
||||
doctor._checkReachable.withArgs(credentials.smtp).yieldsAsync();
|
||||
doctor._checkImap.yieldsAsync();
|
||||
doctor._checkSmtp.yieldsAsync(new Error());
|
||||
|
||||
doctor.check(function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(doctor._checkOnline.calledOnce).to.be.true;
|
||||
expect(doctor._checkReachable.calledTwice).to.be.true;
|
||||
expect(doctor._checkImap.calledOnce).to.be.true;
|
||||
expect(doctor._checkSmtp.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail for imap', function(done) {
|
||||
doctor._checkOnline.yieldsAsync();
|
||||
doctor._checkReachable.withArgs(credentials.imap).yieldsAsync();
|
||||
doctor._checkReachable.withArgs(credentials.smtp).yieldsAsync();
|
||||
doctor._checkImap.yieldsAsync(new Error());
|
||||
|
||||
doctor.check(function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(doctor._checkOnline.calledOnce).to.be.true;
|
||||
expect(doctor._checkReachable.calledTwice).to.be.true;
|
||||
expect(doctor._checkImap.calledOnce).to.be.true;
|
||||
expect(doctor._checkSmtp.called).to.be.false;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail for smtp reachability', function(done) {
|
||||
doctor._checkOnline.yieldsAsync();
|
||||
doctor._checkReachable.withArgs(credentials.imap).yieldsAsync();
|
||||
doctor._checkReachable.withArgs(credentials.smtp).yieldsAsync(new Error());
|
||||
|
||||
doctor.check(function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(doctor._checkOnline.calledOnce).to.be.true;
|
||||
expect(doctor._checkReachable.calledTwice).to.be.true;
|
||||
expect(doctor._checkImap.called).to.be.false;
|
||||
expect(doctor._checkSmtp.called).to.be.false;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail for imap reachability', function(done) {
|
||||
doctor._checkOnline.yieldsAsync();
|
||||
doctor._checkReachable.withArgs(credentials.imap).yieldsAsync(new Error());
|
||||
|
||||
doctor.check(function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(doctor._checkOnline.calledOnce).to.be.true;
|
||||
expect(doctor._checkReachable.calledOnce).to.be.true;
|
||||
expect(doctor._checkImap.called).to.be.false;
|
||||
expect(doctor._checkSmtp.called).to.be.false;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail for offline', function(done) {
|
||||
doctor._checkOnline.yieldsAsync(new Error());
|
||||
|
||||
doctor.check(function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(doctor._checkOnline.calledOnce).to.be.true;
|
||||
expect(doctor._checkReachable.called).to.be.false;
|
||||
expect(doctor._checkImap.called).to.be.false;
|
||||
expect(doctor._checkSmtp.called).to.be.false;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail w/o config', function(done) {
|
||||
doctor.credentials = doctor._imap = doctor._smtp = undefined;
|
||||
|
||||
doctor.check(function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(doctor._checkOnline.called).to.be.false;
|
||||
expect(doctor._checkReachable.called).to.be.false;
|
||||
expect(doctor._checkImap.called).to.be.false;
|
||||
expect(doctor._checkSmtp.called).to.be.false;
|
||||
|
||||
done();
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#_checkReachable', function() {
|
||||
it('should be able to reach the host w/o cert', function(done) {
|
||||
credentials.imap.ca = undefined;
|
||||
|
||||
doctor._checkReachable(credentials.imap, function(error) {
|
||||
expect(error).to.not.exist;
|
||||
expect(TCPSocket.open.calledOnce).to.be.true;
|
||||
expect(TCPSocket.open.calledWith(credentials.imap.host, credentials.imap.port, {
|
||||
binaryType: 'arraybuffer',
|
||||
useSecureTransport: credentials.imap.secure,
|
||||
ca: credentials.imap.ca,
|
||||
tlsWorkerPath: workerPath
|
||||
})).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
socketStub.oncert();
|
||||
socketStub.onopen();
|
||||
});
|
||||
|
||||
it('should catch Mozilla TCPSocket exception', function(done) {
|
||||
// Mozilla forbids extensions to the TCPSocket object
|
||||
Object.defineProperty(socketStub, 'oncert', {
|
||||
set: function() {
|
||||
throw 'Mozilla specific behavior';
|
||||
}
|
||||
});
|
||||
|
||||
doctor._checkReachable(credentials.imap, function(error) {
|
||||
expect(error).to.not.exist;
|
||||
expect(TCPSocket.open.calledOnce).to.be.true;
|
||||
expect(TCPSocket.open.calledWith(credentials.imap.host, credentials.imap.port, {
|
||||
binaryType: 'arraybuffer',
|
||||
useSecureTransport: credentials.imap.secure,
|
||||
ca: credentials.imap.ca,
|
||||
tlsWorkerPath: workerPath
|
||||
})).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
socketStub.onopen();
|
||||
});
|
||||
|
||||
it('should fail w/ wrong cert', function(done) {
|
||||
doctor._checkReachable(credentials.imap, function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(error.code).to.equal(ConnectionDoctor.TLS_WRONG_CERT);
|
||||
expect(TCPSocket.open.calledOnce).to.be.true;
|
||||
expect(TCPSocket.open.calledWith(credentials.imap.host, credentials.imap.port, {
|
||||
binaryType: 'arraybuffer',
|
||||
useSecureTransport: credentials.imap.secure,
|
||||
ca: credentials.imap.ca,
|
||||
tlsWorkerPath: workerPath
|
||||
})).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
socketStub.oncert();
|
||||
socketStub.onerror();
|
||||
socketStub.onclose();
|
||||
});
|
||||
|
||||
it('should fail w/ host unreachable', function(done) {
|
||||
doctor._checkReachable(credentials.imap, function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(error.code).to.equal(ConnectionDoctor.HOST_UNREACHABLE);
|
||||
expect(TCPSocket.open.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
socketStub.onerror({
|
||||
data: new Error()
|
||||
});
|
||||
socketStub.onclose();
|
||||
});
|
||||
|
||||
it('should fail w/ timeout', function(done) {
|
||||
var origTimeout = cfg.connDocTimeout; // remember timeout from the config to reset it on done
|
||||
cfg.connDocTimeout = 20; // set to 20ms for the test
|
||||
|
||||
doctor._checkReachable(credentials.imap, function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(error.code).to.equal(ConnectionDoctor.HOST_TIMEOUT);
|
||||
expect(TCPSocket.open.calledOnce).to.be.true;
|
||||
cfg.connDocTimeout = origTimeout;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#_checkImap', function() {
|
||||
it('should perform IMAP login, list folders, logout', function(done) {
|
||||
imapStub.login.yieldsAsync();
|
||||
imapStub.listWellKnownFolders.yieldsAsync(null, {
|
||||
Inbox: [{}]
|
||||
});
|
||||
imapStub.logout.yieldsAsync();
|
||||
|
||||
doctor._checkImap(function(error) {
|
||||
expect(error).to.not.exist;
|
||||
expect(imapStub.login.calledOnce).to.be.true;
|
||||
expect(imapStub.listWellKnownFolders.calledOnce).to.be.true;
|
||||
expect(imapStub.logout.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail w/ generic error on logout', function(done) {
|
||||
imapStub.login.yieldsAsync();
|
||||
imapStub.listWellKnownFolders.yieldsAsync(null, {
|
||||
Inbox: [{}]
|
||||
});
|
||||
|
||||
doctor._checkImap(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(error.code).to.equal(ConnectionDoctor.GENERIC_ERROR);
|
||||
expect(error.underlyingError).to.exist;
|
||||
expect(imapStub.login.calledOnce).to.be.true;
|
||||
expect(imapStub.listWellKnownFolders.calledOnce).to.be.true;
|
||||
expect(imapStub.logout.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
setTimeout(function() {
|
||||
// this error is thrown while we're waiting for the logout
|
||||
imapStub.onError(new Error());
|
||||
}, 50);
|
||||
});
|
||||
|
||||
it('should fail w/ generic error on inbox missing', function(done) {
|
||||
imapStub.login.yieldsAsync();
|
||||
imapStub.listWellKnownFolders.yieldsAsync(null, {
|
||||
Inbox: []
|
||||
});
|
||||
|
||||
doctor._checkImap(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(error.code).to.equal(ConnectionDoctor.NO_INBOX);
|
||||
expect(imapStub.login.calledOnce).to.be.true;
|
||||
expect(imapStub.listWellKnownFolders.calledOnce).to.be.true;
|
||||
expect(imapStub.logout.called).to.be.false;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail w/ generic error on listing folders fails', function(done) {
|
||||
imapStub.login.yieldsAsync();
|
||||
imapStub.listWellKnownFolders.yieldsAsync(new Error());
|
||||
|
||||
doctor._checkImap(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(error.code).to.equal(ConnectionDoctor.GENERIC_ERROR);
|
||||
expect(error.underlyingError).to.exist;
|
||||
expect(imapStub.login.calledOnce).to.be.true;
|
||||
expect(imapStub.listWellKnownFolders.calledOnce).to.be.true;
|
||||
expect(imapStub.logout.called).to.be.false;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail w/ auth rejected', function(done) {
|
||||
doctor._checkImap(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(error.code).to.equal(ConnectionDoctor.AUTH_REJECTED);
|
||||
expect(error.underlyingError).to.exist;
|
||||
expect(imapStub.login.calledOnce).to.be.true;
|
||||
expect(imapStub.listWellKnownFolders.called).to.be.false;
|
||||
expect(imapStub.logout.called).to.be.false;
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
setTimeout(function() {
|
||||
// this error is thrown while we're waiting for the login
|
||||
imapStub.onError(new Error());
|
||||
}, 50);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#_checkSmtp', function() {
|
||||
it('should perform SMTP login, logout', function(done) {
|
||||
doctor._checkSmtp(function(error) {
|
||||
expect(error).to.not.exist;
|
||||
expect(smtpStub.connect.calledOnce).to.be.true;
|
||||
expect(smtpStub.quit.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
smtpStub.onidle();
|
||||
smtpStub.onclose();
|
||||
});
|
||||
|
||||
it('should fail w/ auth rejected', function(done) {
|
||||
doctor._checkSmtp(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(error.code).to.equal(ConnectionDoctor.AUTH_REJECTED);
|
||||
expect(error.underlyingError).to.exist;
|
||||
expect(smtpStub.connect.calledOnce).to.be.true;
|
||||
expect(smtpStub.quit.called).to.be.false;
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
smtpStub.onerror(new Error());
|
||||
});
|
||||
});
|
||||
|
||||
describe('#check', function() {
|
||||
beforeEach(function() {
|
||||
sinon.stub(doctor, '_checkOnline');
|
||||
sinon.stub(doctor, '_checkReachable');
|
||||
sinon.stub(doctor, '_checkImap');
|
||||
sinon.stub(doctor, '_checkSmtp');
|
||||
});
|
||||
|
||||
it('should perform all tests', function(done) {
|
||||
doctor._checkOnline.yieldsAsync();
|
||||
doctor._checkReachable.withArgs(credentials.imap).yieldsAsync();
|
||||
doctor._checkReachable.withArgs(credentials.smtp).yieldsAsync();
|
||||
doctor._checkImap.yieldsAsync();
|
||||
doctor._checkSmtp.yieldsAsync();
|
||||
|
||||
doctor.check(function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(doctor._checkOnline.calledOnce).to.be.true;
|
||||
expect(doctor._checkReachable.calledTwice).to.be.true;
|
||||
expect(doctor._checkImap.calledOnce).to.be.true;
|
||||
expect(doctor._checkSmtp.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail for smtp', function(done) {
|
||||
doctor._checkOnline.yieldsAsync();
|
||||
doctor._checkReachable.withArgs(credentials.imap).yieldsAsync();
|
||||
doctor._checkReachable.withArgs(credentials.smtp).yieldsAsync();
|
||||
doctor._checkImap.yieldsAsync();
|
||||
doctor._checkSmtp.yieldsAsync(new Error());
|
||||
|
||||
doctor.check(function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(doctor._checkOnline.calledOnce).to.be.true;
|
||||
expect(doctor._checkReachable.calledTwice).to.be.true;
|
||||
expect(doctor._checkImap.calledOnce).to.be.true;
|
||||
expect(doctor._checkSmtp.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail for imap', function(done) {
|
||||
doctor._checkOnline.yieldsAsync();
|
||||
doctor._checkReachable.withArgs(credentials.imap).yieldsAsync();
|
||||
doctor._checkReachable.withArgs(credentials.smtp).yieldsAsync();
|
||||
doctor._checkImap.yieldsAsync(new Error());
|
||||
|
||||
doctor.check(function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(doctor._checkOnline.calledOnce).to.be.true;
|
||||
expect(doctor._checkReachable.calledTwice).to.be.true;
|
||||
expect(doctor._checkImap.calledOnce).to.be.true;
|
||||
expect(doctor._checkSmtp.called).to.be.false;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail for smtp reachability', function(done) {
|
||||
doctor._checkOnline.yieldsAsync();
|
||||
doctor._checkReachable.withArgs(credentials.imap).yieldsAsync();
|
||||
doctor._checkReachable.withArgs(credentials.smtp).yieldsAsync(new Error());
|
||||
|
||||
doctor.check(function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(doctor._checkOnline.calledOnce).to.be.true;
|
||||
expect(doctor._checkReachable.calledTwice).to.be.true;
|
||||
expect(doctor._checkImap.called).to.be.false;
|
||||
expect(doctor._checkSmtp.called).to.be.false;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail for imap reachability', function(done) {
|
||||
doctor._checkOnline.yieldsAsync();
|
||||
doctor._checkReachable.withArgs(credentials.imap).yieldsAsync(new Error());
|
||||
|
||||
doctor.check(function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(doctor._checkOnline.calledOnce).to.be.true;
|
||||
expect(doctor._checkReachable.calledOnce).to.be.true;
|
||||
expect(doctor._checkImap.called).to.be.false;
|
||||
expect(doctor._checkSmtp.called).to.be.false;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail for offline', function(done) {
|
||||
doctor._checkOnline.yieldsAsync(new Error());
|
||||
|
||||
doctor.check(function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(doctor._checkOnline.calledOnce).to.be.true;
|
||||
expect(doctor._checkReachable.called).to.be.false;
|
||||
expect(doctor._checkImap.called).to.be.false;
|
||||
expect(doctor._checkSmtp.called).to.be.false;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail w/o config', function(done) {
|
||||
doctor.credentials = doctor._imap = doctor._smtp = undefined;
|
||||
|
||||
doctor.check(function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(doctor._checkOnline.called).to.be.false;
|
||||
expect(doctor._checkReachable.called).to.be.false;
|
||||
expect(doctor._checkImap.called).to.be.false;
|
||||
expect(doctor._checkSmtp.called).to.be.false;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,190 +1,186 @@
|
|||
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() {
|
||||
var scope, contactsCtrl,
|
||||
origKeychain, keychainMock,
|
||||
origPgp, pgpMock;
|
||||
describe('Contacts Controller unit test', function() {
|
||||
var scope, contactsCtrl,
|
||||
origKeychain, keychainMock,
|
||||
origPgp, pgpMock;
|
||||
|
||||
beforeEach(function() {
|
||||
origPgp = appController._pgp;
|
||||
appController._pgp = pgpMock = sinon.createStubInstance(PGP);
|
||||
origKeychain = appController._keychain;
|
||||
appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
beforeEach(function() {
|
||||
origPgp = appController._pgp;
|
||||
appController._pgp = pgpMock = sinon.createStubInstance(PGP);
|
||||
origKeychain = appController._keychain;
|
||||
appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
|
||||
angular.module('contactstest', []);
|
||||
mocks.module('contactstest');
|
||||
mocks.inject(function($rootScope, $controller) {
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
contactsCtrl = $controller(ContactsCtrl, {
|
||||
$scope: scope
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// restore the module
|
||||
appController._pgp = origPgp;
|
||||
appController._keychain = origKeychain;
|
||||
});
|
||||
|
||||
describe('scope variables', function() {
|
||||
it('should be set correctly', function() {
|
||||
expect(scope.fingerprint).to.equal('XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX');
|
||||
expect(scope.state.contacts.toggle).to.exist;
|
||||
});
|
||||
});
|
||||
|
||||
describe('listKeys', function() {
|
||||
it('should fail due to error in keychain.listLocalPublicKeys', function(done) {
|
||||
keychainMock.listLocalPublicKeys.yields(42);
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.equal(42);
|
||||
done();
|
||||
};
|
||||
|
||||
scope.listKeys();
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
keychainMock.listLocalPublicKeys.yields(null, [{
|
||||
_id: '12345'
|
||||
}]);
|
||||
pgpMock.getKeyParams.returns({
|
||||
fingerprint: 'asdf'
|
||||
});
|
||||
|
||||
scope.$apply = function() {
|
||||
expect(scope.keys.length).to.equal(1);
|
||||
expect(scope.keys[0]._id).to.equal('12345');
|
||||
expect(scope.keys[0].fingerprint).to.equal('asdf');
|
||||
done();
|
||||
};
|
||||
|
||||
expect(scope.keys).to.not.exist;
|
||||
scope.listKeys();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getFingerprint', function() {
|
||||
it('should work', function() {
|
||||
var key = {
|
||||
fingerprint: 'YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY'
|
||||
};
|
||||
|
||||
scope.getFingerprint(key);
|
||||
|
||||
expect(scope.fingerprint).to.equal('YYYY YYYY YYYY YYYY YYYY ... YYYY YYYY YYYY YYYY YYYY');
|
||||
});
|
||||
});
|
||||
|
||||
describe('importKey', function() {
|
||||
it('should work', function(done) {
|
||||
var keyArmored = '-----BEGIN PGP PUBLIC KEY BLOCK-----';
|
||||
|
||||
pgpMock.getKeyParams.returns({
|
||||
_id: '12345',
|
||||
userId: 'max@example.com',
|
||||
userIds: []
|
||||
});
|
||||
|
||||
keychainMock.saveLocalPublicKey.withArgs({
|
||||
_id: '12345',
|
||||
userId: 'max@example.com',
|
||||
userIds: [],
|
||||
publicKey: '-----BEGIN PGP PUBLIC KEY BLOCK-----',
|
||||
imported: true
|
||||
}).yields();
|
||||
|
||||
scope.listKeys = function() {
|
||||
done();
|
||||
};
|
||||
|
||||
scope.importKey(keyArmored);
|
||||
});
|
||||
|
||||
it('should fail due to invalid armored key', function(done) {
|
||||
var keyArmored = '-----BEGIN PGP PRIVATE KEY BLOCK-----';
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.importKey(keyArmored);
|
||||
});
|
||||
|
||||
it('should fail due to error in pgp.getKeyParams', function(done) {
|
||||
var keyArmored = '-----BEGIN PGP PUBLIC KEY BLOCK-----';
|
||||
|
||||
pgpMock.getKeyParams.throws(new Error('WAT'));
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.importKey(keyArmored);
|
||||
});
|
||||
|
||||
it('should fail due to error in keychain.saveLocalPublicKey', function(done) {
|
||||
var keyArmored = '-----BEGIN PGP PUBLIC KEY BLOCK-----';
|
||||
|
||||
pgpMock.getKeyParams.returns({
|
||||
_id: '12345',
|
||||
userId: 'max@example.com'
|
||||
});
|
||||
|
||||
keychainMock.saveLocalPublicKey.yields(42);
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.equal(42);
|
||||
done();
|
||||
};
|
||||
|
||||
scope.importKey(keyArmored);
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeKey', function() {
|
||||
it('should work', function(done) {
|
||||
var key = {
|
||||
_id: '12345'
|
||||
};
|
||||
|
||||
keychainMock.removeLocalPublicKey.withArgs('12345').yields();
|
||||
|
||||
scope.listKeys = function() {
|
||||
done();
|
||||
};
|
||||
|
||||
scope.removeKey(key);
|
||||
});
|
||||
|
||||
it('should fail due to error in keychain.removeLocalPublicKey', function(done) {
|
||||
var key = {
|
||||
_id: '12345'
|
||||
};
|
||||
|
||||
keychainMock.removeLocalPublicKey.withArgs('12345').yields(42);
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.equal(42);
|
||||
done();
|
||||
};
|
||||
|
||||
scope.removeKey(key);
|
||||
angular.module('contactstest', []);
|
||||
mocks.module('contactstest');
|
||||
mocks.inject(function($rootScope, $controller) {
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
contactsCtrl = $controller(ContactsCtrl, {
|
||||
$scope: scope
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// restore the module
|
||||
appController._pgp = origPgp;
|
||||
appController._keychain = origKeychain;
|
||||
});
|
||||
|
||||
describe('scope variables', function() {
|
||||
it('should be set correctly', function() {
|
||||
expect(scope.fingerprint).to.equal('XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX');
|
||||
expect(scope.state.contacts.toggle).to.exist;
|
||||
});
|
||||
});
|
||||
|
||||
describe('listKeys', function() {
|
||||
it('should fail due to error in keychain.listLocalPublicKeys', function(done) {
|
||||
keychainMock.listLocalPublicKeys.yields(42);
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.equal(42);
|
||||
done();
|
||||
};
|
||||
|
||||
scope.listKeys();
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
keychainMock.listLocalPublicKeys.yields(null, [{
|
||||
_id: '12345'
|
||||
}]);
|
||||
pgpMock.getKeyParams.returns({
|
||||
fingerprint: 'asdf'
|
||||
});
|
||||
|
||||
scope.$apply = function() {
|
||||
expect(scope.keys.length).to.equal(1);
|
||||
expect(scope.keys[0]._id).to.equal('12345');
|
||||
expect(scope.keys[0].fingerprint).to.equal('asdf');
|
||||
done();
|
||||
};
|
||||
|
||||
expect(scope.keys).to.not.exist;
|
||||
scope.listKeys();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getFingerprint', function() {
|
||||
it('should work', function() {
|
||||
var key = {
|
||||
fingerprint: 'YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY'
|
||||
};
|
||||
|
||||
scope.getFingerprint(key);
|
||||
|
||||
expect(scope.fingerprint).to.equal('YYYY YYYY YYYY YYYY YYYY ... YYYY YYYY YYYY YYYY YYYY');
|
||||
});
|
||||
});
|
||||
|
||||
describe('importKey', function() {
|
||||
it('should work', function(done) {
|
||||
var keyArmored = '-----BEGIN PGP PUBLIC KEY BLOCK-----';
|
||||
|
||||
pgpMock.getKeyParams.returns({
|
||||
_id: '12345',
|
||||
userId: 'max@example.com',
|
||||
userIds: []
|
||||
});
|
||||
|
||||
keychainMock.saveLocalPublicKey.withArgs({
|
||||
_id: '12345',
|
||||
userId: 'max@example.com',
|
||||
userIds: [],
|
||||
publicKey: '-----BEGIN PGP PUBLIC KEY BLOCK-----',
|
||||
imported: true
|
||||
}).yields();
|
||||
|
||||
scope.listKeys = function() {
|
||||
done();
|
||||
};
|
||||
|
||||
scope.importKey(keyArmored);
|
||||
});
|
||||
|
||||
it('should fail due to invalid armored key', function(done) {
|
||||
var keyArmored = '-----BEGIN PGP PRIVATE KEY BLOCK-----';
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.importKey(keyArmored);
|
||||
});
|
||||
|
||||
it('should fail due to error in pgp.getKeyParams', function(done) {
|
||||
var keyArmored = '-----BEGIN PGP PUBLIC KEY BLOCK-----';
|
||||
|
||||
pgpMock.getKeyParams.throws(new Error('WAT'));
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.importKey(keyArmored);
|
||||
});
|
||||
|
||||
it('should fail due to error in keychain.saveLocalPublicKey', function(done) {
|
||||
var keyArmored = '-----BEGIN PGP PUBLIC KEY BLOCK-----';
|
||||
|
||||
pgpMock.getKeyParams.returns({
|
||||
_id: '12345',
|
||||
userId: 'max@example.com'
|
||||
});
|
||||
|
||||
keychainMock.saveLocalPublicKey.yields(42);
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.equal(42);
|
||||
done();
|
||||
};
|
||||
|
||||
scope.importKey(keyArmored);
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeKey', function() {
|
||||
it('should work', function(done) {
|
||||
var key = {
|
||||
_id: '12345'
|
||||
};
|
||||
|
||||
keychainMock.removeLocalPublicKey.withArgs('12345').yields();
|
||||
|
||||
scope.listKeys = function() {
|
||||
done();
|
||||
};
|
||||
|
||||
scope.removeKey(key);
|
||||
});
|
||||
|
||||
it('should fail due to error in keychain.removeLocalPublicKey', function(done) {
|
||||
var key = {
|
||||
_id: '12345'
|
||||
};
|
||||
|
||||
keychainMock.removeLocalPublicKey.withArgs('12345').yields(42);
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.equal(42);
|
||||
done();
|
||||
};
|
||||
|
||||
scope.removeKey(key);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,58 +1,55 @@
|
|||
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() {
|
||||
this.timeout(20000);
|
||||
describe('Crypto unit tests', function() {
|
||||
this.timeout(20000);
|
||||
|
||||
var crypto,
|
||||
password = 'password',
|
||||
keySize = config.symKeySize,
|
||||
ivSize = config.symIvSize;
|
||||
var crypto,
|
||||
password = 'password',
|
||||
keySize = config.symKeySize,
|
||||
ivSize = config.symIvSize;
|
||||
|
||||
beforeEach(function() {
|
||||
crypto = new Crypto();
|
||||
});
|
||||
beforeEach(function() {
|
||||
crypto = new Crypto();
|
||||
});
|
||||
|
||||
afterEach(function() {});
|
||||
afterEach(function() {});
|
||||
|
||||
describe('AES encrypt/decrypt', function() {
|
||||
it('should work', function(done) {
|
||||
var plaintext = 'Hello, World!';
|
||||
var key = util.random(keySize);
|
||||
var iv = util.random(ivSize);
|
||||
describe('AES encrypt/decrypt', function() {
|
||||
it('should work', function(done) {
|
||||
var plaintext = 'Hello, World!';
|
||||
var key = util.random(keySize);
|
||||
var iv = util.random(ivSize);
|
||||
|
||||
crypto.encrypt(plaintext, key, iv, function(err, ciphertext) {
|
||||
crypto.encrypt(plaintext, key, iv, function(err, ciphertext) {
|
||||
expect(err).to.not.exist;
|
||||
expect(ciphertext).to.exist;
|
||||
|
||||
crypto.decrypt(ciphertext, key, iv, function(err, decrypted) {
|
||||
expect(err).to.not.exist;
|
||||
expect(ciphertext).to.exist;
|
||||
|
||||
crypto.decrypt(ciphertext, key, iv, function(err, decrypted) {
|
||||
expect(err).to.not.exist;
|
||||
expect(decrypted).to.equal(plaintext);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("PBKDF2 (Async/Worker)", function() {
|
||||
it('should work', function(done) {
|
||||
var salt = util.random(keySize);
|
||||
|
||||
crypto.deriveKey(password, salt, keySize, function(err, key) {
|
||||
expect(err).to.not.exist;
|
||||
expect(util.base642Str(key).length * 8).to.equal(keySize);
|
||||
expect(decrypted).to.equal(plaintext);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("PBKDF2 (Async/Worker)", function() {
|
||||
it('should work', function(done) {
|
||||
var salt = util.random(keySize);
|
||||
|
||||
crypto.deriveKey(password, salt, keySize, function(err, key) {
|
||||
expect(err).to.not.exist;
|
||||
expect(util.base642Str(key).length * 8).to.equal(keySize);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
|
@ -1,105 +1,101 @@
|
|||
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;
|
||||
var storageDao, lawnchairDaoStub;
|
||||
|
||||
beforeEach(function() {
|
||||
lawnchairDaoStub = sinon.createStubInstance(LawnchairDAO);
|
||||
storageDao = new DeviceStorageDAO(lawnchairDaoStub);
|
||||
beforeEach(function() {
|
||||
lawnchairDaoStub = sinon.createStubInstance(LawnchairDAO);
|
||||
storageDao = new DeviceStorageDAO(lawnchairDaoStub);
|
||||
});
|
||||
|
||||
afterEach(function() {});
|
||||
|
||||
describe('init', function() {
|
||||
it('should work', function(done) {
|
||||
lawnchairDaoStub.init.yields();
|
||||
|
||||
storageDao.init(testUser, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(lawnchairDaoStub.init.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {});
|
||||
describe('store list', function() {
|
||||
it('should fail', function(done) {
|
||||
var list = [{}];
|
||||
|
||||
describe('init', function() {
|
||||
it('should work', function(done) {
|
||||
lawnchairDaoStub.init.yields();
|
||||
|
||||
storageDao.init(testUser, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(lawnchairDaoStub.init.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
storageDao.storeList(list, '', function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('store list', function() {
|
||||
it('should fail', function(done) {
|
||||
var list = [{}];
|
||||
it('should work with empty list', function(done) {
|
||||
var list = [];
|
||||
|
||||
storageDao.storeList(list, '', function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work with empty list', function(done) {
|
||||
var list = [];
|
||||
|
||||
storageDao.storeList(list, 'email', function(err) {
|
||||
expect(err).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
lawnchairDaoStub.batch.yields();
|
||||
|
||||
var list = [{
|
||||
foo: 'bar'
|
||||
}];
|
||||
|
||||
storageDao.storeList(list, 'email', function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(lawnchairDaoStub.batch.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
storageDao.storeList(list, 'email', function(err) {
|
||||
expect(err).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('remove list', function() {
|
||||
it('should work', function(done) {
|
||||
lawnchairDaoStub.removeList.yields();
|
||||
it('should work', function(done) {
|
||||
lawnchairDaoStub.batch.yields();
|
||||
|
||||
storageDao.removeList('email', function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(lawnchairDaoStub.removeList.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
var list = [{
|
||||
foo: 'bar'
|
||||
}];
|
||||
|
||||
storageDao.storeList(list, 'email', function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(lawnchairDaoStub.batch.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('list items', function() {
|
||||
it('should work', function(done) {
|
||||
lawnchairDaoStub.list.yields();
|
||||
describe('remove list', function() {
|
||||
it('should work', function(done) {
|
||||
lawnchairDaoStub.removeList.yields();
|
||||
|
||||
storageDao.listItems('email', 0, null, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(lawnchairDaoStub.list.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
storageDao.removeList('email', function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(lawnchairDaoStub.removeList.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('clear', function() {
|
||||
it('should work', function(done) {
|
||||
lawnchairDaoStub.clear.yields();
|
||||
describe('list items', function() {
|
||||
it('should work', function(done) {
|
||||
lawnchairDaoStub.list.yields();
|
||||
|
||||
storageDao.clear(function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(lawnchairDaoStub.clear.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
storageDao.listItems('email', 0, null, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(lawnchairDaoStub.list.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('clear', function() {
|
||||
it('should work', function(done) {
|
||||
lawnchairDaoStub.clear.yields();
|
||||
|
||||
storageDao.clear(function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(lawnchairDaoStub.clear.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
|
@ -1,50 +1,46 @@
|
|||
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() {
|
||||
var scope, dialogCtrl;
|
||||
describe('Dialog Controller unit test', function() {
|
||||
var scope, dialogCtrl;
|
||||
|
||||
beforeEach(function() {
|
||||
angular.module('dialogtest', []);
|
||||
mocks.module('dialogtest');
|
||||
mocks.inject(function($rootScope, $controller) {
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {
|
||||
dialog: {}
|
||||
};
|
||||
dialogCtrl = $controller(DialogCtrl, {
|
||||
$scope: scope
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {});
|
||||
|
||||
describe('confirm', function() {
|
||||
it('should work', function(done) {
|
||||
scope.state.dialog.callback = function(confirmed) {
|
||||
expect(confirmed).to.be.true;
|
||||
expect(scope.state.dialog.open).to.be.false;
|
||||
done();
|
||||
};
|
||||
scope.confirm(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('cancel', function() {
|
||||
it('should work', function(done) {
|
||||
scope.state.dialog.callback = function(confirmed) {
|
||||
expect(confirmed).to.be.false;
|
||||
expect(scope.state.dialog.open).to.be.false;
|
||||
done();
|
||||
};
|
||||
scope.confirm(false);
|
||||
beforeEach(function() {
|
||||
angular.module('dialogtest', []);
|
||||
mocks.module('dialogtest');
|
||||
mocks.inject(function($rootScope, $controller) {
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {
|
||||
dialog: {}
|
||||
};
|
||||
dialogCtrl = $controller(DialogCtrl, {
|
||||
$scope: scope
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {});
|
||||
|
||||
describe('confirm', function() {
|
||||
it('should work', function(done) {
|
||||
scope.state.dialog.callback = function(confirmed) {
|
||||
expect(confirmed).to.be.true;
|
||||
expect(scope.state.dialog.open).to.be.false;
|
||||
done();
|
||||
};
|
||||
scope.confirm(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('cancel', function() {
|
||||
it('should work', function(done) {
|
||||
scope.state.dialog.callback = function(confirmed) {
|
||||
expect(confirmed).to.be.false;
|
||||
expect(scope.state.dialog.open).to.be.false;
|
||||
done();
|
||||
};
|
||||
scope.confirm(false);
|
||||
});
|
||||
});
|
||||
});
|
File diff suppressed because it is too large
Load Diff
|
@ -8,12 +8,29 @@
|
|||
</head>
|
||||
<body>
|
||||
<div id="mocha"></div>
|
||||
|
||||
<script src="../lib/chai.js"></script>
|
||||
<script src="../lib/sinon.js"></script>
|
||||
<script src="../lib/mocha.js"></script>
|
||||
|
||||
<script data-main="main.js" src="../../src/lib/require.js"></script>
|
||||
|
||||
<script src="../lib/chai.js"></script>
|
||||
<script src="../lib/mocha.js"></script>
|
||||
<script src="../lib/sinon.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>
|
||||
|
|
|
@ -1,108 +1,105 @@
|
|||
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() {
|
||||
var restDaoStub, invitationDao,
|
||||
alice = 'zuhause@aol.com',
|
||||
bob = 'manfred.mustermann@musterdomain.com',
|
||||
expectedUri = '/invitation/recipient/' + alice + '/sender/' + bob;
|
||||
describe('Invitation DAO unit tests', function() {
|
||||
var restDaoStub, invitationDao,
|
||||
alice = 'zuhause@aol.com',
|
||||
bob = 'manfred.mustermann@musterdomain.com',
|
||||
expectedUri = '/invitation/recipient/' + alice + '/sender/' + bob;
|
||||
|
||||
beforeEach(function() {
|
||||
restDaoStub = sinon.createStubInstance(RestDAO);
|
||||
invitationDao = new InvitationDAO(restDaoStub);
|
||||
beforeEach(function() {
|
||||
restDaoStub = sinon.createStubInstance(RestDAO);
|
||||
invitationDao = new InvitationDAO(restDaoStub);
|
||||
});
|
||||
|
||||
describe('initialization', function() {
|
||||
it('should wire up correctly', function() {
|
||||
expect(invitationDao._restDao).to.equal(restDaoStub);
|
||||
expect(invitationDao.invite).to.exist;
|
||||
expect(InvitationDAO.INVITE_MISSING).to.equal(1);
|
||||
expect(InvitationDAO.INVITE_PENDING).to.equal(2);
|
||||
expect(InvitationDAO.INVITE_SUCCESS).to.equal(4);
|
||||
});
|
||||
});
|
||||
|
||||
describe('initialization', function() {
|
||||
it('should wire up correctly', function() {
|
||||
expect(invitationDao._restDao).to.equal(restDaoStub);
|
||||
expect(invitationDao.invite).to.exist;
|
||||
expect(InvitationDAO.INVITE_MISSING).to.equal(1);
|
||||
expect(InvitationDAO.INVITE_PENDING).to.equal(2);
|
||||
expect(InvitationDAO.INVITE_SUCCESS).to.equal(4);
|
||||
describe('invite', function() {
|
||||
it('should invite the recipient', function(done) {
|
||||
restDaoStub.put.yieldsAsync(null, undefined, 201);
|
||||
|
||||
invitationDao.invite({
|
||||
recipient: alice,
|
||||
sender: bob
|
||||
}, function(err, status) {
|
||||
expect(err).to.not.exist;
|
||||
expect(status).to.equal(InvitationDAO.INVITE_SUCCESS);
|
||||
expect(restDaoStub.put.calledWith({}, expectedUri)).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('invite', function() {
|
||||
it('should invite the recipient', function(done) {
|
||||
restDaoStub.put.yieldsAsync(null, undefined, 201);
|
||||
it('should point out already invited recipient', function(done) {
|
||||
restDaoStub.put.yieldsAsync(null, undefined, 304);
|
||||
|
||||
invitationDao.invite({
|
||||
recipient: alice,
|
||||
sender: bob
|
||||
}, function(err, status) {
|
||||
expect(err).to.not.exist;
|
||||
expect(status).to.equal(InvitationDAO.INVITE_SUCCESS);
|
||||
expect(restDaoStub.put.calledWith({}, expectedUri)).to.be.true;
|
||||
done();
|
||||
});
|
||||
invitationDao.invite({
|
||||
recipient: alice,
|
||||
sender: bob
|
||||
}, function(err, status) {
|
||||
expect(err).to.not.exist;
|
||||
expect(status).to.equal(InvitationDAO.INVITE_PENDING);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not work for http error', function(done) {
|
||||
restDaoStub.put.yieldsAsync({
|
||||
errMsg: 'jawollja.'
|
||||
});
|
||||
|
||||
it('should point out already invited recipient', function(done) {
|
||||
restDaoStub.put.yieldsAsync(null, undefined, 304);
|
||||
|
||||
invitationDao.invite({
|
||||
recipient: alice,
|
||||
sender: bob
|
||||
}, function(err, status) {
|
||||
expect(err).to.not.exist;
|
||||
expect(status).to.equal(InvitationDAO.INVITE_PENDING);
|
||||
done();
|
||||
});
|
||||
invitationDao.invite({
|
||||
recipient: alice,
|
||||
sender: bob
|
||||
}, function(err, status) {
|
||||
expect(err).to.exist;
|
||||
expect(status).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not work for http error', function(done) {
|
||||
restDaoStub.put.yieldsAsync({
|
||||
errMsg: 'jawollja.'
|
||||
});
|
||||
it('should not work for unexpected response', function(done) {
|
||||
restDaoStub.put.yieldsAsync(null, undefined, 1337);
|
||||
|
||||
invitationDao.invite({
|
||||
recipient: alice,
|
||||
sender: bob
|
||||
}, function(err, status) {
|
||||
expect(err).to.exist;
|
||||
expect(status).to.not.exist;
|
||||
done();
|
||||
});
|
||||
invitationDao.invite({
|
||||
recipient: alice,
|
||||
sender: bob
|
||||
}, function(err, status) {
|
||||
expect(err).to.exist;
|
||||
expect(status).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not work for unexpected response', function(done) {
|
||||
restDaoStub.put.yieldsAsync(null, undefined, 1337);
|
||||
it('should report erroneous usage', function() {
|
||||
invitationDao.invite({
|
||||
sender: bob
|
||||
}, expectError);
|
||||
|
||||
invitationDao.invite({
|
||||
recipient: alice,
|
||||
sender: bob
|
||||
}, function(err, status) {
|
||||
expect(err).to.exist;
|
||||
expect(status).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
invitationDao.invite({
|
||||
recipient: alice,
|
||||
}, expectError);
|
||||
|
||||
it('should report erroneous usage', function() {
|
||||
invitationDao.invite({
|
||||
sender: bob
|
||||
}, expectError);
|
||||
invitationDao.invite({
|
||||
recipient: 123,
|
||||
sender: 123
|
||||
}, expectError);
|
||||
|
||||
invitationDao.invite({
|
||||
recipient: alice,
|
||||
}, expectError);
|
||||
invitationDao.invite('asd', expectError);
|
||||
|
||||
invitationDao.invite({
|
||||
recipient: 123,
|
||||
sender: 123
|
||||
}, expectError);
|
||||
|
||||
invitationDao.invite('asd', expectError);
|
||||
|
||||
function expectError(err, status) {
|
||||
expect(err).to.exist;
|
||||
expect(status).to.not.exist;
|
||||
}
|
||||
});
|
||||
function expectError(err, status) {
|
||||
expect(err).to.exist;
|
||||
expect(status).to.not.exist;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
File diff suppressed because it is too large
Load Diff
|
@ -1,152 +1,148 @@
|
|||
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 = {
|
||||
name: 'testName1',
|
||||
type: 'testType1'
|
||||
};
|
||||
var key = 'type_1';
|
||||
var data = {
|
||||
name: 'testName1',
|
||||
type: 'testType1'
|
||||
};
|
||||
|
||||
var key2 = 'type_2';
|
||||
var data2 = {
|
||||
name: 'testName2',
|
||||
type: 'testType2'
|
||||
};
|
||||
var key2 = 'type_2';
|
||||
var data2 = {
|
||||
name: 'testName2',
|
||||
type: 'testType2'
|
||||
};
|
||||
|
||||
describe('Lawnchair DAO unit tests', function() {
|
||||
var lawnchairDao;
|
||||
describe('Lawnchair DAO unit tests', function() {
|
||||
var lawnchairDao;
|
||||
|
||||
beforeEach(function(done) {
|
||||
lawnchairDao = new LawnchairDAO();
|
||||
lawnchairDao.init(dbName, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(lawnchairDao._db).to.exist;
|
||||
beforeEach(function(done) {
|
||||
lawnchairDao = new LawnchairDAO();
|
||||
lawnchairDao.init(dbName, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(lawnchairDao._db).to.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function(done) {
|
||||
lawnchairDao.clear(function(err) {
|
||||
expect(err).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('read', function() {
|
||||
it('should fail', function(done) {
|
||||
lawnchairDao.read(undefined, function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('list', function() {
|
||||
it('should fail', function(done) {
|
||||
lawnchairDao.list(undefined, 0, null, function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('remove list', function() {
|
||||
it('should fail', function(done) {
|
||||
lawnchairDao.removeList(undefined, function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('persist/read/remove', function() {
|
||||
it('should fail', function(done) {
|
||||
lawnchairDao.persist(undefined, data, function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should fail', function(done) {
|
||||
lawnchairDao.persist('1234', undefined, function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function(done) {
|
||||
lawnchairDao.clear(function(err) {
|
||||
it('should work', function(done) {
|
||||
lawnchairDao.persist(key, data, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
lawnchairDao.read(key, onRead);
|
||||
});
|
||||
|
||||
function onRead(err, fetched) {
|
||||
expect(err).to.not.exist;
|
||||
expect(fetched).to.deep.equal(data);
|
||||
lawnchairDao.remove(key, onRemove);
|
||||
}
|
||||
|
||||
function onRemove(err) {
|
||||
expect(err).to.not.exist;
|
||||
lawnchairDao.read(key, onReadAgain);
|
||||
}
|
||||
|
||||
function onReadAgain(err, fetched) {
|
||||
expect(err).to.not.exist;
|
||||
expect(fetched).to.not.exist;
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('batch/list/removeList', function() {
|
||||
it('should fails', function(done) {
|
||||
lawnchairDao.batch({}, function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('read', function() {
|
||||
it('should fail', function(done) {
|
||||
lawnchairDao.read(undefined, function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
it('should work', function(done) {
|
||||
var list = [{
|
||||
key: key,
|
||||
object: data
|
||||
}, {
|
||||
key: key2,
|
||||
object: data2
|
||||
}];
|
||||
|
||||
lawnchairDao.batch(list, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
lawnchairDao.list('type', 0, null, onList);
|
||||
});
|
||||
|
||||
function onList(err, fetched) {
|
||||
expect(err).to.not.exist;
|
||||
expect(fetched.length).to.equal(2);
|
||||
expect(fetched[0]).to.deep.equal(list[0].object);
|
||||
lawnchairDao.removeList('type', onRemoveList);
|
||||
}
|
||||
|
||||
function onRemoveList(err) {
|
||||
expect(err).to.not.exist;
|
||||
lawnchairDao.list('type', 0, null, onListAgain);
|
||||
}
|
||||
|
||||
function onListAgain(err, fetched) {
|
||||
expect(err).to.not.exist;
|
||||
expect(fetched).to.exist;
|
||||
expect(fetched.length).to.equal(0);
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
describe('list', function() {
|
||||
it('should fail', function(done) {
|
||||
lawnchairDao.list(undefined, 0, null, function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('remove list', function() {
|
||||
it('should fail', function(done) {
|
||||
lawnchairDao.removeList(undefined, function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('persist/read/remove', function() {
|
||||
it('should fail', function(done) {
|
||||
lawnchairDao.persist(undefined, data, function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should fail', function(done) {
|
||||
lawnchairDao.persist('1234', undefined, function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
lawnchairDao.persist(key, data, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
lawnchairDao.read(key, onRead);
|
||||
});
|
||||
|
||||
function onRead(err, fetched) {
|
||||
expect(err).to.not.exist;
|
||||
expect(fetched).to.deep.equal(data);
|
||||
lawnchairDao.remove(key, onRemove);
|
||||
}
|
||||
|
||||
function onRemove(err) {
|
||||
expect(err).to.not.exist;
|
||||
lawnchairDao.read(key, onReadAgain);
|
||||
}
|
||||
|
||||
function onReadAgain(err, fetched) {
|
||||
expect(err).to.not.exist;
|
||||
expect(fetched).to.not.exist;
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('batch/list/removeList', function() {
|
||||
it('should fails', function(done) {
|
||||
lawnchairDao.batch({}, function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
var list = [{
|
||||
key: key,
|
||||
object: data
|
||||
}, {
|
||||
key: key2,
|
||||
object: data2
|
||||
}];
|
||||
|
||||
lawnchairDao.batch(list, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
lawnchairDao.list('type', 0, null, onList);
|
||||
});
|
||||
|
||||
function onList(err, fetched) {
|
||||
expect(err).to.not.exist;
|
||||
expect(fetched.length).to.equal(2);
|
||||
expect(fetched[0]).to.deep.equal(list[0].object);
|
||||
lawnchairDao.removeList('type', onRemoveList);
|
||||
}
|
||||
|
||||
function onRemoveList(err) {
|
||||
expect(err).to.not.exist;
|
||||
lawnchairDao.list('type', 0, null, onListAgain);
|
||||
}
|
||||
|
||||
function onListAgain(err, fetched) {
|
||||
expect(err).to.not.exist;
|
||||
expect(fetched).to.exist;
|
||||
expect(fetched.length).to.equal(0);
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
|
@ -1,236 +1,232 @@
|
|||
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() {
|
||||
var scope, location, ctrl,
|
||||
origEmailDao, emailDaoMock,
|
||||
origKeychain, keychainMock,
|
||||
origAuth, authStub,
|
||||
emailAddress = 'fred@foo.com',
|
||||
startAppStub,
|
||||
checkForUpdateStub,
|
||||
initStub;
|
||||
describe('Login Controller unit test', function() {
|
||||
var scope, location, ctrl,
|
||||
origEmailDao, emailDaoMock,
|
||||
origKeychain, keychainMock,
|
||||
origAuth, authStub,
|
||||
emailAddress = 'fred@foo.com',
|
||||
startAppStub,
|
||||
checkForUpdateStub,
|
||||
initStub;
|
||||
|
||||
describe('initialization', function() {
|
||||
var hasChrome, hasIdentity;
|
||||
describe('initialization', function() {
|
||||
var hasChrome, hasIdentity;
|
||||
|
||||
beforeEach(function() {
|
||||
hasChrome = !!window.chrome;
|
||||
hasIdentity = !!window.chrome.identity;
|
||||
window.chrome = window.chrome || {};
|
||||
window.chrome.identity = window.chrome.identity || {};
|
||||
beforeEach(function() {
|
||||
hasChrome = !!window.chrome;
|
||||
hasIdentity = !!window.chrome.identity;
|
||||
window.chrome = window.chrome || {};
|
||||
window.chrome.identity = window.chrome.identity || {};
|
||||
|
||||
// remember original module to restore later, then replace it
|
||||
origEmailDao = appController._emailDao;
|
||||
origKeychain = appController._keychain;
|
||||
origAuth = appController._auth;
|
||||
appController._emailDao = emailDaoMock = sinon.createStubInstance(EmailDAO);
|
||||
appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
appController._auth = authStub = sinon.createStubInstance(Auth);
|
||||
// remember original module to restore later, then replace it
|
||||
origEmailDao = appController._emailDao;
|
||||
origKeychain = appController._keychain;
|
||||
origAuth = appController._auth;
|
||||
appController._emailDao = emailDaoMock = sinon.createStubInstance(EmailDAO);
|
||||
appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
appController._auth = authStub = sinon.createStubInstance(Auth);
|
||||
|
||||
startAppStub = sinon.stub(appController, 'start');
|
||||
checkForUpdateStub = sinon.stub(appController, 'checkForUpdate');
|
||||
initStub = sinon.stub(appController, 'init');
|
||||
startAppStub = sinon.stub(appController, 'start');
|
||||
checkForUpdateStub = sinon.stub(appController, 'checkForUpdate');
|
||||
initStub = sinon.stub(appController, 'init');
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// restore the browser
|
||||
if (!hasIdentity) {
|
||||
delete window.chrome.identity;
|
||||
}
|
||||
|
||||
if (!hasChrome) {
|
||||
delete window.chrome;
|
||||
}
|
||||
|
||||
// restore the app controller module
|
||||
appController._emailDao = origEmailDao;
|
||||
appController._keychain = origKeychain;
|
||||
appController._auth = origAuth;
|
||||
appController.start.restore && appController.start.restore();
|
||||
appController.checkForUpdate.restore && appController.checkForUpdate.restore();
|
||||
appController.init.restore && appController.init.restore();
|
||||
location.path.restore && location.path.restore();
|
||||
|
||||
startAppStub.restore();
|
||||
checkForUpdateStub.restore();
|
||||
initStub.restore();
|
||||
});
|
||||
|
||||
it('should forward directly to desktop for empty passphrase', function(done) {
|
||||
var testKeys = {
|
||||
privateKey: 'a',
|
||||
publicKey: 'b'
|
||||
};
|
||||
|
||||
startAppStub.yields();
|
||||
authStub.getEmailAddress.yields(null, {
|
||||
emailAddress: emailAddress,
|
||||
realname: 'asd'
|
||||
});
|
||||
initStub.yields(null, testKeys);
|
||||
|
||||
afterEach(function() {
|
||||
// restore the browser
|
||||
if (!hasIdentity) {
|
||||
delete window.chrome.identity;
|
||||
}
|
||||
emailDaoMock.unlock.withArgs({
|
||||
keypair: testKeys,
|
||||
passphrase: undefined
|
||||
}).yields();
|
||||
|
||||
if (!hasChrome) {
|
||||
delete window.chrome;
|
||||
}
|
||||
|
||||
// restore the app controller module
|
||||
appController._emailDao = origEmailDao;
|
||||
appController._keychain = origKeychain;
|
||||
appController._auth = origAuth;
|
||||
appController.start.restore && appController.start.restore();
|
||||
appController.checkForUpdate.restore && appController.checkForUpdate.restore();
|
||||
appController.init.restore && appController.init.restore();
|
||||
location.path.restore && location.path.restore();
|
||||
|
||||
startAppStub.restore();
|
||||
checkForUpdateStub.restore();
|
||||
initStub.restore();
|
||||
});
|
||||
|
||||
it('should forward directly to desktop for empty passphrase', function(done) {
|
||||
var testKeys = {
|
||||
privateKey: 'a',
|
||||
publicKey: 'b'
|
||||
};
|
||||
|
||||
startAppStub.yields();
|
||||
authStub.getEmailAddress.yields(null, {
|
||||
emailAddress: emailAddress,
|
||||
realname: 'asd'
|
||||
angular.module('logintest', []);
|
||||
mocks.module('logintest');
|
||||
mocks.inject(function($controller, $rootScope, $location) {
|
||||
location = $location;
|
||||
sinon.stub(location, 'path', function(path) {
|
||||
expect(path).to.equal('/desktop');
|
||||
expect(startAppStub.calledOnce).to.be.true;
|
||||
expect(checkForUpdateStub.calledOnce).to.be.true;
|
||||
expect(authStub.getEmailAddress.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
initStub.yields(null, testKeys);
|
||||
|
||||
emailDaoMock.unlock.withArgs({
|
||||
keypair: testKeys,
|
||||
passphrase: undefined
|
||||
}).yields();
|
||||
|
||||
angular.module('logintest', []);
|
||||
mocks.module('logintest');
|
||||
mocks.inject(function($controller, $rootScope, $location) {
|
||||
location = $location;
|
||||
sinon.stub(location, 'path', function(path) {
|
||||
expect(path).to.equal('/desktop');
|
||||
expect(startAppStub.calledOnce).to.be.true;
|
||||
expect(checkForUpdateStub.calledOnce).to.be.true;
|
||||
expect(authStub.getEmailAddress.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
ctrl = $controller(LoginCtrl, {
|
||||
$location: location,
|
||||
$scope: scope
|
||||
});
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
ctrl = $controller(LoginCtrl, {
|
||||
$location: location,
|
||||
$scope: scope
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should forward to existing user login', function(done) {
|
||||
var testKeys = {
|
||||
privateKey: 'a',
|
||||
publicKey: 'b'
|
||||
};
|
||||
it('should forward to existing user login', function(done) {
|
||||
var testKeys = {
|
||||
privateKey: 'a',
|
||||
publicKey: 'b'
|
||||
};
|
||||
|
||||
startAppStub.yields();
|
||||
authStub.getEmailAddress.yields(null, {
|
||||
emailAddress: emailAddress,
|
||||
realname: 'asd'
|
||||
startAppStub.yields();
|
||||
authStub.getEmailAddress.yields(null, {
|
||||
emailAddress: emailAddress,
|
||||
realname: 'asd'
|
||||
});
|
||||
initStub.yields(null, testKeys);
|
||||
|
||||
emailDaoMock.unlock.withArgs({
|
||||
keypair: testKeys,
|
||||
passphrase: undefined
|
||||
}).yields({});
|
||||
|
||||
angular.module('logintest', []);
|
||||
mocks.module('logintest');
|
||||
mocks.inject(function($controller, $rootScope, $location) {
|
||||
location = $location;
|
||||
sinon.stub(location, 'path', function(path) {
|
||||
expect(path).to.equal('/login-existing');
|
||||
expect(startAppStub.calledOnce).to.be.true;
|
||||
expect(checkForUpdateStub.calledOnce).to.be.true;
|
||||
expect(authStub.getEmailAddress.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
initStub.yields(null, testKeys);
|
||||
|
||||
emailDaoMock.unlock.withArgs({
|
||||
keypair: testKeys,
|
||||
passphrase: undefined
|
||||
}).yields({});
|
||||
|
||||
angular.module('logintest', []);
|
||||
mocks.module('logintest');
|
||||
mocks.inject(function($controller, $rootScope, $location) {
|
||||
location = $location;
|
||||
sinon.stub(location, 'path', function(path) {
|
||||
expect(path).to.equal('/login-existing');
|
||||
expect(startAppStub.calledOnce).to.be.true;
|
||||
expect(checkForUpdateStub.calledOnce).to.be.true;
|
||||
expect(authStub.getEmailAddress.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
ctrl = $controller(LoginCtrl, {
|
||||
$location: location,
|
||||
$scope: scope
|
||||
});
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
ctrl = $controller(LoginCtrl, {
|
||||
$location: location,
|
||||
$scope: scope
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should forward to privatekey download login', function(done) {
|
||||
startAppStub.yields();
|
||||
authStub.getEmailAddress.yields(null, {
|
||||
emailAddress: emailAddress,
|
||||
realname: 'asd'
|
||||
});
|
||||
initStub.yields(null, {
|
||||
publicKey: 'b'
|
||||
});
|
||||
keychainMock.requestPrivateKeyDownload.yields(null, {});
|
||||
it('should forward to privatekey download login', function(done) {
|
||||
startAppStub.yields();
|
||||
authStub.getEmailAddress.yields(null, {
|
||||
emailAddress: emailAddress,
|
||||
realname: 'asd'
|
||||
});
|
||||
initStub.yields(null, {
|
||||
publicKey: 'b'
|
||||
});
|
||||
keychainMock.requestPrivateKeyDownload.yields(null, {});
|
||||
|
||||
angular.module('logintest', []);
|
||||
mocks.module('logintest');
|
||||
mocks.inject(function($controller, $rootScope, $location) {
|
||||
location = $location;
|
||||
sinon.stub(location, 'path', function(path) {
|
||||
expect(path).to.equal('/login-privatekey-download');
|
||||
expect(startAppStub.calledOnce).to.be.true;
|
||||
expect(checkForUpdateStub.calledOnce).to.be.true;
|
||||
expect(authStub.getEmailAddress.calledOnce).to.be.true;
|
||||
expect(keychainMock.requestPrivateKeyDownload.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
ctrl = $controller(LoginCtrl, {
|
||||
$location: location,
|
||||
$scope: scope
|
||||
});
|
||||
angular.module('logintest', []);
|
||||
mocks.module('logintest');
|
||||
mocks.inject(function($controller, $rootScope, $location) {
|
||||
location = $location;
|
||||
sinon.stub(location, 'path', function(path) {
|
||||
expect(path).to.equal('/login-privatekey-download');
|
||||
expect(startAppStub.calledOnce).to.be.true;
|
||||
expect(checkForUpdateStub.calledOnce).to.be.true;
|
||||
expect(authStub.getEmailAddress.calledOnce).to.be.true;
|
||||
expect(keychainMock.requestPrivateKeyDownload.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
ctrl = $controller(LoginCtrl, {
|
||||
$location: location,
|
||||
$scope: scope
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should forward to new device login', function(done) {
|
||||
startAppStub.yields();
|
||||
authStub.getEmailAddress.yields(null, {
|
||||
emailAddress: emailAddress,
|
||||
realname: 'asd'
|
||||
});
|
||||
initStub.yields(null, {
|
||||
publicKey: 'b'
|
||||
});
|
||||
keychainMock.requestPrivateKeyDownload.yields();
|
||||
it('should forward to new device login', function(done) {
|
||||
startAppStub.yields();
|
||||
authStub.getEmailAddress.yields(null, {
|
||||
emailAddress: emailAddress,
|
||||
realname: 'asd'
|
||||
});
|
||||
initStub.yields(null, {
|
||||
publicKey: 'b'
|
||||
});
|
||||
keychainMock.requestPrivateKeyDownload.yields();
|
||||
|
||||
angular.module('logintest', []);
|
||||
mocks.module('logintest');
|
||||
mocks.inject(function($controller, $rootScope, $location) {
|
||||
location = $location;
|
||||
sinon.stub(location, 'path', function(path) {
|
||||
expect(path).to.equal('/login-new-device');
|
||||
expect(startAppStub.calledOnce).to.be.true;
|
||||
expect(checkForUpdateStub.calledOnce).to.be.true;
|
||||
expect(authStub.getEmailAddress.calledOnce).to.be.true;
|
||||
expect(keychainMock.requestPrivateKeyDownload.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
ctrl = $controller(LoginCtrl, {
|
||||
$location: location,
|
||||
$scope: scope
|
||||
});
|
||||
angular.module('logintest', []);
|
||||
mocks.module('logintest');
|
||||
mocks.inject(function($controller, $rootScope, $location) {
|
||||
location = $location;
|
||||
sinon.stub(location, 'path', function(path) {
|
||||
expect(path).to.equal('/login-new-device');
|
||||
expect(startAppStub.calledOnce).to.be.true;
|
||||
expect(checkForUpdateStub.calledOnce).to.be.true;
|
||||
expect(authStub.getEmailAddress.calledOnce).to.be.true;
|
||||
expect(keychainMock.requestPrivateKeyDownload.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
ctrl = $controller(LoginCtrl, {
|
||||
$location: location,
|
||||
$scope: scope
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should forward to initial login', function(done) {
|
||||
startAppStub.yields();
|
||||
authStub.getEmailAddress.yields(null, {
|
||||
emailAddress: emailAddress,
|
||||
realname: 'asd'
|
||||
it('should forward to initial login', function(done) {
|
||||
startAppStub.yields();
|
||||
authStub.getEmailAddress.yields(null, {
|
||||
emailAddress: emailAddress,
|
||||
realname: 'asd'
|
||||
});
|
||||
initStub.yields();
|
||||
|
||||
angular.module('logintest', []);
|
||||
mocks.module('logintest');
|
||||
mocks.inject(function($controller, $rootScope, $location) {
|
||||
location = $location;
|
||||
sinon.stub(location, 'path', function(path) {
|
||||
expect(path).to.equal('/login-initial');
|
||||
expect(startAppStub.calledOnce).to.be.true;
|
||||
expect(checkForUpdateStub.calledOnce).to.be.true;
|
||||
expect(authStub.getEmailAddress.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
initStub.yields();
|
||||
|
||||
angular.module('logintest', []);
|
||||
mocks.module('logintest');
|
||||
mocks.inject(function($controller, $rootScope, $location) {
|
||||
location = $location;
|
||||
sinon.stub(location, 'path', function(path) {
|
||||
expect(path).to.equal('/login-initial');
|
||||
expect(startAppStub.calledOnce).to.be.true;
|
||||
expect(checkForUpdateStub.calledOnce).to.be.true;
|
||||
expect(authStub.getEmailAddress.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
ctrl = $controller(LoginCtrl, {
|
||||
$location: location,
|
||||
$scope: scope
|
||||
});
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
ctrl = $controller(LoginCtrl, {
|
||||
$location: location,
|
||||
$scope: scope
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,116 +1,112 @@
|
|||
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() {
|
||||
var scope, location, ctrl, origEmailDao, emailDaoMock,
|
||||
origAuth, authMock,
|
||||
emailAddress = 'fred@foo.com',
|
||||
passphrase = 'asd',
|
||||
keychainMock;
|
||||
describe('Login (existing user) Controller unit test', function() {
|
||||
var scope, location, ctrl, origEmailDao, emailDaoMock,
|
||||
origAuth, authMock,
|
||||
emailAddress = 'fred@foo.com',
|
||||
passphrase = 'asd',
|
||||
keychainMock;
|
||||
|
||||
beforeEach(function() {
|
||||
// remember original module to restore later
|
||||
origEmailDao = appController._emailDao;
|
||||
origAuth = appController._auth;
|
||||
beforeEach(function() {
|
||||
// remember original module to restore later
|
||||
origEmailDao = appController._emailDao;
|
||||
origAuth = appController._auth;
|
||||
|
||||
appController._emailDao = emailDaoMock = sinon.createStubInstance(EmailDAO);
|
||||
appController._auth = authMock = sinon.createStubInstance(Auth);
|
||||
appController._emailDao = emailDaoMock = sinon.createStubInstance(EmailDAO);
|
||||
appController._auth = authMock = sinon.createStubInstance(Auth);
|
||||
|
||||
keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
emailDaoMock._keychain = keychainMock;
|
||||
keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
emailDaoMock._keychain = keychainMock;
|
||||
|
||||
emailDaoMock._account = {
|
||||
emailAddress: emailAddress,
|
||||
};
|
||||
emailDaoMock._account = {
|
||||
emailAddress: emailAddress,
|
||||
};
|
||||
|
||||
angular.module('loginexistingtest', []);
|
||||
mocks.module('loginexistingtest');
|
||||
mocks.inject(function($rootScope, $controller, $location) {
|
||||
location = $location;
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
ctrl = $controller(LoginExistingCtrl, {
|
||||
$scope: scope,
|
||||
$routeParams: {}
|
||||
});
|
||||
angular.module('loginexistingtest', []);
|
||||
mocks.module('loginexistingtest');
|
||||
mocks.inject(function($rootScope, $controller, $location) {
|
||||
location = $location;
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
ctrl = $controller(LoginExistingCtrl, {
|
||||
$scope: scope,
|
||||
$routeParams: {}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// restore the module
|
||||
appController._emailDao = origEmailDao;
|
||||
appController._auth = origAuth;
|
||||
afterEach(function() {
|
||||
// restore the module
|
||||
appController._emailDao = origEmailDao;
|
||||
appController._auth = origAuth;
|
||||
});
|
||||
|
||||
describe('initial state', function() {
|
||||
it('should be well defined', function() {
|
||||
expect(scope.buttonEnabled).to.be.true;
|
||||
expect(scope.incorrect).to.be.false;
|
||||
expect(scope.change).to.exist;
|
||||
expect(scope.confirmPassphrase).to.exist;
|
||||
});
|
||||
});
|
||||
|
||||
describe('initial state', function() {
|
||||
it('should be well defined', function() {
|
||||
expect(scope.buttonEnabled).to.be.true;
|
||||
describe('functionality', function() {
|
||||
describe('change', function() {
|
||||
it('should set incorrect to false', function() {
|
||||
scope.incorrect = true;
|
||||
|
||||
scope.change();
|
||||
expect(scope.incorrect).to.be.false;
|
||||
expect(scope.change).to.exist;
|
||||
expect(scope.confirmPassphrase).to.exist;
|
||||
});
|
||||
});
|
||||
|
||||
describe('functionality', function() {
|
||||
describe('change', function() {
|
||||
it('should set incorrect to false', function() {
|
||||
scope.incorrect = true;
|
||||
describe('confirm passphrase', function() {
|
||||
it('should unlock crypto and start', function() {
|
||||
var keypair = {},
|
||||
pathSpy = sinon.spy(location, 'path');
|
||||
scope.passphrase = passphrase;
|
||||
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(null, keypair);
|
||||
emailDaoMock.unlock.withArgs({
|
||||
keypair: keypair,
|
||||
passphrase: passphrase
|
||||
}).yields();
|
||||
authMock.storeCredentials.yields();
|
||||
|
||||
scope.change();
|
||||
expect(scope.incorrect).to.be.false;
|
||||
});
|
||||
|
||||
scope.confirmPassphrase();
|
||||
|
||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||
expect(emailDaoMock.unlock.calledOnce).to.be.true;
|
||||
expect(pathSpy.calledOnce).to.be.true;
|
||||
expect(pathSpy.calledWith('/desktop')).to.be.true;
|
||||
});
|
||||
|
||||
describe('confirm passphrase', function() {
|
||||
it('should unlock crypto and start', function() {
|
||||
var keypair = {},
|
||||
pathSpy = sinon.spy(location, 'path');
|
||||
scope.passphrase = passphrase;
|
||||
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(null, keypair);
|
||||
emailDaoMock.unlock.withArgs({
|
||||
keypair: keypair,
|
||||
passphrase: passphrase
|
||||
}).yields();
|
||||
authMock.storeCredentials.yields();
|
||||
it('should not do anything without passphrase', function() {
|
||||
var pathSpy = sinon.spy(location, 'path');
|
||||
scope.passphrase = '';
|
||||
|
||||
scope.confirmPassphrase();
|
||||
expect(pathSpy.callCount).to.equal(0);
|
||||
});
|
||||
|
||||
scope.confirmPassphrase();
|
||||
it('should not work when keypair unavailable', function(done) {
|
||||
scope.passphrase = passphrase;
|
||||
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(new Error('asd'));
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err.message).to.equal('asd');
|
||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||
expect(emailDaoMock.unlock.calledOnce).to.be.true;
|
||||
expect(pathSpy.calledOnce).to.be.true;
|
||||
expect(pathSpy.calledWith('/desktop')).to.be.true;
|
||||
});
|
||||
done();
|
||||
};
|
||||
|
||||
it('should not do anything without passphrase', function() {
|
||||
var pathSpy = sinon.spy(location, 'path');
|
||||
scope.passphrase = '';
|
||||
|
||||
scope.confirmPassphrase();
|
||||
expect(pathSpy.callCount).to.equal(0);
|
||||
});
|
||||
|
||||
it('should not work when keypair unavailable', function(done) {
|
||||
scope.passphrase = passphrase;
|
||||
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(new Error('asd'));
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err.message).to.equal('asd');
|
||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.confirmPassphrase();
|
||||
});
|
||||
scope.confirmPassphrase();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,202 +1,197 @@
|
|||
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() {
|
||||
var scope, ctrl, location, origEmailDao, emailDaoMock,
|
||||
origAuth, authMock,
|
||||
emailAddress = 'fred@foo.com',
|
||||
keyId, expectedKeyId,
|
||||
cryptoMock;
|
||||
describe('Login (initial user) Controller unit test', function() {
|
||||
var scope, ctrl, location, origEmailDao, emailDaoMock,
|
||||
origAuth, authMock,
|
||||
emailAddress = 'fred@foo.com',
|
||||
keyId, expectedKeyId,
|
||||
cryptoMock;
|
||||
|
||||
beforeEach(function() {
|
||||
// remember original module to restore later
|
||||
origEmailDao = appController._emailDao;
|
||||
origAuth = appController._auth;
|
||||
|
||||
appController._emailDao = emailDaoMock = sinon.createStubInstance(EmailDAO);
|
||||
appController._auth = authMock = sinon.createStubInstance(Auth);
|
||||
|
||||
keyId = '9FEB47936E712926';
|
||||
expectedKeyId = '6E712926';
|
||||
cryptoMock = sinon.createStubInstance(PGP);
|
||||
emailDaoMock._crypto = cryptoMock;
|
||||
|
||||
emailDaoMock._account = {
|
||||
emailAddress: emailAddress,
|
||||
};
|
||||
|
||||
angular.module('logininitialtest', []);
|
||||
mocks.module('logininitialtest');
|
||||
mocks.inject(function($rootScope, $controller, $location) {
|
||||
scope = $rootScope.$new();
|
||||
location = $location;
|
||||
scope.state = {
|
||||
ui: {}
|
||||
};
|
||||
ctrl = $controller(LoginInitialCtrl, {
|
||||
$scope: scope,
|
||||
$routeParams: {}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// restore the module
|
||||
appController._emailDao = origEmailDao;
|
||||
appController._auth = origAuth;
|
||||
});
|
||||
|
||||
describe('initial state', function() {
|
||||
it('should be well defined', function() {
|
||||
expect(scope.state.ui).to.equal(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('signUpToNewsletter', function() {
|
||||
var xhrMock, requests;
|
||||
|
||||
beforeEach(function() {
|
||||
// remember original module to restore later
|
||||
origEmailDao = appController._emailDao;
|
||||
origAuth = appController._auth;
|
||||
xhrMock = sinon.useFakeXMLHttpRequest();
|
||||
requests = [];
|
||||
|
||||
appController._emailDao = emailDaoMock = sinon.createStubInstance(EmailDAO);
|
||||
appController._auth = authMock = sinon.createStubInstance(Auth);
|
||||
|
||||
keyId = '9FEB47936E712926';
|
||||
expectedKeyId = '6E712926';
|
||||
cryptoMock = sinon.createStubInstance(PGP);
|
||||
emailDaoMock._crypto = cryptoMock;
|
||||
|
||||
emailDaoMock._account = {
|
||||
emailAddress: emailAddress,
|
||||
xhrMock.onCreate = function(xhr) {
|
||||
requests.push(xhr);
|
||||
};
|
||||
|
||||
angular.module('logininitialtest', []);
|
||||
mocks.module('logininitialtest');
|
||||
mocks.inject(function($rootScope, $controller, $location) {
|
||||
scope = $rootScope.$new();
|
||||
location = $location;
|
||||
scope.state = {
|
||||
ui: {}
|
||||
};
|
||||
ctrl = $controller(LoginInitialCtrl, {
|
||||
$scope: scope,
|
||||
$routeParams: {}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// restore the module
|
||||
appController._emailDao = origEmailDao;
|
||||
appController._auth = origAuth;
|
||||
xhrMock.restore();
|
||||
});
|
||||
|
||||
describe('initial state', function() {
|
||||
it('should be well defined', function() {
|
||||
it('should not signup', function() {
|
||||
scope.state.newsletter = false;
|
||||
|
||||
scope.signUpToNewsletter();
|
||||
expect(requests.length).to.equal(0);
|
||||
});
|
||||
|
||||
it('should fail', function(done) {
|
||||
scope.state.newsletter = true;
|
||||
|
||||
scope.signUpToNewsletter(function(err, xhr) {
|
||||
expect(err).to.exist;
|
||||
expect(xhr).to.not.exist;
|
||||
done();
|
||||
});
|
||||
|
||||
expect(requests.length).to.equal(1);
|
||||
requests[0].onerror('err');
|
||||
});
|
||||
|
||||
it('should work without callback', function() {
|
||||
scope.state.newsletter = true;
|
||||
|
||||
scope.signUpToNewsletter();
|
||||
|
||||
expect(requests.length).to.equal(1);
|
||||
requests[0].respond(200, {
|
||||
"Content-Type": "text/plain"
|
||||
}, 'foobar!');
|
||||
});
|
||||
});
|
||||
|
||||
describe('go to import key', function() {
|
||||
var signUpToNewsletterStub;
|
||||
beforeEach(function() {
|
||||
signUpToNewsletterStub = sinon.stub(scope, 'signUpToNewsletter');
|
||||
});
|
||||
afterEach(function() {
|
||||
signUpToNewsletterStub.restore();
|
||||
});
|
||||
|
||||
it('should not continue if terms are not accepted', function(done) {
|
||||
scope.state.agree = undefined;
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err.message).to.contain('Terms');
|
||||
expect(signUpToNewsletterStub.called).to.be.false;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.importKey();
|
||||
});
|
||||
|
||||
it('should work', function() {
|
||||
scope.state.agree = true;
|
||||
scope.importKey();
|
||||
expect(signUpToNewsletterStub.calledOnce).to.be.true;
|
||||
expect(location.$$path).to.equal('/login-new-device');
|
||||
});
|
||||
});
|
||||
|
||||
describe('generate key', function() {
|
||||
var signUpToNewsletterStub;
|
||||
beforeEach(function() {
|
||||
signUpToNewsletterStub = sinon.stub(scope, 'signUpToNewsletter');
|
||||
});
|
||||
afterEach(function() {
|
||||
signUpToNewsletterStub.restore();
|
||||
});
|
||||
|
||||
it('should not continue if terms are not accepted', function(done) {
|
||||
scope.state.agree = undefined;
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err.message).to.contain('Terms');
|
||||
expect(scope.state.ui).to.equal(1);
|
||||
});
|
||||
expect(signUpToNewsletterStub.called).to.be.false;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.generateKey();
|
||||
});
|
||||
|
||||
describe('signUpToNewsletter', function() {
|
||||
var xhrMock, requests;
|
||||
it('should fail due to error in emailDao.unlock', function(done) {
|
||||
scope.state.agree = true;
|
||||
|
||||
beforeEach(function() {
|
||||
xhrMock = sinon.useFakeXMLHttpRequest();
|
||||
requests = [];
|
||||
emailDaoMock.unlock.withArgs({
|
||||
passphrase: undefined
|
||||
}).yields(new Error());
|
||||
authMock.storeCredentials.yields();
|
||||
|
||||
xhrMock.onCreate = function(xhr) {
|
||||
requests.push(xhr);
|
||||
};
|
||||
});
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(scope.state.ui).to.equal(1);
|
||||
expect(signUpToNewsletterStub.called).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
afterEach(function() {
|
||||
xhrMock.restore();
|
||||
});
|
||||
|
||||
it('should not signup', function() {
|
||||
scope.state.newsletter = false;
|
||||
|
||||
scope.signUpToNewsletter();
|
||||
expect(requests.length).to.equal(0);
|
||||
});
|
||||
|
||||
it('should fail', function(done) {
|
||||
scope.state.newsletter = true;
|
||||
|
||||
scope.signUpToNewsletter(function(err, xhr) {
|
||||
expect(err).to.exist;
|
||||
expect(xhr).to.not.exist;
|
||||
done();
|
||||
});
|
||||
|
||||
expect(requests.length).to.equal(1);
|
||||
requests[0].onerror('err');
|
||||
});
|
||||
|
||||
it('should work without callback', function() {
|
||||
scope.state.newsletter = true;
|
||||
|
||||
scope.signUpToNewsletter();
|
||||
|
||||
expect(requests.length).to.equal(1);
|
||||
requests[0].respond(200, {
|
||||
"Content-Type": "text/plain"
|
||||
}, 'foobar!');
|
||||
});
|
||||
scope.generateKey();
|
||||
expect(scope.state.ui).to.equal(2);
|
||||
});
|
||||
|
||||
describe('go to import key', function() {
|
||||
var signUpToNewsletterStub;
|
||||
beforeEach(function() {
|
||||
signUpToNewsletterStub = sinon.stub(scope, 'signUpToNewsletter');
|
||||
});
|
||||
afterEach(function() {
|
||||
signUpToNewsletterStub.restore();
|
||||
});
|
||||
it('should unlock crypto', function(done) {
|
||||
scope.state.agree = true;
|
||||
|
||||
it('should not continue if terms are not accepted', function(done) {
|
||||
scope.state.agree = undefined;
|
||||
emailDaoMock.unlock.withArgs({
|
||||
passphrase: undefined
|
||||
}).yields();
|
||||
authMock.storeCredentials.yields();
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err.message).to.contain('Terms');
|
||||
expect(signUpToNewsletterStub.called).to.be.false;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.importKey();
|
||||
});
|
||||
|
||||
it('should work', function() {
|
||||
scope.state.agree = true;
|
||||
scope.importKey();
|
||||
expect(signUpToNewsletterStub.calledOnce).to.be.true;
|
||||
expect(location.$$path).to.equal('/login-new-device');
|
||||
});
|
||||
});
|
||||
|
||||
describe('generate key', function() {
|
||||
var signUpToNewsletterStub;
|
||||
beforeEach(function() {
|
||||
signUpToNewsletterStub = sinon.stub(scope, 'signUpToNewsletter');
|
||||
});
|
||||
afterEach(function() {
|
||||
signUpToNewsletterStub.restore();
|
||||
});
|
||||
|
||||
it('should not continue if terms are not accepted', function(done) {
|
||||
scope.state.agree = undefined;
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err.message).to.contain('Terms');
|
||||
expect(scope.state.ui).to.equal(1);
|
||||
expect(signUpToNewsletterStub.called).to.be.false;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.generateKey();
|
||||
});
|
||||
|
||||
it('should fail due to error in emailDao.unlock', function(done) {
|
||||
scope.state.agree = true;
|
||||
|
||||
emailDaoMock.unlock.withArgs({
|
||||
passphrase: undefined
|
||||
}).yields(new Error());
|
||||
authMock.storeCredentials.yields();
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(scope.state.ui).to.equal(1);
|
||||
expect(signUpToNewsletterStub.called).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.generateKey();
|
||||
scope.$apply = function() {
|
||||
expect(scope.state.ui).to.equal(2);
|
||||
});
|
||||
expect(location.$$path).to.equal('/desktop');
|
||||
expect(emailDaoMock.unlock.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
it('should unlock crypto', function(done) {
|
||||
scope.state.agree = true;
|
||||
|
||||
emailDaoMock.unlock.withArgs({
|
||||
passphrase: undefined
|
||||
}).yields();
|
||||
authMock.storeCredentials.yields();
|
||||
|
||||
scope.$apply = function() {
|
||||
expect(scope.state.ui).to.equal(2);
|
||||
expect(location.$$path).to.equal('/desktop');
|
||||
expect(emailDaoMock.unlock.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.generateKey();
|
||||
});
|
||||
scope.generateKey();
|
||||
});
|
||||
|
||||
});
|
||||
});
|
|
@ -1,190 +1,186 @@
|
|||
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() {
|
||||
var scope, ctrl, origEmailDao, emailDaoMock, pgpMock,
|
||||
emailAddress = 'fred@foo.com',
|
||||
passphrase = 'asd',
|
||||
keyId,
|
||||
keychainMock;
|
||||
describe('Login (new device) Controller unit test', function() {
|
||||
var scope, ctrl, origEmailDao, emailDaoMock, pgpMock,
|
||||
emailAddress = 'fred@foo.com',
|
||||
passphrase = 'asd',
|
||||
keyId,
|
||||
keychainMock;
|
||||
|
||||
beforeEach(function() {
|
||||
// remember original module to restore later
|
||||
origEmailDao = appController._emailDao;
|
||||
beforeEach(function() {
|
||||
// remember original module to restore later
|
||||
origEmailDao = appController._emailDao;
|
||||
|
||||
emailDaoMock = sinon.createStubInstance(EmailDAO);
|
||||
appController._emailDao = emailDaoMock;
|
||||
emailDaoMock = sinon.createStubInstance(EmailDAO);
|
||||
appController._emailDao = emailDaoMock;
|
||||
|
||||
keyId = '9FEB47936E712926';
|
||||
emailDaoMock._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
appController._pgp = pgpMock = sinon.createStubInstance(PGP);
|
||||
pgpMock.extractPublicKey.returns('publicKeyArmored');
|
||||
keyId = '9FEB47936E712926';
|
||||
emailDaoMock._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
appController._pgp = pgpMock = sinon.createStubInstance(PGP);
|
||||
pgpMock.extractPublicKey.returns('publicKeyArmored');
|
||||
|
||||
emailDaoMock._account = {
|
||||
emailAddress: emailAddress,
|
||||
emailDaoMock._account = {
|
||||
emailAddress: emailAddress,
|
||||
};
|
||||
|
||||
angular.module('loginnewdevicetest', []);
|
||||
mocks.module('loginnewdevicetest');
|
||||
mocks.inject(function($rootScope, $controller) {
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {
|
||||
ui: {}
|
||||
};
|
||||
|
||||
angular.module('loginnewdevicetest', []);
|
||||
mocks.module('loginnewdevicetest');
|
||||
mocks.inject(function($rootScope, $controller) {
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {
|
||||
ui: {}
|
||||
};
|
||||
ctrl = $controller(LoginNewDeviceCtrl, {
|
||||
$scope: scope,
|
||||
$routeParams: {}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// restore the module
|
||||
appController._emailDao = origEmailDao;
|
||||
});
|
||||
|
||||
describe('initial state', function() {
|
||||
it('should be well defined', function() {
|
||||
expect(scope.incorrect).to.be.false;
|
||||
expect(scope.confirmPassphrase).to.exist;
|
||||
});
|
||||
});
|
||||
|
||||
describe('confirm passphrase', function() {
|
||||
it('should unlock crypto with a public key on the server', function() {
|
||||
scope.passphrase = passphrase;
|
||||
scope.key = {
|
||||
privateKeyArmored: 'b'
|
||||
};
|
||||
|
||||
pgpMock.getKeyParams.returns({
|
||||
_id: 'id',
|
||||
userIds: []
|
||||
});
|
||||
|
||||
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(null, {
|
||||
_id: keyId,
|
||||
publicKey: 'a'
|
||||
});
|
||||
emailDaoMock.unlock.withArgs(sinon.match.any, passphrase).yields();
|
||||
keychainMock.putUserKeyPair.yields();
|
||||
|
||||
scope.confirmPassphrase();
|
||||
|
||||
expect(emailDaoMock.unlock.calledOnce).to.be.true;
|
||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should unlock crypto with no key on the server', function() {
|
||||
scope.passphrase = passphrase;
|
||||
scope.key = {
|
||||
privateKeyArmored: 'b',
|
||||
publicKeyArmored: 'a'
|
||||
};
|
||||
|
||||
pgpMock.getKeyParams.returns({
|
||||
_id: 'id',
|
||||
userIds: []
|
||||
});
|
||||
|
||||
keychainMock.getUserKeyPair.withArgs(emailAddress).yields();
|
||||
emailDaoMock.unlock.withArgs(sinon.match.any, passphrase).yields();
|
||||
keychainMock.putUserKeyPair.yields();
|
||||
|
||||
scope.confirmPassphrase();
|
||||
|
||||
expect(emailDaoMock.unlock.calledOnce).to.be.true;
|
||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should not work when keypair upload fails', function(done) {
|
||||
scope.passphrase = passphrase;
|
||||
scope.key = {
|
||||
privateKeyArmored: 'b'
|
||||
};
|
||||
|
||||
pgpMock.getKeyParams.returns({
|
||||
_id: 'id',
|
||||
userIds: []
|
||||
});
|
||||
|
||||
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(null, {
|
||||
_id: keyId,
|
||||
publicKey: 'a'
|
||||
});
|
||||
emailDaoMock.unlock.yields();
|
||||
keychainMock.putUserKeyPair.yields({
|
||||
errMsg: 'yo mamma.'
|
||||
});
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err.errMsg).to.equal('yo mamma.');
|
||||
done();
|
||||
};
|
||||
|
||||
scope.confirmPassphrase();
|
||||
|
||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||
expect(emailDaoMock.unlock.calledOnce).to.be.true;
|
||||
expect(keychainMock.putUserKeyPair.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should not work when unlock fails', function(done) {
|
||||
scope.passphrase = passphrase;
|
||||
scope.key = {
|
||||
privateKeyArmored: 'b'
|
||||
};
|
||||
|
||||
pgpMock.getKeyParams.returns({
|
||||
_id: 'id',
|
||||
userIds: []
|
||||
});
|
||||
|
||||
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(null, {
|
||||
_id: keyId,
|
||||
publicKey: 'a'
|
||||
});
|
||||
emailDaoMock.unlock.yields({
|
||||
errMsg: 'yo mamma.'
|
||||
});
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err.errMsg).to.equal('yo mamma.');
|
||||
done();
|
||||
};
|
||||
|
||||
scope.confirmPassphrase();
|
||||
|
||||
expect(scope.incorrect).to.be.true;
|
||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||
expect(emailDaoMock.unlock.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should not work when keypair retrieval', function(done) {
|
||||
scope.passphrase = passphrase;
|
||||
|
||||
keychainMock.getUserKeyPair.withArgs(emailAddress).yields({
|
||||
errMsg: 'yo mamma.'
|
||||
});
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err.errMsg).to.equal('yo mamma.');
|
||||
done();
|
||||
};
|
||||
|
||||
scope.confirmPassphrase();
|
||||
|
||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||
ctrl = $controller(LoginNewDeviceCtrl, {
|
||||
$scope: scope,
|
||||
$routeParams: {}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// restore the module
|
||||
appController._emailDao = origEmailDao;
|
||||
});
|
||||
|
||||
describe('initial state', function() {
|
||||
it('should be well defined', function() {
|
||||
expect(scope.incorrect).to.be.false;
|
||||
expect(scope.confirmPassphrase).to.exist;
|
||||
});
|
||||
});
|
||||
|
||||
describe('confirm passphrase', function() {
|
||||
it('should unlock crypto with a public key on the server', function() {
|
||||
scope.passphrase = passphrase;
|
||||
scope.key = {
|
||||
privateKeyArmored: 'b'
|
||||
};
|
||||
|
||||
pgpMock.getKeyParams.returns({
|
||||
_id: 'id',
|
||||
userIds: []
|
||||
});
|
||||
|
||||
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(null, {
|
||||
_id: keyId,
|
||||
publicKey: 'a'
|
||||
});
|
||||
emailDaoMock.unlock.withArgs(sinon.match.any, passphrase).yields();
|
||||
keychainMock.putUserKeyPair.yields();
|
||||
|
||||
scope.confirmPassphrase();
|
||||
|
||||
expect(emailDaoMock.unlock.calledOnce).to.be.true;
|
||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should unlock crypto with no key on the server', function() {
|
||||
scope.passphrase = passphrase;
|
||||
scope.key = {
|
||||
privateKeyArmored: 'b',
|
||||
publicKeyArmored: 'a'
|
||||
};
|
||||
|
||||
pgpMock.getKeyParams.returns({
|
||||
_id: 'id',
|
||||
userIds: []
|
||||
});
|
||||
|
||||
keychainMock.getUserKeyPair.withArgs(emailAddress).yields();
|
||||
emailDaoMock.unlock.withArgs(sinon.match.any, passphrase).yields();
|
||||
keychainMock.putUserKeyPair.yields();
|
||||
|
||||
scope.confirmPassphrase();
|
||||
|
||||
expect(emailDaoMock.unlock.calledOnce).to.be.true;
|
||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should not work when keypair upload fails', function(done) {
|
||||
scope.passphrase = passphrase;
|
||||
scope.key = {
|
||||
privateKeyArmored: 'b'
|
||||
};
|
||||
|
||||
pgpMock.getKeyParams.returns({
|
||||
_id: 'id',
|
||||
userIds: []
|
||||
});
|
||||
|
||||
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(null, {
|
||||
_id: keyId,
|
||||
publicKey: 'a'
|
||||
});
|
||||
emailDaoMock.unlock.yields();
|
||||
keychainMock.putUserKeyPair.yields({
|
||||
errMsg: 'yo mamma.'
|
||||
});
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err.errMsg).to.equal('yo mamma.');
|
||||
done();
|
||||
};
|
||||
|
||||
scope.confirmPassphrase();
|
||||
|
||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||
expect(emailDaoMock.unlock.calledOnce).to.be.true;
|
||||
expect(keychainMock.putUserKeyPair.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should not work when unlock fails', function(done) {
|
||||
scope.passphrase = passphrase;
|
||||
scope.key = {
|
||||
privateKeyArmored: 'b'
|
||||
};
|
||||
|
||||
pgpMock.getKeyParams.returns({
|
||||
_id: 'id',
|
||||
userIds: []
|
||||
});
|
||||
|
||||
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(null, {
|
||||
_id: keyId,
|
||||
publicKey: 'a'
|
||||
});
|
||||
emailDaoMock.unlock.yields({
|
||||
errMsg: 'yo mamma.'
|
||||
});
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err.errMsg).to.equal('yo mamma.');
|
||||
done();
|
||||
};
|
||||
|
||||
scope.confirmPassphrase();
|
||||
|
||||
expect(scope.incorrect).to.be.true;
|
||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||
expect(emailDaoMock.unlock.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should not work when keypair retrieval', function(done) {
|
||||
scope.passphrase = passphrase;
|
||||
|
||||
keychainMock.getUserKeyPair.withArgs(emailAddress).yields({
|
||||
errMsg: 'yo mamma.'
|
||||
});
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err.errMsg).to.equal('yo mamma.');
|
||||
done();
|
||||
};
|
||||
|
||||
scope.confirmPassphrase();
|
||||
|
||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,39 +1,250 @@
|
|||
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() {
|
||||
var scope, location, ctrl,
|
||||
origEmailDao, emailDaoMock,
|
||||
origAuth, authMock,
|
||||
origKeychain, keychainMock,
|
||||
emailAddress = 'fred@foo.com';
|
||||
describe('Login Private Key Download Controller unit test', function() {
|
||||
var scope, location, ctrl,
|
||||
origEmailDao, emailDaoMock,
|
||||
origAuth, authMock,
|
||||
origKeychain, keychainMock,
|
||||
emailAddress = 'fred@foo.com';
|
||||
|
||||
beforeEach(function(done) {
|
||||
// remember original module to restore later, then replace it
|
||||
origEmailDao = appController._emailDao;
|
||||
origKeychain = appController._keychain;
|
||||
origAuth = appController._auth;
|
||||
beforeEach(function(done) {
|
||||
// remember original module to restore later, then replace it
|
||||
origEmailDao = appController._emailDao;
|
||||
origKeychain = appController._keychain;
|
||||
origAuth = appController._auth;
|
||||
|
||||
appController._emailDao = emailDaoMock = sinon.createStubInstance(EmailDAO);
|
||||
appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
appController._auth = authMock = sinon.createStubInstance(Auth);
|
||||
appController._emailDao = emailDaoMock = sinon.createStubInstance(EmailDAO);
|
||||
appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
appController._auth = authMock = sinon.createStubInstance(Auth);
|
||||
|
||||
emailDaoMock._account = {
|
||||
emailAddress: emailAddress
|
||||
emailDaoMock._account = {
|
||||
emailAddress: emailAddress
|
||||
};
|
||||
|
||||
angular.module('login-privatekey-download-test', []);
|
||||
mocks.module('login-privatekey-download-test');
|
||||
mocks.inject(function($controller, $rootScope) {
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
ctrl = $controller(LoginPrivateKeyDownloadCtrl, {
|
||||
$location: location,
|
||||
$scope: scope,
|
||||
$routeParams: {}
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// restore the app controller module
|
||||
appController._emailDao = origEmailDao;
|
||||
appController._keychain = origKeychain;
|
||||
appController._auth = origAuth;
|
||||
});
|
||||
|
||||
describe('initialization', function() {
|
||||
it('should work', function() {
|
||||
expect(scope.step).to.equal(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('verifyRecoveryToken', function() {
|
||||
var testKeypair = {
|
||||
publicKey: {
|
||||
_id: 'id'
|
||||
}
|
||||
};
|
||||
|
||||
it('should fail for empty recovery token', function(done) {
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
};
|
||||
|
||||
angular.module('login-privatekey-download-test', []);
|
||||
mocks.module('login-privatekey-download-test');
|
||||
mocks.inject(function($controller, $rootScope) {
|
||||
scope.recoveryToken = undefined;
|
||||
scope.verifyRecoveryToken();
|
||||
});
|
||||
|
||||
it('should fail in keychain.getUserKeyPair', function(done) {
|
||||
keychainMock.getUserKeyPair.yields(42);
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.recoveryToken = 'token';
|
||||
scope.verifyRecoveryToken();
|
||||
});
|
||||
|
||||
it('should fail in keychain.downloadPrivateKey', function(done) {
|
||||
keychainMock.getUserKeyPair.yields(null, testKeypair);
|
||||
keychainMock.downloadPrivateKey.yields(42);
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||
expect(keychainMock.downloadPrivateKey.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.recoveryToken = 'token';
|
||||
scope.verifyRecoveryToken();
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
keychainMock.getUserKeyPair.yields(null, testKeypair);
|
||||
keychainMock.downloadPrivateKey.yields(null, 'encryptedPrivateKey');
|
||||
|
||||
scope.recoveryToken = 'token';
|
||||
scope.verifyRecoveryToken(function() {
|
||||
expect(scope.encryptedPrivateKey).to.equal('encryptedPrivateKey');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('handlePaste', function() {
|
||||
it('should work', function() {
|
||||
scope.handlePaste({
|
||||
clipboardData: {
|
||||
getData: function(val) {
|
||||
expect(val).to.equal('text/plain');
|
||||
return '1qaz-2wsx-3edc-4rfv-5tgb-6yhn';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
expect(scope.code0).to.equal('1qaz');
|
||||
expect(scope.code1).to.equal('2wsx');
|
||||
expect(scope.code2).to.equal('3edc');
|
||||
expect(scope.code3).to.equal('4rfv');
|
||||
expect(scope.code4).to.equal('5tgb');
|
||||
expect(scope.code5).to.equal('6yhn');
|
||||
});
|
||||
});
|
||||
|
||||
describe('decryptAndStorePrivateKeyLocally', function() {
|
||||
beforeEach(function() {
|
||||
scope.code0 = '0';
|
||||
scope.code1 = '1';
|
||||
scope.code2 = '2';
|
||||
scope.code3 = '3';
|
||||
scope.code4 = '4';
|
||||
scope.code5 = '5';
|
||||
|
||||
scope.encryptedPrivateKey = {
|
||||
encryptedPrivateKey: 'encryptedPrivateKey'
|
||||
};
|
||||
scope.cachedKeypair = {
|
||||
publicKey: {
|
||||
_id: 'keyId'
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
it('should fail on empty code', function(done) {
|
||||
scope.code0 = '';
|
||||
scope.code1 = '';
|
||||
scope.code2 = '';
|
||||
scope.code3 = '';
|
||||
scope.code4 = '';
|
||||
scope.code5 = '';
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.decryptAndStorePrivateKeyLocally();
|
||||
});
|
||||
|
||||
it('should fail on decryptAndStorePrivateKeyLocally', function(done) {
|
||||
keychainMock.decryptAndStorePrivateKeyLocally.yields(42);
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(keychainMock.decryptAndStorePrivateKeyLocally.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.decryptAndStorePrivateKeyLocally();
|
||||
});
|
||||
|
||||
it('should goto /login-existing on emailDao.unlock fail', function(done) {
|
||||
keychainMock.decryptAndStorePrivateKeyLocally.yields(null, {
|
||||
encryptedKey: 'keyArmored'
|
||||
});
|
||||
emailDaoMock.unlock.yields(42);
|
||||
|
||||
scope.goTo = function(location) {
|
||||
expect(location).to.equal('/login-existing');
|
||||
expect(keychainMock.decryptAndStorePrivateKeyLocally.calledOnce).to.be.true;
|
||||
expect(emailDaoMock.unlock.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.decryptAndStorePrivateKeyLocally();
|
||||
});
|
||||
|
||||
it('should goto /desktop on emailDao.unlock success', function(done) {
|
||||
keychainMock.decryptAndStorePrivateKeyLocally.yields(null, {
|
||||
encryptedKey: 'keyArmored'
|
||||
});
|
||||
emailDaoMock.unlock.yields();
|
||||
authMock.storeCredentials.yields();
|
||||
|
||||
scope.goTo = function(location) {
|
||||
expect(location).to.equal('/desktop');
|
||||
expect(keychainMock.decryptAndStorePrivateKeyLocally.calledOnce).to.be.true;
|
||||
expect(emailDaoMock.unlock.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.decryptAndStorePrivateKeyLocally();
|
||||
});
|
||||
});
|
||||
|
||||
describe('goForward', function() {
|
||||
it('should work in step 1', function() {
|
||||
var verifyRecoveryTokenStub = sinon.stub(scope, 'verifyRecoveryToken');
|
||||
verifyRecoveryTokenStub.yields();
|
||||
scope.step = 1;
|
||||
|
||||
scope.goForward();
|
||||
|
||||
expect(verifyRecoveryTokenStub.calledOnce).to.be.true;
|
||||
expect(scope.step).to.equal(2);
|
||||
verifyRecoveryTokenStub.restore();
|
||||
});
|
||||
it('should work in step 2', function() {
|
||||
var decryptAndStorePrivateKeyLocallyStub = sinon.stub(scope, 'decryptAndStorePrivateKeyLocally');
|
||||
decryptAndStorePrivateKeyLocallyStub.returns();
|
||||
scope.step = 2;
|
||||
|
||||
scope.goForward();
|
||||
|
||||
expect(decryptAndStorePrivateKeyLocallyStub.calledOnce).to.be.true;
|
||||
decryptAndStorePrivateKeyLocallyStub.restore();
|
||||
});
|
||||
});
|
||||
|
||||
describe('goTo', function() {
|
||||
it('should work', function(done) {
|
||||
mocks.inject(function($controller, $rootScope, $location) {
|
||||
location = $location;
|
||||
sinon.stub(location, 'path', function(path) {
|
||||
expect(path).to.equal('/desktop');
|
||||
done();
|
||||
});
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
ctrl = $controller(LoginPrivateKeyDownloadCtrl, {
|
||||
|
@ -41,224 +252,9 @@ define(function(require) {
|
|||
$scope: scope,
|
||||
$routeParams: {}
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// restore the app controller module
|
||||
appController._emailDao = origEmailDao;
|
||||
appController._keychain = origKeychain;
|
||||
appController._auth = origAuth;
|
||||
});
|
||||
|
||||
describe('initialization', function() {
|
||||
it('should work', function() {
|
||||
expect(scope.step).to.equal(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('verifyRecoveryToken', function() {
|
||||
var testKeypair = {
|
||||
publicKey: {
|
||||
_id: 'id'
|
||||
}
|
||||
};
|
||||
|
||||
it('should fail for empty recovery token', function(done) {
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.recoveryToken = undefined;
|
||||
scope.verifyRecoveryToken();
|
||||
});
|
||||
|
||||
it('should fail in keychain.getUserKeyPair', function(done) {
|
||||
keychainMock.getUserKeyPair.yields(42);
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.recoveryToken = 'token';
|
||||
scope.verifyRecoveryToken();
|
||||
});
|
||||
|
||||
it('should fail in keychain.downloadPrivateKey', function(done) {
|
||||
keychainMock.getUserKeyPair.yields(null, testKeypair);
|
||||
keychainMock.downloadPrivateKey.yields(42);
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||
expect(keychainMock.downloadPrivateKey.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.recoveryToken = 'token';
|
||||
scope.verifyRecoveryToken();
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
keychainMock.getUserKeyPair.yields(null, testKeypair);
|
||||
keychainMock.downloadPrivateKey.yields(null, 'encryptedPrivateKey');
|
||||
|
||||
scope.recoveryToken = 'token';
|
||||
scope.verifyRecoveryToken(function() {
|
||||
expect(scope.encryptedPrivateKey).to.equal('encryptedPrivateKey');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('handlePaste', function() {
|
||||
it('should work', function() {
|
||||
scope.handlePaste({
|
||||
clipboardData: {
|
||||
getData: function(val) {
|
||||
expect(val).to.equal('text/plain');
|
||||
return '1qaz-2wsx-3edc-4rfv-5tgb-6yhn';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
expect(scope.code0).to.equal('1qaz');
|
||||
expect(scope.code1).to.equal('2wsx');
|
||||
expect(scope.code2).to.equal('3edc');
|
||||
expect(scope.code3).to.equal('4rfv');
|
||||
expect(scope.code4).to.equal('5tgb');
|
||||
expect(scope.code5).to.equal('6yhn');
|
||||
});
|
||||
});
|
||||
|
||||
describe('decryptAndStorePrivateKeyLocally', function() {
|
||||
beforeEach(function() {
|
||||
scope.code0 = '0';
|
||||
scope.code1 = '1';
|
||||
scope.code2 = '2';
|
||||
scope.code3 = '3';
|
||||
scope.code4 = '4';
|
||||
scope.code5 = '5';
|
||||
|
||||
scope.encryptedPrivateKey = {
|
||||
encryptedPrivateKey: 'encryptedPrivateKey'
|
||||
};
|
||||
scope.cachedKeypair = {
|
||||
publicKey: {
|
||||
_id: 'keyId'
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
it('should fail on empty code', function(done) {
|
||||
scope.code0 = '';
|
||||
scope.code1 = '';
|
||||
scope.code2 = '';
|
||||
scope.code3 = '';
|
||||
scope.code4 = '';
|
||||
scope.code5 = '';
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.decryptAndStorePrivateKeyLocally();
|
||||
});
|
||||
|
||||
it('should fail on decryptAndStorePrivateKeyLocally', function(done) {
|
||||
keychainMock.decryptAndStorePrivateKeyLocally.yields(42);
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(keychainMock.decryptAndStorePrivateKeyLocally.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.decryptAndStorePrivateKeyLocally();
|
||||
});
|
||||
|
||||
it('should goto /login-existing on emailDao.unlock fail', function(done) {
|
||||
keychainMock.decryptAndStorePrivateKeyLocally.yields(null, {
|
||||
encryptedKey: 'keyArmored'
|
||||
});
|
||||
emailDaoMock.unlock.yields(42);
|
||||
|
||||
scope.goTo = function(location) {
|
||||
expect(location).to.equal('/login-existing');
|
||||
expect(keychainMock.decryptAndStorePrivateKeyLocally.calledOnce).to.be.true;
|
||||
expect(emailDaoMock.unlock.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.decryptAndStorePrivateKeyLocally();
|
||||
});
|
||||
|
||||
it('should goto /desktop on emailDao.unlock success', function(done) {
|
||||
keychainMock.decryptAndStorePrivateKeyLocally.yields(null, {
|
||||
encryptedKey: 'keyArmored'
|
||||
});
|
||||
emailDaoMock.unlock.yields();
|
||||
authMock.storeCredentials.yields();
|
||||
|
||||
scope.goTo = function(location) {
|
||||
expect(location).to.equal('/desktop');
|
||||
expect(keychainMock.decryptAndStorePrivateKeyLocally.calledOnce).to.be.true;
|
||||
expect(emailDaoMock.unlock.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.decryptAndStorePrivateKeyLocally();
|
||||
});
|
||||
});
|
||||
|
||||
describe('goForward', function() {
|
||||
it('should work in step 1', function() {
|
||||
var verifyRecoveryTokenStub = sinon.stub(scope, 'verifyRecoveryToken');
|
||||
verifyRecoveryTokenStub.yields();
|
||||
scope.step = 1;
|
||||
|
||||
scope.goForward();
|
||||
|
||||
expect(verifyRecoveryTokenStub.calledOnce).to.be.true;
|
||||
expect(scope.step).to.equal(2);
|
||||
verifyRecoveryTokenStub.restore();
|
||||
});
|
||||
it('should work in step 2', function() {
|
||||
var decryptAndStorePrivateKeyLocallyStub = sinon.stub(scope, 'decryptAndStorePrivateKeyLocally');
|
||||
decryptAndStorePrivateKeyLocallyStub.returns();
|
||||
scope.step = 2;
|
||||
|
||||
scope.goForward();
|
||||
|
||||
expect(decryptAndStorePrivateKeyLocallyStub.calledOnce).to.be.true;
|
||||
decryptAndStorePrivateKeyLocallyStub.restore();
|
||||
});
|
||||
});
|
||||
|
||||
describe('goTo', function() {
|
||||
it('should work', function(done) {
|
||||
mocks.inject(function($controller, $rootScope, $location) {
|
||||
location = $location;
|
||||
sinon.stub(location, 'path', function(path) {
|
||||
expect(path).to.equal('/desktop');
|
||||
done();
|
||||
});
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
ctrl = $controller(LoginPrivateKeyDownloadCtrl, {
|
||||
$location: location,
|
||||
$scope: scope,
|
||||
$routeParams: {}
|
||||
});
|
||||
});
|
||||
|
||||
scope.goTo('/desktop');
|
||||
});
|
||||
scope.goTo('/desktop');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,101 +1,97 @@
|
|||
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() {
|
||||
// Angular parameters
|
||||
var scope, location, provider;
|
||||
describe('Login (Set Credentials) Controller unit test', function() {
|
||||
// Angular parameters
|
||||
var scope, location, provider;
|
||||
|
||||
// Stubs
|
||||
var auth, origAuth, doctor, origDoctor;
|
||||
// Stubs
|
||||
var auth, origAuth, doctor, origDoctor;
|
||||
|
||||
// SUT
|
||||
var setCredentialsCtrl;
|
||||
// SUT
|
||||
var setCredentialsCtrl;
|
||||
|
||||
beforeEach(function() {
|
||||
// remeber pre-test state to restore later
|
||||
origAuth = appController._auth;
|
||||
origDoctor = appController._doctor;
|
||||
auth = appController._auth = sinon.createStubInstance(Auth);
|
||||
doctor = appController._doctor = sinon.createStubInstance(ConnectionDoctor);
|
||||
beforeEach(function() {
|
||||
// remeber pre-test state to restore later
|
||||
origAuth = appController._auth;
|
||||
origDoctor = appController._doctor;
|
||||
auth = appController._auth = sinon.createStubInstance(Auth);
|
||||
doctor = appController._doctor = sinon.createStubInstance(ConnectionDoctor);
|
||||
|
||||
// setup the controller
|
||||
angular.module('setcredentialstest', []);
|
||||
mocks.module('setcredentialstest');
|
||||
mocks.inject(function($rootScope, $controller, $location) {
|
||||
scope = $rootScope.$new();
|
||||
location = $location;
|
||||
location.search({
|
||||
provider: provider
|
||||
});
|
||||
|
||||
scope.state = {};
|
||||
setCredentialsCtrl = $controller(SetCredentialsCtrl, {
|
||||
$scope: scope,
|
||||
$routeParams: {}
|
||||
});
|
||||
// setup the controller
|
||||
angular.module('setcredentialstest', []);
|
||||
mocks.module('setcredentialstest');
|
||||
mocks.inject(function($rootScope, $controller, $location) {
|
||||
scope = $rootScope.$new();
|
||||
location = $location;
|
||||
location.search({
|
||||
provider: provider
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// restore pre-test state
|
||||
appController._auth = origAuth;
|
||||
appController._doctor = origDoctor;
|
||||
});
|
||||
|
||||
describe('set credentials', function() {
|
||||
it('should work', function() {
|
||||
scope.emailAddress = 'emailemailemailemail';
|
||||
scope.password = 'passwdpasswdpasswdpasswd';
|
||||
scope.smtpHost = 'hosthosthost';
|
||||
scope.smtpPort = 1337;
|
||||
scope.smtpEncryption = '1'; // STARTTLS
|
||||
scope.imapHost = 'hosthosthost';
|
||||
scope.imapPort = 1337;
|
||||
scope.imapEncryption = '2'; // TLS
|
||||
scope.realname = 'peter pan';
|
||||
|
||||
var expectedCredentials = {
|
||||
provider: provider,
|
||||
emailAddress: scope.emailAddress,
|
||||
username: scope.username || scope.emailAddress,
|
||||
realname: scope.realname,
|
||||
password: scope.password,
|
||||
xoauth2: undefined,
|
||||
imap: {
|
||||
host: scope.imapHost.toLowerCase(),
|
||||
port: scope.imapPort,
|
||||
secure: true,
|
||||
ignoreTLS: false,
|
||||
ca: undefined,
|
||||
pinned: false
|
||||
},
|
||||
smtp: {
|
||||
host: scope.smtpHost.toLowerCase(),
|
||||
port: scope.smtpPort,
|
||||
secure: false,
|
||||
ignoreTLS: false,
|
||||
ca: undefined,
|
||||
pinned: false
|
||||
}
|
||||
};
|
||||
|
||||
doctor.check.yields(); // synchronous yields!
|
||||
|
||||
scope.test();
|
||||
|
||||
expect(doctor.check.calledOnce).to.be.true;
|
||||
expect(doctor.configure.calledOnce).to.be.true;
|
||||
expect(doctor.configure.calledWith(expectedCredentials)).to.be.true;
|
||||
expect(auth.setCredentials.calledOnce).to.be.true;
|
||||
scope.state = {};
|
||||
setCredentialsCtrl = $controller(SetCredentialsCtrl, {
|
||||
$scope: scope,
|
||||
$routeParams: {}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// restore pre-test state
|
||||
appController._auth = origAuth;
|
||||
appController._doctor = origDoctor;
|
||||
});
|
||||
|
||||
describe('set credentials', function() {
|
||||
it('should work', function() {
|
||||
scope.emailAddress = 'emailemailemailemail';
|
||||
scope.password = 'passwdpasswdpasswdpasswd';
|
||||
scope.smtpHost = 'hosthosthost';
|
||||
scope.smtpPort = 1337;
|
||||
scope.smtpEncryption = '1'; // STARTTLS
|
||||
scope.imapHost = 'hosthosthost';
|
||||
scope.imapPort = 1337;
|
||||
scope.imapEncryption = '2'; // TLS
|
||||
scope.realname = 'peter pan';
|
||||
|
||||
var expectedCredentials = {
|
||||
provider: provider,
|
||||
emailAddress: scope.emailAddress,
|
||||
username: scope.username || scope.emailAddress,
|
||||
realname: scope.realname,
|
||||
password: scope.password,
|
||||
xoauth2: undefined,
|
||||
imap: {
|
||||
host: scope.imapHost.toLowerCase(),
|
||||
port: scope.imapPort,
|
||||
secure: true,
|
||||
ignoreTLS: false,
|
||||
ca: undefined,
|
||||
pinned: false
|
||||
},
|
||||
smtp: {
|
||||
host: scope.smtpHost.toLowerCase(),
|
||||
port: scope.smtpPort,
|
||||
secure: false,
|
||||
ignoreTLS: false,
|
||||
ca: undefined,
|
||||
pinned: false
|
||||
}
|
||||
};
|
||||
|
||||
doctor.check.yields(); // synchronous yields!
|
||||
|
||||
scope.test();
|
||||
|
||||
expect(doctor.check.calledOnce).to.be.true;
|
||||
expect(doctor.configure.calledOnce).to.be.true;
|
||||
expect(doctor.configure.calledWith(expectedCredentials)).to.be.true;
|
||||
expect(auth.setCredentials.calledOnce).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,451 +1,447 @@
|
|||
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() {
|
||||
var scope, ctrl, origEmailDao, emailDaoMock, keychainMock, deviceStorageMock,
|
||||
emailAddress, emails,
|
||||
hasChrome, hasSocket, hasRuntime, hasIdentity;
|
||||
describe('Mail List controller unit test', function() {
|
||||
var scope, ctrl, origEmailDao, emailDaoMock, keychainMock, deviceStorageMock,
|
||||
emailAddress, emails,
|
||||
hasChrome, hasSocket, hasRuntime, hasIdentity;
|
||||
|
||||
beforeEach(function() {
|
||||
hasChrome = !!window.chrome;
|
||||
hasSocket = !!window.chrome.socket;
|
||||
hasIdentity = !!window.chrome.identity;
|
||||
if (!hasChrome) {
|
||||
window.chrome = {};
|
||||
}
|
||||
if (!hasSocket) {
|
||||
window.chrome.socket = {};
|
||||
}
|
||||
if (!hasRuntime) {
|
||||
window.chrome.runtime = {
|
||||
getURL: function() {}
|
||||
};
|
||||
}
|
||||
if (!hasIdentity) {
|
||||
window.chrome.identity = {};
|
||||
}
|
||||
beforeEach(function() {
|
||||
hasChrome = !!window.chrome;
|
||||
hasSocket = !!window.chrome.socket;
|
||||
hasIdentity = !!window.chrome.identity;
|
||||
if (!hasChrome) {
|
||||
window.chrome = {};
|
||||
}
|
||||
if (!hasSocket) {
|
||||
window.chrome.socket = {};
|
||||
}
|
||||
if (!hasRuntime) {
|
||||
window.chrome.runtime = {
|
||||
getURL: function() {}
|
||||
};
|
||||
}
|
||||
if (!hasIdentity) {
|
||||
window.chrome.identity = {};
|
||||
}
|
||||
|
||||
emails = [{
|
||||
unread: true
|
||||
}, {
|
||||
unread: true
|
||||
}, {
|
||||
unread: true
|
||||
}];
|
||||
appController._outboxBo = {
|
||||
pendingEmails: emails
|
||||
emails = [{
|
||||
unread: true
|
||||
}, {
|
||||
unread: true
|
||||
}, {
|
||||
unread: true
|
||||
}];
|
||||
appController._outboxBo = {
|
||||
pendingEmails: emails
|
||||
};
|
||||
|
||||
origEmailDao = appController._emailDao;
|
||||
emailDaoMock = sinon.createStubInstance(EmailDAO);
|
||||
appController._emailDao = emailDaoMock;
|
||||
emailAddress = 'fred@foo.com';
|
||||
emailDaoMock._account = {
|
||||
emailAddress: emailAddress,
|
||||
};
|
||||
|
||||
|
||||
keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
appController._keychain = keychainMock;
|
||||
|
||||
deviceStorageMock = sinon.createStubInstance(DeviceStorageDAO);
|
||||
emailDaoMock._devicestorage = deviceStorageMock;
|
||||
|
||||
angular.module('maillisttest', []);
|
||||
mocks.module('maillisttest');
|
||||
mocks.inject(function($rootScope, $controller) {
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {
|
||||
read: {
|
||||
toggle: function() {}
|
||||
}
|
||||
};
|
||||
|
||||
origEmailDao = appController._emailDao;
|
||||
emailDaoMock = sinon.createStubInstance(EmailDAO);
|
||||
appController._emailDao = emailDaoMock;
|
||||
emailAddress = 'fred@foo.com';
|
||||
emailDaoMock._account = {
|
||||
emailAddress: emailAddress,
|
||||
};
|
||||
|
||||
|
||||
keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
appController._keychain = keychainMock;
|
||||
|
||||
deviceStorageMock = sinon.createStubInstance(DeviceStorageDAO);
|
||||
emailDaoMock._devicestorage = deviceStorageMock;
|
||||
|
||||
angular.module('maillisttest', []);
|
||||
mocks.module('maillisttest');
|
||||
mocks.inject(function($rootScope, $controller) {
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {
|
||||
read: {
|
||||
toggle: function() {}
|
||||
}
|
||||
};
|
||||
|
||||
scope.loadVisibleBodies = function() {};
|
||||
ctrl = $controller(MailListCtrl, {
|
||||
$scope: scope,
|
||||
$routeParams: {}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
if (!hasSocket) {
|
||||
delete window.chrome.socket;
|
||||
}
|
||||
if (!hasRuntime) {
|
||||
delete window.chrome.runtime;
|
||||
}
|
||||
if (!hasChrome) {
|
||||
delete window.chrome;
|
||||
}
|
||||
if (!hasIdentity) {
|
||||
delete window.chrome.identity;
|
||||
}
|
||||
|
||||
// restore the module
|
||||
appController._emailDao = origEmailDao;
|
||||
});
|
||||
|
||||
describe('displayMore', function() {
|
||||
beforeEach(function() {
|
||||
scope.state.nav = {
|
||||
currentFolder: {
|
||||
messages: ['a', 'b']
|
||||
}
|
||||
};
|
||||
});
|
||||
it('should not do anything when display length equals messages length', function() {
|
||||
scope.displayMessages = ['a', 'b'];
|
||||
|
||||
scope.displayMore();
|
||||
expect(scope.displayMessages.length).to.equal(scope.state.nav.currentFolder.messages.length);
|
||||
});
|
||||
it('should append next message interval', function() {
|
||||
scope.displayMessages = ['a'];
|
||||
|
||||
scope.displayMore();
|
||||
expect(scope.displayMessages.length).to.equal(scope.state.nav.currentFolder.messages.length);
|
||||
});
|
||||
});
|
||||
|
||||
describe('displaySearchResults', function() {
|
||||
var clock;
|
||||
|
||||
beforeEach(function() {
|
||||
scope.state.nav = {
|
||||
currentFolder: {
|
||||
messages: ['a', 'b']
|
||||
}
|
||||
};
|
||||
scope.watchMessages();
|
||||
scope.watchOnline();
|
||||
clock = sinon.useFakeTimers();
|
||||
});
|
||||
afterEach(function() {
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
it('should show initial message on empty', function() {
|
||||
scope.displaySearchResults();
|
||||
expect(scope.searching).to.be.false;
|
||||
expect(scope.lastUpdateLbl).to.equal('Online');
|
||||
expect(scope.displayMessages.length).to.equal(2);
|
||||
});
|
||||
it('should show initial message on empty', function() {
|
||||
var searchStub = sinon.stub(scope, 'search');
|
||||
searchStub.returns(['a']);
|
||||
|
||||
|
||||
scope.displaySearchResults('query');
|
||||
expect(scope.searching).to.be.true;
|
||||
expect(scope.lastUpdateLbl).to.equal('Searching ...');
|
||||
clock.tick(500);
|
||||
|
||||
expect(scope.displayMessages).to.deep.equal(['a']);
|
||||
expect(scope.searching).to.be.false;
|
||||
expect(scope.lastUpdateLbl).to.equal('Matches in this folder');
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('search', function() {
|
||||
var message1 = {
|
||||
to: [{
|
||||
name: 'name1',
|
||||
address: 'address1'
|
||||
}],
|
||||
subject: 'subject1',
|
||||
body: 'body1',
|
||||
html: 'html1'
|
||||
},
|
||||
message2 = {
|
||||
to: [{
|
||||
name: 'name2',
|
||||
address: 'address2'
|
||||
}],
|
||||
subject: 'subject2',
|
||||
body: 'body2',
|
||||
html: 'html2'
|
||||
},
|
||||
message3 = {
|
||||
to: [{
|
||||
name: 'name3',
|
||||
address: 'address3'
|
||||
}],
|
||||
subject: 'subject3',
|
||||
body: 'body1',
|
||||
html: 'html1',
|
||||
encrypted: true
|
||||
},
|
||||
message4 = {
|
||||
to: [{
|
||||
name: 'name4',
|
||||
address: 'address4'
|
||||
}],
|
||||
subject: 'subject4',
|
||||
body: 'body1',
|
||||
html: 'html1',
|
||||
encrypted: true,
|
||||
decrypted: true
|
||||
},
|
||||
testMessages = [message1, message2, message3, message4];
|
||||
|
||||
it('return same messages array on empty query string', function() {
|
||||
var result = scope.search(testMessages, '');
|
||||
expect(result).to.equal(testMessages);
|
||||
});
|
||||
|
||||
it('return message1 on matching subject', function() {
|
||||
var result = scope.search(testMessages, 'subject1');
|
||||
expect(result.length).to.equal(1);
|
||||
expect(result[0]).to.equal(message1);
|
||||
});
|
||||
|
||||
it('return message1 on matching name', function() {
|
||||
var result = scope.search(testMessages, 'name1');
|
||||
expect(result.length).to.equal(1);
|
||||
expect(result[0]).to.equal(message1);
|
||||
});
|
||||
|
||||
it('return message1 on matching address', function() {
|
||||
var result = scope.search(testMessages, 'address1');
|
||||
expect(result.length).to.equal(1);
|
||||
expect(result[0]).to.equal(message1);
|
||||
});
|
||||
|
||||
it('return plaintext and decrypted messages on matching body', function() {
|
||||
var result = scope.search(testMessages, 'body1');
|
||||
expect(result.length).to.equal(2);
|
||||
expect(result[0]).to.equal(message1);
|
||||
expect(result[1]).to.equal(message4);
|
||||
});
|
||||
|
||||
it('return plaintext and decrypted messages on matching html', function() {
|
||||
var result = scope.search(testMessages, 'html1');
|
||||
expect(result.length).to.equal(2);
|
||||
expect(result[0]).to.equal(message1);
|
||||
expect(result[1]).to.equal(message4);
|
||||
});
|
||||
});
|
||||
|
||||
describe('scope variables', function() {
|
||||
it('should be set correctly', function() {
|
||||
expect(scope.select).to.exist;
|
||||
expect(scope.remove).to.exist;
|
||||
expect(scope.state.mailList).to.exist;
|
||||
});
|
||||
});
|
||||
|
||||
describe('push notification', function() {
|
||||
beforeEach(function() {
|
||||
scope._stopWatchTask();
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
notification.create.restore();
|
||||
});
|
||||
|
||||
it('should succeed for single mail', function(done) {
|
||||
var mail = {
|
||||
uid: 123,
|
||||
from: [{
|
||||
address: 'asd'
|
||||
}],
|
||||
subject: 'this is the subject!',
|
||||
unread: true
|
||||
};
|
||||
|
||||
sinon.stub(notification, 'create', function(opts) {
|
||||
expect(opts.title).to.equal(mail.from[0].address);
|
||||
expect(opts.message).to.equal(mail.subject);
|
||||
|
||||
opts.onClick();
|
||||
expect(scope.state.mailList.selected).to.equal(mail);
|
||||
done();
|
||||
});
|
||||
|
||||
scope.state.nav = {
|
||||
currentFolder: {
|
||||
type: 'asd',
|
||||
messages: [mail]
|
||||
}
|
||||
};
|
||||
|
||||
emailDaoMock.onIncomingMessage([mail]);
|
||||
});
|
||||
|
||||
it('should succeed for multiple mails', function(done) {
|
||||
var mails = [{
|
||||
uid: 1,
|
||||
from: [{
|
||||
address: 'asd'
|
||||
}],
|
||||
subject: 'this is the subject!',
|
||||
unread: true
|
||||
}, {
|
||||
uid: 2,
|
||||
from: [{
|
||||
address: 'qwe'
|
||||
}],
|
||||
subject: 'this is the other subject!',
|
||||
unread: true
|
||||
}, {
|
||||
uid: 3,
|
||||
from: [{
|
||||
address: 'qwe'
|
||||
}],
|
||||
subject: 'this is the other subject!',
|
||||
unread: false
|
||||
}];
|
||||
|
||||
sinon.stub(notification, 'create', function(opts) {
|
||||
expect(opts.title).to.equal('2 new messages');
|
||||
expect(opts.message).to.equal(mails[0].subject + '\n' + mails[1].subject);
|
||||
|
||||
opts.onClick();
|
||||
expect(scope.state.mailList.selected).to.equal(mails[0]);
|
||||
done();
|
||||
});
|
||||
|
||||
scope.state.nav = {
|
||||
currentFolder: {
|
||||
type: 'asd',
|
||||
messages: mails
|
||||
}
|
||||
};
|
||||
|
||||
emailDaoMock.onIncomingMessage(mails);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getBody', function() {
|
||||
it('should get the mail content', function() {
|
||||
scope.state.nav = {
|
||||
currentFolder: {
|
||||
type: 'asd',
|
||||
}
|
||||
};
|
||||
|
||||
scope.getBody();
|
||||
expect(emailDaoMock.getBody.calledOnce).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('select', function() {
|
||||
it('should decrypt, focus mark an unread mail as read', function() {
|
||||
scope.pendingNotifications = ['asd'];
|
||||
sinon.stub(notification, 'close');
|
||||
|
||||
var mail = {
|
||||
from: [{
|
||||
address: 'asd'
|
||||
}],
|
||||
unread: true,
|
||||
};
|
||||
scope.state = {
|
||||
nav: {
|
||||
currentFolder: {
|
||||
type: 'Inbox'
|
||||
}
|
||||
},
|
||||
mailList: {},
|
||||
read: {
|
||||
toggle: function() {}
|
||||
}
|
||||
};
|
||||
|
||||
keychainMock.refreshKeyForUserId.withArgs(mail.from[0].address).yields();
|
||||
|
||||
scope.select(mail);
|
||||
|
||||
expect(emailDaoMock.decryptBody.calledOnce).to.be.true;
|
||||
expect(keychainMock.refreshKeyForUserId.calledOnce).to.be.true;
|
||||
expect(scope.state.mailList.selected).to.equal(mail);
|
||||
expect(notification.close.calledWith('asd')).to.be.true;
|
||||
expect(notification.close.calledOnce).to.be.true;
|
||||
|
||||
notification.close.restore();
|
||||
});
|
||||
|
||||
it('should decrypt and focus a read mail', function() {
|
||||
var mail = {
|
||||
from: [{
|
||||
address: 'asd'
|
||||
}],
|
||||
unread: false
|
||||
};
|
||||
|
||||
scope.state = {
|
||||
mailList: {},
|
||||
read: {
|
||||
toggle: function() {}
|
||||
},
|
||||
nav: {
|
||||
currentFolder: {
|
||||
type: 'asd'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
keychainMock.refreshKeyForUserId.withArgs(mail.from[0].address).yields();
|
||||
|
||||
scope.select(mail);
|
||||
|
||||
expect(emailDaoMock.decryptBody.calledOnce).to.be.true;
|
||||
expect(keychainMock.refreshKeyForUserId.calledOnce).to.be.true;
|
||||
expect(scope.state.mailList.selected).to.equal(mail);
|
||||
});
|
||||
});
|
||||
|
||||
describe('remove', function() {
|
||||
it('should not delete without a selected mail', function() {
|
||||
scope.remove();
|
||||
});
|
||||
|
||||
it('should delete the selected mail', function() {
|
||||
var uid, mail, currentFolder;
|
||||
|
||||
scope._stopWatchTask();
|
||||
|
||||
scope.account = {};
|
||||
uid = 123;
|
||||
mail = {
|
||||
uid: uid,
|
||||
from: [{
|
||||
address: 'asd'
|
||||
}],
|
||||
subject: '[whiteout] asdasd',
|
||||
unread: true
|
||||
};
|
||||
currentFolder = {
|
||||
type: 'Inbox',
|
||||
path: 'INBOX',
|
||||
messages: [mail]
|
||||
};
|
||||
scope.account.folders = [currentFolder];
|
||||
scope.state.nav = {
|
||||
currentFolder: currentFolder
|
||||
};
|
||||
emailDaoMock.deleteMessage.yields();
|
||||
|
||||
scope.remove(mail);
|
||||
|
||||
expect(emailDaoMock.deleteMessage.calledOnce).to.be.true;
|
||||
expect(scope.state.mailList.selected).to.exist;
|
||||
scope.loadVisibleBodies = function() {};
|
||||
ctrl = $controller(MailListCtrl, {
|
||||
$scope: scope,
|
||||
$routeParams: {}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
if (!hasSocket) {
|
||||
delete window.chrome.socket;
|
||||
}
|
||||
if (!hasRuntime) {
|
||||
delete window.chrome.runtime;
|
||||
}
|
||||
if (!hasChrome) {
|
||||
delete window.chrome;
|
||||
}
|
||||
if (!hasIdentity) {
|
||||
delete window.chrome.identity;
|
||||
}
|
||||
|
||||
// restore the module
|
||||
appController._emailDao = origEmailDao;
|
||||
});
|
||||
|
||||
describe('displayMore', function() {
|
||||
beforeEach(function() {
|
||||
scope.state.nav = {
|
||||
currentFolder: {
|
||||
messages: ['a', 'b']
|
||||
}
|
||||
};
|
||||
});
|
||||
it('should not do anything when display length equals messages length', function() {
|
||||
scope.displayMessages = ['a', 'b'];
|
||||
|
||||
scope.displayMore();
|
||||
expect(scope.displayMessages.length).to.equal(scope.state.nav.currentFolder.messages.length);
|
||||
});
|
||||
it('should append next message interval', function() {
|
||||
scope.displayMessages = ['a'];
|
||||
|
||||
scope.displayMore();
|
||||
expect(scope.displayMessages.length).to.equal(scope.state.nav.currentFolder.messages.length);
|
||||
});
|
||||
});
|
||||
|
||||
describe('displaySearchResults', function() {
|
||||
var clock;
|
||||
|
||||
beforeEach(function() {
|
||||
scope.state.nav = {
|
||||
currentFolder: {
|
||||
messages: ['a', 'b']
|
||||
}
|
||||
};
|
||||
scope.watchMessages();
|
||||
scope.watchOnline();
|
||||
clock = sinon.useFakeTimers();
|
||||
});
|
||||
afterEach(function() {
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
it('should show initial message on empty', function() {
|
||||
scope.displaySearchResults();
|
||||
expect(scope.searching).to.be.false;
|
||||
expect(scope.lastUpdateLbl).to.equal('Online');
|
||||
expect(scope.displayMessages.length).to.equal(2);
|
||||
});
|
||||
it('should show initial message on empty', function() {
|
||||
var searchStub = sinon.stub(scope, 'search');
|
||||
searchStub.returns(['a']);
|
||||
|
||||
|
||||
scope.displaySearchResults('query');
|
||||
expect(scope.searching).to.be.true;
|
||||
expect(scope.lastUpdateLbl).to.equal('Searching ...');
|
||||
clock.tick(500);
|
||||
|
||||
expect(scope.displayMessages).to.deep.equal(['a']);
|
||||
expect(scope.searching).to.be.false;
|
||||
expect(scope.lastUpdateLbl).to.equal('Matches in this folder');
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('search', function() {
|
||||
var message1 = {
|
||||
to: [{
|
||||
name: 'name1',
|
||||
address: 'address1'
|
||||
}],
|
||||
subject: 'subject1',
|
||||
body: 'body1',
|
||||
html: 'html1'
|
||||
},
|
||||
message2 = {
|
||||
to: [{
|
||||
name: 'name2',
|
||||
address: 'address2'
|
||||
}],
|
||||
subject: 'subject2',
|
||||
body: 'body2',
|
||||
html: 'html2'
|
||||
},
|
||||
message3 = {
|
||||
to: [{
|
||||
name: 'name3',
|
||||
address: 'address3'
|
||||
}],
|
||||
subject: 'subject3',
|
||||
body: 'body1',
|
||||
html: 'html1',
|
||||
encrypted: true
|
||||
},
|
||||
message4 = {
|
||||
to: [{
|
||||
name: 'name4',
|
||||
address: 'address4'
|
||||
}],
|
||||
subject: 'subject4',
|
||||
body: 'body1',
|
||||
html: 'html1',
|
||||
encrypted: true,
|
||||
decrypted: true
|
||||
},
|
||||
testMessages = [message1, message2, message3, message4];
|
||||
|
||||
it('return same messages array on empty query string', function() {
|
||||
var result = scope.search(testMessages, '');
|
||||
expect(result).to.equal(testMessages);
|
||||
});
|
||||
|
||||
it('return message1 on matching subject', function() {
|
||||
var result = scope.search(testMessages, 'subject1');
|
||||
expect(result.length).to.equal(1);
|
||||
expect(result[0]).to.equal(message1);
|
||||
});
|
||||
|
||||
it('return message1 on matching name', function() {
|
||||
var result = scope.search(testMessages, 'name1');
|
||||
expect(result.length).to.equal(1);
|
||||
expect(result[0]).to.equal(message1);
|
||||
});
|
||||
|
||||
it('return message1 on matching address', function() {
|
||||
var result = scope.search(testMessages, 'address1');
|
||||
expect(result.length).to.equal(1);
|
||||
expect(result[0]).to.equal(message1);
|
||||
});
|
||||
|
||||
it('return plaintext and decrypted messages on matching body', function() {
|
||||
var result = scope.search(testMessages, 'body1');
|
||||
expect(result.length).to.equal(2);
|
||||
expect(result[0]).to.equal(message1);
|
||||
expect(result[1]).to.equal(message4);
|
||||
});
|
||||
|
||||
it('return plaintext and decrypted messages on matching html', function() {
|
||||
var result = scope.search(testMessages, 'html1');
|
||||
expect(result.length).to.equal(2);
|
||||
expect(result[0]).to.equal(message1);
|
||||
expect(result[1]).to.equal(message4);
|
||||
});
|
||||
});
|
||||
|
||||
describe('scope variables', function() {
|
||||
it('should be set correctly', function() {
|
||||
expect(scope.select).to.exist;
|
||||
expect(scope.remove).to.exist;
|
||||
expect(scope.state.mailList).to.exist;
|
||||
});
|
||||
});
|
||||
|
||||
describe('push notification', function() {
|
||||
beforeEach(function() {
|
||||
scope._stopWatchTask();
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
notification.create.restore();
|
||||
});
|
||||
|
||||
it('should succeed for single mail', function(done) {
|
||||
var mail = {
|
||||
uid: 123,
|
||||
from: [{
|
||||
address: 'asd'
|
||||
}],
|
||||
subject: 'this is the subject!',
|
||||
unread: true
|
||||
};
|
||||
|
||||
sinon.stub(notification, 'create', function(opts) {
|
||||
expect(opts.title).to.equal(mail.from[0].address);
|
||||
expect(opts.message).to.equal(mail.subject);
|
||||
|
||||
opts.onClick();
|
||||
expect(scope.state.mailList.selected).to.equal(mail);
|
||||
done();
|
||||
});
|
||||
|
||||
scope.state.nav = {
|
||||
currentFolder: {
|
||||
type: 'asd',
|
||||
messages: [mail]
|
||||
}
|
||||
};
|
||||
|
||||
emailDaoMock.onIncomingMessage([mail]);
|
||||
});
|
||||
|
||||
it('should succeed for multiple mails', function(done) {
|
||||
var mails = [{
|
||||
uid: 1,
|
||||
from: [{
|
||||
address: 'asd'
|
||||
}],
|
||||
subject: 'this is the subject!',
|
||||
unread: true
|
||||
}, {
|
||||
uid: 2,
|
||||
from: [{
|
||||
address: 'qwe'
|
||||
}],
|
||||
subject: 'this is the other subject!',
|
||||
unread: true
|
||||
}, {
|
||||
uid: 3,
|
||||
from: [{
|
||||
address: 'qwe'
|
||||
}],
|
||||
subject: 'this is the other subject!',
|
||||
unread: false
|
||||
}];
|
||||
|
||||
sinon.stub(notification, 'create', function(opts) {
|
||||
expect(opts.title).to.equal('2 new messages');
|
||||
expect(opts.message).to.equal(mails[0].subject + '\n' + mails[1].subject);
|
||||
|
||||
opts.onClick();
|
||||
expect(scope.state.mailList.selected).to.equal(mails[0]);
|
||||
done();
|
||||
});
|
||||
|
||||
scope.state.nav = {
|
||||
currentFolder: {
|
||||
type: 'asd',
|
||||
messages: mails
|
||||
}
|
||||
};
|
||||
|
||||
emailDaoMock.onIncomingMessage(mails);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getBody', function() {
|
||||
it('should get the mail content', function() {
|
||||
scope.state.nav = {
|
||||
currentFolder: {
|
||||
type: 'asd',
|
||||
}
|
||||
};
|
||||
|
||||
scope.getBody();
|
||||
expect(emailDaoMock.getBody.calledOnce).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('select', function() {
|
||||
it('should decrypt, focus mark an unread mail as read', function() {
|
||||
scope.pendingNotifications = ['asd'];
|
||||
sinon.stub(notification, 'close');
|
||||
|
||||
var mail = {
|
||||
from: [{
|
||||
address: 'asd'
|
||||
}],
|
||||
unread: true,
|
||||
};
|
||||
scope.state = {
|
||||
nav: {
|
||||
currentFolder: {
|
||||
type: 'Inbox'
|
||||
}
|
||||
},
|
||||
mailList: {},
|
||||
read: {
|
||||
toggle: function() {}
|
||||
}
|
||||
};
|
||||
|
||||
keychainMock.refreshKeyForUserId.withArgs(mail.from[0].address).yields();
|
||||
|
||||
scope.select(mail);
|
||||
|
||||
expect(emailDaoMock.decryptBody.calledOnce).to.be.true;
|
||||
expect(keychainMock.refreshKeyForUserId.calledOnce).to.be.true;
|
||||
expect(scope.state.mailList.selected).to.equal(mail);
|
||||
expect(notification.close.calledWith('asd')).to.be.true;
|
||||
expect(notification.close.calledOnce).to.be.true;
|
||||
|
||||
notification.close.restore();
|
||||
});
|
||||
|
||||
it('should decrypt and focus a read mail', function() {
|
||||
var mail = {
|
||||
from: [{
|
||||
address: 'asd'
|
||||
}],
|
||||
unread: false
|
||||
};
|
||||
|
||||
scope.state = {
|
||||
mailList: {},
|
||||
read: {
|
||||
toggle: function() {}
|
||||
},
|
||||
nav: {
|
||||
currentFolder: {
|
||||
type: 'asd'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
keychainMock.refreshKeyForUserId.withArgs(mail.from[0].address).yields();
|
||||
|
||||
scope.select(mail);
|
||||
|
||||
expect(emailDaoMock.decryptBody.calledOnce).to.be.true;
|
||||
expect(keychainMock.refreshKeyForUserId.calledOnce).to.be.true;
|
||||
expect(scope.state.mailList.selected).to.equal(mail);
|
||||
});
|
||||
});
|
||||
|
||||
describe('remove', function() {
|
||||
it('should not delete without a selected mail', function() {
|
||||
scope.remove();
|
||||
});
|
||||
|
||||
it('should delete the selected mail', function() {
|
||||
var uid, mail, currentFolder;
|
||||
|
||||
scope._stopWatchTask();
|
||||
|
||||
scope.account = {};
|
||||
uid = 123;
|
||||
mail = {
|
||||
uid: uid,
|
||||
from: [{
|
||||
address: 'asd'
|
||||
}],
|
||||
subject: '[whiteout] asdasd',
|
||||
unread: true
|
||||
};
|
||||
currentFolder = {
|
||||
type: 'Inbox',
|
||||
path: 'INBOX',
|
||||
messages: [mail]
|
||||
};
|
||||
scope.account.folders = [currentFolder];
|
||||
scope.state.nav = {
|
||||
currentFolder: currentFolder
|
||||
};
|
||||
emailDaoMock.deleteMessage.yields();
|
||||
|
||||
scope.remove(mail);
|
||||
|
||||
expect(emailDaoMock.deleteMessage.calledOnce).to.be.true;
|
||||
expect(scope.state.mailList.selected).to.exist;
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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();
|
||||
}
|
||||
);
|
||||
}
|
|
@ -1,101 +1,97 @@
|
|||
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() {
|
||||
var scope, ctrl, origEmailDao, emailDaoMock, outboxBoMock, outboxFolder, onConnectStub;
|
||||
describe('Navigation Controller unit test', function() {
|
||||
var scope, ctrl, origEmailDao, emailDaoMock, outboxBoMock, outboxFolder, onConnectStub;
|
||||
|
||||
beforeEach(function(done) {
|
||||
// remember original module to restore later
|
||||
origEmailDao = appController._emailDao;
|
||||
emailDaoMock = sinon.createStubInstance(EmailDAO);
|
||||
emailDaoMock._account = {
|
||||
folders: [{
|
||||
type: 'Inbox',
|
||||
count: 2,
|
||||
path: 'INBOX'
|
||||
}, {
|
||||
type: 'Outbox',
|
||||
count: 0,
|
||||
path: 'OUTBOX'
|
||||
}]
|
||||
};
|
||||
outboxFolder = emailDaoMock._account.folders[1];
|
||||
appController._emailDao = emailDaoMock;
|
||||
outboxBoMock = sinon.createStubInstance(OutboxBO);
|
||||
appController._outboxBo = outboxBoMock;
|
||||
outboxBoMock.startChecking.returns();
|
||||
onConnectStub = sinon.stub(appController, 'onConnect');
|
||||
onConnectStub.yields();
|
||||
beforeEach(function(done) {
|
||||
// remember original module to restore later
|
||||
origEmailDao = appController._emailDao;
|
||||
emailDaoMock = sinon.createStubInstance(EmailDAO);
|
||||
emailDaoMock._account = {
|
||||
folders: [{
|
||||
type: 'Inbox',
|
||||
count: 2,
|
||||
path: 'INBOX'
|
||||
}, {
|
||||
type: 'Outbox',
|
||||
count: 0,
|
||||
path: 'OUTBOX'
|
||||
}]
|
||||
};
|
||||
outboxFolder = emailDaoMock._account.folders[1];
|
||||
appController._emailDao = emailDaoMock;
|
||||
outboxBoMock = sinon.createStubInstance(OutboxBO);
|
||||
appController._outboxBo = outboxBoMock;
|
||||
outboxBoMock.startChecking.returns();
|
||||
onConnectStub = sinon.stub(appController, 'onConnect');
|
||||
onConnectStub.yields();
|
||||
|
||||
angular.module('navigationtest', []);
|
||||
mocks.module('navigationtest');
|
||||
mocks.inject(function($rootScope, $controller) {
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
ctrl = $controller(NavigationCtrl, {
|
||||
$scope: scope,
|
||||
$routeParams: {}
|
||||
});
|
||||
done();
|
||||
angular.module('navigationtest', []);
|
||||
mocks.module('navigationtest');
|
||||
mocks.inject(function($rootScope, $controller) {
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
ctrl = $controller(NavigationCtrl, {
|
||||
$scope: scope,
|
||||
$routeParams: {}
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// restore the module
|
||||
appController._emailDao = origEmailDao;
|
||||
onConnectStub.restore();
|
||||
afterEach(function() {
|
||||
// restore the module
|
||||
appController._emailDao = origEmailDao;
|
||||
onConnectStub.restore();
|
||||
});
|
||||
|
||||
describe('initial state', function() {
|
||||
it('should be well defined', function() {
|
||||
expect(scope.state).to.exist;
|
||||
expect(scope.state.lightbox).to.be.undefined;
|
||||
expect(scope.account.folders).to.not.be.empty;
|
||||
expect(scope.openFolder).to.exist;
|
||||
});
|
||||
});
|
||||
|
||||
describe('initial state', function() {
|
||||
it('should be well defined', function() {
|
||||
expect(scope.state).to.exist;
|
||||
expect(scope.state.lightbox).to.be.undefined;
|
||||
expect(scope.account.folders).to.not.be.empty;
|
||||
expect(scope.openFolder).to.exist;
|
||||
});
|
||||
describe('open/close nav view', function() {
|
||||
it('should open/close', function() {
|
||||
expect(scope.state.nav.open).to.be.false;
|
||||
scope.state.nav.toggle(true);
|
||||
expect(scope.state.nav.open).to.be.true;
|
||||
scope.state.nav.toggle(false);
|
||||
expect(scope.state.nav.open).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('open/close nav view', function() {
|
||||
it('should open/close', function() {
|
||||
expect(scope.state.nav.open).to.be.false;
|
||||
scope.state.nav.toggle(true);
|
||||
expect(scope.state.nav.open).to.be.true;
|
||||
scope.state.nav.toggle(false);
|
||||
expect(scope.state.nav.open).to.be.false;
|
||||
});
|
||||
describe('open folder', function() {
|
||||
it('should work', function() {
|
||||
scope.state.nav.open = true;
|
||||
|
||||
scope.openFolder('asd');
|
||||
expect(scope.state.nav.currentFolder).to.equal('asd');
|
||||
expect(scope.state.nav.open).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('open folder', function() {
|
||||
it('should work', function() {
|
||||
scope.state.nav.open = true;
|
||||
describe('empty outbox', function() {
|
||||
it('should work', function() {
|
||||
var callback;
|
||||
|
||||
scope.openFolder('asd');
|
||||
expect(scope.state.nav.currentFolder).to.equal('asd');
|
||||
expect(scope.state.nav.open).to.be.false;
|
||||
});
|
||||
});
|
||||
expect(outboxBoMock.startChecking.callCount).to.equal(1);
|
||||
|
||||
describe('empty outbox', function() {
|
||||
it('should work', function() {
|
||||
var callback;
|
||||
outboxBoMock.startChecking.calledWith(sinon.match(function(cb) {
|
||||
callback = cb;
|
||||
}));
|
||||
|
||||
expect(outboxBoMock.startChecking.callCount).to.equal(1);
|
||||
|
||||
outboxBoMock.startChecking.calledWith(sinon.match(function(cb) {
|
||||
callback = cb;
|
||||
}));
|
||||
|
||||
callback(null, 5);
|
||||
expect(outboxFolder.count).to.equal(5);
|
||||
});
|
||||
callback(null, 5);
|
||||
expect(outboxFolder.count).to.equal(5);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,201 +1,198 @@
|
|||
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() {
|
||||
var oauth, googleApiStub, identityStub, getPlatformInfoStub, removeCachedStub,
|
||||
testEmail = 'test@example.com';
|
||||
describe('OAuth unit tests', function() {
|
||||
var oauth, googleApiStub, identityStub, getPlatformInfoStub, removeCachedStub,
|
||||
testEmail = 'test@example.com';
|
||||
|
||||
beforeEach(function() {
|
||||
googleApiStub = sinon.createStubInstance(RestDAO);
|
||||
oauth = new OAuth(googleApiStub);
|
||||
|
||||
window.chrome = window.chrome || {};
|
||||
|
||||
window.chrome.identity = window.chrome.identity || {};
|
||||
if (typeof window.chrome.identity.getAuthToken !== 'function') {
|
||||
window.chrome.identity.getAuthToken = function() {};
|
||||
}
|
||||
identityStub = sinon.stub(window.chrome.identity, 'getAuthToken');
|
||||
|
||||
if (typeof window.chrome.identity.removeCachedAuthToken !== 'function') {
|
||||
window.chrome.identity.removeCachedAuthToken = function() {};
|
||||
}
|
||||
removeCachedStub = sinon.stub(window.chrome.identity, 'removeCachedAuthToken');
|
||||
|
||||
window.chrome.runtime = window.chrome.runtime || {};
|
||||
if (typeof window.chrome.runtime.getPlatformInfo !== 'function') {
|
||||
window.chrome.runtime.getPlatformInfo = function() {};
|
||||
}
|
||||
getPlatformInfoStub = sinon.stub(window.chrome.runtime, 'getPlatformInfo');
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
identityStub.restore();
|
||||
getPlatformInfoStub.restore();
|
||||
removeCachedStub.restore();
|
||||
});
|
||||
|
||||
describe('isSupported', function() {
|
||||
it('should work', function() {
|
||||
expect(oauth.isSupported()).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('refreshToken', function() {
|
||||
var getOAuthTokenStub;
|
||||
|
||||
beforeEach(function() {
|
||||
googleApiStub = sinon.createStubInstance(RestDAO);
|
||||
oauth = new OAuth(googleApiStub);
|
||||
|
||||
window.chrome = window.chrome || {};
|
||||
|
||||
window.chrome.identity = window.chrome.identity || {};
|
||||
if (typeof window.chrome.identity.getAuthToken !== 'function') {
|
||||
window.chrome.identity.getAuthToken = function() {};
|
||||
}
|
||||
identityStub = sinon.stub(window.chrome.identity, 'getAuthToken');
|
||||
|
||||
if (typeof window.chrome.identity.removeCachedAuthToken !== 'function') {
|
||||
window.chrome.identity.removeCachedAuthToken = function() {};
|
||||
}
|
||||
removeCachedStub = sinon.stub(window.chrome.identity, 'removeCachedAuthToken');
|
||||
|
||||
window.chrome.runtime = window.chrome.runtime || {};
|
||||
if (typeof window.chrome.runtime.getPlatformInfo !== 'function') {
|
||||
window.chrome.runtime.getPlatformInfo = function() {};
|
||||
}
|
||||
getPlatformInfoStub = sinon.stub(window.chrome.runtime, 'getPlatformInfo');
|
||||
getOAuthTokenStub = sinon.stub(oauth, 'getOAuthToken');
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
identityStub.restore();
|
||||
getPlatformInfoStub.restore();
|
||||
removeCachedStub.restore();
|
||||
getOAuthTokenStub.restore();
|
||||
});
|
||||
|
||||
describe('isSupported', function() {
|
||||
it('should work', function() {
|
||||
expect(oauth.isSupported()).to.be.true;
|
||||
it('should work', function() {
|
||||
removeCachedStub.withArgs({
|
||||
token: 'oldToken'
|
||||
}).yields();
|
||||
|
||||
getOAuthTokenStub.withArgs(testEmail).yields();
|
||||
|
||||
oauth.refreshToken({
|
||||
oldToken: 'oldToken',
|
||||
emailAddress: testEmail
|
||||
}, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(removeCachedStub.calledOnce).to.be.true;
|
||||
expect(getOAuthTokenStub.calledOnce).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('refreshToken', function() {
|
||||
var getOAuthTokenStub;
|
||||
it('should work without email', function() {
|
||||
removeCachedStub.withArgs({
|
||||
token: 'oldToken'
|
||||
}).yields();
|
||||
|
||||
beforeEach(function() {
|
||||
getOAuthTokenStub = sinon.stub(oauth, 'getOAuthToken');
|
||||
});
|
||||
afterEach(function() {
|
||||
getOAuthTokenStub.restore();
|
||||
});
|
||||
getOAuthTokenStub.withArgs(undefined).yields();
|
||||
|
||||
it('should work', function() {
|
||||
removeCachedStub.withArgs({
|
||||
token: 'oldToken'
|
||||
}).yields();
|
||||
|
||||
getOAuthTokenStub.withArgs(testEmail).yields();
|
||||
|
||||
oauth.refreshToken({
|
||||
oldToken: 'oldToken',
|
||||
emailAddress: testEmail
|
||||
}, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(removeCachedStub.calledOnce).to.be.true;
|
||||
expect(getOAuthTokenStub.calledOnce).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
it('should work without email', function() {
|
||||
removeCachedStub.withArgs({
|
||||
token: 'oldToken'
|
||||
}).yields();
|
||||
|
||||
getOAuthTokenStub.withArgs(undefined).yields();
|
||||
|
||||
oauth.refreshToken({
|
||||
oldToken: 'oldToken',
|
||||
}, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(removeCachedStub.calledOnce).to.be.true;
|
||||
expect(getOAuthTokenStub.calledOnce).to.be.true;
|
||||
expect(getOAuthTokenStub.calledWith(undefined)).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail without all options', function() {
|
||||
oauth.refreshToken({
|
||||
emailAddress: testEmail
|
||||
}, function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(removeCachedStub.called).to.be.false;
|
||||
expect(getOAuthTokenStub.called).to.be.false;
|
||||
});
|
||||
oauth.refreshToken({
|
||||
oldToken: 'oldToken',
|
||||
}, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(removeCachedStub.calledOnce).to.be.true;
|
||||
expect(getOAuthTokenStub.calledOnce).to.be.true;
|
||||
expect(getOAuthTokenStub.calledWith(undefined)).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('getOAuthToken', function() {
|
||||
it('should work for empty emailAddress', function(done) {
|
||||
getPlatformInfoStub.yields({
|
||||
os: 'android'
|
||||
});
|
||||
identityStub.withArgs({
|
||||
interactive: true
|
||||
}).yields('token');
|
||||
|
||||
oauth.getOAuthToken(undefined, function(err, token) {
|
||||
expect(err).to.not.exist;
|
||||
expect(token).to.equal('token');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work on android app', function(done) {
|
||||
getPlatformInfoStub.yields({
|
||||
os: 'android'
|
||||
});
|
||||
identityStub.withArgs({
|
||||
interactive: true,
|
||||
accountHint: testEmail
|
||||
}).yields('token');
|
||||
|
||||
oauth.getOAuthToken(testEmail, function(err, token) {
|
||||
expect(err).to.not.exist;
|
||||
expect(token).to.equal('token');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work on desktop chrome', function(done) {
|
||||
getPlatformInfoStub.yields({
|
||||
os: 'mac'
|
||||
});
|
||||
identityStub.withArgs({
|
||||
interactive: true
|
||||
}).yields('token');
|
||||
|
||||
oauth.getOAuthToken(testEmail, function(err, token) {
|
||||
expect(err).to.not.exist;
|
||||
expect(token).to.equal('token');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail', function(done) {
|
||||
getPlatformInfoStub.yields({
|
||||
os: 'android'
|
||||
});
|
||||
identityStub.yields();
|
||||
|
||||
oauth.getOAuthToken(testEmail, function(err, token) {
|
||||
expect(err).to.exist;
|
||||
expect(token).to.not.exist;
|
||||
done();
|
||||
});
|
||||
it('should fail without all options', function() {
|
||||
oauth.refreshToken({
|
||||
emailAddress: testEmail
|
||||
}, function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(removeCachedStub.called).to.be.false;
|
||||
expect(getOAuthTokenStub.called).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('queryEmailAddress', function() {
|
||||
it('should work', function(done) {
|
||||
googleApiStub.get.withArgs({
|
||||
uri: '/oauth2/v3/userinfo?access_token=token'
|
||||
}).yields(null, {
|
||||
email: 'asdf@example.com'
|
||||
});
|
||||
|
||||
oauth.queryEmailAddress('token', function(err, emailAddress) {
|
||||
expect(err).to.not.exist;
|
||||
expect(emailAddress).to.equal('asdf@example.com');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail due to invalid token', function(done) {
|
||||
oauth.queryEmailAddress('', function(err, emailAddress) {
|
||||
expect(err).to.exist;
|
||||
expect(emailAddress).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail due to error in rest api', function(done) {
|
||||
googleApiStub.get.withArgs({
|
||||
uri: '/oauth2/v3/userinfo?access_token=token'
|
||||
}).yields(new Error());
|
||||
|
||||
oauth.queryEmailAddress('token', function(err, emailAddress) {
|
||||
expect(err).to.exist;
|
||||
expect(emailAddress).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('getOAuthToken', function() {
|
||||
it('should work for empty emailAddress', function(done) {
|
||||
getPlatformInfoStub.yields({
|
||||
os: 'android'
|
||||
});
|
||||
identityStub.withArgs({
|
||||
interactive: true
|
||||
}).yields('token');
|
||||
|
||||
oauth.getOAuthToken(undefined, function(err, token) {
|
||||
expect(err).to.not.exist;
|
||||
expect(token).to.equal('token');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work on android app', function(done) {
|
||||
getPlatformInfoStub.yields({
|
||||
os: 'android'
|
||||
});
|
||||
identityStub.withArgs({
|
||||
interactive: true,
|
||||
accountHint: testEmail
|
||||
}).yields('token');
|
||||
|
||||
oauth.getOAuthToken(testEmail, function(err, token) {
|
||||
expect(err).to.not.exist;
|
||||
expect(token).to.equal('token');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work on desktop chrome', function(done) {
|
||||
getPlatformInfoStub.yields({
|
||||
os: 'mac'
|
||||
});
|
||||
identityStub.withArgs({
|
||||
interactive: true
|
||||
}).yields('token');
|
||||
|
||||
oauth.getOAuthToken(testEmail, function(err, token) {
|
||||
expect(err).to.not.exist;
|
||||
expect(token).to.equal('token');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail', function(done) {
|
||||
getPlatformInfoStub.yields({
|
||||
os: 'android'
|
||||
});
|
||||
identityStub.yields();
|
||||
|
||||
oauth.getOAuthToken(testEmail, function(err, token) {
|
||||
expect(err).to.exist;
|
||||
expect(token).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('queryEmailAddress', function() {
|
||||
it('should work', function(done) {
|
||||
googleApiStub.get.withArgs({
|
||||
uri: '/oauth2/v3/userinfo?access_token=token'
|
||||
}).yields(null, {
|
||||
email: 'asdf@example.com'
|
||||
});
|
||||
|
||||
oauth.queryEmailAddress('token', function(err, emailAddress) {
|
||||
expect(err).to.not.exist;
|
||||
expect(emailAddress).to.equal('asdf@example.com');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail due to invalid token', function(done) {
|
||||
oauth.queryEmailAddress('', function(err, emailAddress) {
|
||||
expect(err).to.exist;
|
||||
expect(emailAddress).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail due to error in rest api', function(done) {
|
||||
googleApiStub.get.withArgs({
|
||||
uri: '/oauth2/v3/userinfo?access_token=token'
|
||||
}).yields(new Error());
|
||||
|
||||
oauth.queryEmailAddress('token', function(err, emailAddress) {
|
||||
expect(err).to.exist;
|
||||
expect(emailAddress).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
|
@ -1,279 +1,276 @@
|
|||
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() {
|
||||
var outbox, emailDaoStub, devicestorageStub, keychainStub,
|
||||
dummyUser = 'spiderpig@springfield.com';
|
||||
describe('Outbox Business Object unit test', function() {
|
||||
var outbox, emailDaoStub, devicestorageStub, keychainStub,
|
||||
dummyUser = 'spiderpig@springfield.com';
|
||||
|
||||
beforeEach(function() {
|
||||
emailDaoStub = sinon.createStubInstance(EmailDAO);
|
||||
emailDaoStub._account = {
|
||||
emailAddress: dummyUser,
|
||||
folders: [{
|
||||
type: 'Outbox'
|
||||
}],
|
||||
online: true
|
||||
beforeEach(function() {
|
||||
emailDaoStub = sinon.createStubInstance(EmailDAO);
|
||||
emailDaoStub._account = {
|
||||
emailAddress: dummyUser,
|
||||
folders: [{
|
||||
type: 'Outbox'
|
||||
}],
|
||||
online: true
|
||||
};
|
||||
devicestorageStub = sinon.createStubInstance(DeviceStorageDAO);
|
||||
keychainStub = sinon.createStubInstance(KeychainDAO);
|
||||
outbox = new OutboxBO(emailDaoStub, keychainStub, devicestorageStub);
|
||||
});
|
||||
|
||||
afterEach(function() {});
|
||||
|
||||
describe('start/stop checking', function() {
|
||||
it('should work', function() {
|
||||
function onOutboxUpdate(err) {
|
||||
expect(err).to.not.exist;
|
||||
}
|
||||
|
||||
outbox.startChecking(onOutboxUpdate);
|
||||
expect(outbox._intervalId).to.exist;
|
||||
|
||||
outbox.stopChecking();
|
||||
expect(outbox._intervalId).to.not.exist;
|
||||
});
|
||||
});
|
||||
|
||||
describe('put', function() {
|
||||
it('should not encrypt and store a mail', function(done) {
|
||||
var mail, senderKey, receiverKey;
|
||||
|
||||
senderKey = {
|
||||
publicKey: 'SENDER PUBLIC KEY'
|
||||
};
|
||||
devicestorageStub = sinon.createStubInstance(DeviceStorageDAO);
|
||||
keychainStub = sinon.createStubInstance(KeychainDAO);
|
||||
outbox = new OutboxBO(emailDaoStub, keychainStub, devicestorageStub);
|
||||
});
|
||||
|
||||
afterEach(function() {});
|
||||
|
||||
describe('start/stop checking', function() {
|
||||
it('should work', function() {
|
||||
function onOutboxUpdate(err) {
|
||||
expect(err).to.not.exist;
|
||||
}
|
||||
|
||||
outbox.startChecking(onOutboxUpdate);
|
||||
expect(outbox._intervalId).to.exist;
|
||||
|
||||
outbox.stopChecking();
|
||||
expect(outbox._intervalId).to.not.exist;
|
||||
});
|
||||
});
|
||||
|
||||
describe('put', function() {
|
||||
it('should not encrypt and store a mail', function(done) {
|
||||
var mail, senderKey, receiverKey;
|
||||
|
||||
senderKey = {
|
||||
publicKey: 'SENDER PUBLIC KEY'
|
||||
};
|
||||
receiverKey = {
|
||||
publicKey: 'RECEIVER PUBLIC KEY'
|
||||
};
|
||||
mail = {
|
||||
from: [{
|
||||
name: 'member',
|
||||
address: 'member@whiteout.io'
|
||||
}],
|
||||
to: [{
|
||||
name: 'member',
|
||||
address: 'member'
|
||||
}, {
|
||||
name: 'notamember',
|
||||
address: 'notamember'
|
||||
}],
|
||||
cc: [],
|
||||
bcc: []
|
||||
};
|
||||
|
||||
keychainStub.getReceiverPublicKey.withArgs(mail.from[0].address).yieldsAsync(null, senderKey);
|
||||
keychainStub.getReceiverPublicKey.withArgs(mail.to[0].address).yieldsAsync(null, receiverKey);
|
||||
keychainStub.getReceiverPublicKey.withArgs(mail.to[1].address).yieldsAsync();
|
||||
|
||||
devicestorageStub.storeList.withArgs([mail]).yieldsAsync();
|
||||
|
||||
outbox.put(mail, function(error) {
|
||||
expect(error).to.not.exist;
|
||||
|
||||
expect(mail.publicKeysArmored.length).to.equal(2);
|
||||
expect(emailDaoStub.encrypt.called).to.be.false;
|
||||
expect(devicestorageStub.storeList.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not encrypt a mail with bcc and store a mail', function(done) {
|
||||
var mail;
|
||||
|
||||
mail = {
|
||||
from: [{
|
||||
name: 'member',
|
||||
address: 'member@whiteout.io'
|
||||
}],
|
||||
to: [{
|
||||
name: 'member',
|
||||
address: 'member@whiteout.io'
|
||||
}],
|
||||
cc: [],
|
||||
bcc: [{
|
||||
name: 'member',
|
||||
address: 'member@whiteout.io'
|
||||
}]
|
||||
};
|
||||
|
||||
devicestorageStub.storeList.withArgs([mail]).yieldsAsync();
|
||||
|
||||
outbox.put(mail, function(error) {
|
||||
expect(error).to.not.exist;
|
||||
|
||||
expect(mail.publicKeysArmored.length).to.equal(0);
|
||||
expect(keychainStub.getReceiverPublicKey.called).to.be.false;
|
||||
expect(emailDaoStub.encrypt.called).to.be.false;
|
||||
expect(devicestorageStub.storeList.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should encrypt and store a mail', function(done) {
|
||||
var mail, senderKey, receiverKey;
|
||||
|
||||
senderKey = {
|
||||
publicKey: 'SENDER PUBLIC KEY'
|
||||
};
|
||||
receiverKey = {
|
||||
publicKey: 'RECEIVER PUBLIC KEY'
|
||||
};
|
||||
mail = {
|
||||
from: [{
|
||||
name: 'member',
|
||||
address: 'member@whiteout.io'
|
||||
}],
|
||||
to: [{
|
||||
name: 'member',
|
||||
address: 'member'
|
||||
}, {
|
||||
name: 'notamember',
|
||||
address: 'notamember'
|
||||
}],
|
||||
cc: [],
|
||||
bcc: []
|
||||
};
|
||||
|
||||
keychainStub.getReceiverPublicKey.withArgs(mail.from[0].address).yieldsAsync(null, senderKey);
|
||||
keychainStub.getReceiverPublicKey.withArgs(mail.to[0].address).yieldsAsync(null, receiverKey);
|
||||
keychainStub.getReceiverPublicKey.withArgs(mail.to[1].address).yieldsAsync(null, receiverKey);
|
||||
|
||||
emailDaoStub.encrypt.withArgs({
|
||||
mail: mail,
|
||||
publicKeysArmored: [senderKey.publicKey, receiverKey.publicKey, receiverKey.publicKey]
|
||||
}).yieldsAsync();
|
||||
|
||||
devicestorageStub.storeList.withArgs([mail]).yieldsAsync();
|
||||
|
||||
outbox.put(mail, function(error) {
|
||||
expect(error).to.not.exist;
|
||||
|
||||
expect(mail.publicKeysArmored.length).to.equal(3);
|
||||
expect(emailDaoStub.encrypt.calledOnce).to.be.true;
|
||||
expect(devicestorageStub.storeList.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('process outbox', function() {
|
||||
it('should send to registered users and update pending mails', function(done) {
|
||||
var from, member, invited, notinvited, newlyjoined, dummyMails, newlyjoinedKey;
|
||||
|
||||
from = [{
|
||||
receiverKey = {
|
||||
publicKey: 'RECEIVER PUBLIC KEY'
|
||||
};
|
||||
mail = {
|
||||
from: [{
|
||||
name: 'member',
|
||||
address: 'member@whiteout.io'
|
||||
}];
|
||||
member = {
|
||||
id: '12',
|
||||
from: from,
|
||||
to: [{
|
||||
name: 'member',
|
||||
address: 'member'
|
||||
}],
|
||||
encrypted: true,
|
||||
publicKeysArmored: ['ARMORED KEY OF MEMBER'],
|
||||
unregisteredUsers: []
|
||||
};
|
||||
invited = {
|
||||
id: '34',
|
||||
from: from,
|
||||
to: [{
|
||||
name: 'invited',
|
||||
address: 'invited'
|
||||
}],
|
||||
publicKeysArmored: [],
|
||||
unregisteredUsers: [{
|
||||
name: 'invited',
|
||||
address: 'invited'
|
||||
}]
|
||||
};
|
||||
notinvited = {
|
||||
id: '56',
|
||||
from: from,
|
||||
to: [{
|
||||
name: 'notinvited',
|
||||
address: 'notinvited'
|
||||
}],
|
||||
publicKeysArmored: [],
|
||||
unregisteredUsers: [{
|
||||
name: 'notinvited',
|
||||
address: 'notinvited'
|
||||
}]
|
||||
};
|
||||
newlyjoined = {
|
||||
id: '78',
|
||||
from: from,
|
||||
to: [{
|
||||
name: 'newlyjoined',
|
||||
address: 'newlyjoined'
|
||||
}],
|
||||
encrypted: true,
|
||||
publicKeysArmored: [],
|
||||
unregisteredUsers: [{
|
||||
name: 'newlyjoined',
|
||||
address: 'newlyjoined'
|
||||
}]
|
||||
};
|
||||
newlyjoinedKey = {
|
||||
publicKey: 'THIS IS THE NEWLY JOINED PUBLIC KEY!'
|
||||
};
|
||||
}],
|
||||
to: [{
|
||||
name: 'member',
|
||||
address: 'member'
|
||||
}, {
|
||||
name: 'notamember',
|
||||
address: 'notamember'
|
||||
}],
|
||||
cc: [],
|
||||
bcc: []
|
||||
};
|
||||
|
||||
dummyMails = [member, invited, notinvited, newlyjoined];
|
||||
keychainStub.getReceiverPublicKey.withArgs(mail.from[0].address).yieldsAsync(null, senderKey);
|
||||
keychainStub.getReceiverPublicKey.withArgs(mail.to[0].address).yieldsAsync(null, receiverKey);
|
||||
keychainStub.getReceiverPublicKey.withArgs(mail.to[1].address).yieldsAsync();
|
||||
|
||||
devicestorageStub.listItems.yieldsAsync(null, dummyMails);
|
||||
devicestorageStub.storeList.withArgs([mail]).yieldsAsync();
|
||||
|
||||
emailDaoStub.sendPlaintext.yieldsAsync();
|
||||
outbox.put(mail, function(error) {
|
||||
expect(error).to.not.exist;
|
||||
|
||||
emailDaoStub.sendEncrypted.withArgs({
|
||||
email: newlyjoined
|
||||
}).yieldsAsync();
|
||||
expect(mail.publicKeysArmored.length).to.equal(2);
|
||||
expect(emailDaoStub.encrypt.called).to.be.false;
|
||||
expect(devicestorageStub.storeList.calledOnce).to.be.true;
|
||||
|
||||
emailDaoStub.sendEncrypted.withArgs({
|
||||
email: member
|
||||
}).yieldsAsync();
|
||||
|
||||
devicestorageStub.removeList.yieldsAsync();
|
||||
|
||||
function onOutboxUpdate(err, count) {
|
||||
expect(err).to.not.exist;
|
||||
expect(count).to.equal(0);
|
||||
|
||||
expect(outbox._outboxBusy).to.be.false;
|
||||
expect(emailDaoStub.sendEncrypted.callCount).to.equal(2);
|
||||
expect(emailDaoStub.sendPlaintext.callCount).to.equal(2);
|
||||
expect(devicestorageStub.listItems.callCount).to.equal(1);
|
||||
expect(devicestorageStub.removeList.callCount).to.equal(4);
|
||||
expect(keychainStub.getReceiverPublicKey.callCount).to.equal(0);
|
||||
|
||||
done();
|
||||
}
|
||||
|
||||
outbox._processOutbox(onOutboxUpdate);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not process outbox in offline mode', function(done) {
|
||||
emailDaoStub._account.online = false;
|
||||
devicestorageStub.listItems.yieldsAsync(null, [{}]);
|
||||
it('should not encrypt a mail with bcc and store a mail', function(done) {
|
||||
var mail;
|
||||
|
||||
outbox._processOutbox(function(err, count) {
|
||||
expect(err).to.not.exist;
|
||||
expect(count).to.equal(1);
|
||||
expect(devicestorageStub.listItems.callCount).to.equal(1);
|
||||
expect(outbox._outboxBusy).to.be.false;
|
||||
done();
|
||||
});
|
||||
mail = {
|
||||
from: [{
|
||||
name: 'member',
|
||||
address: 'member@whiteout.io'
|
||||
}],
|
||||
to: [{
|
||||
name: 'member',
|
||||
address: 'member@whiteout.io'
|
||||
}],
|
||||
cc: [],
|
||||
bcc: [{
|
||||
name: 'member',
|
||||
address: 'member@whiteout.io'
|
||||
}]
|
||||
};
|
||||
|
||||
devicestorageStub.storeList.withArgs([mail]).yieldsAsync();
|
||||
|
||||
outbox.put(mail, function(error) {
|
||||
expect(error).to.not.exist;
|
||||
|
||||
expect(mail.publicKeysArmored.length).to.equal(0);
|
||||
expect(keychainStub.getReceiverPublicKey.called).to.be.false;
|
||||
expect(emailDaoStub.encrypt.called).to.be.false;
|
||||
expect(devicestorageStub.storeList.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should encrypt and store a mail', function(done) {
|
||||
var mail, senderKey, receiverKey;
|
||||
|
||||
senderKey = {
|
||||
publicKey: 'SENDER PUBLIC KEY'
|
||||
};
|
||||
receiverKey = {
|
||||
publicKey: 'RECEIVER PUBLIC KEY'
|
||||
};
|
||||
mail = {
|
||||
from: [{
|
||||
name: 'member',
|
||||
address: 'member@whiteout.io'
|
||||
}],
|
||||
to: [{
|
||||
name: 'member',
|
||||
address: 'member'
|
||||
}, {
|
||||
name: 'notamember',
|
||||
address: 'notamember'
|
||||
}],
|
||||
cc: [],
|
||||
bcc: []
|
||||
};
|
||||
|
||||
keychainStub.getReceiverPublicKey.withArgs(mail.from[0].address).yieldsAsync(null, senderKey);
|
||||
keychainStub.getReceiverPublicKey.withArgs(mail.to[0].address).yieldsAsync(null, receiverKey);
|
||||
keychainStub.getReceiverPublicKey.withArgs(mail.to[1].address).yieldsAsync(null, receiverKey);
|
||||
|
||||
emailDaoStub.encrypt.withArgs({
|
||||
mail: mail,
|
||||
publicKeysArmored: [senderKey.publicKey, receiverKey.publicKey, receiverKey.publicKey]
|
||||
}).yieldsAsync();
|
||||
|
||||
devicestorageStub.storeList.withArgs([mail]).yieldsAsync();
|
||||
|
||||
outbox.put(mail, function(error) {
|
||||
expect(error).to.not.exist;
|
||||
|
||||
expect(mail.publicKeysArmored.length).to.equal(3);
|
||||
expect(emailDaoStub.encrypt.calledOnce).to.be.true;
|
||||
expect(devicestorageStub.storeList.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('process outbox', function() {
|
||||
it('should send to registered users and update pending mails', function(done) {
|
||||
var from, member, invited, notinvited, newlyjoined, dummyMails, newlyjoinedKey;
|
||||
|
||||
from = [{
|
||||
name: 'member',
|
||||
address: 'member@whiteout.io'
|
||||
}];
|
||||
member = {
|
||||
id: '12',
|
||||
from: from,
|
||||
to: [{
|
||||
name: 'member',
|
||||
address: 'member'
|
||||
}],
|
||||
encrypted: true,
|
||||
publicKeysArmored: ['ARMORED KEY OF MEMBER'],
|
||||
unregisteredUsers: []
|
||||
};
|
||||
invited = {
|
||||
id: '34',
|
||||
from: from,
|
||||
to: [{
|
||||
name: 'invited',
|
||||
address: 'invited'
|
||||
}],
|
||||
publicKeysArmored: [],
|
||||
unregisteredUsers: [{
|
||||
name: 'invited',
|
||||
address: 'invited'
|
||||
}]
|
||||
};
|
||||
notinvited = {
|
||||
id: '56',
|
||||
from: from,
|
||||
to: [{
|
||||
name: 'notinvited',
|
||||
address: 'notinvited'
|
||||
}],
|
||||
publicKeysArmored: [],
|
||||
unregisteredUsers: [{
|
||||
name: 'notinvited',
|
||||
address: 'notinvited'
|
||||
}]
|
||||
};
|
||||
newlyjoined = {
|
||||
id: '78',
|
||||
from: from,
|
||||
to: [{
|
||||
name: 'newlyjoined',
|
||||
address: 'newlyjoined'
|
||||
}],
|
||||
encrypted: true,
|
||||
publicKeysArmored: [],
|
||||
unregisteredUsers: [{
|
||||
name: 'newlyjoined',
|
||||
address: 'newlyjoined'
|
||||
}]
|
||||
};
|
||||
newlyjoinedKey = {
|
||||
publicKey: 'THIS IS THE NEWLY JOINED PUBLIC KEY!'
|
||||
};
|
||||
|
||||
dummyMails = [member, invited, notinvited, newlyjoined];
|
||||
|
||||
devicestorageStub.listItems.yieldsAsync(null, dummyMails);
|
||||
|
||||
emailDaoStub.sendPlaintext.yieldsAsync();
|
||||
|
||||
emailDaoStub.sendEncrypted.withArgs({
|
||||
email: newlyjoined
|
||||
}).yieldsAsync();
|
||||
|
||||
emailDaoStub.sendEncrypted.withArgs({
|
||||
email: member
|
||||
}).yieldsAsync();
|
||||
|
||||
devicestorageStub.removeList.yieldsAsync();
|
||||
|
||||
function onOutboxUpdate(err, count) {
|
||||
expect(err).to.not.exist;
|
||||
expect(count).to.equal(0);
|
||||
|
||||
expect(outbox._outboxBusy).to.be.false;
|
||||
expect(emailDaoStub.sendEncrypted.callCount).to.equal(2);
|
||||
expect(emailDaoStub.sendPlaintext.callCount).to.equal(2);
|
||||
expect(devicestorageStub.listItems.callCount).to.equal(1);
|
||||
expect(devicestorageStub.removeList.callCount).to.equal(4);
|
||||
expect(keychainStub.getReceiverPublicKey.callCount).to.equal(0);
|
||||
|
||||
done();
|
||||
}
|
||||
|
||||
outbox._processOutbox(onOutboxUpdate);
|
||||
});
|
||||
|
||||
it('should not process outbox in offline mode', function(done) {
|
||||
emailDaoStub._account.online = false;
|
||||
devicestorageStub.listItems.yieldsAsync(null, [{}]);
|
||||
|
||||
outbox._processOutbox(function(err, count) {
|
||||
expect(err).to.not.exist;
|
||||
expect(count).to.equal(1);
|
||||
expect(devicestorageStub.listItems.callCount).to.equal(1);
|
||||
expect(outbox._outboxBusy).to.be.false;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,464 +1,460 @@
|
|||
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() {
|
||||
this.timeout(20000);
|
||||
describe('PGP Crypto Api unit tests', function() {
|
||||
this.timeout(20000);
|
||||
|
||||
var pgp,
|
||||
user = 'whiteout.test@t-online.de',
|
||||
passphrase = 'asdf',
|
||||
keySize = 512,
|
||||
keyId = 'F6F60E9B42CDFF4C',
|
||||
pubkey = '-----BEGIN PGP PUBLIC KEY BLOCK-----\r\n' +
|
||||
'Version: OpenPGP.js v0.7.2\r\n' +
|
||||
'Comment: Whiteout Mail - https://whiteout.io\r\n' +
|
||||
'\r\n' +
|
||||
'xk0EUlhMvAEB/2MZtCUOAYvyLFjDp3OBMGn3Ev8FwjzyPbIF0JUw+L7y2XR5\r\n' +
|
||||
'RVGvbK88unV3cU/1tOYdNsXI6pSp/Ztjyv7vbBUAEQEAAc0pV2hpdGVvdXQg\r\n' +
|
||||
'VXNlciA8d2hpdGVvdXQudGVzdEB0LW9ubGluZS5kZT7CXAQQAQgAEAUCUlhM\r\n' +
|
||||
'vQkQ9vYOm0LN/0wAAAW4Af9C+kYW1AvNWmivdtr0M0iYCUjM9DNOQH1fcvXq\r\n' +
|
||||
'IiN602mWrkd8jcEzLsW5IUNzVPLhrFIuKyBDTpLnC07Loce1\r\n' +
|
||||
'=6XMW\r\n' +
|
||||
'-----END PGP PUBLIC KEY BLOCK-----\r\n\r\n',
|
||||
privkey = '-----BEGIN PGP PRIVATE KEY BLOCK-----\r\n' +
|
||||
'Version: OpenPGP.js v0.7.2\r\n' +
|
||||
'Comment: Whiteout Mail - https://whiteout.io\r\n' +
|
||||
'\r\n' +
|
||||
'xcBeBFJYTLwBAf9jGbQlDgGL8ixYw6dzgTBp9xL/BcI88j2yBdCVMPi+8tl0\r\n' +
|
||||
'eUVRr2yvPLp1d3FP9bTmHTbFyOqUqf2bY8r+72wVABEBAAH+AwMIhNB4ivtv\r\n' +
|
||||
'Y2xg6VeMcjjHxZayESHACV+nQx5Tx6ev6xzIF1Qh72fNPDppLhFSFOuTTMsU\r\n' +
|
||||
'kTN4c+BVYt29spH+cA1jcDAxQ2ULrNAXo+hheOqhpedTs8aCbcLFkJAS16hk\r\n' +
|
||||
'YSk4OnJgp/z24rVju1SHRSFbgundPzmNgXeX9e8IkviGhhQ11Wc5YwVkx03t\r\n' +
|
||||
'Z3MdDMF0jyhopbPIoBdyJB0dhvBh98w3JmwpYh9wjUA9MBHD1tvHpRmSZ3BM\r\n' +
|
||||
'UCmATn2ZLWBRWiYqFbgDnL1GM80pV2hpdGVvdXQgVXNlciA8d2hpdGVvdXQu\r\n' +
|
||||
'dGVzdEB0LW9ubGluZS5kZT7CXAQQAQgAEAUCUlhMvQkQ9vYOm0LN/0wAAAW4\r\n' +
|
||||
'Af9C+kYW1AvNWmivdtr0M0iYCUjM9DNOQH1fcvXqIiN602mWrkd8jcEzLsW5\r\n' +
|
||||
'IUNzVPLhrFIuKyBDTpLnC07Loce1\r\n' +
|
||||
'=ULta\r\n' +
|
||||
'-----END PGP PRIVATE KEY BLOCK-----\r\n';
|
||||
var pgp,
|
||||
user = 'whiteout.test@t-online.de',
|
||||
passphrase = 'asdf',
|
||||
keySize = 512,
|
||||
keyId = 'F6F60E9B42CDFF4C',
|
||||
pubkey = '-----BEGIN PGP PUBLIC KEY BLOCK-----\r\n' +
|
||||
'Version: OpenPGP.js v0.7.2\r\n' +
|
||||
'Comment: Whiteout Mail - https://whiteout.io\r\n' +
|
||||
'\r\n' +
|
||||
'xk0EUlhMvAEB/2MZtCUOAYvyLFjDp3OBMGn3Ev8FwjzyPbIF0JUw+L7y2XR5\r\n' +
|
||||
'RVGvbK88unV3cU/1tOYdNsXI6pSp/Ztjyv7vbBUAEQEAAc0pV2hpdGVvdXQg\r\n' +
|
||||
'VXNlciA8d2hpdGVvdXQudGVzdEB0LW9ubGluZS5kZT7CXAQQAQgAEAUCUlhM\r\n' +
|
||||
'vQkQ9vYOm0LN/0wAAAW4Af9C+kYW1AvNWmivdtr0M0iYCUjM9DNOQH1fcvXq\r\n' +
|
||||
'IiN602mWrkd8jcEzLsW5IUNzVPLhrFIuKyBDTpLnC07Loce1\r\n' +
|
||||
'=6XMW\r\n' +
|
||||
'-----END PGP PUBLIC KEY BLOCK-----\r\n\r\n',
|
||||
privkey = '-----BEGIN PGP PRIVATE KEY BLOCK-----\r\n' +
|
||||
'Version: OpenPGP.js v0.7.2\r\n' +
|
||||
'Comment: Whiteout Mail - https://whiteout.io\r\n' +
|
||||
'\r\n' +
|
||||
'xcBeBFJYTLwBAf9jGbQlDgGL8ixYw6dzgTBp9xL/BcI88j2yBdCVMPi+8tl0\r\n' +
|
||||
'eUVRr2yvPLp1d3FP9bTmHTbFyOqUqf2bY8r+72wVABEBAAH+AwMIhNB4ivtv\r\n' +
|
||||
'Y2xg6VeMcjjHxZayESHACV+nQx5Tx6ev6xzIF1Qh72fNPDppLhFSFOuTTMsU\r\n' +
|
||||
'kTN4c+BVYt29spH+cA1jcDAxQ2ULrNAXo+hheOqhpedTs8aCbcLFkJAS16hk\r\n' +
|
||||
'YSk4OnJgp/z24rVju1SHRSFbgundPzmNgXeX9e8IkviGhhQ11Wc5YwVkx03t\r\n' +
|
||||
'Z3MdDMF0jyhopbPIoBdyJB0dhvBh98w3JmwpYh9wjUA9MBHD1tvHpRmSZ3BM\r\n' +
|
||||
'UCmATn2ZLWBRWiYqFbgDnL1GM80pV2hpdGVvdXQgVXNlciA8d2hpdGVvdXQu\r\n' +
|
||||
'dGVzdEB0LW9ubGluZS5kZT7CXAQQAQgAEAUCUlhMvQkQ9vYOm0LN/0wAAAW4\r\n' +
|
||||
'Af9C+kYW1AvNWmivdtr0M0iYCUjM9DNOQH1fcvXqIiN602mWrkd8jcEzLsW5\r\n' +
|
||||
'IUNzVPLhrFIuKyBDTpLnC07Loce1\r\n' +
|
||||
'=ULta\r\n' +
|
||||
'-----END PGP PRIVATE KEY BLOCK-----\r\n';
|
||||
|
||||
beforeEach(function() {
|
||||
pgp = new PGP();
|
||||
beforeEach(function() {
|
||||
pgp = new PGP();
|
||||
});
|
||||
|
||||
afterEach(function() {});
|
||||
|
||||
describe('Generate key pair', function() {
|
||||
it('should fail', function(done) {
|
||||
pgp.generateKeys({
|
||||
emailAddress: 'whiteout.test@t-onlinede',
|
||||
keySize: keySize,
|
||||
passphrase: passphrase
|
||||
}, function(err, keys) {
|
||||
expect(err).to.exist;
|
||||
expect(keys).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {});
|
||||
|
||||
describe('Generate key pair', function() {
|
||||
it('should fail', function(done) {
|
||||
pgp.generateKeys({
|
||||
emailAddress: 'whiteout.test@t-onlinede',
|
||||
keySize: keySize,
|
||||
passphrase: passphrase
|
||||
}, function(err, keys) {
|
||||
expect(err).to.exist;
|
||||
expect(keys).to.not.exist;
|
||||
done();
|
||||
});
|
||||
it('should fail', function(done) {
|
||||
pgp.generateKeys({
|
||||
emailAddress: 'whiteout.testt-online.de',
|
||||
keySize: keySize,
|
||||
passphrase: passphrase
|
||||
}, function(err, keys) {
|
||||
expect(err).to.exist;
|
||||
expect(keys).to.not.exist;
|
||||
done();
|
||||
});
|
||||
it('should fail', function(done) {
|
||||
pgp.generateKeys({
|
||||
emailAddress: 'whiteout.testt-online.de',
|
||||
keySize: keySize,
|
||||
passphrase: passphrase
|
||||
}, function(err, keys) {
|
||||
expect(err).to.exist;
|
||||
expect(keys).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should work with passphrase', function(done) {
|
||||
pgp.generateKeys({
|
||||
emailAddress: user,
|
||||
keySize: keySize,
|
||||
passphrase: passphrase
|
||||
}, function(err, keys) {
|
||||
});
|
||||
it('should work with passphrase', function(done) {
|
||||
pgp.generateKeys({
|
||||
emailAddress: user,
|
||||
keySize: keySize,
|
||||
passphrase: passphrase
|
||||
}, function(err, keys) {
|
||||
expect(err).to.not.exist;
|
||||
expect(keys.keyId).to.exist;
|
||||
expect(keys.privateKeyArmored).to.exist;
|
||||
expect(keys.publicKeyArmored).to.exist;
|
||||
|
||||
// test encrypt/decrypt
|
||||
pgp.importKeys({
|
||||
passphrase: passphrase,
|
||||
privateKeyArmored: keys.privateKeyArmored,
|
||||
publicKeyArmored: keys.publicKeyArmored
|
||||
}, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(keys.keyId).to.exist;
|
||||
expect(keys.privateKeyArmored).to.exist;
|
||||
expect(keys.publicKeyArmored).to.exist;
|
||||
|
||||
// test encrypt/decrypt
|
||||
pgp.importKeys({
|
||||
passphrase: passphrase,
|
||||
privateKeyArmored: keys.privateKeyArmored,
|
||||
publicKeyArmored: keys.publicKeyArmored
|
||||
}, function(err) {
|
||||
pgp.encrypt('secret', [keys.publicKeyArmored], function(err, ct) {
|
||||
expect(err).to.not.exist;
|
||||
expect(ct).to.exist;
|
||||
|
||||
pgp.encrypt('secret', [keys.publicKeyArmored], function(err, ct) {
|
||||
pgp.decrypt(ct, keys.publicKeyArmored, function(err, pt, signValid) {
|
||||
expect(err).to.not.exist;
|
||||
expect(ct).to.exist;
|
||||
|
||||
pgp.decrypt(ct, keys.publicKeyArmored, function(err, pt, signValid) {
|
||||
expect(err).to.not.exist;
|
||||
expect(pt).to.equal('secret');
|
||||
expect(signValid).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
it('should work without passphrase', function(done) {
|
||||
pgp.generateKeys({
|
||||
emailAddress: user,
|
||||
keySize: keySize,
|
||||
passphrase: ''
|
||||
}, function(err, keys) {
|
||||
expect(err).to.not.exist;
|
||||
expect(keys.keyId).to.exist;
|
||||
expect(keys.privateKeyArmored).to.exist;
|
||||
expect(keys.publicKeyArmored).to.exist;
|
||||
|
||||
// test encrypt/decrypt
|
||||
pgp.importKeys({
|
||||
passphrase: undefined,
|
||||
privateKeyArmored: keys.privateKeyArmored,
|
||||
publicKeyArmored: keys.publicKeyArmored
|
||||
}, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
|
||||
pgp.encrypt('secret', [keys.publicKeyArmored], function(err, ct) {
|
||||
expect(err).to.not.exist;
|
||||
expect(ct).to.exist;
|
||||
|
||||
pgp.decrypt(ct, keys.publicKeyArmored, function(err, pt, signValid) {
|
||||
expect(err).to.not.exist;
|
||||
expect(pt).to.equal('secret');
|
||||
expect(signValid).to.be.true;
|
||||
done();
|
||||
});
|
||||
expect(pt).to.equal('secret');
|
||||
expect(signValid).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
it('should work without passphrase', function(done) {
|
||||
pgp.generateKeys({
|
||||
emailAddress: user,
|
||||
keySize: keySize,
|
||||
passphrase: ''
|
||||
}, function(err, keys) {
|
||||
expect(err).to.not.exist;
|
||||
expect(keys.keyId).to.exist;
|
||||
expect(keys.privateKeyArmored).to.exist;
|
||||
expect(keys.publicKeyArmored).to.exist;
|
||||
|
||||
describe('Import/Export key pair', function() {
|
||||
it('should fail', function(done) {
|
||||
// test encrypt/decrypt
|
||||
pgp.importKeys({
|
||||
passphrase: 'asd',
|
||||
privateKeyArmored: privkey,
|
||||
publicKeyArmored: pubkey
|
||||
}, function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(err.message).to.equal('Incorrect passphrase!');
|
||||
|
||||
pgp.exportKeys(function(err, keys) {
|
||||
expect(err).to.exist;
|
||||
expect(keys).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
it('should work', function(done) {
|
||||
pgp.importKeys({
|
||||
passphrase: passphrase,
|
||||
privateKeyArmored: privkey,
|
||||
publicKeyArmored: pubkey
|
||||
passphrase: undefined,
|
||||
privateKeyArmored: keys.privateKeyArmored,
|
||||
publicKeyArmored: keys.publicKeyArmored
|
||||
}, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
|
||||
pgp.exportKeys(function(err, keys) {
|
||||
expect(err).to.not.exist;
|
||||
expect(keys.keyId).to.equal(keyId);
|
||||
expect(keys.privateKeyArmored.replace(/\r/g, '')).to.equal(privkey.replace(/\r/g, ''));
|
||||
expect(keys.publicKeyArmored.replace(/\r/g, '')).to.equal(pubkey.replace(/\r/g, ''));
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Change passphrase of private key', function() {
|
||||
it('should work with new passphrase', function(done) {
|
||||
pgp.changePassphrase({
|
||||
privateKeyArmored: privkey,
|
||||
oldPassphrase: passphrase,
|
||||
newPassphrase: 'yxcv'
|
||||
}, function(err, reEncryptedKey) {
|
||||
expect(err).to.not.exist;
|
||||
expect(reEncryptedKey).to.exist;
|
||||
|
||||
pgp.importKeys({
|
||||
passphrase: 'yxcv',
|
||||
privateKeyArmored: reEncryptedKey,
|
||||
publicKeyArmored: pubkey
|
||||
}, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
it('should work with empty passphrase', function(done) {
|
||||
pgp.changePassphrase({
|
||||
privateKeyArmored: privkey,
|
||||
oldPassphrase: passphrase,
|
||||
newPassphrase: undefined
|
||||
}, function(err, reEncryptedKey) {
|
||||
expect(err).to.not.exist;
|
||||
expect(reEncryptedKey).to.exist;
|
||||
|
||||
pgp.importKeys({
|
||||
passphrase: undefined,
|
||||
privateKeyArmored: reEncryptedKey,
|
||||
publicKeyArmored: pubkey
|
||||
}, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
it('should fail when passphrases are equal', function(done) {
|
||||
pgp.changePassphrase({
|
||||
privateKeyArmored: privkey,
|
||||
oldPassphrase: passphrase,
|
||||
newPassphrase: passphrase
|
||||
}, function(err, reEncryptedKey) {
|
||||
expect(err).to.exist;
|
||||
expect(reEncryptedKey).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should fail when old passphrase is incorrect', function(done) {
|
||||
pgp.changePassphrase({
|
||||
privateKeyArmored: privkey,
|
||||
oldPassphrase: 'asd',
|
||||
newPassphrase: 'yxcv'
|
||||
}, function(err, reEncryptedKey) {
|
||||
expect(err).to.exist;
|
||||
expect(reEncryptedKey).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Encrypt/Sign/Decrypt/Verify', function() {
|
||||
var message = 'asdfs\n\nThursday, Nov 21, 2013 7:38 PM asdf@example.com wrote:\n' +
|
||||
'> asdf\n' +
|
||||
'> \n' +
|
||||
'> Thursday, Nov 21, 2013 7:32 PM asdf@example.com wrote:\n' +
|
||||
'> > secret 3';
|
||||
var wrongPubkey = '-----BEGIN PGP PUBLIC KEY BLOCK-----\r\nVersion: OpenPGP.js v.1.20131116\r\nComment: Whiteout Mail - http://whiteout.io\r\n\r\nxsBNBFKODs4BB/9iOF4THsjQMY+WEpT7ShgKxj4bHzRRaQkqczS4nZvP0U3g\r\nqeqCnbpagyeKXA+bhWFQW4GmXtgAoeD5PXs6AZYrw3tWNxLKu2Oe6Tp9K/XI\r\nxTMQ2wl4qZKDXHvuPsJ7cmgaWqpPyXtxA4zHHS3WrkI/6VzHAcI/y6x4szSB\r\nKgSuhI3hjh3s7TybUC1U6AfoQGx/S7e3WwlCOrK8GTClirN/2mCPRC5wuIft\r\nnkoMfA6jK8d2OPrJ63shy5cgwHOjQg/xuk46dNS7tkvGmbaa+X0PgqSKB+Hf\r\nYPPNS/ylg911DH9qa8BqYU2QpNh9jUKXSF+HbaOM+plWkCSAL7czV+R3ABEB\r\nAAHNLVdoaXRlb3V0IFVzZXIgPHNhZmV3aXRobWUudGVzdHVzZXJAZ21haWwu\r\nY29tPsLAXAQQAQgAEAUCUo4O2gkQ1/uT/N+/wjwAAN2cB/9gFRmAfvEQ2qz+\r\nWubmT2EsSSnjPMxzG4uyykFoa+TaZCWo2Xa2tQghmU103kEkQb1OEjRjpgwJ\r\nYX9Kghnl8DByM686L5AXnRyHP78qRJCLXSXl0AGicboUDp5sovaa4rswQceH\r\nvcdWgZ/mgHTRoiQeJddy9k+H6MPFiyFaVcFwegVsmpc+dCcC8yT+qh8ZIbyG\r\nRJU60PmKKN7LUusP+8DbSv39zCGJCBlVVKyA4MzdF5uM+sqTdXbKzOrT5DGd\r\nCZaox4s+w16Sq1rHzZKFWfQPfKLDB9pyA0ufCVRA3AF6BUi7G3ZqhZiHNhMP\r\nNvE45V/hS1PbZcfPVoUjE2qc1Ix1\r\n=7Wpe\r\n-----END PGP PUBLIC KEY BLOCK-----';
|
||||
|
||||
beforeEach(function(done) {
|
||||
pgp.importKeys({
|
||||
passphrase: passphrase,
|
||||
privateKeyArmored: privkey,
|
||||
publicKeyArmored: pubkey
|
||||
}, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Get KeyId', function() {
|
||||
it('should work without param', function() {
|
||||
var keyId = pgp.getKeyId();
|
||||
expect(keyId).to.equal('F6F60E9B42CDFF4C');
|
||||
});
|
||||
it('should work with param', function() {
|
||||
var keyId = pgp.getKeyId(pubkey);
|
||||
expect(keyId).to.equal('F6F60E9B42CDFF4C');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Get Fingerprint', function() {
|
||||
it('should work without param', function() {
|
||||
var fingerprint = pgp.getFingerprint();
|
||||
expect(fingerprint).to.equal('5856CEF789C3A307E8A1B976F6F60E9B42CDFF4C');
|
||||
});
|
||||
it('should work with param', function() {
|
||||
var fingerprint = pgp.getFingerprint(pubkey);
|
||||
expect(fingerprint).to.equal('5856CEF789C3A307E8A1B976F6F60E9B42CDFF4C');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getKeyParams', function() {
|
||||
it('should work with param', function() {
|
||||
var params = pgp.getKeyParams(pubkey);
|
||||
expect(params.fingerprint).to.equal('5856CEF789C3A307E8A1B976F6F60E9B42CDFF4C');
|
||||
expect(params._id).to.equal("F6F60E9B42CDFF4C");
|
||||
expect(params.bitSize).to.equal(keySize);
|
||||
expect(params.userId).to.equal("whiteout.test@t-online.de");
|
||||
expect(params.userIds[0].name).to.equal("Whiteout User");
|
||||
expect(params.userIds[0].emailAddress).to.equal("whiteout.test@t-online.de");
|
||||
expect(params.algorithm).to.equal("rsa_encrypt_sign");
|
||||
});
|
||||
|
||||
it('should work without param', function() {
|
||||
var params = pgp.getKeyParams();
|
||||
expect(params.fingerprint).to.equal('5856CEF789C3A307E8A1B976F6F60E9B42CDFF4C');
|
||||
expect(params._id).to.equal("F6F60E9B42CDFF4C");
|
||||
expect(params.bitSize).to.equal(keySize);
|
||||
expect(params.userId).to.equal("whiteout.test@t-online.de");
|
||||
expect(params.userIds[0].name).to.equal("Whiteout User");
|
||||
expect(params.userIds[0].emailAddress).to.equal("whiteout.test@t-online.de");
|
||||
expect(params.algorithm).to.equal("rsa_encrypt_sign");
|
||||
});
|
||||
});
|
||||
|
||||
describe('extractPublicKey', function() {
|
||||
it('should work', function() {
|
||||
var pk = pgp.extractPublicKey(privkey);
|
||||
expect(pk).to.exist;
|
||||
expect(pk).to.contain('-----BEGIN PGP PUBLIC KEY BLOCK-----');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Encrypt and sign', function() {
|
||||
it('should fail', function(done) {
|
||||
var input = null;
|
||||
|
||||
pgp.encrypt(input, [pubkey], function(err, ct) {
|
||||
expect(err).to.exist;
|
||||
expect(ct).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should work', function(done) {
|
||||
pgp.encrypt(message, [pubkey], function(err, ct) {
|
||||
pgp.encrypt('secret', [keys.publicKeyArmored], function(err, ct) {
|
||||
expect(err).to.not.exist;
|
||||
expect(ct).to.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should encrypt to myself if public keys are empty', function(done) {
|
||||
pgp.encrypt(message, undefined, function(err, ct) {
|
||||
expect(err).to.not.exist;
|
||||
expect(ct).to.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Decrypt and verify', function() {
|
||||
var ciphertext;
|
||||
|
||||
beforeEach(function(done) {
|
||||
pgp.encrypt(message, [pubkey], function(err, ct) {
|
||||
expect(err).to.not.exist;
|
||||
expect(ct).to.exist;
|
||||
ciphertext = ct;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail', function(done) {
|
||||
var input = 'asdfa\rsdf';
|
||||
|
||||
pgp.decrypt(input, pubkey, function(err, pt) {
|
||||
expect(err).to.exist;
|
||||
expect(pt).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should work', function(done) {
|
||||
pgp.decrypt(ciphertext, pubkey, function(err, pt, signValid) {
|
||||
expect(err).to.not.exist;
|
||||
expect(pt).to.equal(message);
|
||||
expect(signValid).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should work without signature', function(done) {
|
||||
var ct = openpgp.encryptMessage([pgp._publicKey], message);
|
||||
|
||||
pgp.decrypt(ct, undefined, function(err, pt, signValid) {
|
||||
expect(err).to.not.exist;
|
||||
expect(pt).to.equal(message);
|
||||
expect(signValid).to.be.undefined;
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should fail to verify if public keys are empty', function(done) {
|
||||
// setup another public key so that signature verification fails
|
||||
pgp._publicKey = openpgp.key.readArmored(wrongPubkey).keys[0];
|
||||
pgp.decrypt(ciphertext, undefined, function(err, pt, signValid) {
|
||||
expect(err).to.not.exist;
|
||||
expect(pt).to.equal(message);
|
||||
expect(signValid).to.be.null;
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should decrypt but signValid should be null for wrong public key', function(done) {
|
||||
pgp.decrypt(ciphertext, wrongPubkey, function(err, pt, signValid) {
|
||||
expect(err).to.not.exist;
|
||||
expect(pt).to.equal(message);
|
||||
expect(signValid).to.be.null;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Verify clearsigned message', function() {
|
||||
var clearsigned;
|
||||
|
||||
beforeEach(function() {
|
||||
clearsigned = openpgp.signClearMessage(pgp._privateKey, 'this is a clearsigned message');
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
pgp.verifyClearSignedMessage(clearsigned, pubkey, function(err, signaturesValid) {
|
||||
expect(err).to.not.exist;
|
||||
expect(signaturesValid).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail', function(done) {
|
||||
pgp.verifyClearSignedMessage(clearsigned.replace('clearsigned', 'invalid'), pubkey, function(err, signaturesValid) {
|
||||
expect(err).to.not.exist;
|
||||
expect(signaturesValid).to.be.false;
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should be null for wrong public key', function(done) {
|
||||
pgp.verifyClearSignedMessage(clearsigned, wrongPubkey, function(err, signaturesValid) {
|
||||
expect(err).to.not.exist;
|
||||
expect(signaturesValid).to.be.null;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Verify detached signature', function() {
|
||||
var signedMessage, signature;
|
||||
|
||||
beforeEach(function() {
|
||||
signedMessage = 'this is a signed message';
|
||||
var clearsigned = openpgp.signClearMessage(pgp._privateKey, signedMessage);
|
||||
var signatureHeader = '-----BEGIN PGP SIGNATURE-----';
|
||||
signature = signatureHeader + clearsigned.split(signatureHeader).pop();
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
pgp.verifySignedMessage(signedMessage, signature, pubkey, function(err, signaturesValid) {
|
||||
expect(err).to.not.exist;
|
||||
expect(signaturesValid).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail', function(done) {
|
||||
pgp.verifySignedMessage(signedMessage.replace('signed', 'invalid'), signature, pubkey, function(err, signaturesValid) {
|
||||
expect(err).to.not.exist;
|
||||
expect(signaturesValid).to.be.false;
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should be null for wrong public key', function(done) {
|
||||
pgp.verifySignedMessage(signedMessage, signature, wrongPubkey, function(err, signaturesValid) {
|
||||
expect(err).to.not.exist;
|
||||
expect(signaturesValid).to.be.null;
|
||||
done();
|
||||
pgp.decrypt(ct, keys.publicKeyArmored, function(err, pt, signValid) {
|
||||
expect(err).to.not.exist;
|
||||
expect(pt).to.equal('secret');
|
||||
expect(signValid).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Import/Export key pair', function() {
|
||||
it('should fail', function(done) {
|
||||
pgp.importKeys({
|
||||
passphrase: 'asd',
|
||||
privateKeyArmored: privkey,
|
||||
publicKeyArmored: pubkey
|
||||
}, function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(err.message).to.equal('Incorrect passphrase!');
|
||||
|
||||
pgp.exportKeys(function(err, keys) {
|
||||
expect(err).to.exist;
|
||||
expect(keys).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
it('should work', function(done) {
|
||||
pgp.importKeys({
|
||||
passphrase: passphrase,
|
||||
privateKeyArmored: privkey,
|
||||
publicKeyArmored: pubkey
|
||||
}, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
|
||||
pgp.exportKeys(function(err, keys) {
|
||||
expect(err).to.not.exist;
|
||||
expect(keys.keyId).to.equal(keyId);
|
||||
expect(keys.privateKeyArmored.replace(/\r/g, '')).to.equal(privkey.replace(/\r/g, ''));
|
||||
expect(keys.publicKeyArmored.replace(/\r/g, '')).to.equal(pubkey.replace(/\r/g, ''));
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Change passphrase of private key', function() {
|
||||
it('should work with new passphrase', function(done) {
|
||||
pgp.changePassphrase({
|
||||
privateKeyArmored: privkey,
|
||||
oldPassphrase: passphrase,
|
||||
newPassphrase: 'yxcv'
|
||||
}, function(err, reEncryptedKey) {
|
||||
expect(err).to.not.exist;
|
||||
expect(reEncryptedKey).to.exist;
|
||||
|
||||
pgp.importKeys({
|
||||
passphrase: 'yxcv',
|
||||
privateKeyArmored: reEncryptedKey,
|
||||
publicKeyArmored: pubkey
|
||||
}, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
it('should work with empty passphrase', function(done) {
|
||||
pgp.changePassphrase({
|
||||
privateKeyArmored: privkey,
|
||||
oldPassphrase: passphrase,
|
||||
newPassphrase: undefined
|
||||
}, function(err, reEncryptedKey) {
|
||||
expect(err).to.not.exist;
|
||||
expect(reEncryptedKey).to.exist;
|
||||
|
||||
pgp.importKeys({
|
||||
passphrase: undefined,
|
||||
privateKeyArmored: reEncryptedKey,
|
||||
publicKeyArmored: pubkey
|
||||
}, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
it('should fail when passphrases are equal', function(done) {
|
||||
pgp.changePassphrase({
|
||||
privateKeyArmored: privkey,
|
||||
oldPassphrase: passphrase,
|
||||
newPassphrase: passphrase
|
||||
}, function(err, reEncryptedKey) {
|
||||
expect(err).to.exist;
|
||||
expect(reEncryptedKey).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should fail when old passphrase is incorrect', function(done) {
|
||||
pgp.changePassphrase({
|
||||
privateKeyArmored: privkey,
|
||||
oldPassphrase: 'asd',
|
||||
newPassphrase: 'yxcv'
|
||||
}, function(err, reEncryptedKey) {
|
||||
expect(err).to.exist;
|
||||
expect(reEncryptedKey).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Encrypt/Sign/Decrypt/Verify', function() {
|
||||
var message = 'asdfs\n\nThursday, Nov 21, 2013 7:38 PM asdf@example.com wrote:\n' +
|
||||
'> asdf\n' +
|
||||
'> \n' +
|
||||
'> Thursday, Nov 21, 2013 7:32 PM asdf@example.com wrote:\n' +
|
||||
'> > secret 3';
|
||||
var wrongPubkey = '-----BEGIN PGP PUBLIC KEY BLOCK-----\r\nVersion: OpenPGP.js v.1.20131116\r\nComment: Whiteout Mail - http://whiteout.io\r\n\r\nxsBNBFKODs4BB/9iOF4THsjQMY+WEpT7ShgKxj4bHzRRaQkqczS4nZvP0U3g\r\nqeqCnbpagyeKXA+bhWFQW4GmXtgAoeD5PXs6AZYrw3tWNxLKu2Oe6Tp9K/XI\r\nxTMQ2wl4qZKDXHvuPsJ7cmgaWqpPyXtxA4zHHS3WrkI/6VzHAcI/y6x4szSB\r\nKgSuhI3hjh3s7TybUC1U6AfoQGx/S7e3WwlCOrK8GTClirN/2mCPRC5wuIft\r\nnkoMfA6jK8d2OPrJ63shy5cgwHOjQg/xuk46dNS7tkvGmbaa+X0PgqSKB+Hf\r\nYPPNS/ylg911DH9qa8BqYU2QpNh9jUKXSF+HbaOM+plWkCSAL7czV+R3ABEB\r\nAAHNLVdoaXRlb3V0IFVzZXIgPHNhZmV3aXRobWUudGVzdHVzZXJAZ21haWwu\r\nY29tPsLAXAQQAQgAEAUCUo4O2gkQ1/uT/N+/wjwAAN2cB/9gFRmAfvEQ2qz+\r\nWubmT2EsSSnjPMxzG4uyykFoa+TaZCWo2Xa2tQghmU103kEkQb1OEjRjpgwJ\r\nYX9Kghnl8DByM686L5AXnRyHP78qRJCLXSXl0AGicboUDp5sovaa4rswQceH\r\nvcdWgZ/mgHTRoiQeJddy9k+H6MPFiyFaVcFwegVsmpc+dCcC8yT+qh8ZIbyG\r\nRJU60PmKKN7LUusP+8DbSv39zCGJCBlVVKyA4MzdF5uM+sqTdXbKzOrT5DGd\r\nCZaox4s+w16Sq1rHzZKFWfQPfKLDB9pyA0ufCVRA3AF6BUi7G3ZqhZiHNhMP\r\nNvE45V/hS1PbZcfPVoUjE2qc1Ix1\r\n=7Wpe\r\n-----END PGP PUBLIC KEY BLOCK-----';
|
||||
|
||||
beforeEach(function(done) {
|
||||
pgp.importKeys({
|
||||
passphrase: passphrase,
|
||||
privateKeyArmored: privkey,
|
||||
publicKeyArmored: pubkey
|
||||
}, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Get KeyId', function() {
|
||||
it('should work without param', function() {
|
||||
var keyId = pgp.getKeyId();
|
||||
expect(keyId).to.equal('F6F60E9B42CDFF4C');
|
||||
});
|
||||
it('should work with param', function() {
|
||||
var keyId = pgp.getKeyId(pubkey);
|
||||
expect(keyId).to.equal('F6F60E9B42CDFF4C');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Get Fingerprint', function() {
|
||||
it('should work without param', function() {
|
||||
var fingerprint = pgp.getFingerprint();
|
||||
expect(fingerprint).to.equal('5856CEF789C3A307E8A1B976F6F60E9B42CDFF4C');
|
||||
});
|
||||
it('should work with param', function() {
|
||||
var fingerprint = pgp.getFingerprint(pubkey);
|
||||
expect(fingerprint).to.equal('5856CEF789C3A307E8A1B976F6F60E9B42CDFF4C');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getKeyParams', function() {
|
||||
it('should work with param', function() {
|
||||
var params = pgp.getKeyParams(pubkey);
|
||||
expect(params.fingerprint).to.equal('5856CEF789C3A307E8A1B976F6F60E9B42CDFF4C');
|
||||
expect(params._id).to.equal("F6F60E9B42CDFF4C");
|
||||
expect(params.bitSize).to.equal(keySize);
|
||||
expect(params.userId).to.equal("whiteout.test@t-online.de");
|
||||
expect(params.userIds[0].name).to.equal("Whiteout User");
|
||||
expect(params.userIds[0].emailAddress).to.equal("whiteout.test@t-online.de");
|
||||
expect(params.algorithm).to.equal("rsa_encrypt_sign");
|
||||
});
|
||||
|
||||
it('should work without param', function() {
|
||||
var params = pgp.getKeyParams();
|
||||
expect(params.fingerprint).to.equal('5856CEF789C3A307E8A1B976F6F60E9B42CDFF4C');
|
||||
expect(params._id).to.equal("F6F60E9B42CDFF4C");
|
||||
expect(params.bitSize).to.equal(keySize);
|
||||
expect(params.userId).to.equal("whiteout.test@t-online.de");
|
||||
expect(params.userIds[0].name).to.equal("Whiteout User");
|
||||
expect(params.userIds[0].emailAddress).to.equal("whiteout.test@t-online.de");
|
||||
expect(params.algorithm).to.equal("rsa_encrypt_sign");
|
||||
});
|
||||
});
|
||||
|
||||
describe('extractPublicKey', function() {
|
||||
it('should work', function() {
|
||||
var pk = pgp.extractPublicKey(privkey);
|
||||
expect(pk).to.exist;
|
||||
expect(pk).to.contain('-----BEGIN PGP PUBLIC KEY BLOCK-----');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Encrypt and sign', function() {
|
||||
it('should fail', function(done) {
|
||||
var input = null;
|
||||
|
||||
pgp.encrypt(input, [pubkey], function(err, ct) {
|
||||
expect(err).to.exist;
|
||||
expect(ct).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should work', function(done) {
|
||||
pgp.encrypt(message, [pubkey], function(err, ct) {
|
||||
expect(err).to.not.exist;
|
||||
expect(ct).to.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should encrypt to myself if public keys are empty', function(done) {
|
||||
pgp.encrypt(message, undefined, function(err, ct) {
|
||||
expect(err).to.not.exist;
|
||||
expect(ct).to.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Decrypt and verify', function() {
|
||||
var ciphertext;
|
||||
|
||||
beforeEach(function(done) {
|
||||
pgp.encrypt(message, [pubkey], function(err, ct) {
|
||||
expect(err).to.not.exist;
|
||||
expect(ct).to.exist;
|
||||
ciphertext = ct;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail', function(done) {
|
||||
var input = 'asdfa\rsdf';
|
||||
|
||||
pgp.decrypt(input, pubkey, function(err, pt) {
|
||||
expect(err).to.exist;
|
||||
expect(pt).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should work', function(done) {
|
||||
pgp.decrypt(ciphertext, pubkey, function(err, pt, signValid) {
|
||||
expect(err).to.not.exist;
|
||||
expect(pt).to.equal(message);
|
||||
expect(signValid).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should work without signature', function(done) {
|
||||
var ct = openpgp.encryptMessage([pgp._publicKey], message);
|
||||
|
||||
pgp.decrypt(ct, undefined, function(err, pt, signValid) {
|
||||
expect(err).to.not.exist;
|
||||
expect(pt).to.equal(message);
|
||||
expect(signValid).to.be.undefined;
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should fail to verify if public keys are empty', function(done) {
|
||||
// setup another public key so that signature verification fails
|
||||
pgp._publicKey = openpgp.key.readArmored(wrongPubkey).keys[0];
|
||||
pgp.decrypt(ciphertext, undefined, function(err, pt, signValid) {
|
||||
expect(err).to.not.exist;
|
||||
expect(pt).to.equal(message);
|
||||
expect(signValid).to.be.null;
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should decrypt but signValid should be null for wrong public key', function(done) {
|
||||
pgp.decrypt(ciphertext, wrongPubkey, function(err, pt, signValid) {
|
||||
expect(err).to.not.exist;
|
||||
expect(pt).to.equal(message);
|
||||
expect(signValid).to.be.null;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Verify clearsigned message', function() {
|
||||
var clearsigned;
|
||||
|
||||
beforeEach(function() {
|
||||
clearsigned = openpgp.signClearMessage(pgp._privateKey, 'this is a clearsigned message');
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
pgp.verifyClearSignedMessage(clearsigned, pubkey, function(err, signaturesValid) {
|
||||
expect(err).to.not.exist;
|
||||
expect(signaturesValid).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail', function(done) {
|
||||
pgp.verifyClearSignedMessage(clearsigned.replace('clearsigned', 'invalid'), pubkey, function(err, signaturesValid) {
|
||||
expect(err).to.not.exist;
|
||||
expect(signaturesValid).to.be.false;
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should be null for wrong public key', function(done) {
|
||||
pgp.verifyClearSignedMessage(clearsigned, wrongPubkey, function(err, signaturesValid) {
|
||||
expect(err).to.not.exist;
|
||||
expect(signaturesValid).to.be.null;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Verify detached signature', function() {
|
||||
var signedMessage, signature;
|
||||
|
||||
beforeEach(function() {
|
||||
signedMessage = 'this is a signed message';
|
||||
var clearsigned = openpgp.signClearMessage(pgp._privateKey, signedMessage);
|
||||
var signatureHeader = '-----BEGIN PGP SIGNATURE-----';
|
||||
signature = signatureHeader + clearsigned.split(signatureHeader).pop();
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
pgp.verifySignedMessage(signedMessage, signature, pubkey, function(err, signaturesValid) {
|
||||
expect(err).to.not.exist;
|
||||
expect(signaturesValid).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail', function(done) {
|
||||
pgp.verifySignedMessage(signedMessage.replace('signed', 'invalid'), signature, pubkey, function(err, signaturesValid) {
|
||||
expect(err).to.not.exist;
|
||||
expect(signaturesValid).to.be.false;
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should be null for wrong public key', function(done) {
|
||||
pgp.verifySignedMessage(signedMessage, signature, wrongPubkey, function(err, signaturesValid) {
|
||||
expect(err).to.not.exist;
|
||||
expect(signaturesValid).to.be.null;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,226 +1,222 @@
|
|||
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',
|
||||
deviceName = 'iPhone Work';
|
||||
var privkeyDao, restDaoStub,
|
||||
emailAddress = 'test@example.com',
|
||||
deviceName = 'iPhone Work';
|
||||
|
||||
beforeEach(function() {
|
||||
restDaoStub = sinon.createStubInstance(RestDAO);
|
||||
privkeyDao = new PrivateKeyDAO(restDaoStub);
|
||||
});
|
||||
beforeEach(function() {
|
||||
restDaoStub = sinon.createStubInstance(RestDAO);
|
||||
privkeyDao = new PrivateKeyDAO(restDaoStub);
|
||||
});
|
||||
|
||||
afterEach(function() {});
|
||||
afterEach(function() {});
|
||||
|
||||
describe('requestDeviceRegistration', function() {
|
||||
it('should fail due to invalid args', function(done) {
|
||||
privkeyDao.requestDeviceRegistration({}, function(err, sessionKey) {
|
||||
expect(err).to.exist;
|
||||
expect(sessionKey).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
restDaoStub.post.yields(null, {
|
||||
encryptedRegSessionKey: 'asdf'
|
||||
});
|
||||
|
||||
privkeyDao.requestDeviceRegistration({
|
||||
userId: emailAddress,
|
||||
deviceName: deviceName
|
||||
}, function(err, sessionKey) {
|
||||
expect(err).to.not.exist;
|
||||
expect(sessionKey).to.exist;
|
||||
done();
|
||||
});
|
||||
describe('requestDeviceRegistration', function() {
|
||||
it('should fail due to invalid args', function(done) {
|
||||
privkeyDao.requestDeviceRegistration({}, function(err, sessionKey) {
|
||||
expect(err).to.exist;
|
||||
expect(sessionKey).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('uploadDeviceSecret', function() {
|
||||
it('should fail due to invalid args', function(done) {
|
||||
privkeyDao.uploadDeviceSecret({}, function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
it('should work', function(done) {
|
||||
restDaoStub.post.yields(null, {
|
||||
encryptedRegSessionKey: 'asdf'
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
restDaoStub.put.yields();
|
||||
privkeyDao.requestDeviceRegistration({
|
||||
userId: emailAddress,
|
||||
deviceName: deviceName
|
||||
}, function(err, sessionKey) {
|
||||
expect(err).to.not.exist;
|
||||
expect(sessionKey).to.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
privkeyDao.uploadDeviceSecret({
|
||||
userId: emailAddress,
|
||||
deviceName: deviceName,
|
||||
encryptedDeviceSecret: 'asdf',
|
||||
iv: 'iv'
|
||||
}, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
done();
|
||||
});
|
||||
describe('uploadDeviceSecret', function() {
|
||||
it('should fail due to invalid args', function(done) {
|
||||
privkeyDao.uploadDeviceSecret({}, function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('requestAuthSessionKey', function() {
|
||||
it('should fail due to invalid args', function(done) {
|
||||
privkeyDao.requestAuthSessionKey({}, function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
it('should work', function(done) {
|
||||
restDaoStub.put.yields();
|
||||
|
||||
privkeyDao.uploadDeviceSecret({
|
||||
userId: emailAddress,
|
||||
deviceName: deviceName,
|
||||
encryptedDeviceSecret: 'asdf',
|
||||
iv: 'iv'
|
||||
}, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
restDaoStub.post.withArgs(undefined, '/auth/user/' + emailAddress).yields();
|
||||
|
||||
privkeyDao.requestAuthSessionKey({
|
||||
userId: emailAddress
|
||||
}, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
done();
|
||||
});
|
||||
describe('requestAuthSessionKey', function() {
|
||||
it('should fail due to invalid args', function(done) {
|
||||
privkeyDao.requestAuthSessionKey({}, function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('verifyAuthentication', function() {
|
||||
it('should fail due to invalid args', function(done) {
|
||||
privkeyDao.verifyAuthentication({}, function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
it('should work', function(done) {
|
||||
restDaoStub.post.withArgs(undefined, '/auth/user/' + emailAddress).yields();
|
||||
|
||||
privkeyDao.requestAuthSessionKey({
|
||||
userId: emailAddress
|
||||
}, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
var sessionId = '1';
|
||||
|
||||
var options = {
|
||||
userId: emailAddress,
|
||||
sessionId: sessionId,
|
||||
encryptedChallenge: 'asdf',
|
||||
encryptedDeviceSecret: 'qwer',
|
||||
iv: ' iv'
|
||||
};
|
||||
|
||||
restDaoStub.put.withArgs(options, '/auth/user/' + emailAddress + '/session/' + sessionId).yields();
|
||||
|
||||
privkeyDao.verifyAuthentication(options, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
done();
|
||||
});
|
||||
describe('verifyAuthentication', function() {
|
||||
it('should fail due to invalid args', function(done) {
|
||||
privkeyDao.verifyAuthentication({}, function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('upload', function() {
|
||||
it('should fail due to invalid args', function(done) {
|
||||
privkeyDao.upload({}, function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
it('should work', function(done) {
|
||||
var sessionId = '1';
|
||||
|
||||
var options = {
|
||||
userId: emailAddress,
|
||||
sessionId: sessionId,
|
||||
encryptedChallenge: 'asdf',
|
||||
encryptedDeviceSecret: 'qwer',
|
||||
iv: ' iv'
|
||||
};
|
||||
|
||||
restDaoStub.put.withArgs(options, '/auth/user/' + emailAddress + '/session/' + sessionId).yields();
|
||||
|
||||
privkeyDao.verifyAuthentication(options, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
var options = {
|
||||
_id: '12345',
|
||||
userId: emailAddress,
|
||||
encryptedPrivateKey: 'asdf',
|
||||
sessionId: '1',
|
||||
salt: 'salt',
|
||||
iv: 'iv'
|
||||
};
|
||||
|
||||
restDaoStub.post.withArgs(options, '/privatekey/user/' + emailAddress + '/session/' + options.sessionId).yields();
|
||||
|
||||
privkeyDao.upload(options, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
done();
|
||||
});
|
||||
describe('upload', function() {
|
||||
it('should fail due to invalid args', function(done) {
|
||||
privkeyDao.upload({}, function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('requestDownload', function() {
|
||||
it('should fail due to invalid args', function(done) {
|
||||
privkeyDao.requestDownload({}, function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
it('should work', function(done) {
|
||||
var options = {
|
||||
_id: '12345',
|
||||
userId: emailAddress,
|
||||
encryptedPrivateKey: 'asdf',
|
||||
sessionId: '1',
|
||||
salt: 'salt',
|
||||
iv: 'iv'
|
||||
};
|
||||
|
||||
restDaoStub.post.withArgs(options, '/privatekey/user/' + emailAddress + '/session/' + options.sessionId).yields();
|
||||
|
||||
privkeyDao.upload(options, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
var keyId = '12345';
|
||||
|
||||
restDaoStub.get.withArgs({
|
||||
uri: '/privatekey/user/' + emailAddress + '/key/' + keyId
|
||||
}).yields();
|
||||
|
||||
privkeyDao.requestDownload({
|
||||
userId: emailAddress,
|
||||
keyId: keyId
|
||||
}, function(err, found) {
|
||||
expect(err).to.not.exist;
|
||||
expect(found).to.be.true;
|
||||
done();
|
||||
});
|
||||
describe('requestDownload', function() {
|
||||
it('should fail due to invalid args', function(done) {
|
||||
privkeyDao.requestDownload({}, function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('hasPrivateKey', function() {
|
||||
it('should fail due to invalid args', function(done) {
|
||||
privkeyDao.hasPrivateKey({}, function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
it('should work', function(done) {
|
||||
var keyId = '12345';
|
||||
|
||||
restDaoStub.get.withArgs({
|
||||
uri: '/privatekey/user/' + emailAddress + '/key/' + keyId
|
||||
}).yields();
|
||||
|
||||
privkeyDao.requestDownload({
|
||||
userId: emailAddress,
|
||||
keyId: keyId
|
||||
}, function(err, found) {
|
||||
expect(err).to.not.exist;
|
||||
expect(found).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
var keyId = '12345';
|
||||
|
||||
restDaoStub.get.withArgs({
|
||||
uri: '/privatekey/user/' + emailAddress + '/key/' + keyId + '?ignoreRecovery=true'
|
||||
}).yields();
|
||||
|
||||
privkeyDao.hasPrivateKey({
|
||||
userId: emailAddress,
|
||||
keyId: keyId
|
||||
}, function(err, found) {
|
||||
expect(err).to.not.exist;
|
||||
expect(found).to.be.true;
|
||||
done();
|
||||
});
|
||||
describe('hasPrivateKey', function() {
|
||||
it('should fail due to invalid args', function(done) {
|
||||
privkeyDao.hasPrivateKey({}, function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('download', function() {
|
||||
it('should fail due to invalid args', function(done) {
|
||||
privkeyDao.download({}, function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
it('should work', function(done) {
|
||||
var keyId = '12345';
|
||||
|
||||
restDaoStub.get.withArgs({
|
||||
uri: '/privatekey/user/' + emailAddress + '/key/' + keyId + '?ignoreRecovery=true'
|
||||
}).yields();
|
||||
|
||||
privkeyDao.hasPrivateKey({
|
||||
userId: emailAddress,
|
||||
keyId: keyId
|
||||
}, function(err, found) {
|
||||
expect(err).to.not.exist;
|
||||
expect(found).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
var key = {
|
||||
_id: '12345'
|
||||
};
|
||||
|
||||
restDaoStub.get.withArgs({
|
||||
uri: '/privatekey/user/' + emailAddress + '/key/' + key._id + '/recovery/token'
|
||||
}).yields();
|
||||
|
||||
privkeyDao.download({
|
||||
userId: emailAddress,
|
||||
keyId: key._id,
|
||||
recoveryToken: 'token'
|
||||
}, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
done();
|
||||
});
|
||||
describe('download', function() {
|
||||
it('should fail due to invalid args', function(done) {
|
||||
privkeyDao.download({}, function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
var key = {
|
||||
_id: '12345'
|
||||
};
|
||||
|
||||
restDaoStub.get.withArgs({
|
||||
uri: '/privatekey/user/' + emailAddress + '/key/' + key._id + '/recovery/token'
|
||||
}).yields();
|
||||
|
||||
privkeyDao.download({
|
||||
userId: emailAddress,
|
||||
keyId: key._id,
|
||||
recoveryToken: 'token'
|
||||
}, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
|
@ -1,286 +1,281 @@
|
|||
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() {
|
||||
var scope, location, ctrl,
|
||||
origEmailDao, emailDaoMock,
|
||||
origKeychain, keychainMock,
|
||||
pgpStub,
|
||||
emailAddress = 'fred@foo.com';
|
||||
describe('Private Key Upload Controller unit test', function() {
|
||||
var scope, location, ctrl,
|
||||
origEmailDao, emailDaoMock,
|
||||
origKeychain, keychainMock,
|
||||
pgpStub,
|
||||
emailAddress = 'fred@foo.com';
|
||||
|
||||
beforeEach(function(done) {
|
||||
// remember original module to restore later, then replace it
|
||||
origEmailDao = appController._emailDao;
|
||||
appController._emailDao = emailDaoMock = {
|
||||
_account: {
|
||||
emailAddress: emailAddress
|
||||
}
|
||||
beforeEach(function(done) {
|
||||
// remember original module to restore later, then replace it
|
||||
origEmailDao = appController._emailDao;
|
||||
appController._emailDao = emailDaoMock = {
|
||||
_account: {
|
||||
emailAddress: emailAddress
|
||||
}
|
||||
};
|
||||
origKeychain = appController._keychain;
|
||||
appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
keychainMock._pgp = pgpStub = sinon.createStubInstance(PGP);
|
||||
|
||||
angular.module('login-privatekey-download-test', []);
|
||||
mocks.module('login-privatekey-download-test');
|
||||
mocks.inject(function($controller, $rootScope) {
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
ctrl = $controller(PrivateKeyUploadCtrl, {
|
||||
$location: location,
|
||||
$scope: scope
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// restore the app controller module
|
||||
appController._keychain = origKeychain;
|
||||
appController._emailDao = origEmailDao;
|
||||
});
|
||||
|
||||
describe('checkServerForKey', function() {
|
||||
var keyParams = {
|
||||
userId: emailAddress,
|
||||
_id: 'keyId',
|
||||
};
|
||||
|
||||
it('should fail', function(done) {
|
||||
pgpStub.getKeyParams.returns(keyParams);
|
||||
keychainMock.hasPrivateKey.yields(42);
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(keychainMock.hasPrivateKey.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
origKeychain = appController._keychain;
|
||||
appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
keychainMock._pgp = pgpStub = sinon.createStubInstance(PGP);
|
||||
|
||||
angular.module('login-privatekey-download-test', []);
|
||||
mocks.module('login-privatekey-download-test');
|
||||
mocks.inject(function($controller, $rootScope) {
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
ctrl = $controller(PrivateKeyUploadCtrl, {
|
||||
$location: location,
|
||||
$scope: scope
|
||||
});
|
||||
scope.checkServerForKey();
|
||||
});
|
||||
|
||||
it('should return true', function(done) {
|
||||
pgpStub.getKeyParams.returns(keyParams);
|
||||
keychainMock.hasPrivateKey.withArgs({
|
||||
userId: keyParams.userId,
|
||||
keyId: keyParams._id
|
||||
}).yields(null, true);
|
||||
|
||||
scope.checkServerForKey(function(privateKeySynced) {
|
||||
expect(privateKeySynced).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// restore the app controller module
|
||||
appController._keychain = origKeychain;
|
||||
appController._emailDao = origEmailDao;
|
||||
it('should return undefined', function(done) {
|
||||
pgpStub.getKeyParams.returns(keyParams);
|
||||
keychainMock.hasPrivateKey.withArgs({
|
||||
userId: keyParams.userId,
|
||||
keyId: keyParams._id
|
||||
}).yields(null, false);
|
||||
|
||||
scope.checkServerForKey(function(privateKeySynced) {
|
||||
expect(privateKeySynced).to.be.undefined;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('handlePaste', function() {
|
||||
it('should work', function() {
|
||||
scope.handlePaste({
|
||||
clipboardData: {
|
||||
getData: function(val) {
|
||||
expect(val).to.equal('text/plain');
|
||||
return '1qaz-2wsx-3edc-4rfv-5tgb-6yhn';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
expect(scope.code0).to.equal('1qaz');
|
||||
expect(scope.code1).to.equal('2wsx');
|
||||
expect(scope.code2).to.equal('3edc');
|
||||
expect(scope.code3).to.equal('4rfv');
|
||||
expect(scope.code4).to.equal('5tgb');
|
||||
expect(scope.code5).to.equal('6yhn');
|
||||
});
|
||||
});
|
||||
|
||||
describe('displayUploadUi', function() {
|
||||
it('should work', function() {
|
||||
// add some artifacts from a previous key input
|
||||
scope.code0 = scope.code1 = scope.code2 = scope.code3 = scope.code4 = scope.code5 = 'asdasd';
|
||||
|
||||
scope.displayUploadUi();
|
||||
expect(scope.step).to.equal(1);
|
||||
expect(scope.code.length).to.equal(24);
|
||||
|
||||
// artifacts should be cleared
|
||||
expect(scope.code0).to.be.empty;
|
||||
expect(scope.code1).to.be.empty;
|
||||
expect(scope.code2).to.be.empty;
|
||||
expect(scope.code3).to.be.empty;
|
||||
expect(scope.code4).to.be.empty;
|
||||
expect(scope.code5).to.be.empty;
|
||||
});
|
||||
});
|
||||
|
||||
describe('verifyCode', function() {
|
||||
it('should fail for wrong code', function() {
|
||||
scope.code0 = 'b';
|
||||
scope.code1 = 'b';
|
||||
scope.code2 = 'b';
|
||||
scope.code3 = 'b';
|
||||
scope.code4 = 'b';
|
||||
scope.code5 = 'b';
|
||||
scope.code = 'aaaaaa';
|
||||
|
||||
scope.onError = function() {};
|
||||
expect(scope.verifyCode()).to.be.false;
|
||||
});
|
||||
|
||||
describe('checkServerForKey', function() {
|
||||
var keyParams = {
|
||||
userId: emailAddress,
|
||||
_id: 'keyId',
|
||||
it('should work', function() {
|
||||
scope.code0 = 'a';
|
||||
scope.code1 = 'a';
|
||||
scope.code2 = 'a';
|
||||
scope.code3 = 'a';
|
||||
scope.code4 = 'a';
|
||||
scope.code5 = 'a';
|
||||
scope.code = 'aaaaaa';
|
||||
|
||||
scope.onError = function() {};
|
||||
expect(scope.verifyCode()).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('setDeviceName', function() {
|
||||
it('should work', function(done) {
|
||||
keychainMock.setDeviceName.yields();
|
||||
scope.setDeviceName(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('encryptAndUploadKey', function() {
|
||||
it('should fail due to keychain.registerDevice', function(done) {
|
||||
keychainMock.registerDevice.yields(42);
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(keychainMock.registerDevice.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
it('should fail', function(done) {
|
||||
pgpStub.getKeyParams.returns(keyParams);
|
||||
keychainMock.hasPrivateKey.yields(42);
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(keychainMock.hasPrivateKey.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.checkServerForKey();
|
||||
});
|
||||
|
||||
it('should return true', function(done) {
|
||||
pgpStub.getKeyParams.returns(keyParams);
|
||||
keychainMock.hasPrivateKey.withArgs({
|
||||
userId: keyParams.userId,
|
||||
keyId: keyParams._id
|
||||
}).yields(null, true);
|
||||
|
||||
scope.checkServerForKey(function(privateKeySynced) {
|
||||
expect(privateKeySynced).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should return undefined', function(done) {
|
||||
pgpStub.getKeyParams.returns(keyParams);
|
||||
keychainMock.hasPrivateKey.withArgs({
|
||||
userId: keyParams.userId,
|
||||
keyId: keyParams._id
|
||||
}).yields(null, false);
|
||||
|
||||
scope.checkServerForKey(function(privateKeySynced) {
|
||||
expect(privateKeySynced).to.be.undefined;
|
||||
done();
|
||||
});
|
||||
});
|
||||
scope.encryptAndUploadKey();
|
||||
});
|
||||
|
||||
describe('handlePaste', function() {
|
||||
it('should work', function() {
|
||||
scope.handlePaste({
|
||||
clipboardData: {
|
||||
getData: function(val) {
|
||||
expect(val).to.equal('text/plain');
|
||||
return '1qaz-2wsx-3edc-4rfv-5tgb-6yhn';
|
||||
}
|
||||
}
|
||||
});
|
||||
it('should work', function(done) {
|
||||
keychainMock.registerDevice.yields();
|
||||
keychainMock.uploadPrivateKey.yields();
|
||||
|
||||
expect(scope.code0).to.equal('1qaz');
|
||||
expect(scope.code1).to.equal('2wsx');
|
||||
expect(scope.code2).to.equal('3edc');
|
||||
expect(scope.code3).to.equal('4rfv');
|
||||
expect(scope.code4).to.equal('5tgb');
|
||||
expect(scope.code5).to.equal('6yhn');
|
||||
scope.encryptAndUploadKey(function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(keychainMock.registerDevice.calledOnce).to.be.true;
|
||||
expect(keychainMock.uploadPrivateKey.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('displayUploadUi', function() {
|
||||
it('should work', function() {
|
||||
// add some artifacts from a previous key input
|
||||
scope.code0 = scope.code1 = scope.code2 = scope.code3 = scope.code4 = scope.code5 = 'asdasd';
|
||||
|
||||
scope.displayUploadUi();
|
||||
expect(scope.step).to.equal(1);
|
||||
expect(scope.code.length).to.equal(24);
|
||||
|
||||
// artifacts should be cleared
|
||||
expect(scope.code0).to.be.empty;
|
||||
expect(scope.code1).to.be.empty;
|
||||
expect(scope.code2).to.be.empty;
|
||||
expect(scope.code3).to.be.empty;
|
||||
expect(scope.code4).to.be.empty;
|
||||
expect(scope.code5).to.be.empty;
|
||||
});
|
||||
describe('goBack', function() {
|
||||
it('should work', function() {
|
||||
scope.step = 2;
|
||||
scope.goBack();
|
||||
expect(scope.step).to.equal(1);
|
||||
});
|
||||
|
||||
describe('verifyCode', function() {
|
||||
it('should fail for wrong code', function() {
|
||||
scope.code0 = 'b';
|
||||
scope.code1 = 'b';
|
||||
scope.code2 = 'b';
|
||||
scope.code3 = 'b';
|
||||
scope.code4 = 'b';
|
||||
scope.code5 = 'b';
|
||||
scope.code = 'aaaaaa';
|
||||
it('should not work for < 2', function() {
|
||||
scope.step = 1;
|
||||
scope.goBack();
|
||||
expect(scope.step).to.equal(1);
|
||||
});
|
||||
});
|
||||
|
||||
scope.onError = function() {};
|
||||
expect(scope.verifyCode()).to.be.false;
|
||||
});
|
||||
|
||||
it('should work', function() {
|
||||
scope.code0 = 'a';
|
||||
scope.code1 = 'a';
|
||||
scope.code2 = 'a';
|
||||
scope.code3 = 'a';
|
||||
scope.code4 = 'a';
|
||||
scope.code5 = 'a';
|
||||
scope.code = 'aaaaaa';
|
||||
|
||||
scope.onError = function() {};
|
||||
expect(scope.verifyCode()).to.be.false;
|
||||
});
|
||||
describe('goForward', function() {
|
||||
var verifyCodeStub, setDeviceNameStub, encryptAndUploadKeyStub;
|
||||
beforeEach(function() {
|
||||
verifyCodeStub = sinon.stub(scope, 'verifyCode');
|
||||
setDeviceNameStub = sinon.stub(scope, 'setDeviceName');
|
||||
encryptAndUploadKeyStub = sinon.stub(scope, 'encryptAndUploadKey');
|
||||
});
|
||||
afterEach(function() {
|
||||
verifyCodeStub.restore();
|
||||
setDeviceNameStub.restore();
|
||||
encryptAndUploadKeyStub.restore();
|
||||
});
|
||||
|
||||
describe('setDeviceName', function() {
|
||||
it('should work', function(done) {
|
||||
keychainMock.setDeviceName.yields();
|
||||
scope.setDeviceName(done);
|
||||
});
|
||||
it('should work for < 2', function() {
|
||||
scope.step = 1;
|
||||
scope.goForward();
|
||||
expect(scope.step).to.equal(2);
|
||||
});
|
||||
|
||||
describe('encryptAndUploadKey', function() {
|
||||
it('should fail due to keychain.registerDevice', function(done) {
|
||||
keychainMock.registerDevice.yields(42);
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(keychainMock.registerDevice.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.encryptAndUploadKey();
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
keychainMock.registerDevice.yields();
|
||||
keychainMock.uploadPrivateKey.yields();
|
||||
|
||||
scope.encryptAndUploadKey(function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(keychainMock.registerDevice.calledOnce).to.be.true;
|
||||
expect(keychainMock.uploadPrivateKey.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should work for 2', function() {
|
||||
verifyCodeStub.returns(true);
|
||||
scope.step = 2;
|
||||
scope.goForward();
|
||||
expect(scope.step).to.equal(3);
|
||||
});
|
||||
|
||||
describe('goBack', function() {
|
||||
it('should work', function() {
|
||||
scope.step = 2;
|
||||
scope.goBack();
|
||||
expect(scope.step).to.equal(1);
|
||||
});
|
||||
|
||||
it('should not work for < 2', function() {
|
||||
scope.step = 1;
|
||||
scope.goBack();
|
||||
expect(scope.step).to.equal(1);
|
||||
});
|
||||
it('should not work for 2 when code invalid', function() {
|
||||
verifyCodeStub.returns(false);
|
||||
scope.step = 2;
|
||||
scope.goForward();
|
||||
expect(scope.step).to.equal(2);
|
||||
});
|
||||
|
||||
describe('goForward', function() {
|
||||
var verifyCodeStub, setDeviceNameStub, encryptAndUploadKeyStub;
|
||||
beforeEach(function() {
|
||||
verifyCodeStub = sinon.stub(scope, 'verifyCode');
|
||||
setDeviceNameStub = sinon.stub(scope, 'setDeviceName');
|
||||
encryptAndUploadKeyStub = sinon.stub(scope, 'encryptAndUploadKey');
|
||||
});
|
||||
afterEach(function() {
|
||||
verifyCodeStub.restore();
|
||||
setDeviceNameStub.restore();
|
||||
encryptAndUploadKeyStub.restore();
|
||||
});
|
||||
it('should fail for 3 due to error in setDeviceName', function(done) {
|
||||
scope.step = 3;
|
||||
setDeviceNameStub.yields(42);
|
||||
|
||||
it('should work for < 2', function() {
|
||||
scope.step = 1;
|
||||
scope.goForward();
|
||||
expect(scope.step).to.equal(2);
|
||||
});
|
||||
|
||||
it('should work for 2', function() {
|
||||
verifyCodeStub.returns(true);
|
||||
scope.step = 2;
|
||||
scope.goForward();
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(scope.step).to.equal(3);
|
||||
});
|
||||
done();
|
||||
};
|
||||
|
||||
it('should not work for 2 when code invalid', function() {
|
||||
verifyCodeStub.returns(false);
|
||||
scope.step = 2;
|
||||
scope.goForward();
|
||||
expect(scope.step).to.equal(2);
|
||||
});
|
||||
|
||||
it('should fail for 3 due to error in setDeviceName', function(done) {
|
||||
scope.step = 3;
|
||||
setDeviceNameStub.yields(42);
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(scope.step).to.equal(3);
|
||||
done();
|
||||
};
|
||||
|
||||
scope.goForward();
|
||||
});
|
||||
|
||||
it('should fail for 3 due to error in encryptAndUploadKey', function(done) {
|
||||
scope.step = 3;
|
||||
setDeviceNameStub.yields();
|
||||
encryptAndUploadKeyStub.yields(42);
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(scope.step).to.equal(4);
|
||||
done();
|
||||
};
|
||||
|
||||
scope.goForward();
|
||||
});
|
||||
|
||||
it('should work for 3', function(done) {
|
||||
scope.step = 3;
|
||||
setDeviceNameStub.yields();
|
||||
encryptAndUploadKeyStub.yields();
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err.title).to.equal('Success');
|
||||
expect(scope.step).to.equal(4);
|
||||
done();
|
||||
};
|
||||
|
||||
scope.goForward();
|
||||
});
|
||||
scope.goForward();
|
||||
});
|
||||
|
||||
it('should fail for 3 due to error in encryptAndUploadKey', function(done) {
|
||||
scope.step = 3;
|
||||
setDeviceNameStub.yields();
|
||||
encryptAndUploadKeyStub.yields(42);
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(scope.step).to.equal(4);
|
||||
done();
|
||||
};
|
||||
|
||||
scope.goForward();
|
||||
});
|
||||
|
||||
it('should work for 3', function(done) {
|
||||
scope.step = 3;
|
||||
setDeviceNameStub.yields();
|
||||
encryptAndUploadKeyStub.yields();
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err.title).to.equal('Success');
|
||||
expect(scope.step).to.equal(4);
|
||||
done();
|
||||
};
|
||||
|
||||
scope.goForward();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,165 +1,161 @@
|
|||
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;
|
||||
var pubkeyDao, restDaoStub;
|
||||
|
||||
beforeEach(function() {
|
||||
restDaoStub = sinon.createStubInstance(RestDAO);
|
||||
pubkeyDao = new PublicKeyDAO(restDaoStub);
|
||||
});
|
||||
beforeEach(function() {
|
||||
restDaoStub = sinon.createStubInstance(RestDAO);
|
||||
pubkeyDao = new PublicKeyDAO(restDaoStub);
|
||||
});
|
||||
|
||||
afterEach(function() {});
|
||||
afterEach(function() {});
|
||||
|
||||
describe('get', function() {
|
||||
it('should fail', function(done) {
|
||||
restDaoStub.get.yields(42);
|
||||
describe('get', function() {
|
||||
it('should fail', function(done) {
|
||||
restDaoStub.get.yields(42);
|
||||
|
||||
pubkeyDao.get('id', function(err, key) {
|
||||
expect(err).to.exist;
|
||||
expect(key).to.not.exist;
|
||||
expect(restDaoStub.get.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
restDaoStub.get.yields(null, {
|
||||
_id: '12345',
|
||||
publicKey: 'asdf'
|
||||
});
|
||||
|
||||
pubkeyDao.get('id', function(err, key) {
|
||||
expect(err).to.not.exist;
|
||||
expect(key).to.exist;
|
||||
expect(key._id).to.exist;
|
||||
expect(key.publicKey).to.exist;
|
||||
expect(restDaoStub.get.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
pubkeyDao.get('id', function(err, key) {
|
||||
expect(err).to.exist;
|
||||
expect(key).to.not.exist;
|
||||
expect(restDaoStub.get.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('verify', function() {
|
||||
it('should fail', function(done) {
|
||||
restDaoStub.get.yields(42);
|
||||
|
||||
pubkeyDao.verify('id', function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
it('should work', function(done) {
|
||||
restDaoStub.get.yields(null, {
|
||||
_id: '12345',
|
||||
publicKey: 'asdf'
|
||||
});
|
||||
|
||||
it('should not error for 400', function(done) {
|
||||
restDaoStub.get.yields({
|
||||
code: 400
|
||||
});
|
||||
|
||||
pubkeyDao.verify('id', function(err) {
|
||||
expect(err).to.not.exist;
|
||||
done();
|
||||
});
|
||||
pubkeyDao.get('id', function(err, key) {
|
||||
expect(err).to.not.exist;
|
||||
expect(key).to.exist;
|
||||
expect(key._id).to.exist;
|
||||
expect(key.publicKey).to.exist;
|
||||
expect(restDaoStub.get.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
var uuid = 'c621e328-8548-40a1-8309-adf1955e98a9';
|
||||
restDaoStub.get.yields(null);
|
||||
describe('verify', function() {
|
||||
it('should fail', function(done) {
|
||||
restDaoStub.get.yields(42);
|
||||
|
||||
pubkeyDao.verify(uuid, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(restDaoStub.get.calledWith(sinon.match(function(arg){
|
||||
return arg.uri === '/verify/' + uuid && arg.type === 'text';
|
||||
}))).to.be.true;
|
||||
done();
|
||||
});
|
||||
pubkeyDao.verify('id', function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('get by userId', function() {
|
||||
it('should fail', function(done) {
|
||||
restDaoStub.get.yields(42);
|
||||
|
||||
pubkeyDao.getByUserId('userId', function(err, key) {
|
||||
expect(err).to.exist;
|
||||
expect(key).to.not.exist;
|
||||
expect(restDaoStub.get.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
it('should not error for 400', function(done) {
|
||||
restDaoStub.get.yields({
|
||||
code: 400
|
||||
});
|
||||
|
||||
it('should react to 404', function(done) {
|
||||
restDaoStub.get.yields({
|
||||
code: 404
|
||||
});
|
||||
|
||||
pubkeyDao.getByUserId('userId', function(err, key) {
|
||||
expect(err).to.not.exist;
|
||||
expect(key).to.not.exist;
|
||||
expect(restDaoStub.get.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should return empty array', function(done) {
|
||||
restDaoStub.get.yields(null, []);
|
||||
|
||||
pubkeyDao.getByUserId('userId', function(err, key) {
|
||||
expect(err).to.not.exist;
|
||||
expect(key).to.not.exist;
|
||||
expect(restDaoStub.get.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
restDaoStub.get.yields(null, [{
|
||||
_id: '12345',
|
||||
publicKey: 'asdf'
|
||||
}]);
|
||||
|
||||
pubkeyDao.getByUserId('userId', function(err, key) {
|
||||
expect(err).to.not.exist;
|
||||
expect(key).to.exist;
|
||||
expect(key._id).to.exist;
|
||||
expect(key.publicKey).to.exist;
|
||||
expect(restDaoStub.get.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
pubkeyDao.verify('id', function(err) {
|
||||
expect(err).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('put', function() {
|
||||
it('should fail', function(done) {
|
||||
restDaoStub.put.yields();
|
||||
it('should work', function(done) {
|
||||
var uuid = 'c621e328-8548-40a1-8309-adf1955e98a9';
|
||||
restDaoStub.get.yields(null);
|
||||
|
||||
pubkeyDao.put({
|
||||
_id: '12345',
|
||||
publicKey: 'asdf'
|
||||
}, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(restDaoStub.put.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
pubkeyDao.verify(uuid, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(restDaoStub.get.calledWith(sinon.match(function(arg) {
|
||||
return arg.uri === '/verify/' + uuid && arg.type === 'text';
|
||||
}))).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('get by userId', function() {
|
||||
it('should fail', function(done) {
|
||||
restDaoStub.get.yields(42);
|
||||
|
||||
pubkeyDao.getByUserId('userId', function(err, key) {
|
||||
expect(err).to.exist;
|
||||
expect(key).to.not.exist;
|
||||
expect(restDaoStub.get.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('remove', function() {
|
||||
it('should fail', function(done) {
|
||||
restDaoStub.remove.yields();
|
||||
it('should react to 404', function(done) {
|
||||
restDaoStub.get.yields({
|
||||
code: 404
|
||||
});
|
||||
|
||||
pubkeyDao.remove('12345', function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(restDaoStub.remove.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
pubkeyDao.getByUserId('userId', function(err, key) {
|
||||
expect(err).to.not.exist;
|
||||
expect(key).to.not.exist;
|
||||
expect(restDaoStub.get.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should return empty array', function(done) {
|
||||
restDaoStub.get.yields(null, []);
|
||||
|
||||
pubkeyDao.getByUserId('userId', function(err, key) {
|
||||
expect(err).to.not.exist;
|
||||
expect(key).to.not.exist;
|
||||
expect(restDaoStub.get.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
restDaoStub.get.yields(null, [{
|
||||
_id: '12345',
|
||||
publicKey: 'asdf'
|
||||
}]);
|
||||
|
||||
pubkeyDao.getByUserId('userId', function(err, key) {
|
||||
expect(err).to.not.exist;
|
||||
expect(key).to.exist;
|
||||
expect(key._id).to.exist;
|
||||
expect(key.publicKey).to.exist;
|
||||
expect(restDaoStub.get.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('put', function() {
|
||||
it('should fail', function(done) {
|
||||
restDaoStub.put.yields();
|
||||
|
||||
pubkeyDao.put({
|
||||
_id: '12345',
|
||||
publicKey: 'asdf'
|
||||
}, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(restDaoStub.put.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('remove', function() {
|
||||
it('should fail', function(done) {
|
||||
restDaoStub.remove.yields();
|
||||
|
||||
pubkeyDao.remove('12345', function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(restDaoStub.remove.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
|
@ -1,197 +1,193 @@
|
|||
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() {
|
||||
var scope, ctrl,
|
||||
origKeychain, keychainMock,
|
||||
origInvitation, invitationMock,
|
||||
origCrypto, cryptoMock,
|
||||
origOutbox, outboxMock,
|
||||
origEmailDao;
|
||||
describe('Read Controller unit test', function() {
|
||||
var scope, ctrl,
|
||||
origKeychain, keychainMock,
|
||||
origInvitation, invitationMock,
|
||||
origCrypto, cryptoMock,
|
||||
origOutbox, outboxMock,
|
||||
origEmailDao;
|
||||
|
||||
beforeEach(function() {
|
||||
origKeychain = appController._keychain;
|
||||
appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
beforeEach(function() {
|
||||
origKeychain = appController._keychain;
|
||||
appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
|
||||
origInvitation = appController._invitationDao;
|
||||
appController._invitationDao = invitationMock = sinon.createStubInstance(InvitationDAO);
|
||||
origInvitation = appController._invitationDao;
|
||||
appController._invitationDao = invitationMock = sinon.createStubInstance(InvitationDAO);
|
||||
|
||||
origCrypto = appController._pgp;
|
||||
appController._pgp = cryptoMock = sinon.createStubInstance(PGP);
|
||||
origCrypto = appController._pgp;
|
||||
appController._pgp = cryptoMock = sinon.createStubInstance(PGP);
|
||||
|
||||
origOutbox = appController._outboxBo;
|
||||
appController._outboxBo = outboxMock = sinon.createStubInstance(OutboxBO);
|
||||
origOutbox = appController._outboxBo;
|
||||
appController._outboxBo = outboxMock = sinon.createStubInstance(OutboxBO);
|
||||
|
||||
origEmailDao = appController._emailDao;
|
||||
appController._emailDao = {
|
||||
_account: 'sender@example.com'
|
||||
origEmailDao = appController._emailDao;
|
||||
appController._emailDao = {
|
||||
_account: 'sender@example.com'
|
||||
};
|
||||
|
||||
angular.module('readtest', []);
|
||||
mocks.module('readtest');
|
||||
mocks.inject(function($rootScope, $controller) {
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
ctrl = $controller(ReadCtrl, {
|
||||
$scope: scope
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
appController._keychain = origKeychain;
|
||||
appController._invitationDao = origInvitation;
|
||||
appController._pgp = origCrypto;
|
||||
appController._outboxBo = origOutbox;
|
||||
appController._emailDao = origEmailDao;
|
||||
});
|
||||
|
||||
describe('scope variables', function() {
|
||||
it('should be set correctly', function() {
|
||||
expect(scope.state.read).to.exist;
|
||||
expect(scope.state.read.open).to.be.false;
|
||||
expect(scope.state.read.toggle).to.exist;
|
||||
});
|
||||
});
|
||||
|
||||
describe('open/close read view', function() {
|
||||
it('should open/close', function() {
|
||||
expect(scope.state.read.open).to.be.false;
|
||||
scope.state.read.toggle(true);
|
||||
expect(scope.state.read.open).to.be.true;
|
||||
scope.state.read.toggle(false);
|
||||
expect(scope.state.read.open).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('getKeyId', function() {
|
||||
var address = 'asfd@asdf.com';
|
||||
|
||||
it('should show searching on error', function() {
|
||||
expect(scope.keyId).to.equal('No key found.');
|
||||
keychainMock.getReceiverPublicKey.yields(42);
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.equal(42);
|
||||
expect(scope.keyId).to.equal('Searching...');
|
||||
};
|
||||
|
||||
angular.module('readtest', []);
|
||||
mocks.module('readtest');
|
||||
mocks.inject(function($rootScope, $controller) {
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
ctrl = $controller(ReadCtrl, {
|
||||
$scope: scope
|
||||
});
|
||||
});
|
||||
scope.getKeyId(address);
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
appController._keychain = origKeychain;
|
||||
appController._invitationDao = origInvitation;
|
||||
appController._pgp = origCrypto;
|
||||
appController._outboxBo = origOutbox;
|
||||
appController._emailDao = origEmailDao;
|
||||
it('should allow invitation on empty key', function() {
|
||||
keychainMock.getReceiverPublicKey.yields();
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).not.exist;
|
||||
expect(scope.keyId).to.equal('User has no key. Click to invite.');
|
||||
};
|
||||
|
||||
scope.getKeyId(address);
|
||||
});
|
||||
|
||||
describe('scope variables', function() {
|
||||
it('should be set correctly', function() {
|
||||
expect(scope.state.read).to.exist;
|
||||
expect(scope.state.read.open).to.be.false;
|
||||
expect(scope.state.read.toggle).to.exist;
|
||||
it('should show searching on error', function() {
|
||||
keychainMock.getReceiverPublicKey.yields(null, {
|
||||
publicKey: 'PUBLIC KEY'
|
||||
});
|
||||
|
||||
cryptoMock.getFingerprint.returns('XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX');
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(scope.keyId).to.equal('PGP key: XXXXXXXX');
|
||||
};
|
||||
|
||||
scope.getKeyId(address);
|
||||
});
|
||||
|
||||
describe('open/close read view', function() {
|
||||
it('should open/close', function() {
|
||||
expect(scope.state.read.open).to.be.false;
|
||||
scope.state.read.toggle(true);
|
||||
expect(scope.state.read.open).to.be.true;
|
||||
scope.state.read.toggle(false);
|
||||
expect(scope.state.read.open).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('getKeyId', function() {
|
||||
var address = 'asfd@asdf.com';
|
||||
|
||||
it('should show searching on error', function() {
|
||||
expect(scope.keyId).to.equal('No key found.');
|
||||
keychainMock.getReceiverPublicKey.yields(42);
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.equal(42);
|
||||
expect(scope.keyId).to.equal('Searching...');
|
||||
};
|
||||
|
||||
scope.getKeyId(address);
|
||||
});
|
||||
|
||||
it('should allow invitation on empty key', function() {
|
||||
keychainMock.getReceiverPublicKey.yields();
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).not.exist;
|
||||
expect(scope.keyId).to.equal('User has no key. Click to invite.');
|
||||
};
|
||||
|
||||
scope.getKeyId(address);
|
||||
});
|
||||
|
||||
it('should show searching on error', function() {
|
||||
keychainMock.getReceiverPublicKey.yields(null, {
|
||||
publicKey: 'PUBLIC KEY'
|
||||
});
|
||||
|
||||
cryptoMock.getFingerprint.returns('XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX');
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(scope.keyId).to.equal('PGP key: XXXXXXXX');
|
||||
};
|
||||
|
||||
scope.getKeyId(address);
|
||||
});
|
||||
});
|
||||
|
||||
describe('invite', function() {
|
||||
it('not allow invitation for secure users', function() {
|
||||
expect(scope.keyId).to.equal('No key found.');
|
||||
|
||||
scope.invite({
|
||||
secure: true,
|
||||
address: 'asdf@asdf.de'
|
||||
});
|
||||
|
||||
expect(scope.keyId).to.equal('No key found.');
|
||||
});
|
||||
|
||||
it('should show error on invitation dao invite error', function() {
|
||||
invitationMock.invite.yields(42);
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.equal(42);
|
||||
};
|
||||
|
||||
scope.invite({
|
||||
address: 'asdf@asdf.de'
|
||||
});
|
||||
});
|
||||
|
||||
it('should show error on outbox put error', function() {
|
||||
invitationMock.invite.yields();
|
||||
outboxMock.put.yields(42);
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.equal(42);
|
||||
};
|
||||
|
||||
scope.invite({
|
||||
address: 'asdf@asdf.de'
|
||||
});
|
||||
});
|
||||
|
||||
it('should work', function() {
|
||||
invitationMock.invite.yields();
|
||||
outboxMock.put.yields();
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.not.exist;
|
||||
};
|
||||
|
||||
scope.invite({
|
||||
address: 'asdf@asdf.de'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('parseConversation', function() {
|
||||
it.skip('should work', function() {
|
||||
var body = 'foo\n' +
|
||||
'\n' +
|
||||
'> bar\n' +
|
||||
'>\n' +
|
||||
'> foofoo\n' +
|
||||
'>> foofoobar\n' +
|
||||
'\ncomment\n' +
|
||||
'>> barbar';
|
||||
|
||||
var nodes = scope.parseConversation({
|
||||
body: body
|
||||
});
|
||||
expect(nodes).to.exist;
|
||||
|
||||
var expectedJson = '{"children":["foo\\n",{"children":["bar\\n\\nfoofoo",{"children":["foofoobar"]}]},"\\ncomment",{"children":[{"children":["barbar"]}]}]}';
|
||||
var json = JSON.stringify(nodes);
|
||||
expect(json).to.equal(expectedJson);
|
||||
|
||||
var expectedHtml = '<div class="view-read-body"><div class="line"><span>foo</span><br></div><div class="line empty-line"><span></span><br></div><div class="prev-message"><div class="line"><span>bar</span><br></div><div class="line empty-line"><span></span><br></div><div class="line"><span>foofoo</span><br></div></div><div class="prev-message"><div class="prev-message"><div class="line"><span>foofoobar</span><br></div></div></div><div class="line empty-line"><span></span><br></div><div class="line"><span>comment</span><br></div><div class="prev-message"><div class="prev-message"><div class="line"><span>barbar</span><br></div></div></div></div>';
|
||||
var html = scope.renderNodes(nodes);
|
||||
expect(html).to.equal(expectedHtml);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('invite', function() {
|
||||
it('not allow invitation for secure users', function() {
|
||||
expect(scope.keyId).to.equal('No key found.');
|
||||
|
||||
scope.invite({
|
||||
secure: true,
|
||||
address: 'asdf@asdf.de'
|
||||
});
|
||||
|
||||
expect(scope.keyId).to.equal('No key found.');
|
||||
});
|
||||
|
||||
it('should show error on invitation dao invite error', function() {
|
||||
invitationMock.invite.yields(42);
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.equal(42);
|
||||
};
|
||||
|
||||
scope.invite({
|
||||
address: 'asdf@asdf.de'
|
||||
});
|
||||
});
|
||||
|
||||
it('should show error on outbox put error', function() {
|
||||
invitationMock.invite.yields();
|
||||
outboxMock.put.yields(42);
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.equal(42);
|
||||
};
|
||||
|
||||
scope.invite({
|
||||
address: 'asdf@asdf.de'
|
||||
});
|
||||
});
|
||||
|
||||
it('should work', function() {
|
||||
invitationMock.invite.yields();
|
||||
outboxMock.put.yields();
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.not.exist;
|
||||
};
|
||||
|
||||
scope.invite({
|
||||
address: 'asdf@asdf.de'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('parseConversation', function() {
|
||||
it.skip('should work', function() {
|
||||
var body = 'foo\n' +
|
||||
'\n' +
|
||||
'> bar\n' +
|
||||
'>\n' +
|
||||
'> foofoo\n' +
|
||||
'>> foofoobar\n' +
|
||||
'\ncomment\n' +
|
||||
'>> barbar';
|
||||
|
||||
var nodes = scope.parseConversation({
|
||||
body: body
|
||||
});
|
||||
expect(nodes).to.exist;
|
||||
|
||||
var expectedJson = '{"children":["foo\\n",{"children":["bar\\n\\nfoofoo",{"children":["foofoobar"]}]},"\\ncomment",{"children":[{"children":["barbar"]}]}]}';
|
||||
var json = JSON.stringify(nodes);
|
||||
expect(json).to.equal(expectedJson);
|
||||
|
||||
var expectedHtml = '<div class="view-read-body"><div class="line"><span>foo</span><br></div><div class="line empty-line"><span></span><br></div><div class="prev-message"><div class="line"><span>bar</span><br></div><div class="line empty-line"><span></span><br></div><div class="line"><span>foofoo</span><br></div></div><div class="prev-message"><div class="prev-message"><div class="line"><span>foofoobar</span><br></div></div></div><div class="line empty-line"><span></span><br></div><div class="line"><span>comment</span><br></div><div class="prev-message"><div class="prev-message"><div class="line"><span>barbar</span><br></div></div></div></div>';
|
||||
var html = scope.renderNodes(nodes);
|
||||
expect(html).to.equal(expectedHtml);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
|
@ -1,225 +1,221 @@
|
|||
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;
|
||||
var restDao, xhrMock, requests;
|
||||
|
||||
beforeEach(function() {
|
||||
beforeEach(function() {
|
||||
restDao = new RestDAO();
|
||||
xhrMock = sinon.useFakeXMLHttpRequest();
|
||||
requests = [];
|
||||
|
||||
xhrMock.onCreate = function(xhr) {
|
||||
requests.push(xhr);
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
xhrMock.restore();
|
||||
});
|
||||
|
||||
describe('contructor', function() {
|
||||
it('should set default base uri', function() {
|
||||
restDao = new RestDAO();
|
||||
xhrMock = sinon.useFakeXMLHttpRequest();
|
||||
requests = [];
|
||||
|
||||
xhrMock.onCreate = function(xhr) {
|
||||
requests.push(xhr);
|
||||
};
|
||||
expect(restDao).to.exist;
|
||||
expect(restDao._baseUri).to.exist;
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
xhrMock.restore();
|
||||
});
|
||||
it('should accept default base uri', function() {
|
||||
var baseUri = 'http://custom.com';
|
||||
|
||||
describe('contructor', function() {
|
||||
it('should set default base uri', function() {
|
||||
restDao = new RestDAO();
|
||||
expect(restDao).to.exist;
|
||||
expect(restDao._baseUri).to.exist;
|
||||
restDao = new RestDAO(baseUri);
|
||||
expect(restDao).to.exist;
|
||||
expect(restDao._baseUri).to.equal(baseUri);
|
||||
});
|
||||
});
|
||||
|
||||
describe('get', function() {
|
||||
it('should work with json as default type', function() {
|
||||
restDao.get({
|
||||
uri: '/asdf'
|
||||
}, function(err, data, status) {
|
||||
expect(err).to.not.exist;
|
||||
expect(data.foo).to.equal('bar');
|
||||
var req = requests[0];
|
||||
expect(req.requestHeaders.Accept).to.equal('application/json');
|
||||
expect(status).to.equal(200);
|
||||
});
|
||||
|
||||
it('should accept default base uri', function() {
|
||||
var baseUri = 'http://custom.com';
|
||||
expect(requests.length).to.equal(1);
|
||||
requests[0].respond(200, {
|
||||
"Content-Type": "application/json"
|
||||
}, '{"foo": "bar"}');
|
||||
});
|
||||
|
||||
restDao = new RestDAO(baseUri);
|
||||
expect(restDao).to.exist;
|
||||
expect(restDao._baseUri).to.equal(baseUri);
|
||||
it('should work with jsonz', function() {
|
||||
restDao.get({
|
||||
uri: '/asdf',
|
||||
type: 'json'
|
||||
}, function(err, data, status) {
|
||||
expect(err).to.not.exist;
|
||||
expect(data.foo).to.equal('bar');
|
||||
var req = requests[0];
|
||||
expect(req.requestHeaders.Accept).to.equal('application/json');
|
||||
expect(status).to.equal(200);
|
||||
});
|
||||
|
||||
expect(requests.length).to.equal(1);
|
||||
requests[0].respond(200, {
|
||||
"Content-Type": "application/json"
|
||||
}, '{"foo": "bar"}');
|
||||
});
|
||||
|
||||
it('should work with plain text', function() {
|
||||
restDao.get({
|
||||
uri: '/asdf',
|
||||
type: 'text'
|
||||
}, function(err, data, status) {
|
||||
expect(err).to.not.exist;
|
||||
expect(data).to.equal('foobar!');
|
||||
var req = requests[0];
|
||||
expect(req.requestHeaders.Accept).to.equal('text/plain');
|
||||
expect(status).to.equal(200);
|
||||
});
|
||||
|
||||
expect(requests.length).to.equal(1);
|
||||
requests[0].respond(200, {
|
||||
"Content-Type": "text/plain"
|
||||
}, 'foobar!');
|
||||
});
|
||||
|
||||
it('should work with xml', function() {
|
||||
restDao.get({
|
||||
uri: '/asdf',
|
||||
type: 'xml'
|
||||
}, function(err, data, status) {
|
||||
expect(err).to.not.exist;
|
||||
expect(data).to.equal('<foo>bar</foo>');
|
||||
var req = requests[0];
|
||||
expect(req.requestHeaders.Accept).to.equal('application/xml');
|
||||
expect(status).to.equal(200);
|
||||
});
|
||||
|
||||
expect(requests.length).to.equal(1);
|
||||
requests[0].respond(200, {
|
||||
"Content-Type": "application/xml"
|
||||
}, '<foo>bar</foo>');
|
||||
});
|
||||
|
||||
it('should fail for missing uri parameter', function() {
|
||||
restDao.get({}, function(err, data) {
|
||||
expect(err).to.exist;
|
||||
expect(err.code).to.equal(400);
|
||||
expect(data).to.not.exist;
|
||||
});
|
||||
});
|
||||
|
||||
describe('get', function() {
|
||||
it('should work with json as default type', function() {
|
||||
restDao.get({
|
||||
uri: '/asdf'
|
||||
}, function(err, data, status) {
|
||||
expect(err).to.not.exist;
|
||||
expect(data.foo).to.equal('bar');
|
||||
var req = requests[0];
|
||||
expect(req.requestHeaders.Accept).to.equal('application/json');
|
||||
expect(status).to.equal(200);
|
||||
});
|
||||
|
||||
expect(requests.length).to.equal(1);
|
||||
requests[0].respond(200, {
|
||||
"Content-Type": "application/json"
|
||||
}, '{"foo": "bar"}');
|
||||
});
|
||||
|
||||
it('should work with jsonz', function() {
|
||||
restDao.get({
|
||||
uri: '/asdf',
|
||||
type: 'json'
|
||||
}, function(err, data, status) {
|
||||
expect(err).to.not.exist;
|
||||
expect(data.foo).to.equal('bar');
|
||||
var req = requests[0];
|
||||
expect(req.requestHeaders.Accept).to.equal('application/json');
|
||||
expect(status).to.equal(200);
|
||||
});
|
||||
|
||||
expect(requests.length).to.equal(1);
|
||||
requests[0].respond(200, {
|
||||
"Content-Type": "application/json"
|
||||
}, '{"foo": "bar"}');
|
||||
});
|
||||
|
||||
it('should work with plain text', function() {
|
||||
restDao.get({
|
||||
uri: '/asdf',
|
||||
type: 'text'
|
||||
}, function(err, data, status) {
|
||||
expect(err).to.not.exist;
|
||||
expect(data).to.equal('foobar!');
|
||||
var req = requests[0];
|
||||
expect(req.requestHeaders.Accept).to.equal('text/plain');
|
||||
expect(status).to.equal(200);
|
||||
});
|
||||
|
||||
expect(requests.length).to.equal(1);
|
||||
requests[0].respond(200, {
|
||||
"Content-Type": "text/plain"
|
||||
}, 'foobar!');
|
||||
});
|
||||
|
||||
it('should work with xml', function() {
|
||||
restDao.get({
|
||||
uri: '/asdf',
|
||||
type: 'xml'
|
||||
}, function(err, data, status) {
|
||||
expect(err).to.not.exist;
|
||||
expect(data).to.equal('<foo>bar</foo>');
|
||||
var req = requests[0];
|
||||
expect(req.requestHeaders.Accept).to.equal('application/xml');
|
||||
expect(status).to.equal(200);
|
||||
});
|
||||
|
||||
expect(requests.length).to.equal(1);
|
||||
requests[0].respond(200, {
|
||||
"Content-Type": "application/xml"
|
||||
}, '<foo>bar</foo>');
|
||||
});
|
||||
|
||||
it('should fail for missing uri parameter', function() {
|
||||
restDao.get({}, function(err, data) {
|
||||
expect(err).to.exist;
|
||||
expect(err.code).to.equal(400);
|
||||
expect(data).to.not.exist;
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail for unhandled data type', function() {
|
||||
restDao.get({
|
||||
uri: '/asdf',
|
||||
type: 'snafu'
|
||||
}, function(err, data) {
|
||||
expect(err).to.exist;
|
||||
expect(err.code).to.equal(400);
|
||||
expect(data).to.not.exist;
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail for server error', function() {
|
||||
restDao.get({
|
||||
uri: '/asdf'
|
||||
}, function(err, data) {
|
||||
expect(err).to.exist;
|
||||
expect(err.code).to.equal(500);
|
||||
expect(data).to.not.exist;
|
||||
});
|
||||
|
||||
expect(requests.length).to.equal(1);
|
||||
requests[0].respond(500, {
|
||||
"Content-Type": "text/plain"
|
||||
}, 'Internal error');
|
||||
it('should fail for unhandled data type', function() {
|
||||
restDao.get({
|
||||
uri: '/asdf',
|
||||
type: 'snafu'
|
||||
}, function(err, data) {
|
||||
expect(err).to.exist;
|
||||
expect(err.code).to.equal(400);
|
||||
expect(data).to.not.exist;
|
||||
});
|
||||
});
|
||||
|
||||
describe('post', function() {
|
||||
it('should fail', function() {
|
||||
restDao.post('/asdf', {}, function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(err.code).to.equal(500);
|
||||
});
|
||||
|
||||
expect(requests.length).to.equal(1);
|
||||
requests[0].respond(500, {
|
||||
"Content-Type": "text/plain"
|
||||
}, 'Internal error');
|
||||
it('should fail for server error', function() {
|
||||
restDao.get({
|
||||
uri: '/asdf'
|
||||
}, function(err, data) {
|
||||
expect(err).to.exist;
|
||||
expect(err.code).to.equal(500);
|
||||
expect(data).to.not.exist;
|
||||
});
|
||||
|
||||
it('should work', function() {
|
||||
restDao.post('/asdf', {}, function(err, res, status) {
|
||||
expect(err).to.not.exist;
|
||||
expect(res).to.equal('');
|
||||
expect(status).to.equal(201);
|
||||
});
|
||||
expect(requests.length).to.equal(1);
|
||||
requests[0].respond(500, {
|
||||
"Content-Type": "text/plain"
|
||||
}, 'Internal error');
|
||||
});
|
||||
});
|
||||
|
||||
expect(requests.length).to.equal(1);
|
||||
requests[0].respond(201);
|
||||
describe('post', function() {
|
||||
it('should fail', function() {
|
||||
restDao.post('/asdf', {}, function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(err.code).to.equal(500);
|
||||
});
|
||||
|
||||
expect(requests.length).to.equal(1);
|
||||
requests[0].respond(500, {
|
||||
"Content-Type": "text/plain"
|
||||
}, 'Internal error');
|
||||
});
|
||||
|
||||
describe('put', function() {
|
||||
it('should fail', function() {
|
||||
restDao.put('/asdf', {}, function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(err.code).to.equal(500);
|
||||
});
|
||||
|
||||
expect(requests.length).to.equal(1);
|
||||
requests[0].respond(500, {
|
||||
"Content-Type": "text/plain"
|
||||
}, 'Internal error');
|
||||
it('should work', function() {
|
||||
restDao.post('/asdf', {}, function(err, res, status) {
|
||||
expect(err).to.not.exist;
|
||||
expect(res).to.equal('');
|
||||
expect(status).to.equal(201);
|
||||
});
|
||||
|
||||
it('should work', function() {
|
||||
restDao.put('/asdf', {}, function(err, res, status) {
|
||||
expect(err).to.not.exist;
|
||||
expect(res).to.equal('');
|
||||
expect(status).to.equal(201);
|
||||
});
|
||||
expect(requests.length).to.equal(1);
|
||||
requests[0].respond(201);
|
||||
});
|
||||
});
|
||||
|
||||
expect(requests.length).to.equal(1);
|
||||
requests[0].respond(201);
|
||||
describe('put', function() {
|
||||
it('should fail', function() {
|
||||
restDao.put('/asdf', {}, function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(err.code).to.equal(500);
|
||||
});
|
||||
|
||||
expect(requests.length).to.equal(1);
|
||||
requests[0].respond(500, {
|
||||
"Content-Type": "text/plain"
|
||||
}, 'Internal error');
|
||||
});
|
||||
|
||||
describe('remove', function() {
|
||||
it('should fail', function() {
|
||||
restDao.remove('/asdf', function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(err.code).to.equal(500);
|
||||
});
|
||||
|
||||
expect(requests.length).to.equal(1);
|
||||
requests[0].respond(500, {
|
||||
"Content-Type": "text/plain"
|
||||
}, 'Internal error');
|
||||
it('should work', function() {
|
||||
restDao.put('/asdf', {}, function(err, res, status) {
|
||||
expect(err).to.not.exist;
|
||||
expect(res).to.equal('');
|
||||
expect(status).to.equal(201);
|
||||
});
|
||||
|
||||
it('should work', function() {
|
||||
restDao.remove('/asdf', function(err, res, status) {
|
||||
expect(err).to.not.exist;
|
||||
expect(res).to.equal('');
|
||||
expect(status).to.equal(200);
|
||||
});
|
||||
expect(requests.length).to.equal(1);
|
||||
requests[0].respond(201);
|
||||
});
|
||||
});
|
||||
|
||||
expect(requests.length).to.equal(1);
|
||||
requests[0].respond(200);
|
||||
describe('remove', function() {
|
||||
it('should fail', function() {
|
||||
restDao.remove('/asdf', function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(err.code).to.equal(500);
|
||||
});
|
||||
|
||||
expect(requests.length).to.equal(1);
|
||||
requests[0].respond(500, {
|
||||
"Content-Type": "text/plain"
|
||||
}, 'Internal error');
|
||||
});
|
||||
|
||||
it('should work', function() {
|
||||
restDao.remove('/asdf', function(err, res, status) {
|
||||
expect(err).to.not.exist;
|
||||
expect(res).to.equal('');
|
||||
expect(status).to.equal(200);
|
||||
});
|
||||
|
||||
expect(requests.length).to.equal(1);
|
||||
requests[0].respond(200);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
|
@ -1,126 +1,122 @@
|
|||
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() {
|
||||
var scope, setPassphraseCtrl,
|
||||
dummyFingerprint, expectedFingerprint,
|
||||
dummyKeyId, expectedKeyId,
|
||||
emailAddress, keySize, cryptoMock, keychainMock;
|
||||
describe('Set Passphrase Controller unit test', function() {
|
||||
var scope, setPassphraseCtrl,
|
||||
dummyFingerprint, expectedFingerprint,
|
||||
dummyKeyId, expectedKeyId,
|
||||
emailAddress, keySize, cryptoMock, keychainMock;
|
||||
|
||||
beforeEach(function() {
|
||||
appController._pgp = cryptoMock = sinon.createStubInstance(PGP);
|
||||
appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
beforeEach(function() {
|
||||
appController._pgp = cryptoMock = sinon.createStubInstance(PGP);
|
||||
appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
|
||||
dummyFingerprint = '3A2D39B4E1404190B8B949DE7D7E99036E712926';
|
||||
expectedFingerprint = '3A2D 39B4 E140 4190 B8B9 49DE 7D7E 9903 6E71 2926';
|
||||
dummyKeyId = '9FEB47936E712926';
|
||||
expectedKeyId = '6E712926';
|
||||
cryptoMock.getFingerprint.returns(dummyFingerprint);
|
||||
cryptoMock.getKeyId.returns(dummyKeyId);
|
||||
emailAddress = 'fred@foo.com';
|
||||
keySize = 1234;
|
||||
dummyFingerprint = '3A2D39B4E1404190B8B949DE7D7E99036E712926';
|
||||
expectedFingerprint = '3A2D 39B4 E140 4190 B8B9 49DE 7D7E 9903 6E71 2926';
|
||||
dummyKeyId = '9FEB47936E712926';
|
||||
expectedKeyId = '6E712926';
|
||||
cryptoMock.getFingerprint.returns(dummyFingerprint);
|
||||
cryptoMock.getKeyId.returns(dummyKeyId);
|
||||
emailAddress = 'fred@foo.com';
|
||||
keySize = 1234;
|
||||
|
||||
cryptoMock.getKeyParams.returns({
|
||||
cryptoMock.getKeyParams.returns({
|
||||
_id: dummyKeyId,
|
||||
fingerprint: dummyFingerprint,
|
||||
userId: emailAddress,
|
||||
userIds: [],
|
||||
bitSize: keySize
|
||||
});
|
||||
|
||||
angular.module('setpassphrasetest', []);
|
||||
mocks.module('setpassphrasetest');
|
||||
mocks.inject(function($rootScope, $controller) {
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
setPassphraseCtrl = $controller(SetPassphraseCtrl, {
|
||||
$scope: scope
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {});
|
||||
|
||||
describe('setPassphrase', function() {
|
||||
it('should work', function(done) {
|
||||
scope.oldPassphrase = 'old';
|
||||
scope.newPassphrase = 'new';
|
||||
|
||||
keychainMock.lookupPrivateKey.withArgs(dummyKeyId).yields(null, {
|
||||
encryptedKey: 'encrypted'
|
||||
});
|
||||
|
||||
cryptoMock.changePassphrase.withArgs({
|
||||
privateKeyArmored: 'encrypted',
|
||||
oldPassphrase: 'old',
|
||||
newPassphrase: 'new'
|
||||
}).yields(null, 'newArmoredKey');
|
||||
|
||||
keychainMock.saveLocalPrivateKey.withArgs({
|
||||
_id: dummyKeyId,
|
||||
fingerprint: dummyFingerprint,
|
||||
userId: emailAddress,
|
||||
userIds: [],
|
||||
bitSize: keySize
|
||||
});
|
||||
encryptedKey: 'newArmoredKey'
|
||||
}).yields();
|
||||
|
||||
angular.module('setpassphrasetest', []);
|
||||
mocks.module('setpassphrasetest');
|
||||
mocks.inject(function($rootScope, $controller) {
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
setPassphraseCtrl = $controller(SetPassphraseCtrl, {
|
||||
$scope: scope
|
||||
});
|
||||
});
|
||||
scope.onError = function(err) {
|
||||
expect(err.title).to.equal('Success');
|
||||
done();
|
||||
};
|
||||
|
||||
scope.setPassphrase();
|
||||
});
|
||||
|
||||
afterEach(function() {});
|
||||
|
||||
describe('setPassphrase', function() {
|
||||
it('should work', function(done) {
|
||||
scope.oldPassphrase = 'old';
|
||||
scope.newPassphrase = 'new';
|
||||
|
||||
keychainMock.lookupPrivateKey.withArgs(dummyKeyId).yields(null, {
|
||||
encryptedKey: 'encrypted'
|
||||
});
|
||||
|
||||
cryptoMock.changePassphrase.withArgs({
|
||||
privateKeyArmored: 'encrypted',
|
||||
oldPassphrase: 'old',
|
||||
newPassphrase: 'new'
|
||||
}).yields(null, 'newArmoredKey');
|
||||
|
||||
keychainMock.saveLocalPrivateKey.withArgs({
|
||||
_id: dummyKeyId,
|
||||
userId: emailAddress,
|
||||
userIds: [],
|
||||
encryptedKey: 'newArmoredKey'
|
||||
}).yields();
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err.title).to.equal('Success');
|
||||
done();
|
||||
};
|
||||
|
||||
scope.setPassphrase();
|
||||
});
|
||||
});
|
||||
|
||||
describe('check passphrase quality', function() {
|
||||
it('should be too short', function() {
|
||||
scope.newPassphrase = '&§DG36';
|
||||
scope.checkPassphraseQuality();
|
||||
|
||||
expect(scope.passphraseMsg).to.equal('Very weak');
|
||||
expect(scope.passphraseRating).to.equal(0);
|
||||
});
|
||||
|
||||
it('should be very weak', function() {
|
||||
scope.newPassphrase = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
|
||||
scope.checkPassphraseQuality();
|
||||
|
||||
expect(scope.passphraseMsg).to.equal('Very weak');
|
||||
expect(scope.passphraseRating).to.equal(0);
|
||||
});
|
||||
|
||||
it('should be weak', function() {
|
||||
scope.newPassphrase = 'asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf';
|
||||
scope.checkPassphraseQuality();
|
||||
|
||||
expect(scope.passphraseMsg).to.equal('Weak');
|
||||
expect(scope.passphraseRating).to.equal(1);
|
||||
});
|
||||
|
||||
it('should be good', function() {
|
||||
scope.newPassphrase = 'asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf5';
|
||||
scope.checkPassphraseQuality();
|
||||
|
||||
expect(scope.passphraseMsg).to.equal('Good');
|
||||
expect(scope.passphraseRating).to.equal(2);
|
||||
});
|
||||
|
||||
it('should be strong', function() {
|
||||
scope.newPassphrase = '&§DG36abcd';
|
||||
scope.checkPassphraseQuality();
|
||||
|
||||
expect(scope.passphraseMsg).to.equal('Strong');
|
||||
expect(scope.passphraseRating).to.equal(3);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('check passphrase quality', function() {
|
||||
it('should be too short', function() {
|
||||
scope.newPassphrase = '&§DG36';
|
||||
scope.checkPassphraseQuality();
|
||||
|
||||
expect(scope.passphraseMsg).to.equal('Very weak');
|
||||
expect(scope.passphraseRating).to.equal(0);
|
||||
});
|
||||
|
||||
it('should be very weak', function() {
|
||||
scope.newPassphrase = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
|
||||
scope.checkPassphraseQuality();
|
||||
|
||||
expect(scope.passphraseMsg).to.equal('Very weak');
|
||||
expect(scope.passphraseRating).to.equal(0);
|
||||
});
|
||||
|
||||
it('should be weak', function() {
|
||||
scope.newPassphrase = 'asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf';
|
||||
scope.checkPassphraseQuality();
|
||||
|
||||
expect(scope.passphraseMsg).to.equal('Weak');
|
||||
expect(scope.passphraseRating).to.equal(1);
|
||||
});
|
||||
|
||||
it('should be good', function() {
|
||||
scope.newPassphrase = 'asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf5';
|
||||
scope.checkPassphraseQuality();
|
||||
|
||||
expect(scope.passphraseMsg).to.equal('Good');
|
||||
expect(scope.passphraseRating).to.equal(2);
|
||||
});
|
||||
|
||||
it('should be strong', function() {
|
||||
scope.newPassphrase = '&§DG36abcd';
|
||||
scope.checkPassphraseQuality();
|
||||
|
||||
expect(scope.passphraseMsg).to.equal('Strong');
|
||||
expect(scope.passphraseRating).to.equal(3);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
|
@ -1,472 +1,469 @@
|
|||
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() {
|
||||
var updateHandler, appConfigStorageStub, authStub, userStorageStub, origDbVersion;
|
||||
describe('UpdateHandler', function() {
|
||||
var updateHandler, appConfigStorageStub, authStub, userStorageStub, origDbVersion;
|
||||
|
||||
chai.Assertion.includeStack = true;
|
||||
chai.Assertion.includeStack = true;
|
||||
|
||||
beforeEach(function() {
|
||||
origDbVersion = cfg.dbVersion;
|
||||
appConfigStorageStub = sinon.createStubInstance(DeviceStorageDAO);
|
||||
userStorageStub = sinon.createStubInstance(DeviceStorageDAO);
|
||||
authStub = sinon.createStubInstance(Auth);
|
||||
updateHandler = new UpdateHandler(appConfigStorageStub, userStorageStub, authStub);
|
||||
beforeEach(function() {
|
||||
origDbVersion = cfg.dbVersion;
|
||||
appConfigStorageStub = sinon.createStubInstance(DeviceStorageDAO);
|
||||
userStorageStub = sinon.createStubInstance(DeviceStorageDAO);
|
||||
authStub = sinon.createStubInstance(Auth);
|
||||
updateHandler = new UpdateHandler(appConfigStorageStub, userStorageStub, authStub);
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
cfg.dbVersion = origDbVersion;
|
||||
});
|
||||
|
||||
describe('#constructor', function() {
|
||||
it('should create instance', function() {
|
||||
expect(updateHandler).to.exist;
|
||||
expect(updateHandler._appConfigStorage).to.equal(appConfigStorageStub);
|
||||
expect(updateHandler._userStorage).to.equal(userStorageStub);
|
||||
|
||||
// the update handler must contain as many db update sripts as there are database versions
|
||||
expect(updateHandler._updateScripts.length).to.equal(cfg.dbVersion);
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
cfg.dbVersion = origDbVersion;
|
||||
});
|
||||
describe('#update', function() {
|
||||
var versionDbType = 'dbVersion';
|
||||
|
||||
describe('#constructor', function() {
|
||||
it('should create instance', function() {
|
||||
expect(updateHandler).to.exist;
|
||||
expect(updateHandler._appConfigStorage).to.equal(appConfigStorageStub);
|
||||
expect(updateHandler._userStorage).to.equal(userStorageStub);
|
||||
it('should not update when up to date', function(done) {
|
||||
cfg.dbVersion = 10; // app requires database version 10
|
||||
appConfigStorageStub.listItems.withArgs(versionDbType).yieldsAsync(null, ['10']); // database version is 10
|
||||
|
||||
// the update handler must contain as many db update sripts as there are database versions
|
||||
expect(updateHandler._updateScripts.length).to.equal(cfg.dbVersion);
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.not.exist;
|
||||
expect(appConfigStorageStub.listItems.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('#update', function() {
|
||||
var versionDbType = 'dbVersion';
|
||||
describe('dummy updates for v2 to v4', function() {
|
||||
var updateCounter;
|
||||
|
||||
it('should not update when up to date', function(done) {
|
||||
cfg.dbVersion = 10; // app requires database version 10
|
||||
appConfigStorageStub.listItems.withArgs(versionDbType).yieldsAsync(null, ['10']); // database version is 10
|
||||
beforeEach(function() {
|
||||
updateCounter = 0;
|
||||
appConfigStorageStub.listItems.withArgs(versionDbType).yieldsAsync(null, ['2']); // database version is 0
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
expect(appConfigStorageStub.listItems.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
|
||||
it('should work', function(done) {
|
||||
cfg.dbVersion = 4; // app requires database version 4
|
||||
|
||||
// a simple dummy update to executed that only increments the update counter
|
||||
function dummyUpdate(options, callback) {
|
||||
updateCounter++;
|
||||
callback();
|
||||
}
|
||||
|
||||
// inject the dummy updates instead of live ones
|
||||
updateHandler._updateScripts = [dummyUpdate, dummyUpdate, dummyUpdate, dummyUpdate];
|
||||
|
||||
// execute test
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.not.exist;
|
||||
expect(appConfigStorageStub.listItems.calledOnce).to.be.true;
|
||||
expect(updateCounter).to.equal(2);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('dummy updates for v2 to v4', function() {
|
||||
var updateCounter;
|
||||
it('should fail while updating to v3', function(done) {
|
||||
cfg.dbVersion = 4; // app requires database version 4
|
||||
|
||||
beforeEach(function() {
|
||||
updateCounter = 0;
|
||||
appConfigStorageStub.listItems.withArgs(versionDbType).yieldsAsync(null, ['2']); // database version is 0
|
||||
});
|
||||
function dummyUpdate(options, callback) {
|
||||
updateCounter++;
|
||||
callback();
|
||||
}
|
||||
|
||||
afterEach(function() {
|
||||
expect(appConfigStorageStub.listItems.calledOnce).to.be.true;
|
||||
});
|
||||
function failingUpdate(options, callback) {
|
||||
callback({});
|
||||
}
|
||||
|
||||
// inject the dummy updates instead of live ones
|
||||
updateHandler._updateScripts = [dummyUpdate, dummyUpdate, failingUpdate, dummyUpdate];
|
||||
|
||||
it('should work', function(done) {
|
||||
cfg.dbVersion = 4; // app requires database version 4
|
||||
// execute test
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(updateCounter).to.equal(0);
|
||||
|
||||
// a simple dummy update to executed that only increments the update counter
|
||||
function dummyUpdate(options, callback) {
|
||||
updateCounter++;
|
||||
callback();
|
||||
}
|
||||
|
||||
// inject the dummy updates instead of live ones
|
||||
updateHandler._updateScripts = [dummyUpdate, dummyUpdate, dummyUpdate, dummyUpdate];
|
||||
|
||||
// execute test
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.not.exist;
|
||||
expect(updateCounter).to.equal(2);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail while updating to v3', function(done) {
|
||||
cfg.dbVersion = 4; // app requires database version 4
|
||||
|
||||
function dummyUpdate(options, callback) {
|
||||
updateCounter++;
|
||||
callback();
|
||||
}
|
||||
|
||||
function failingUpdate(options, callback) {
|
||||
callback({});
|
||||
}
|
||||
|
||||
// inject the dummy updates instead of live ones
|
||||
updateHandler._updateScripts = [dummyUpdate, dummyUpdate, failingUpdate, dummyUpdate];
|
||||
|
||||
// execute test
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(updateCounter).to.equal(0);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('v0 -> v1', function() {
|
||||
var emailDbType = 'email_';
|
||||
|
||||
beforeEach(function() {
|
||||
cfg.dbVersion = 1; // app requires database version 1
|
||||
appConfigStorageStub.listItems.withArgs(versionDbType).yieldsAsync(); // database version is 0
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// database version is only queried for version checking prior to the update script
|
||||
// so no need to check this in case-specific tests
|
||||
expect(appConfigStorageStub.listItems.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
userStorageStub.removeList.withArgs(emailDbType).yieldsAsync();
|
||||
appConfigStorageStub.storeList.withArgs([1], versionDbType).yieldsAsync();
|
||||
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.not.exist;
|
||||
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
||||
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail when persisting database version fails', function(done) {
|
||||
userStorageStub.removeList.yieldsAsync();
|
||||
appConfigStorageStub.storeList.yieldsAsync(new Error());
|
||||
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
||||
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail when wiping emails from database fails', function(done) {
|
||||
userStorageStub.removeList.yieldsAsync(new Error());
|
||||
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
||||
expect(appConfigStorageStub.storeList.called).to.be.false;
|
||||
|
||||
done();
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('v1 -> v2', function() {
|
||||
var emailDbType = 'email_';
|
||||
});
|
||||
|
||||
beforeEach(function() {
|
||||
cfg.dbVersion = 2; // app requires database version 2
|
||||
appConfigStorageStub.listItems.withArgs(versionDbType).yieldsAsync(null, [1]); // database version is 0
|
||||
});
|
||||
describe('v0 -> v1', function() {
|
||||
var emailDbType = 'email_';
|
||||
|
||||
afterEach(function() {
|
||||
// database version is only queried for version checking prior to the update script
|
||||
// so no need to check this in case-specific tests
|
||||
expect(appConfigStorageStub.listItems.calledOnce).to.be.true;
|
||||
});
|
||||
beforeEach(function() {
|
||||
cfg.dbVersion = 1; // app requires database version 1
|
||||
appConfigStorageStub.listItems.withArgs(versionDbType).yieldsAsync(); // database version is 0
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
userStorageStub.removeList.withArgs(emailDbType).yieldsAsync();
|
||||
appConfigStorageStub.storeList.withArgs([2], versionDbType).yieldsAsync();
|
||||
afterEach(function() {
|
||||
// database version is only queried for version checking prior to the update script
|
||||
// so no need to check this in case-specific tests
|
||||
expect(appConfigStorageStub.listItems.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.not.exist;
|
||||
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
||||
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
||||
it('should work', function(done) {
|
||||
userStorageStub.removeList.withArgs(emailDbType).yieldsAsync();
|
||||
appConfigStorageStub.storeList.withArgs([1], versionDbType).yieldsAsync();
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.not.exist;
|
||||
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
||||
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
||||
|
||||
it('should fail when persisting database version fails', function(done) {
|
||||
userStorageStub.removeList.yieldsAsync();
|
||||
appConfigStorageStub.storeList.yieldsAsync(new Error());
|
||||
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
||||
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail when wiping emails from database fails', function(done) {
|
||||
userStorageStub.removeList.yieldsAsync(new Error());
|
||||
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
||||
expect(appConfigStorageStub.storeList.called).to.be.false;
|
||||
|
||||
done();
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('v2 -> v3', function() {
|
||||
var emailDbType = 'email_';
|
||||
it('should fail when persisting database version fails', function(done) {
|
||||
userStorageStub.removeList.yieldsAsync();
|
||||
appConfigStorageStub.storeList.yieldsAsync(new Error());
|
||||
|
||||
beforeEach(function() {
|
||||
cfg.dbVersion = 3; // app requires database version 2
|
||||
appConfigStorageStub.listItems.withArgs(versionDbType).yieldsAsync(null, [2]); // database version is 0
|
||||
});
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
||||
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
||||
|
||||
afterEach(function() {
|
||||
// database version is only queried for version checking prior to the update script
|
||||
// so no need to check this in case-specific tests
|
||||
expect(appConfigStorageStub.listItems.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
userStorageStub.removeList.withArgs(emailDbType).yieldsAsync();
|
||||
appConfigStorageStub.storeList.withArgs([3], versionDbType).yieldsAsync();
|
||||
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.not.exist;
|
||||
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
||||
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail when persisting database version fails', function(done) {
|
||||
userStorageStub.removeList.yieldsAsync();
|
||||
appConfigStorageStub.storeList.yieldsAsync(new Error());
|
||||
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
||||
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail when wiping emails from database fails', function(done) {
|
||||
userStorageStub.removeList.yieldsAsync(new Error());
|
||||
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
||||
expect(appConfigStorageStub.storeList.called).to.be.false;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('v3 -> v4', function() {
|
||||
var EMAIL_ADDR_DB_KEY = 'emailaddress';
|
||||
var USERNAME_DB_KEY = 'username';
|
||||
var PROVIDER_DB_KEY = 'provider';
|
||||
var IMAP_DB_KEY = 'imap';
|
||||
var SMTP_DB_KEY = 'smtp';
|
||||
var REALNAME_DB_KEY = 'realname';
|
||||
var emailaddress = 'bla@blubb.io';
|
||||
|
||||
var imap = config.gmail.imap,
|
||||
smtp = config.gmail.smtp;
|
||||
|
||||
beforeEach(function() {
|
||||
cfg.dbVersion = 4; // app requires database version 4
|
||||
appConfigStorageStub.listItems.withArgs(versionDbType).yieldsAsync(null, [3]); // database version is 3
|
||||
});
|
||||
|
||||
it('should add gmail as mail service provider with email address and no provider present in db', function(done) {
|
||||
appConfigStorageStub.listItems.withArgs(EMAIL_ADDR_DB_KEY).yieldsAsync(null, [emailaddress]);
|
||||
appConfigStorageStub.listItems.withArgs(PROVIDER_DB_KEY).yieldsAsync(null, []);
|
||||
appConfigStorageStub.storeList.withArgs([4], versionDbType).yieldsAsync();
|
||||
appConfigStorageStub.storeList.withArgs(['gmail'], PROVIDER_DB_KEY).yieldsAsync();
|
||||
appConfigStorageStub.storeList.withArgs([emailaddress], USERNAME_DB_KEY).yieldsAsync();
|
||||
appConfigStorageStub.storeList.withArgs([imap], IMAP_DB_KEY).yieldsAsync();
|
||||
appConfigStorageStub.storeList.withArgs([smtp], SMTP_DB_KEY).yieldsAsync();
|
||||
appConfigStorageStub.storeList.withArgs([''], REALNAME_DB_KEY).yieldsAsync();
|
||||
authStub._loadCredentials.yieldsAsync();
|
||||
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.not.exist;
|
||||
expect(appConfigStorageStub.storeList.callCount).to.equal(6);
|
||||
expect(appConfigStorageStub.listItems.calledThrice).to.be.true;
|
||||
expect(authStub._loadCredentials.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not add a provider when no email adress is in db', function(done) {
|
||||
appConfigStorageStub.listItems.withArgs(EMAIL_ADDR_DB_KEY).yieldsAsync(null, []);
|
||||
appConfigStorageStub.listItems.withArgs(PROVIDER_DB_KEY).yieldsAsync(null, []);
|
||||
appConfigStorageStub.storeList.withArgs([4], versionDbType).yieldsAsync();
|
||||
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.not.exist;
|
||||
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
||||
expect(appConfigStorageStub.listItems.calledThrice).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail when appConfigStore write fails', function(done) {
|
||||
appConfigStorageStub.listItems.yieldsAsync(null, []);
|
||||
appConfigStorageStub.storeList.yieldsAsync(new Error());
|
||||
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(appConfigStorageStub.listItems.calledThrice).to.be.true;
|
||||
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail when appConfigStore read fails', function(done) {
|
||||
appConfigStorageStub.listItems.withArgs(EMAIL_ADDR_DB_KEY).yieldsAsync(new Error());
|
||||
appConfigStorageStub.storeList.yieldsAsync(new Error());
|
||||
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(appConfigStorageStub.listItems.calledTwice).to.be.true;
|
||||
expect(appConfigStorageStub.storeList.called).to.be.false;
|
||||
|
||||
done();
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('v4 -> v5', function() {
|
||||
var FOLDER_TYPE_INBOX = 'Inbox';
|
||||
var FOLDER_TYPE_SENT = 'Sent';
|
||||
var FOLDER_TYPE_DRAFTS = 'Drafts';
|
||||
var FOLDER_TYPE_TRASH = 'Trash';
|
||||
it('should fail when wiping emails from database fails', function(done) {
|
||||
userStorageStub.removeList.yieldsAsync(new Error());
|
||||
|
||||
var FOLDER_DB_TYPE = 'folders';
|
||||
var VERSION_DB_TYPE = 'dbVersion';
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
||||
expect(appConfigStorageStub.storeList.called).to.be.false;
|
||||
|
||||
var POST_UPDATE_DB_VERSION = 5;
|
||||
|
||||
beforeEach(function() {
|
||||
cfg.dbVersion = 5; // app requires database version 4
|
||||
appConfigStorageStub.listItems.withArgs(VERSION_DB_TYPE).yieldsAsync(null, [4]); // database version is 4
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// database version is only queried for version checking prior to the update script
|
||||
// so no need to check this in case-specific tests
|
||||
expect(appConfigStorageStub.listItems.calledOnce).to.be.true;
|
||||
describe('v1 -> v2', function() {
|
||||
var emailDbType = 'email_';
|
||||
|
||||
beforeEach(function() {
|
||||
cfg.dbVersion = 2; // app requires database version 2
|
||||
appConfigStorageStub.listItems.withArgs(versionDbType).yieldsAsync(null, [1]); // database version is 0
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// database version is only queried for version checking prior to the update script
|
||||
// so no need to check this in case-specific tests
|
||||
expect(appConfigStorageStub.listItems.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
userStorageStub.removeList.withArgs(emailDbType).yieldsAsync();
|
||||
appConfigStorageStub.storeList.withArgs([2], versionDbType).yieldsAsync();
|
||||
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.not.exist;
|
||||
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
||||
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
userStorageStub.listItems.withArgs(FOLDER_DB_TYPE).yieldsAsync(null, [
|
||||
[{
|
||||
name: 'inbox1',
|
||||
type: FOLDER_TYPE_INBOX
|
||||
}, {
|
||||
name: 'inbox2',
|
||||
type: FOLDER_TYPE_INBOX
|
||||
}, {
|
||||
name: 'sent1',
|
||||
type: FOLDER_TYPE_SENT
|
||||
}, {
|
||||
name: 'sent2',
|
||||
type: FOLDER_TYPE_SENT
|
||||
}, {
|
||||
name: 'drafts1',
|
||||
type: FOLDER_TYPE_DRAFTS
|
||||
}, {
|
||||
name: 'drafts2',
|
||||
type: FOLDER_TYPE_DRAFTS
|
||||
}, {
|
||||
name: 'trash1',
|
||||
type: FOLDER_TYPE_TRASH
|
||||
}, {
|
||||
name: 'trash2',
|
||||
type: FOLDER_TYPE_TRASH
|
||||
}]
|
||||
]);
|
||||
it('should fail when persisting database version fails', function(done) {
|
||||
userStorageStub.removeList.yieldsAsync();
|
||||
appConfigStorageStub.storeList.yieldsAsync(new Error());
|
||||
|
||||
userStorageStub.storeList.withArgs([
|
||||
[{
|
||||
name: 'inbox1',
|
||||
type: FOLDER_TYPE_INBOX
|
||||
}, {
|
||||
name: 'sent1',
|
||||
type: FOLDER_TYPE_SENT
|
||||
}, {
|
||||
name: 'drafts1',
|
||||
type: FOLDER_TYPE_DRAFTS
|
||||
}, {
|
||||
name: 'trash1',
|
||||
type: FOLDER_TYPE_TRASH
|
||||
}]
|
||||
], FOLDER_DB_TYPE).yieldsAsync();
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
||||
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
||||
|
||||
appConfigStorageStub.storeList.withArgs([POST_UPDATE_DB_VERSION], VERSION_DB_TYPE).yieldsAsync();
|
||||
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.not.exist;
|
||||
expect(userStorageStub.listItems.calledOnce).to.be.true;
|
||||
expect(userStorageStub.storeList.calledOnce).to.be.true;
|
||||
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail when persisting database version fails', function(done) {
|
||||
userStorageStub.listItems.yieldsAsync(null, []);
|
||||
userStorageStub.storeList.yieldsAsync();
|
||||
appConfigStorageStub.storeList.yieldsAsync(new Error());
|
||||
it('should fail when wiping emails from database fails', function(done) {
|
||||
userStorageStub.removeList.yieldsAsync(new Error());
|
||||
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(userStorageStub.listItems.calledOnce).to.be.true;
|
||||
expect(userStorageStub.storeList.calledOnce).to.be.true;
|
||||
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
||||
expect(appConfigStorageStub.storeList.called).to.be.false;
|
||||
|
||||
done();
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail when persisting folders fails', function(done) {
|
||||
userStorageStub.listItems.yieldsAsync(null, []);
|
||||
userStorageStub.storeList.yieldsAsync(new Error());
|
||||
describe('v2 -> v3', function() {
|
||||
var emailDbType = 'email_';
|
||||
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(userStorageStub.listItems.calledOnce).to.be.true;
|
||||
expect(userStorageStub.storeList.calledOnce).to.be.true;
|
||||
expect(appConfigStorageStub.storeList.called).to.be.false;
|
||||
beforeEach(function() {
|
||||
cfg.dbVersion = 3; // app requires database version 2
|
||||
appConfigStorageStub.listItems.withArgs(versionDbType).yieldsAsync(null, [2]); // database version is 0
|
||||
});
|
||||
|
||||
done();
|
||||
});
|
||||
afterEach(function() {
|
||||
// database version is only queried for version checking prior to the update script
|
||||
// so no need to check this in case-specific tests
|
||||
expect(appConfigStorageStub.listItems.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
userStorageStub.removeList.withArgs(emailDbType).yieldsAsync();
|
||||
appConfigStorageStub.storeList.withArgs([3], versionDbType).yieldsAsync();
|
||||
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.not.exist;
|
||||
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
||||
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail when listing folders fails', function(done) {
|
||||
userStorageStub.listItems.yieldsAsync(new Error());
|
||||
it('should fail when persisting database version fails', function(done) {
|
||||
userStorageStub.removeList.yieldsAsync();
|
||||
appConfigStorageStub.storeList.yieldsAsync(new Error());
|
||||
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(userStorageStub.listItems.calledOnce).to.be.true;
|
||||
expect(userStorageStub.storeList.called).to.be.false;
|
||||
expect(appConfigStorageStub.storeList.called).to.be.false;
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
||||
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail when wiping emails from database fails', function(done) {
|
||||
userStorageStub.removeList.yieldsAsync(new Error());
|
||||
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
||||
expect(appConfigStorageStub.storeList.called).to.be.false;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('v3 -> v4', function() {
|
||||
var EMAIL_ADDR_DB_KEY = 'emailaddress';
|
||||
var USERNAME_DB_KEY = 'username';
|
||||
var PROVIDER_DB_KEY = 'provider';
|
||||
var IMAP_DB_KEY = 'imap';
|
||||
var SMTP_DB_KEY = 'smtp';
|
||||
var REALNAME_DB_KEY = 'realname';
|
||||
var emailaddress = 'bla@blubb.io';
|
||||
|
||||
var imap = config.gmail.imap,
|
||||
smtp = config.gmail.smtp;
|
||||
|
||||
beforeEach(function() {
|
||||
cfg.dbVersion = 4; // app requires database version 4
|
||||
appConfigStorageStub.listItems.withArgs(versionDbType).yieldsAsync(null, [3]); // database version is 3
|
||||
});
|
||||
|
||||
it('should add gmail as mail service provider with email address and no provider present in db', function(done) {
|
||||
appConfigStorageStub.listItems.withArgs(EMAIL_ADDR_DB_KEY).yieldsAsync(null, [emailaddress]);
|
||||
appConfigStorageStub.listItems.withArgs(PROVIDER_DB_KEY).yieldsAsync(null, []);
|
||||
appConfigStorageStub.storeList.withArgs([4], versionDbType).yieldsAsync();
|
||||
appConfigStorageStub.storeList.withArgs(['gmail'], PROVIDER_DB_KEY).yieldsAsync();
|
||||
appConfigStorageStub.storeList.withArgs([emailaddress], USERNAME_DB_KEY).yieldsAsync();
|
||||
appConfigStorageStub.storeList.withArgs([imap], IMAP_DB_KEY).yieldsAsync();
|
||||
appConfigStorageStub.storeList.withArgs([smtp], SMTP_DB_KEY).yieldsAsync();
|
||||
appConfigStorageStub.storeList.withArgs([''], REALNAME_DB_KEY).yieldsAsync();
|
||||
authStub._loadCredentials.yieldsAsync();
|
||||
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.not.exist;
|
||||
expect(appConfigStorageStub.storeList.callCount).to.equal(6);
|
||||
expect(appConfigStorageStub.listItems.calledThrice).to.be.true;
|
||||
expect(authStub._loadCredentials.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not add a provider when no email adress is in db', function(done) {
|
||||
appConfigStorageStub.listItems.withArgs(EMAIL_ADDR_DB_KEY).yieldsAsync(null, []);
|
||||
appConfigStorageStub.listItems.withArgs(PROVIDER_DB_KEY).yieldsAsync(null, []);
|
||||
appConfigStorageStub.storeList.withArgs([4], versionDbType).yieldsAsync();
|
||||
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.not.exist;
|
||||
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
||||
expect(appConfigStorageStub.listItems.calledThrice).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail when appConfigStore write fails', function(done) {
|
||||
appConfigStorageStub.listItems.yieldsAsync(null, []);
|
||||
appConfigStorageStub.storeList.yieldsAsync(new Error());
|
||||
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(appConfigStorageStub.listItems.calledThrice).to.be.true;
|
||||
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail when appConfigStore read fails', function(done) {
|
||||
appConfigStorageStub.listItems.withArgs(EMAIL_ADDR_DB_KEY).yieldsAsync(new Error());
|
||||
appConfigStorageStub.storeList.yieldsAsync(new Error());
|
||||
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(appConfigStorageStub.listItems.calledTwice).to.be.true;
|
||||
expect(appConfigStorageStub.storeList.called).to.be.false;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('v4 -> v5', function() {
|
||||
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 POST_UPDATE_DB_VERSION = 5;
|
||||
|
||||
beforeEach(function() {
|
||||
cfg.dbVersion = 5; // app requires database version 4
|
||||
appConfigStorageStub.listItems.withArgs(VERSION_DB_TYPE).yieldsAsync(null, [4]); // database version is 4
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// database version is only queried for version checking prior to the update script
|
||||
// so no need to check this in case-specific tests
|
||||
expect(appConfigStorageStub.listItems.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
userStorageStub.listItems.withArgs(FOLDER_DB_TYPE).yieldsAsync(null, [
|
||||
[{
|
||||
name: 'inbox1',
|
||||
type: FOLDER_TYPE_INBOX
|
||||
}, {
|
||||
name: 'inbox2',
|
||||
type: FOLDER_TYPE_INBOX
|
||||
}, {
|
||||
name: 'sent1',
|
||||
type: FOLDER_TYPE_SENT
|
||||
}, {
|
||||
name: 'sent2',
|
||||
type: FOLDER_TYPE_SENT
|
||||
}, {
|
||||
name: 'drafts1',
|
||||
type: FOLDER_TYPE_DRAFTS
|
||||
}, {
|
||||
name: 'drafts2',
|
||||
type: FOLDER_TYPE_DRAFTS
|
||||
}, {
|
||||
name: 'trash1',
|
||||
type: FOLDER_TYPE_TRASH
|
||||
}, {
|
||||
name: 'trash2',
|
||||
type: FOLDER_TYPE_TRASH
|
||||
}]
|
||||
]);
|
||||
|
||||
userStorageStub.storeList.withArgs([
|
||||
[{
|
||||
name: 'inbox1',
|
||||
type: FOLDER_TYPE_INBOX
|
||||
}, {
|
||||
name: 'sent1',
|
||||
type: FOLDER_TYPE_SENT
|
||||
}, {
|
||||
name: 'drafts1',
|
||||
type: FOLDER_TYPE_DRAFTS
|
||||
}, {
|
||||
name: 'trash1',
|
||||
type: FOLDER_TYPE_TRASH
|
||||
}]
|
||||
], FOLDER_DB_TYPE).yieldsAsync();
|
||||
|
||||
appConfigStorageStub.storeList.withArgs([POST_UPDATE_DB_VERSION], VERSION_DB_TYPE).yieldsAsync();
|
||||
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.not.exist;
|
||||
expect(userStorageStub.listItems.calledOnce).to.be.true;
|
||||
expect(userStorageStub.storeList.calledOnce).to.be.true;
|
||||
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail when persisting database version fails', function(done) {
|
||||
userStorageStub.listItems.yieldsAsync(null, []);
|
||||
userStorageStub.storeList.yieldsAsync();
|
||||
appConfigStorageStub.storeList.yieldsAsync(new Error());
|
||||
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(userStorageStub.listItems.calledOnce).to.be.true;
|
||||
expect(userStorageStub.storeList.calledOnce).to.be.true;
|
||||
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail when persisting folders fails', function(done) {
|
||||
userStorageStub.listItems.yieldsAsync(null, []);
|
||||
userStorageStub.storeList.yieldsAsync(new Error());
|
||||
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(userStorageStub.listItems.calledOnce).to.be.true;
|
||||
expect(userStorageStub.storeList.calledOnce).to.be.true;
|
||||
expect(appConfigStorageStub.storeList.called).to.be.false;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail when listing folders fails', function(done) {
|
||||
userStorageStub.listItems.yieldsAsync(new Error());
|
||||
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(userStorageStub.listItems.calledOnce).to.be.true;
|
||||
expect(userStorageStub.storeList.called).to.be.false;
|
||||
expect(appConfigStorageStub.storeList.called).to.be.false;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,401 +1,397 @@
|
|||
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() {
|
||||
var ctrl, scope,
|
||||
origEmailDao, origOutbox, origKeychain,
|
||||
emailDaoMock, keychainMock, outboxMock, emailAddress, realname;
|
||||
describe('Write controller unit test', function() {
|
||||
var ctrl, scope,
|
||||
origEmailDao, origOutbox, origKeychain,
|
||||
emailDaoMock, keychainMock, outboxMock, emailAddress, realname;
|
||||
|
||||
beforeEach(function() {
|
||||
// the app controller is a singleton, we need to remember the
|
||||
// outbox and email dao to restore it after the tests
|
||||
origEmailDao = appController._emailDao;
|
||||
origOutbox = appController._outboxBo;
|
||||
origKeychain = appController._keychain;
|
||||
beforeEach(function() {
|
||||
// the app controller is a singleton, we need to remember the
|
||||
// outbox and email dao to restore it after the tests
|
||||
origEmailDao = appController._emailDao;
|
||||
origOutbox = appController._outboxBo;
|
||||
origKeychain = appController._keychain;
|
||||
|
||||
outboxMock = sinon.createStubInstance(OutboxBO);
|
||||
appController._outboxBo = outboxMock;
|
||||
outboxMock = sinon.createStubInstance(OutboxBO);
|
||||
appController._outboxBo = outboxMock;
|
||||
|
||||
emailDaoMock = sinon.createStubInstance(EmailDAO);
|
||||
appController._emailDao = emailDaoMock;
|
||||
emailDaoMock = sinon.createStubInstance(EmailDAO);
|
||||
appController._emailDao = emailDaoMock;
|
||||
|
||||
emailAddress = 'fred@foo.com';
|
||||
realname = 'Fred Foo';
|
||||
emailDaoMock._account = {
|
||||
emailAddress: emailAddress,
|
||||
realname: realname
|
||||
};
|
||||
emailAddress = 'fred@foo.com';
|
||||
realname = 'Fred Foo';
|
||||
emailDaoMock._account = {
|
||||
emailAddress: emailAddress,
|
||||
realname: realname
|
||||
};
|
||||
|
||||
keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
appController._keychain = keychainMock;
|
||||
keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
appController._keychain = keychainMock;
|
||||
|
||||
angular.module('writetest', []);
|
||||
mocks.module('writetest');
|
||||
mocks.inject(function($rootScope, $controller) {
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
ctrl = $controller(WriteCtrl, {
|
||||
$scope: scope
|
||||
});
|
||||
angular.module('writetest', []);
|
||||
mocks.module('writetest');
|
||||
mocks.inject(function($rootScope, $controller) {
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
ctrl = $controller(WriteCtrl, {
|
||||
$scope: scope
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// restore the app controller
|
||||
appController._emailDao = origEmailDao;
|
||||
appController._outboxBo = origOutbox;
|
||||
appController._keychain = origKeychain;
|
||||
afterEach(function() {
|
||||
// restore the app controller
|
||||
appController._emailDao = origEmailDao;
|
||||
appController._outboxBo = origOutbox;
|
||||
appController._keychain = origKeychain;
|
||||
});
|
||||
|
||||
describe('scope variables', function() {
|
||||
it('should be set correctly', function() {
|
||||
expect(scope.state.writer).to.exist;
|
||||
expect(scope.state.lightbox).to.be.undefined;
|
||||
expect(scope.state.writer.write).to.exist;
|
||||
expect(scope.state.writer.close).to.exist;
|
||||
expect(scope.verify).to.exist;
|
||||
expect(scope.checkSendStatus).to.exist;
|
||||
expect(scope.sendToOutbox).to.exist;
|
||||
expect(scope.tagStyle).to.exist;
|
||||
expect(scope.lookupAddressBook).to.exist;
|
||||
});
|
||||
});
|
||||
|
||||
describe('close', function() {
|
||||
it('should close the writer', function() {
|
||||
scope.state.lightbox = 'write';
|
||||
|
||||
scope.state.writer.close();
|
||||
|
||||
expect(scope.state.lightbox).to.be.undefined;
|
||||
});
|
||||
});
|
||||
|
||||
describe('write', function() {
|
||||
it('should prepare write view', function() {
|
||||
var verifyMock = sinon.stub(scope, 'verify');
|
||||
|
||||
scope.state.writer.write();
|
||||
|
||||
expect(scope.writerTitle).to.equal('New email');
|
||||
expect(scope.to).to.deep.equal([]);
|
||||
expect(scope.subject).to.equal('');
|
||||
expect(scope.body).to.equal('');
|
||||
expect(verifyMock.calledOnce).to.be.true;
|
||||
|
||||
scope.verify.restore();
|
||||
});
|
||||
|
||||
describe('scope variables', function() {
|
||||
it('should be set correctly', function() {
|
||||
expect(scope.state.writer).to.exist;
|
||||
expect(scope.state.lightbox).to.be.undefined;
|
||||
expect(scope.state.writer.write).to.exist;
|
||||
expect(scope.state.writer.close).to.exist;
|
||||
expect(scope.verify).to.exist;
|
||||
expect(scope.checkSendStatus).to.exist;
|
||||
expect(scope.sendToOutbox).to.exist;
|
||||
expect(scope.tagStyle).to.exist;
|
||||
expect(scope.lookupAddressBook).to.exist;
|
||||
});
|
||||
it('should prefill write view for response', function() {
|
||||
var verifyMock = sinon.stub(scope, 'verify'),
|
||||
address = 'pity@dafool',
|
||||
subject = 'Ermahgerd!',
|
||||
body = 'so much body!',
|
||||
re = {
|
||||
id: 'abc',
|
||||
from: [{
|
||||
address: address
|
||||
}],
|
||||
subject: subject,
|
||||
sentDate: new Date(),
|
||||
body: body,
|
||||
references: ['ghi', 'def']
|
||||
};
|
||||
|
||||
scope.sendBtnSecure = true;
|
||||
|
||||
scope.state.writer.write(re);
|
||||
|
||||
expect(scope.writerTitle).to.equal('Reply');
|
||||
expect(scope.to).to.deep.equal([{
|
||||
address: address,
|
||||
}]);
|
||||
expect(scope.subject).to.equal('Re: ' + subject);
|
||||
expect(scope.body).to.contain(body);
|
||||
expect(scope.references).to.deep.equal(['ghi', 'def', 'abc']);
|
||||
expect(verifyMock.called).to.be.true;
|
||||
|
||||
scope.verify.restore();
|
||||
});
|
||||
|
||||
describe('close', function() {
|
||||
it('should close the writer', function() {
|
||||
scope.state.lightbox = 'write';
|
||||
|
||||
scope.state.writer.close();
|
||||
|
||||
expect(scope.state.lightbox).to.be.undefined;
|
||||
});
|
||||
});
|
||||
|
||||
describe('write', function() {
|
||||
it('should prepare write view', function() {
|
||||
var verifyMock = sinon.stub(scope, 'verify');
|
||||
|
||||
scope.state.writer.write();
|
||||
|
||||
expect(scope.writerTitle).to.equal('New email');
|
||||
expect(scope.to).to.deep.equal([]);
|
||||
expect(scope.subject).to.equal('');
|
||||
expect(scope.body).to.equal('');
|
||||
expect(verifyMock.calledOnce).to.be.true;
|
||||
|
||||
scope.verify.restore();
|
||||
});
|
||||
|
||||
it('should prefill write view for response', function() {
|
||||
var verifyMock = sinon.stub(scope, 'verify'),
|
||||
address = 'pity@dafool',
|
||||
subject = 'Ermahgerd!',
|
||||
body = 'so much body!',
|
||||
re = {
|
||||
id: 'abc',
|
||||
from: [{
|
||||
address: address
|
||||
}],
|
||||
subject: subject,
|
||||
sentDate: new Date(),
|
||||
body: body,
|
||||
references: ['ghi', 'def']
|
||||
};
|
||||
|
||||
scope.sendBtnSecure = true;
|
||||
|
||||
scope.state.writer.write(re);
|
||||
|
||||
expect(scope.writerTitle).to.equal('Reply');
|
||||
expect(scope.to).to.deep.equal([{
|
||||
address: address,
|
||||
}]);
|
||||
expect(scope.subject).to.equal('Re: ' + subject);
|
||||
expect(scope.body).to.contain(body);
|
||||
expect(scope.references).to.deep.equal(['ghi', 'def', 'abc']);
|
||||
expect(verifyMock.called).to.be.true;
|
||||
|
||||
scope.verify.restore();
|
||||
});
|
||||
|
||||
it('should prefill write view for forward', function() {
|
||||
var verifyMock = sinon.stub(scope, 'verify'),
|
||||
address = 'pity@dafool',
|
||||
subject = 'Ermahgerd!',
|
||||
body = 'so much body!',
|
||||
re = {
|
||||
from: [{
|
||||
address: address
|
||||
}],
|
||||
to: [{
|
||||
address: address
|
||||
}],
|
||||
subject: subject,
|
||||
sentDate: new Date(),
|
||||
body: body,
|
||||
attachments: [{}]
|
||||
};
|
||||
|
||||
scope.sendBtnSecure = false;
|
||||
|
||||
scope.state.writer.write(re, null, true);
|
||||
|
||||
expect(scope.writerTitle).to.equal('Forward');
|
||||
expect(scope.to).to.deep.equal([]);
|
||||
expect(scope.subject).to.equal('Fwd: ' + subject);
|
||||
expect(scope.body).to.contain(body);
|
||||
expect(verifyMock.called).to.be.true;
|
||||
expect(scope.attachments).to.not.equal(re.attachments); // not the same reference
|
||||
expect(scope.attachments).to.deep.equal(re.attachments); // but the same content
|
||||
|
||||
scope.verify.restore();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('verify', function() {
|
||||
var checkSendStatusMock;
|
||||
|
||||
beforeEach(function() {
|
||||
checkSendStatusMock = sinon.stub(scope, 'checkSendStatus');
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
scope.checkSendStatus.restore();
|
||||
});
|
||||
|
||||
it('should do nothing if recipient is not provided', function() {
|
||||
scope.verify(undefined);
|
||||
});
|
||||
|
||||
it('should not work for invalid email addresses', function() {
|
||||
var recipient = {
|
||||
address: ''
|
||||
it('should prefill write view for forward', function() {
|
||||
var verifyMock = sinon.stub(scope, 'verify'),
|
||||
address = 'pity@dafool',
|
||||
subject = 'Ermahgerd!',
|
||||
body = 'so much body!',
|
||||
re = {
|
||||
from: [{
|
||||
address: address
|
||||
}],
|
||||
to: [{
|
||||
address: address
|
||||
}],
|
||||
subject: subject,
|
||||
sentDate: new Date(),
|
||||
body: body,
|
||||
attachments: [{}]
|
||||
};
|
||||
|
||||
scope.verify(recipient);
|
||||
scope.sendBtnSecure = false;
|
||||
|
||||
expect(recipient.key).to.be.undefined;
|
||||
expect(recipient.secure).to.be.undefined;
|
||||
expect(scope.checkSendStatus.callCount).to.equal(2);
|
||||
expect(keychainMock.getReceiverPublicKey.called).to.be.false;
|
||||
});
|
||||
scope.state.writer.write(re, null, true);
|
||||
|
||||
it('should not work for error in keychain', function(done) {
|
||||
var recipient = {
|
||||
address: 'asds@example.com'
|
||||
};
|
||||
expect(scope.writerTitle).to.equal('Forward');
|
||||
expect(scope.to).to.deep.equal([]);
|
||||
expect(scope.subject).to.equal('Fwd: ' + subject);
|
||||
expect(scope.body).to.contain(body);
|
||||
expect(verifyMock.called).to.be.true;
|
||||
expect(scope.attachments).to.not.equal(re.attachments); // not the same reference
|
||||
expect(scope.attachments).to.deep.equal(re.attachments); // but the same content
|
||||
|
||||
keychainMock.refreshKeyForUserId.withArgs(recipient.address).yields({
|
||||
errMsg: '404 not found yadda yadda'
|
||||
});
|
||||
|
||||
scope.onError = function() {
|
||||
expect(recipient.key).to.be.undefined;
|
||||
expect(recipient.secure).to.be.false;
|
||||
expect(scope.checkSendStatus.callCount).to.equal(1);
|
||||
expect(keychainMock.refreshKeyForUserId.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.verify(recipient);
|
||||
});
|
||||
|
||||
it('should work for main userId', function(done) {
|
||||
var recipient = {
|
||||
address: 'asdf@example.com'
|
||||
};
|
||||
|
||||
keychainMock.refreshKeyForUserId.withArgs(recipient.address).yields(null, {
|
||||
userId: 'asdf@example.com'
|
||||
});
|
||||
|
||||
scope.$digest = function() {
|
||||
expect(recipient.key).to.deep.equal({
|
||||
userId: 'asdf@example.com'
|
||||
});
|
||||
expect(recipient.secure).to.be.true;
|
||||
expect(scope.checkSendStatus.callCount).to.equal(2);
|
||||
expect(keychainMock.refreshKeyForUserId.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.verify(recipient);
|
||||
});
|
||||
|
||||
it('should work for secondary userId', function(done) {
|
||||
var recipient = {
|
||||
address: 'asdf@example.com'
|
||||
};
|
||||
var key = {
|
||||
userId: 'qwer@example.com',
|
||||
userIds: [{
|
||||
emailAddress: 'asdf@example.com'
|
||||
}]
|
||||
};
|
||||
|
||||
keychainMock.refreshKeyForUserId.withArgs(recipient.address).yields(null, key);
|
||||
|
||||
scope.$digest = function() {
|
||||
expect(recipient.key).to.deep.equal(key);
|
||||
expect(recipient.secure).to.be.true;
|
||||
expect(scope.checkSendStatus.callCount).to.equal(2);
|
||||
expect(keychainMock.refreshKeyForUserId.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.verify(recipient);
|
||||
});
|
||||
});
|
||||
|
||||
describe('checkSendStatus', function() {
|
||||
beforeEach(function() {
|
||||
scope.state.writer.write();
|
||||
});
|
||||
|
||||
afterEach(function() {});
|
||||
|
||||
it('should not be able to send with no recipients', function() {
|
||||
scope.checkSendStatus();
|
||||
|
||||
expect(scope.okToSend).to.be.false;
|
||||
expect(scope.sendBtnText).to.be.undefined;
|
||||
expect(scope.sendBtnSecure).to.be.undefined;
|
||||
});
|
||||
|
||||
it('should be able to send plaintext', function() {
|
||||
scope.to = [{
|
||||
address: 'asdf@asdf.de'
|
||||
}];
|
||||
scope.checkSendStatus();
|
||||
|
||||
expect(scope.okToSend).to.be.true;
|
||||
expect(scope.sendBtnText).to.equal('Send');
|
||||
expect(scope.sendBtnSecure).to.be.false;
|
||||
});
|
||||
|
||||
it('should send plaintext if one receiver is not secure', function() {
|
||||
scope.to = [{
|
||||
address: 'asdf@asdf.de',
|
||||
secure: true
|
||||
}, {
|
||||
address: 'asdf@asdfg.de'
|
||||
}];
|
||||
scope.checkSendStatus();
|
||||
|
||||
expect(scope.okToSend).to.be.true;
|
||||
expect(scope.sendBtnText).to.equal('Send');
|
||||
expect(scope.sendBtnSecure).to.be.false;
|
||||
});
|
||||
|
||||
it('should be able to send securely to multiple recipients', function() {
|
||||
scope.to = [{
|
||||
address: 'asdf@asdf.de',
|
||||
secure: true
|
||||
}, {
|
||||
address: 'asdf@asdfg.de',
|
||||
secure: true
|
||||
}];
|
||||
scope.checkSendStatus();
|
||||
|
||||
expect(scope.okToSend).to.be.true;
|
||||
expect(scope.sendBtnText).to.equal('Send securely');
|
||||
expect(scope.sendBtnSecure).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('send to outbox', function() {
|
||||
it('should work', function() {
|
||||
scope.to = [{
|
||||
address: 'pity@dafool'
|
||||
}];
|
||||
scope.cc = [];
|
||||
scope.bcc = [];
|
||||
scope.subject = 'Ermahgerd!';
|
||||
scope.body = 'wow. much body! very text!';
|
||||
scope.attachments = [];
|
||||
scope.state.nav = {
|
||||
currentFolder: 'currentFolder'
|
||||
};
|
||||
|
||||
scope.replyTo = {};
|
||||
|
||||
outboxMock.put.withArgs(sinon.match(function(mail) {
|
||||
expect(mail.from).to.deep.equal([{
|
||||
address: emailAddress,
|
||||
name: realname
|
||||
}]);
|
||||
expect(mail.to).to.deep.equal(scope.to);
|
||||
expect(mail.cc).to.deep.equal(scope.cc);
|
||||
expect(mail.bcc).to.deep.equal(scope.bcc);
|
||||
expect(mail.body).to.contain(scope.body);
|
||||
expect(mail.subject).to.equal(scope.subject);
|
||||
expect(mail.attachments).to.be.empty;
|
||||
expect(mail.sentDate).to.exist;
|
||||
|
||||
|
||||
return true;
|
||||
})).yields();
|
||||
emailDaoMock.setFlags.yields();
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.not.exist;
|
||||
};
|
||||
|
||||
scope.sendToOutbox();
|
||||
|
||||
expect(outboxMock.put.calledOnce).to.be.true;
|
||||
expect(emailDaoMock.setFlags.calledOnce).to.be.true;
|
||||
expect(scope.state.lightbox).to.be.undefined;
|
||||
expect(scope.replyTo.answered).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('lookupAddressBook', function() {
|
||||
it('should work', function(done) {
|
||||
keychainMock.listLocalPublicKeys.yields(null, [{
|
||||
userId: 'test@asdf.com',
|
||||
publicKey: 'KEY'
|
||||
}]);
|
||||
|
||||
var result = scope.lookupAddressBook('test');
|
||||
|
||||
result.then(function(response) {
|
||||
expect(response).to.deep.equal([{
|
||||
address: 'test@asdf.com'
|
||||
}]);
|
||||
done();
|
||||
});
|
||||
scope.$digest();
|
||||
});
|
||||
|
||||
it('should work with cache', function(done) {
|
||||
scope.addressBookCache = [{
|
||||
address: 'test@asdf.com'
|
||||
}, {
|
||||
address: 'tes@asdf.com'
|
||||
}];
|
||||
|
||||
var result = scope.lookupAddressBook('test');
|
||||
|
||||
result.then(function(response) {
|
||||
expect(response).to.deep.equal([{
|
||||
address: 'test@asdf.com'
|
||||
}]);
|
||||
done();
|
||||
});
|
||||
scope.$digest();
|
||||
});
|
||||
scope.verify.restore();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('verify', function() {
|
||||
var checkSendStatusMock;
|
||||
|
||||
beforeEach(function() {
|
||||
checkSendStatusMock = sinon.stub(scope, 'checkSendStatus');
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
scope.checkSendStatus.restore();
|
||||
});
|
||||
|
||||
it('should do nothing if recipient is not provided', function() {
|
||||
scope.verify(undefined);
|
||||
});
|
||||
|
||||
it('should not work for invalid email addresses', function() {
|
||||
var recipient = {
|
||||
address: ''
|
||||
};
|
||||
|
||||
scope.verify(recipient);
|
||||
|
||||
expect(recipient.key).to.be.undefined;
|
||||
expect(recipient.secure).to.be.undefined;
|
||||
expect(scope.checkSendStatus.callCount).to.equal(2);
|
||||
expect(keychainMock.getReceiverPublicKey.called).to.be.false;
|
||||
});
|
||||
|
||||
it('should not work for error in keychain', function(done) {
|
||||
var recipient = {
|
||||
address: 'asds@example.com'
|
||||
};
|
||||
|
||||
keychainMock.refreshKeyForUserId.withArgs(recipient.address).yields({
|
||||
errMsg: '404 not found yadda yadda'
|
||||
});
|
||||
|
||||
scope.onError = function() {
|
||||
expect(recipient.key).to.be.undefined;
|
||||
expect(recipient.secure).to.be.false;
|
||||
expect(scope.checkSendStatus.callCount).to.equal(1);
|
||||
expect(keychainMock.refreshKeyForUserId.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.verify(recipient);
|
||||
});
|
||||
|
||||
it('should work for main userId', function(done) {
|
||||
var recipient = {
|
||||
address: 'asdf@example.com'
|
||||
};
|
||||
|
||||
keychainMock.refreshKeyForUserId.withArgs(recipient.address).yields(null, {
|
||||
userId: 'asdf@example.com'
|
||||
});
|
||||
|
||||
scope.$digest = function() {
|
||||
expect(recipient.key).to.deep.equal({
|
||||
userId: 'asdf@example.com'
|
||||
});
|
||||
expect(recipient.secure).to.be.true;
|
||||
expect(scope.checkSendStatus.callCount).to.equal(2);
|
||||
expect(keychainMock.refreshKeyForUserId.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.verify(recipient);
|
||||
});
|
||||
|
||||
it('should work for secondary userId', function(done) {
|
||||
var recipient = {
|
||||
address: 'asdf@example.com'
|
||||
};
|
||||
var key = {
|
||||
userId: 'qwer@example.com',
|
||||
userIds: [{
|
||||
emailAddress: 'asdf@example.com'
|
||||
}]
|
||||
};
|
||||
|
||||
keychainMock.refreshKeyForUserId.withArgs(recipient.address).yields(null, key);
|
||||
|
||||
scope.$digest = function() {
|
||||
expect(recipient.key).to.deep.equal(key);
|
||||
expect(recipient.secure).to.be.true;
|
||||
expect(scope.checkSendStatus.callCount).to.equal(2);
|
||||
expect(keychainMock.refreshKeyForUserId.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
scope.verify(recipient);
|
||||
});
|
||||
});
|
||||
|
||||
describe('checkSendStatus', function() {
|
||||
beforeEach(function() {
|
||||
scope.state.writer.write();
|
||||
});
|
||||
|
||||
afterEach(function() {});
|
||||
|
||||
it('should not be able to send with no recipients', function() {
|
||||
scope.checkSendStatus();
|
||||
|
||||
expect(scope.okToSend).to.be.false;
|
||||
expect(scope.sendBtnText).to.be.undefined;
|
||||
expect(scope.sendBtnSecure).to.be.undefined;
|
||||
});
|
||||
|
||||
it('should be able to send plaintext', function() {
|
||||
scope.to = [{
|
||||
address: 'asdf@asdf.de'
|
||||
}];
|
||||
scope.checkSendStatus();
|
||||
|
||||
expect(scope.okToSend).to.be.true;
|
||||
expect(scope.sendBtnText).to.equal('Send');
|
||||
expect(scope.sendBtnSecure).to.be.false;
|
||||
});
|
||||
|
||||
it('should send plaintext if one receiver is not secure', function() {
|
||||
scope.to = [{
|
||||
address: 'asdf@asdf.de',
|
||||
secure: true
|
||||
}, {
|
||||
address: 'asdf@asdfg.de'
|
||||
}];
|
||||
scope.checkSendStatus();
|
||||
|
||||
expect(scope.okToSend).to.be.true;
|
||||
expect(scope.sendBtnText).to.equal('Send');
|
||||
expect(scope.sendBtnSecure).to.be.false;
|
||||
});
|
||||
|
||||
it('should be able to send securely to multiple recipients', function() {
|
||||
scope.to = [{
|
||||
address: 'asdf@asdf.de',
|
||||
secure: true
|
||||
}, {
|
||||
address: 'asdf@asdfg.de',
|
||||
secure: true
|
||||
}];
|
||||
scope.checkSendStatus();
|
||||
|
||||
expect(scope.okToSend).to.be.true;
|
||||
expect(scope.sendBtnText).to.equal('Send securely');
|
||||
expect(scope.sendBtnSecure).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('send to outbox', function() {
|
||||
it('should work', function() {
|
||||
scope.to = [{
|
||||
address: 'pity@dafool'
|
||||
}];
|
||||
scope.cc = [];
|
||||
scope.bcc = [];
|
||||
scope.subject = 'Ermahgerd!';
|
||||
scope.body = 'wow. much body! very text!';
|
||||
scope.attachments = [];
|
||||
scope.state.nav = {
|
||||
currentFolder: 'currentFolder'
|
||||
};
|
||||
|
||||
scope.replyTo = {};
|
||||
|
||||
outboxMock.put.withArgs(sinon.match(function(mail) {
|
||||
expect(mail.from).to.deep.equal([{
|
||||
address: emailAddress,
|
||||
name: realname
|
||||
}]);
|
||||
expect(mail.to).to.deep.equal(scope.to);
|
||||
expect(mail.cc).to.deep.equal(scope.cc);
|
||||
expect(mail.bcc).to.deep.equal(scope.bcc);
|
||||
expect(mail.body).to.contain(scope.body);
|
||||
expect(mail.subject).to.equal(scope.subject);
|
||||
expect(mail.attachments).to.be.empty;
|
||||
expect(mail.sentDate).to.exist;
|
||||
|
||||
|
||||
return true;
|
||||
})).yields();
|
||||
emailDaoMock.setFlags.yields();
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.not.exist;
|
||||
};
|
||||
|
||||
scope.sendToOutbox();
|
||||
|
||||
expect(outboxMock.put.calledOnce).to.be.true;
|
||||
expect(emailDaoMock.setFlags.calledOnce).to.be.true;
|
||||
expect(scope.state.lightbox).to.be.undefined;
|
||||
expect(scope.replyTo.answered).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('lookupAddressBook', function() {
|
||||
it('should work', function(done) {
|
||||
keychainMock.listLocalPublicKeys.yields(null, [{
|
||||
userId: 'test@asdf.com',
|
||||
publicKey: 'KEY'
|
||||
}]);
|
||||
|
||||
var result = scope.lookupAddressBook('test');
|
||||
|
||||
result.then(function(response) {
|
||||
expect(response).to.deep.equal([{
|
||||
address: 'test@asdf.com'
|
||||
}]);
|
||||
done();
|
||||
});
|
||||
scope.$digest();
|
||||
});
|
||||
|
||||
it('should work with cache', function(done) {
|
||||
scope.addressBookCache = [{
|
||||
address: 'test@asdf.com'
|
||||
}, {
|
||||
address: 'tes@asdf.com'
|
||||
}];
|
||||
|
||||
var result = scope.lookupAddressBook('test');
|
||||
|
||||
result.then(function(response) {
|
||||
expect(response).to.deep.equal([{
|
||||
address: 'test@asdf.com'
|
||||
}]);
|
||||
done();
|
||||
});
|
||||
scope.$digest();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
Loading…
Reference in New Issue