From 60342b3902e6b786c4855b0726417ea9f58aece4 Mon Sep 17 00:00:00 2001
From: Tankred Hase
Date: Fri, 10 Jan 2014 21:35:34 +0100
Subject: [PATCH 1/8] work in progress
---
src/js/controller/write.js | 83 +++++++++++++++++++++++++++++---------
src/tpl/write.html | 18 ++++++---
2 files changed, 78 insertions(+), 23 deletions(-)
diff --git a/src/js/controller/write.js b/src/js/controller/write.js
index 9771a79..7ec76a5 100644
--- a/src/js/controller/write.js
+++ b/src/js/controller/write.js
@@ -39,7 +39,9 @@ define(function(require) {
function resetFields() {
$scope.writerTitle = 'New email';
- $scope.to = '';
+ $scope.to = [{
+ address: ''
+ }];
$scope.subject = '';
$scope.body = '';
$scope.ciphertextPreview = '';
@@ -73,43 +75,59 @@ define(function(require) {
// Editing headers
//
- $scope.verifyTo = function() {
- if (!$scope.to) {
- resetDisplay();
+ $scope.onAddressUpdate = function(recipient) {
+ var to = $scope.to,
+ address = recipient.address;
+
+ // handle number of email inputs for multiple recipients
+ if (address.indexOf(' ') !== -1) {
+ recipient.address = address.replace(' ', '');
+ to.push({
+ address: ''
+ });
+ } else if (address.length === 0 && to.length > 1) {
+ to.splice(to.indexOf(recipient), 1);
+ }
+
+ verify(recipient);
+ };
+
+ function verify(recipient) {
+ // set display to insecure while fetching keys
+ recipient.key = undefined;
+ recipient.secure = false;
+
+ // verify email address
+ if (!util.validateEmailAddress(recipient.address)) {
+ recipient.secure = undefined;
return;
}
- // set display to insecure while fetching keys
- $scope.toKey = undefined;
- displayInsecure();
// check if to address is contained in known public keys
- emailDao._keychain.getReceiverPublicKey($scope.to, function(err, key) {
+ emailDao._keychain.getReceiverPublicKey(recipient.address, function(err, key) {
if (err) {
$scope.onError(err);
return;
}
// compare again since model could have changed during the roundtrip
- if (key && key.userId === $scope.to) {
- $scope.toKey = key;
- displaySecure();
+ if (key && key.userId === recipient.address) {
+ recipient.key = key;
+ recipient.secure = true;
$scope.$apply();
}
});
- };
+ }
function resetDisplay() {
- $scope.toSecure = undefined;
$scope.sendBtnText = undefined;
}
function displaySecure() {
- $scope.toSecure = true;
$scope.sendBtnText = 'Send securely';
}
function displayInsecure() {
- $scope.toSecure = false;
$scope.sendBtnText = 'Invite & send securely';
}
@@ -254,16 +272,45 @@ define(function(require) {
link: function(scope, elm, attrs) {
var model = $parse(attrs.autoSize);
scope.$watch(model, function(value) {
- if (!value) {
- return;
+ var width;
+
+ if (value.length < 12) {
+ width = (14 * 8) + 'px';
+ } else {
+ width = ((value.length + 2) * 8) + 'px';
}
- var width = ((value.length + 2) * 8) + 'px';
elm.css('width', width);
});
}
};
});
+ ngModule.directive('addressInput', function($timeout, $parse) {
+ return {
+ //scope: true, // optionally create a child scope
+ link: function(scope, element, attrs) {
+ element.bind('keydown', function(e) {
+ if (e.keyCode === 32) {
+ // space -> go to next input
+ e.preventDefault();
+ element[0].parent[0].nextSibling[0].children[0].focus();
+ }
+ scope.$apply();
+ });
+
+
+ var model = $parse(attrs.focusMe);
+ scope.$watch(model, function(value) {
+ if (value === true) {
+ $timeout(function() {
+ element[0].focus();
+ }, 100);
+ }
+ });
+ }
+ };
+ });
+
return WriteCtrl;
});
\ No newline at end of file
diff --git a/src/tpl/write.html b/src/tpl/write.html
index 5f00615..2ce5e1b 100644
--- a/src/tpl/write.html
+++ b/src/tpl/write.html
@@ -9,17 +9,25 @@
-
+
-----BEGIN ENCRYPTED PREVIEW-----
{{ciphertextPreview}}
-----END ENCRYPTED PREVIEW-----
@@ -35,7 +43,7 @@
-
+
From 6d0e562351e9a345d6cceb29cf3abda9625e5dd3 Mon Sep 17 00:00:00 2001
From: Tankred Hase
Date: Mon, 13 Jan 2014 16:42:10 +0100
Subject: [PATCH 2/8] implement field jumping on spaceand tab press
---
src/js/controller/write.js | 39 +++++++++++++++++++-------------------
src/tpl/write.html | 18 ++++++++++--------
2 files changed, 30 insertions(+), 27 deletions(-)
diff --git a/src/js/controller/write.js b/src/js/controller/write.js
index 7ec76a5..56b9a3b 100644
--- a/src/js/controller/write.js
+++ b/src/js/controller/write.js
@@ -30,7 +30,7 @@ define(function(require) {
fillFields(replyTo);
$scope.updatePreview();
- $scope.verifyTo();
+ verify($scope.to[0]);
},
close: function() {
this.open = false;
@@ -42,6 +42,12 @@ define(function(require) {
$scope.to = [{
address: ''
}];
+ $scope.cc = [{
+ address: ''
+ }];
+ $scope.bcc = [{
+ address: ''
+ }];
$scope.subject = '';
$scope.body = '';
$scope.ciphertextPreview = '';
@@ -56,7 +62,7 @@ define(function(require) {
$scope.writerTitle = 'Reply';
// fill recipient field
- $scope.to = re.from[0].address;
+ $scope.to[0].address = re.from[0].address;
// fill subject
$scope.subject = 'Re: ' + ((re.subject) ? re.subject.replace('Re: ', '') : '');
@@ -75,18 +81,18 @@ define(function(require) {
// Editing headers
//
- $scope.onAddressUpdate = function(recipient) {
- var to = $scope.to,
+ $scope.onAddressUpdate = function(field, index) {
+ var recipient = field[index],
address = recipient.address;
// handle number of email inputs for multiple recipients
if (address.indexOf(' ') !== -1) {
recipient.address = address.replace(' ', '');
- to.push({
+ field.push({
address: ''
});
- } else if (address.length === 0 && to.length > 1) {
- to.splice(to.indexOf(recipient), 1);
+ } else if (address.length === 0 && field.length > 1) {
+ field.splice(field.indexOf(recipient), 1);
}
verify(recipient);
@@ -286,25 +292,20 @@ define(function(require) {
};
});
- ngModule.directive('addressInput', function($timeout, $parse) {
+ ngModule.directive('addressInput', function($timeout) {
return {
//scope: true, // optionally create a child scope
link: function(scope, element, attrs) {
+ // get prefix for id
+ var idPrefix = attrs.addressInput;
element.bind('keydown', function(e) {
if (e.keyCode === 32) {
// space -> go to next input
- e.preventDefault();
- element[0].parent[0].nextSibling[0].children[0].focus();
- }
- scope.$apply();
- });
-
-
- var model = $parse(attrs.focusMe);
- scope.$watch(model, function(value) {
- if (value === true) {
$timeout(function() {
- element[0].focus();
+ // find next input and focus
+ var index = attrs.id.replace(idPrefix, '');
+ var nextId = idPrefix + (parseInt(index, 10) + 1);
+ document.getElementById(nextId).focus();
}, 100);
}
});
diff --git a/src/tpl/write.html b/src/tpl/write.html
index 2ce5e1b..99e33bc 100644
--- a/src/tpl/write.html
+++ b/src/tpl/write.html
@@ -9,25 +9,27 @@
-
+
-----BEGIN ENCRYPTED PREVIEW-----
{{ciphertextPreview}}
-----END ENCRYPTED PREVIEW-----
@@ -43,7 +45,7 @@
-
+
From 87d26383f5e2433bf177264ecb14aefa7283a885 Mon Sep 17 00:00:00 2001
From: Tankred Hase
Date: Mon, 13 Jan 2014 18:38:45 +0100
Subject: [PATCH 3/8] sending email to multiple receivers works
---
src/js/controller/write.js | 50 ++++++++++++++++++++++++--------------
src/js/dao/email-dao.js | 46 +++++------------------------------
2 files changed, 38 insertions(+), 58 deletions(-)
diff --git a/src/js/controller/write.js b/src/js/controller/write.js
index 56b9a3b..8818522 100644
--- a/src/js/controller/write.js
+++ b/src/js/controller/write.js
@@ -153,20 +153,13 @@ define(function(require) {
};
$scope.sendToOutbox = function() {
- var to, email;
-
- // validate recipients
- to = $scope.to.replace(/\s/g, '').split(/[,;]/);
- if (!to || to.length < 1) {
- $scope.onError({
- errMsg: 'Seperate recipients with a comma!',
- sync: true
- });
- return;
- }
+ var email;
+ // build email model for smtp-client
email = {
- to: [], // list of receivers
+ to: [],
+ cc: [],
+ bcc: [],
subject: $scope.subject, // Subject line
body: $scope.body // use parsed plaintext body
};
@@ -174,13 +167,34 @@ define(function(require) {
name: '',
address: emailDao._account.emailAddress
}];
- to.forEach(function(address) {
- email.to.push({
- name: '',
- address: address
- });
- });
+ // validate recipients and gather public keys
+ email.receiverKeys = []; // gather public keys for emailDao._encrypt
+
+ appendReceivers($scope.to, email.to);
+ appendReceivers($scope.cc, email.cc);
+ appendReceivers($scope.bcc, email.bcc);
+
+ function appendReceivers(srcField, destField) {
+ srcField.forEach(function(recipient) {
+ // validate address
+ if (!util.validateEmailAddress(recipient.address)) {
+ return;
+ }
+
+ // append address to email model
+ destField.push({
+ address: recipient.address
+ });
+
+ // add public key to list of recipient keys
+ if (recipient.key && recipient.key.publicKey) {
+ email.receiverKeys.push(recipient.key.publicKey);
+ }
+ });
+ }
+
+ // persist the email locally for later smtp transmission
emailDao.store(email, function(err) {
if (err) {
$scope.onError(err);
diff --git a/src/js/dao/email-dao.js b/src/js/dao/email-dao.js
index 8352354..ebe818c 100644
--- a/src/js/dao/email-dao.js
+++ b/src/js/dao/email-dao.js
@@ -882,51 +882,17 @@ define(function(require) {
return;
}
- // validate email addresses
- for (var i = email.to.length - 1; i >= 0; i--) {
- if (!util.validateEmailAddress(email.to[i].address)) {
- callback({
- errMsg: 'Invalid recipient: ' + email.to[i].address
- });
- return;
- }
- }
-
- if (!util.validateEmailAddress(email.from[0].address)) {
- callback({
- errMsg: 'Invalid sender: ' + email.from
- });
- return;
- }
-
- // only support single recipient for e-2-e encryption
- // check if receiver has a public key
- self._keychain.getReceiverPublicKey(email.to[0].address, function(err, receiverPubkey) {
+ // public key found... encrypt and send
+ self._encrypt({
+ email: email,
+ keys: email.receiverKeys // this Array is set in writer controller
+ }, function(err, email) {
if (err) {
callback(err);
return;
}
- // validate public key
- if (!receiverPubkey) {
- callback({
- errMsg: 'User has no public key yet!'
- });
- return;
- }
-
- // public key found... encrypt and send
- self._encrypt({
- email: email,
- keys: [receiverPubkey.publicKey]
- }, function(err, email) {
- if (err) {
- callback(err);
- return;
- }
-
- self._smtpClient.send(email, callback);
- });
+ self._smtpClient.send(email, callback);
});
};
From 4b638a0deed3662c8d96b73ce03369b639ca2aa9 Mon Sep 17 00:00:00 2001
From: Tankred Hase
Date: Mon, 13 Jan 2014 22:43:43 +0100
Subject: [PATCH 4/8] reading and writing to multiple recipients works
---
src/js/controller/read.js | 2 ++
src/js/controller/write.js | 4 +++-
src/tpl/read.html | 2 +-
src/tpl/write.html | 6 ------
4 files changed, 6 insertions(+), 8 deletions(-)
diff --git a/src/js/controller/read.js b/src/js/controller/read.js
index b93d21b..90aaac8 100644
--- a/src/js/controller/read.js
+++ b/src/js/controller/read.js
@@ -53,6 +53,8 @@ define(function(require) {
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) {
diff --git a/src/js/controller/write.js b/src/js/controller/write.js
index 8818522..691afc4 100644
--- a/src/js/controller/write.js
+++ b/src/js/controller/write.js
@@ -62,7 +62,9 @@ define(function(require) {
$scope.writerTitle = 'Reply';
// fill recipient field
- $scope.to[0].address = re.from[0].address;
+ $scope.to.unshift({
+ address: re.from[0].address
+ });
// fill subject
$scope.subject = 'Re: ' + ((re.subject) ? re.subject.replace('Re: ', '') : '');
diff --git a/src/tpl/read.html b/src/tpl/read.html
index bcaefe1..ca2dbf9 100644
--- a/src/tpl/read.html
+++ b/src/tpl/read.html
@@ -16,7 +16,7 @@
- CC: {{u.name || u.address}}
+ Cc: {{u.name || u.address}}
diff --git a/src/tpl/write.html b/src/tpl/write.html
index 99e33bc..9f76f5f 100644
--- a/src/tpl/write.html
+++ b/src/tpl/write.html
@@ -19,12 +19,6 @@
-
- Bcc:
-
-
-
-
From 2eaf7ca17230bb3d9fbc12461827edd1ed7de7b2 Mon Sep 17 00:00:00 2001
From: Tankred Hase
Date: Mon, 13 Jan 2014 23:54:53 +0100
Subject: [PATCH 5/8] check valid send states in editor
---
src/js/controller/write.js | 44 +++++++++++++++++++++++++++++++-------
src/sass/views/_read.scss | 2 +-
src/sass/views/_write.scss | 6 ++++++
src/tpl/write.html | 2 +-
4 files changed, 44 insertions(+), 10 deletions(-)
diff --git a/src/js/controller/write.js b/src/js/controller/write.js
index 691afc4..96236e9 100644
--- a/src/js/controller/write.js
+++ b/src/js/controller/write.js
@@ -108,6 +108,7 @@ define(function(require) {
// verify email address
if (!util.validateEmailAddress(recipient.address)) {
recipient.secure = undefined;
+ checkSendStatus();
return;
}
@@ -122,21 +123,48 @@ define(function(require) {
if (key && key.userId === recipient.address) {
recipient.key = key;
recipient.secure = true;
- $scope.$apply();
}
+
+ checkSendStatus();
+ $scope.$apply();
});
}
- function resetDisplay() {
+ function checkSendStatus() {
+ $scope.okToSend = false;
$scope.sendBtnText = undefined;
- }
+ $scope.sendBtnSecure = undefined;
- function displaySecure() {
- $scope.sendBtnText = 'Send securely';
- }
+ var allSecure = true;
+ var numReceivers = 0;
- function displayInsecure() {
- $scope.sendBtnText = 'Invite & send securely';
+ // count number of receivers and check security
+ $scope.to.forEach(check);
+ $scope.cc.forEach(check);
+ $scope.bcc.forEach(check);
+
+ function check(recipient) {
+ // validate address
+ if (!util.validateEmailAddress(recipient.address)) {
+ return;
+ }
+ numReceivers++;
+ if (!recipient.secure) {
+ allSecure = false;
+ }
+ }
+
+ // sender can invite only one use at a time
+ if (!allSecure && numReceivers === 1) {
+ $scope.sendBtnText = 'Invite & send securely';
+ $scope.okToSend = true;
+ $scope.sendBtnSecure = false;
+ } else if (allSecure && numReceivers > 0) {
+ // all recipients are secure
+ $scope.sendBtnText = 'Send securely';
+ $scope.okToSend = true;
+ $scope.sendBtnSecure = true;
+ }
}
//
diff --git a/src/sass/views/_read.scss b/src/sass/views/_read.scss
index 00f6ef7..535488b 100644
--- a/src/sass/views/_read.scss
+++ b/src/sass/views/_read.scss
@@ -7,7 +7,7 @@
.headers {
p {
margin: 0px;
- padding: 0px;
+ padding: 0px;
}
.subject {
diff --git a/src/sass/views/_write.scss b/src/sass/views/_write.scss
index a7c3780..bb309d4 100644
--- a/src/sass/views/_write.scss
+++ b/src/sass/views/_write.scss
@@ -20,6 +20,12 @@
.headers {
margin-top: 10px;
+
+ p {
+ margin: 0.2em 0;
+ padding: 0.2em 0;
+ }
+
span {
color: $color-grey;
}
diff --git a/src/tpl/write.html b/src/tpl/write.html
index 9f76f5f..c3ddf1c 100644
--- a/src/tpl/write.html
+++ b/src/tpl/write.html
@@ -39,7 +39,7 @@
-
+
From 9bb70fac5daea2841cd808557ad4cd5ca9cce07e Mon Sep 17 00:00:00 2001
From: Tankred Hase
Date: Tue, 14 Jan 2014 13:26:23 +0100
Subject: [PATCH 6/8] fix email dao tests
---
src/js/dao/email-dao.js | 2 +-
test/new-unit/email-dao-test.js | 66 ++-------------------------------
2 files changed, 4 insertions(+), 64 deletions(-)
diff --git a/src/js/dao/email-dao.js b/src/js/dao/email-dao.js
index ebe818c..66800c7 100644
--- a/src/js/dao/email-dao.js
+++ b/src/js/dao/email-dao.js
@@ -875,7 +875,7 @@ define(function(require) {
}
// validate the email input
- if (!email.to || !email.from || !email.to[0].address || !email.from[0].address) {
+ if (!email.to || !email.from || !email.to[0].address || !email.from[0].address || !Array.isArray(email.receiverKeys)) {
callback({
errMsg: 'Invalid email object!'
});
diff --git a/test/new-unit/email-dao-test.js b/test/new-unit/email-dao-test.js
index f596085..146183f 100644
--- a/test/new-unit/email-dao-test.js
+++ b/test/new-unit/email-dao-test.js
@@ -75,7 +75,8 @@ define(function(require) {
subject: 'qweasd',
body: 'asd',
unread: false,
- answered: false
+ answered: false,
+ receiverKeys: ['-----BEGIN PGP PUBLIC KEY-----\nasd\n-----END PGP PUBLIC KEY-----']
};
nonWhitelistedMail = {
uid: 1234,
@@ -2409,11 +2410,7 @@ define(function(require) {
describe('sendEncrypted', function() {
it('should work', function(done) {
var encryptStub = sinon.stub(dao, '_encrypt').yields(null, {});
- keychainStub.getReceiverPublicKey.withArgs(dummyDecryptedMail.to[0].address).yields(null, {
- _id: "fcf8b4aa-5d09-4089-8b4f-e3bc5091daf3",
- userId: dummyDecryptedMail.to[0].address,
- publicKey: publicKey
- });
+
smtpClientStub.send.yields();
dao.sendEncrypted({
@@ -2421,7 +2418,6 @@ define(function(require) {
}, function(err) {
expect(err).to.not.exist;
- expect(keychainStub.getReceiverPublicKey.calledOnce).to.be.true;
expect(encryptStub.calledOnce).to.be.true;
expect(smtpClientStub.send.calledOnce).to.be.true;
@@ -2430,72 +2426,18 @@ define(function(require) {
});
it('should not work when encryption fails', function(done) {
var encryptStub = sinon.stub(dao, '_encrypt').yields({});
- keychainStub.getReceiverPublicKey.withArgs(dummyDecryptedMail.to[0].address).yields(null, {
- _id: "fcf8b4aa-5d09-4089-8b4f-e3bc5091daf3",
- userId: dummyDecryptedMail.to[0].address,
- publicKey: publicKey
- });
dao.sendEncrypted({
email: dummyDecryptedMail
}, function(err) {
expect(err).to.exist;
- expect(keychainStub.getReceiverPublicKey.calledOnce).to.be.true;
expect(encryptStub.calledOnce).to.be.true;
expect(smtpClientStub.send.called).to.be.false;
done();
});
});
- it('should not work when key retrieval fails', function(done) {
- var encryptStub = sinon.stub(dao, '_encrypt');
- keychainStub.getReceiverPublicKey.withArgs(dummyDecryptedMail.to[0].address).yields({});
-
- dao.sendEncrypted({
- email: dummyDecryptedMail
- }, function(err) {
- expect(err).to.exist;
-
- expect(keychainStub.getReceiverPublicKey.calledOnce).to.be.true;
- expect(encryptStub.called).to.be.false;
- expect(smtpClientStub.send.called).to.be.false;
-
- done();
- });
- });
- it('should not work invalid recipients', function(done) {
- var encryptStub = sinon.stub(dao, '_encrypt');
- dummyDecryptedMail.to[0].address = 'asd@asd';
-
- dao.sendEncrypted({
- email: dummyDecryptedMail
- }, function(err) {
- expect(err).to.exist;
-
- expect(keychainStub.getReceiverPublicKey.called).to.be.false;
- expect(encryptStub.called).to.be.false;
- expect(smtpClientStub.send.called).to.be.false;
-
- done();
- });
- });
- it('should not work with without sender', function(done) {
- var encryptStub = sinon.stub(dao, '_encrypt');
- dummyDecryptedMail.from[0].address = 'asd@asd';
-
- dao.sendEncrypted({
- email: dummyDecryptedMail
- }, function(err) {
- expect(err).to.exist;
-
- expect(keychainStub.getReceiverPublicKey.called).to.be.false;
- expect(encryptStub.called).to.be.false;
- expect(smtpClientStub.send.called).to.be.false;
-
- done();
- });
- });
it('should not work without recipients', function(done) {
var encryptStub = sinon.stub(dao, '_encrypt');
delete dummyDecryptedMail.to;
@@ -2505,7 +2447,6 @@ define(function(require) {
}, function(err) {
expect(err).to.exist;
- expect(keychainStub.getReceiverPublicKey.called).to.be.false;
expect(encryptStub.called).to.be.false;
expect(smtpClientStub.send.called).to.be.false;
@@ -2521,7 +2462,6 @@ define(function(require) {
}, function(err) {
expect(err).to.exist;
- expect(keychainStub.getReceiverPublicKey.called).to.be.false;
expect(encryptStub.called).to.be.false;
expect(smtpClientStub.send.called).to.be.false;
From 0dc1cc68bccccfb77a920626baf54c2cb82e5c8a Mon Sep 17 00:00:00 2001
From: Tankred Hase
Date: Tue, 14 Jan 2014 16:11:59 +0100
Subject: [PATCH 7/8] fix tests
---
src/js/controller/write.js | 25 ++-
test/new-unit/write-ctrl-test.js | 280 ++++++++++++++++++++++++-------
2 files changed, 232 insertions(+), 73 deletions(-)
diff --git a/src/js/controller/write.js b/src/js/controller/write.js
index 96236e9..46e5e35 100644
--- a/src/js/controller/write.js
+++ b/src/js/controller/write.js
@@ -30,7 +30,7 @@ define(function(require) {
fillFields(replyTo);
$scope.updatePreview();
- verify($scope.to[0]);
+ $scope.verify($scope.to[0]);
},
close: function() {
this.open = false;
@@ -83,6 +83,9 @@ define(function(require) {
// Editing headers
//
+ /**
+ * This event is fired when editing the email address headers. It checks is space is pressed and if so, creates a new address field.
+ */
$scope.onAddressUpdate = function(field, index) {
var recipient = field[index],
address = recipient.address;
@@ -97,10 +100,13 @@ define(function(require) {
field.splice(field.indexOf(recipient), 1);
}
- verify(recipient);
+ $scope.verify(recipient);
};
- function verify(recipient) {
+ /**
+ * Verify and email address and fetch its public key
+ */
+ $scope.verify = function(recipient) {
// set display to insecure while fetching keys
recipient.key = undefined;
recipient.secure = false;
@@ -108,7 +114,7 @@ define(function(require) {
// verify email address
if (!util.validateEmailAddress(recipient.address)) {
recipient.secure = undefined;
- checkSendStatus();
+ $scope.checkSendStatus();
return;
}
@@ -125,12 +131,15 @@ define(function(require) {
recipient.secure = true;
}
- checkSendStatus();
+ $scope.checkSendStatus();
$scope.$apply();
});
- }
+ };
- function checkSendStatus() {
+ /**
+ * Check if it is ok to send an email depending on the invitation state of the addresses
+ */
+ $scope.checkSendStatus = function() {
$scope.okToSend = false;
$scope.sendBtnText = undefined;
$scope.sendBtnSecure = undefined;
@@ -165,7 +174,7 @@ define(function(require) {
$scope.okToSend = true;
$scope.sendBtnSecure = true;
}
- }
+ };
//
// Editing email body
diff --git a/test/new-unit/write-ctrl-test.js b/test/new-unit/write-ctrl-test.js
index 69e3b51..c4490bf 100644
--- a/test/new-unit/write-ctrl-test.js
+++ b/test/new-unit/write-ctrl-test.js
@@ -47,7 +47,9 @@ define(function(require) {
expect(scope.state.writer.open).to.be.false;
expect(scope.state.writer.write).to.exist;
expect(scope.state.writer.close).to.exist;
- expect(scope.verifyTo).to.exist;
+ expect(scope.verify).to.exist;
+ expect(scope.onAddressUpdate).to.exist;
+ expect(scope.checkSendStatus).to.exist;
expect(scope.updatePreview).to.exist;
expect(scope.sendToOutbox).to.exist;
});
@@ -65,22 +67,24 @@ define(function(require) {
describe('write', function() {
it('should prepare write view', function() {
- var verifyToMock = sinon.stub(scope, 'verifyTo');
+ var verifyMock = sinon.stub(scope, 'verify');
scope.state.writer.write();
expect(scope.writerTitle).to.equal('New email');
- expect(scope.to).to.equal('');
+ expect(scope.to).to.deep.equal([{
+ address: ''
+ }]);
expect(scope.subject).to.equal('');
expect(scope.body).to.equal('');
expect(scope.ciphertextPreview).to.equal('');
- expect(verifyToMock.calledOnce).to.be.true;
+ expect(verifyMock.calledOnce).to.be.true;
- scope.verifyTo.restore();
+ scope.verify.restore();
});
it('should prefill write view for response', function() {
- var verifyToMock = sinon.stub(scope, 'verifyTo'),
+ var verifyMock = sinon.stub(scope, 'verify'),
address = 'pity@dafool',
subject = 'Ermahgerd!',
body = 'so much body!',
@@ -96,61 +100,210 @@ define(function(require) {
scope.state.writer.write(re);
expect(scope.writerTitle).to.equal('Reply');
- expect(scope.to).to.equal(address);
+ expect(scope.to).to.deep.equal([{
+ address: address,
+ }, {
+ address: ''
+ }]);
expect(scope.subject).to.equal('Re: ' + subject);
expect(scope.body).to.contain(body);
expect(scope.ciphertextPreview).to.not.be.empty;
- expect(verifyToMock.calledOnce).to.be.true;
+ expect(verifyMock.calledOnce).to.be.true;
- scope.verifyTo.restore();
+ scope.verify.restore();
});
});
- describe('verifyTo', function() {
- it('should verify the recipient as secure', function() {
- var id = scope.to = 'pity@da.fool';
- keychainMock.getReceiverPublicKey.withArgs(id).yields(null, {
- userId: id
- });
+ describe('onAddressUpdate', function() {
+ var verifyMock;
- scope.verifyTo();
-
- expect(scope.toSecure).to.be.true;
- expect(scope.sendBtnText).to.equal('Send securely');
+ beforeEach(function() {
+ verifyMock = sinon.stub(scope, 'verify');
});
- it('should verify the recipient as not secure', function(done) {
- var id = scope.to = 'pity@da.fool';
- keychainMock.getReceiverPublicKey.withArgs(id).yields({
+ afterEach(function() {
+ scope.verify.restore();
+ });
+
+ it('should add new field item if space is pressed', function() {
+ var to = [{
+ address: 'asdf@asdf.de '
+ }];
+ scope.onAddressUpdate(to, 0);
+
+ expect(to.length).to.equal(2);
+ expect(to[0].address).to.equal('asdf@asdf.de');
+ expect(to[1].address).to.equal('');
+ expect(verifyMock.calledOnce).to.be.true;
+ });
+
+ it('should remove field item if address is empty', function() {
+ var to = [{
+ address: 'asdf@asdf.de'
+ }, {
+ address: ''
+ }];
+ scope.onAddressUpdate(to, 1);
+
+ expect(to.length).to.equal(1);
+ expect(to[0].address).to.equal('asdf@asdf.de');
+ expect(verifyMock.calledOnce).to.be.true;
+ });
+
+ it('should not remove last field item if address is empty', function() {
+ var to = [{
+ address: ''
+ }];
+ scope.onAddressUpdate(to, 0);
+
+ expect(to.length).to.equal(1);
+ expect(to[0].address).to.equal('');
+ expect(verifyMock.calledOnce).to.be.true;
+ });
+
+ it('should do nothing for normal address', function() {
+ var to = [{
+ address: 'asdf@asdf.de'
+ }];
+ scope.onAddressUpdate(to, 0);
+
+ expect(to.length).to.equal(1);
+ expect(to[0].address).to.equal('asdf@asdf.de');
+ expect(verifyMock.calledOnce).to.be.true;
+ });
+ });
+
+ describe('verify', function() {
+ var checkSendStatusMock;
+
+ beforeEach(function() {
+ checkSendStatusMock = sinon.stub(scope, 'checkSendStatus');
+ });
+
+ afterEach(function() {
+ scope.checkSendStatus.restore();
+ });
+
+ 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.calledOnce).to.be.true;
+ expect(keychainMock.getReceiverPublicKey.called).to.be.false;
+ });
+
+ it('should not work for error in keychain', function(done) {
+ var recipient = {
+ address: 'asds@example.com'
+ };
+
+ keychainMock.getReceiverPublicKey.withArgs(recipient.address).yields({
errMsg: '404 not found yadda yadda'
});
scope.onError = function() {
- expect(scope.toSecure).to.be.false;
- expect(scope.sendBtnText).to.equal('Invite & send securely');
+ expect(recipient.key).to.be.undefined;
+ expect(recipient.secure).to.be.false;
+ expect(scope.checkSendStatus.called).to.be.false;
+ expect(keychainMock.getReceiverPublicKey.calledOnce).to.be.true;
done();
};
- scope.verifyTo();
+ scope.verify(recipient);
});
- it('should reset display if there is no recipient', function() {
- scope.to = undefined;
- scope.verifyTo();
+ it('should work', function(done) {
+ var recipient = {
+ address: 'asdf@example.com'
+ };
+
+ keychainMock.getReceiverPublicKey.yields(null, {
+ userId: 'asdf@example.com'
+ });
+ scope.$apply = function() {
+ expect(recipient.key).to.deep.equal({
+ userId: 'asdf@example.com'
+ });
+ expect(recipient.secure).to.be.true;
+ expect(scope.checkSendStatus.calledOnce).to.be.true;
+ expect(keychainMock.getReceiverPublicKey.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 not be to invite 1 user', function() {
+ scope.to = [{
+ address: 'asdf@asdf.de'
+ }];
+ scope.checkSendStatus();
+
+ expect(scope.okToSend).to.be.true;
+ expect(scope.sendBtnText).to.equal('Invite & send securely');
+ expect(scope.sendBtnSecure).to.be.false;
+ });
+
+ it('should not be able to invite multiple recipients', function() {
+ scope.to = [{
+ address: 'asdf@asdf.de'
+ }, {
+ address: 'asdf@asdfg.de'
+ }];
+ 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 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 when offline', function(done) {
- var verifyToSpy = sinon.spy(scope, 'verifyTo'),
- re = {
- from: [{
- address: 'pity@dafool'
- }],
- subject: 'Ermahgerd!',
- sentDate: new Date(),
- body: 'so much body!'
- };
+ var re = {
+ from: [{
+ address: 'pity@dafool'
+ }],
+ subject: 'Ermahgerd!',
+ sentDate: new Date(),
+ body: 'so much body!'
+ };
scope.state.nav = {
currentFolder: 'currentFolder'
@@ -160,11 +313,9 @@ define(function(require) {
scope.onError = function(err) {
expect(err).to.not.exist;
expect(scope.state.writer.open).to.be.false;
- expect(verifyToSpy.calledOnce).to.be.true;
expect(emailDaoMock.store.calledOnce).to.be.true;
expect(emailDaoMock.sync.calledOnce).to.be.true;
- scope.verifyTo.restore();
done();
};
@@ -178,15 +329,14 @@ define(function(require) {
});
it('should work', function(done) {
- var verifyToSpy = sinon.spy(scope, 'verifyTo'),
- re = {
- from: [{
- address: 'pity@dafool'
- }],
- subject: 'Ermahgerd!',
- sentDate: new Date(),
- body: 'so much body!'
- };
+ var re = {
+ from: [{
+ address: 'pity@dafool'
+ }],
+ subject: 'Ermahgerd!',
+ sentDate: new Date(),
+ body: 'so much body!'
+ };
scope.state.nav = {
currentFolder: 'currentFolder'
@@ -196,11 +346,9 @@ define(function(require) {
scope.onError = function(err) {
expect(err).to.not.exist;
expect(scope.state.writer.open).to.be.false;
- expect(verifyToSpy.calledOnce).to.be.true;
expect(emailDaoMock.store.calledOnce).to.be.true;
expect(emailDaoMock.sync.calledOnce).to.be.true;
- scope.verifyTo.restore();
done();
};
@@ -212,15 +360,14 @@ define(function(require) {
});
it('should fail', function(done) {
- var verifyToSpy = sinon.spy(scope, 'verifyTo'),
- re = {
- from: [{
- address: 'pity@dafool'
- }],
- subject: 'Ermahgerd!',
- sentDate: new Date(),
- body: 'so much body!'
- };
+ var re = {
+ from: [{
+ address: 'pity@dafool'
+ }],
+ subject: 'Ermahgerd!',
+ sentDate: new Date(),
+ body: 'so much body!'
+ };
scope.state.nav = {
currentFolder: 'currentFolder'
@@ -230,11 +377,9 @@ define(function(require) {
scope.onError = function(err) {
expect(err).to.exist;
expect(scope.state.writer.open).to.be.false;
- expect(verifyToSpy.calledOnce).to.be.true;
expect(emailDaoMock.store.calledOnce).to.be.true;
expect(emailDaoMock.sync.calledOnce).to.be.true;
- scope.verifyTo.restore();
done();
};
@@ -246,14 +391,20 @@ define(function(require) {
});
it('should not work and not close the write view', function(done) {
- scope.state.writer.open = true;
- scope.to = 'a, b, c';
+ scope.state.writer.write();
+
+ scope.to = [{
+ address: 'pity@dafool.de',
+ key: {
+ publicKey: '----- PGP Stuff -----'
+ }
+ }];
scope.body = 'asd';
scope.subject = 'yaddablabla';
scope.toKey = 'Public Key';
emailDaoMock.store.withArgs(sinon.match(function(mail) {
- return mail.from[0].address === emailAddress && mail.to.length === 3;
+ return mail.from[0].address === emailAddress && mail.to.length === 1 && mail.receiverKeys.length === 1;
})).yields({
errMsg: 'snafu'
});
@@ -264,7 +415,6 @@ define(function(require) {
expect(emailDaoMock.store.calledOnce).to.be.true;
done();
};
-
scope.sendToOutbox();
});
});
From 8dd30e3752cb1ce57642c3307002224acc3cbc14 Mon Sep 17 00:00:00 2001
From: Tankred Hase
Date: Wed, 15 Jan 2014 10:57:28 +0100
Subject: [PATCH 8/8] externalize strings from editor
---
src/js/app-config.js | 4 +++-
src/js/controller/write.js | 5 +++--
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/src/js/app-config.js b/src/js/app-config.js
index 7106b4e..026f38e 100644
--- a/src/js/app-config.js
+++ b/src/js/app-config.js
@@ -54,7 +54,9 @@ define(function(require) {
cryptSuffix: '-----END PGP MESSAGE-----',
signature: 'Sent securely from Whiteout Mail',
webSite: 'http://whiteout.io',
- verificationSubject: 'New public key uploaded'
+ verificationSubject: 'New public key uploaded',
+ sendBtnInvite: 'Invite & send securely',
+ sendBtnSecure: 'Send securely'
};
return app;
diff --git a/src/js/controller/write.js b/src/js/controller/write.js
index 46e5e35..547228d 100644
--- a/src/js/controller/write.js
+++ b/src/js/controller/write.js
@@ -5,6 +5,7 @@ define(function(require) {
appController = require('js/app-controller'),
aes = require('cryptoLib/aes-cbc'),
util = require('cryptoLib/util'),
+ str = require('js/app-config').string,
emailDao;
//
@@ -165,12 +166,12 @@ define(function(require) {
// sender can invite only one use at a time
if (!allSecure && numReceivers === 1) {
- $scope.sendBtnText = 'Invite & send securely';
+ $scope.sendBtnText = str.sendBtnInvite;
$scope.okToSend = true;
$scope.sendBtnSecure = false;
} else if (allSecure && numReceivers > 0) {
// all recipients are secure
- $scope.sendBtnText = 'Send securely';
+ $scope.sendBtnText = str.sendBtnSecure;
$scope.okToSend = true;
$scope.sendBtnSecure = true;
}