mirror of
https://github.com/moparisthebest/mail
synced 2025-02-12 05:00:19 -05:00
Merge pull request #27 from whiteout-io/dev/send-plaintext
Dev/send plaintext
This commit is contained in:
commit
02628a259a
@ -53,14 +53,14 @@ define(function(require) {
|
|||||||
subjectPrefix: '[whiteout] ',
|
subjectPrefix: '[whiteout] ',
|
||||||
fallbackSubject: '(no subject)',
|
fallbackSubject: '(no subject)',
|
||||||
invitationSubject: 'Invitation to a private conversation',
|
invitationSubject: 'Invitation to a private conversation',
|
||||||
invitationMessage: 'I would like to invite you to a private conversation!\n\nIn order to read my encrypted message please install the Whiteout Mail application. This application is used to read and write messages securely with strong encryption applied.\n\nGo to the Whiteout Networks homepage to learn more and to download the application: https://whiteout.io',
|
invitationMessage: 'Hi,\n\nI would like to invite you to a private conversation!\n\nPlease install the Whiteout Mail application. This application is used to read and write messages securely with strong encryption applied.\n\nGo to the Whiteout Networks homepage to learn more and to download the application: https://whiteout.io',
|
||||||
message: 'Hi,\n\nthis is a private conversation. To read my encrypted message below, simply open it in Whiteout Mail.\n\nOpen Whiteout Mail: https://chrome.google.com/webstore/detail/jjgghafhamholjigjoghcfcekhkonijg\n\n',
|
message: 'Hi,\n\nthis is a private conversation. To read my encrypted message below, simply open it in Whiteout Mail.\nOpen Whiteout Mail: https://chrome.google.com/webstore/detail/jjgghafhamholjigjoghcfcekhkonijg',
|
||||||
cryptPrefix: '-----BEGIN PGP MESSAGE-----',
|
cryptPrefix: '-----BEGIN PGP MESSAGE-----',
|
||||||
cryptSuffix: '-----END PGP MESSAGE-----',
|
cryptSuffix: '-----END PGP MESSAGE-----',
|
||||||
signature: 'Sent securely from Whiteout Mail',
|
signature: '\n\n\n--\nSent from Whiteout Mail - PGP encryption for the rest of us.\nhttps://whiteout.io\n\n',
|
||||||
webSite: 'http://whiteout.io',
|
webSite: 'http://whiteout.io',
|
||||||
verificationSubject: 'New public key uploaded',
|
verificationSubject: 'New public key uploaded',
|
||||||
sendBtnInvite: 'Invite & send securely',
|
sendBtnClear: 'Send',
|
||||||
sendBtnSecure: 'Send securely'
|
sendBtnSecure: 'Send securely'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -348,17 +348,15 @@ define(function(require) {
|
|||||||
// init objects and inject dependencies
|
// init objects and inject dependencies
|
||||||
restDao = new RestDAO();
|
restDao = new RestDAO();
|
||||||
pubkeyDao = new PublicKeyDAO(restDao);
|
pubkeyDao = new PublicKeyDAO(restDao);
|
||||||
invitationDao = new InvitationDAO(restDao);
|
|
||||||
lawnchairDao = new LawnchairDAO();
|
lawnchairDao = new LawnchairDAO();
|
||||||
userStorage = new DeviceStorageDAO(lawnchairDao);
|
userStorage = new DeviceStorageDAO(lawnchairDao);
|
||||||
|
|
||||||
keychain = new KeychainDAO(lawnchairDao, pubkeyDao);
|
self._invitationDao = invitationDao = new InvitationDAO(restDao);
|
||||||
self._keychain = keychain;
|
self._keychain = keychain = new KeychainDAO(lawnchairDao, pubkeyDao);
|
||||||
pgp = new PGP();
|
self._crypto = pgp = new PGP();
|
||||||
self._crypto = pgp;
|
|
||||||
self._pgpbuilder = pgpbuilder = new PgpBuilder();
|
self._pgpbuilder = pgpbuilder = new PgpBuilder();
|
||||||
self._emailDao = emailDao = new EmailDAO(keychain, pgp, userStorage, pgpbuilder, mailreader);
|
self._emailDao = emailDao = new EmailDAO(keychain, pgp, userStorage, pgpbuilder, mailreader);
|
||||||
self._outboxBo = new OutboxBO(emailDao, keychain, userStorage, invitationDao);
|
self._outboxBo = new OutboxBO(emailDao, keychain, userStorage);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -3,9 +3,7 @@ define(function(require) {
|
|||||||
|
|
||||||
var _ = require('underscore'),
|
var _ = require('underscore'),
|
||||||
util = require('cryptoLib/util'),
|
util = require('cryptoLib/util'),
|
||||||
str = require('js/app-config').string,
|
|
||||||
config = require('js/app-config').config,
|
config = require('js/app-config').config,
|
||||||
InvitationDAO = require('js/dao/invitation-dao'),
|
|
||||||
outboxDb = 'email_OUTBOX';
|
outboxDb = 'email_OUTBOX';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -13,7 +11,7 @@ define(function(require) {
|
|||||||
* The local outbox takes care of the emails before they are being sent.
|
* The local outbox takes care of the emails before they are being sent.
|
||||||
* It also checks periodically if there are any mails in the local device storage to be sent.
|
* It also checks periodically if there are any mails in the local device storage to be sent.
|
||||||
*/
|
*/
|
||||||
var OutboxBO = function(emailDao, keychain, devicestorage, invitationDao) {
|
var OutboxBO = function(emailDao, keychain, devicestorage) {
|
||||||
/** @private */
|
/** @private */
|
||||||
this._emailDao = emailDao;
|
this._emailDao = emailDao;
|
||||||
|
|
||||||
@ -23,10 +21,6 @@ define(function(require) {
|
|||||||
/** @private */
|
/** @private */
|
||||||
this._devicestorage = devicestorage;
|
this._devicestorage = devicestorage;
|
||||||
|
|
||||||
|
|
||||||
/** @private */
|
|
||||||
this._invitationDao = invitationDao;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Semaphore-esque flag to avoid 'concurrent' calls to _processOutbox when the timeout fires, but a call is still in process.
|
* Semaphore-esque flag to avoid 'concurrent' calls to _processOutbox when the timeout fires, but a call is still in process.
|
||||||
* @private */
|
* @private */
|
||||||
@ -38,8 +32,10 @@ define(function(require) {
|
|||||||
* @param {Function} callback(error, pendingMailsCount) Callback that informs you about the count of pending mails.
|
* @param {Function} callback(error, pendingMailsCount) Callback that informs you about the count of pending mails.
|
||||||
*/
|
*/
|
||||||
OutboxBO.prototype.startChecking = function(callback) {
|
OutboxBO.prototype.startChecking = function(callback) {
|
||||||
|
// remember global callback
|
||||||
|
this._onUpdate = callback;
|
||||||
// start periodic checking of outbox
|
// start periodic checking of outbox
|
||||||
this._intervalId = setInterval(this._processOutbox.bind(this, callback), config.checkOutboxInterval);
|
this._intervalId = setInterval(this._processOutbox.bind(this, this._onUpdate), config.checkOutboxInterval);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -64,7 +60,6 @@ define(function(require) {
|
|||||||
allReaders = mail.from.concat(mail.to.concat(mail.cc.concat(mail.bcc))); // all the users that should be able to read the mail
|
allReaders = mail.from.concat(mail.to.concat(mail.cc.concat(mail.bcc))); // all the users that should be able to read the mail
|
||||||
|
|
||||||
mail.publicKeysArmored = []; // gather the public keys
|
mail.publicKeysArmored = []; // gather the public keys
|
||||||
mail.unregisteredUsers = []; // gather the recipients for which no public key is available
|
|
||||||
mail.id = util.UUID(); // the mail needs a random uuid for storage in the database
|
mail.id = util.UUID(); // the mail needs a random uuid for storage in the database
|
||||||
|
|
||||||
checkRecipients(allReaders);
|
checkRecipients(allReaders);
|
||||||
@ -72,7 +67,7 @@ define(function(require) {
|
|||||||
// check if there are unregistered recipients
|
// check if there are unregistered recipients
|
||||||
function checkRecipients(recipients) {
|
function checkRecipients(recipients) {
|
||||||
var after = _.after(recipients.length, function() {
|
var after = _.after(recipients.length, function() {
|
||||||
encryptAndPersist();
|
checkEncrypt();
|
||||||
});
|
});
|
||||||
|
|
||||||
// find out if there are unregistered users
|
// find out if there are unregistered users
|
||||||
@ -87,8 +82,6 @@ define(function(require) {
|
|||||||
// otherwise remember the recipient as unregistered for later sending
|
// otherwise remember the recipient as unregistered for later sending
|
||||||
if (key) {
|
if (key) {
|
||||||
mail.publicKeysArmored.push(key.publicKey);
|
mail.publicKeysArmored.push(key.publicKey);
|
||||||
} else {
|
|
||||||
mail.unregisteredUsers.push(recipient);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
after();
|
after();
|
||||||
@ -96,8 +89,14 @@ define(function(require) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// encrypts the body and attachments and persists the mail object
|
function checkEncrypt() {
|
||||||
function encryptAndPersist() {
|
// only encrypt if all recipients have public keys
|
||||||
|
if (mail.publicKeysArmored.length < allReaders.length) {
|
||||||
|
storeAndForward(mail);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// encrypts the body and attachments and persists the mail object
|
||||||
self._emailDao.encrypt({
|
self._emailDao.encrypt({
|
||||||
mail: mail,
|
mail: mail,
|
||||||
publicKeysArmored: mail.publicKeysArmored
|
publicKeysArmored: mail.publicKeysArmored
|
||||||
@ -107,7 +106,21 @@ define(function(require) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self._devicestorage.storeList([mail], outboxDb, callback);
|
storeAndForward(mail);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -152,90 +165,27 @@ define(function(require) {
|
|||||||
|
|
||||||
// send pending mails if possible
|
// send pending mails if possible
|
||||||
pendingMails.forEach(function(mail) {
|
pendingMails.forEach(function(mail) {
|
||||||
handleMail(mail, after);
|
send(mail, after);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// if we can send the mail, do that. otherwise check if there are users that need to be invited
|
// send the message
|
||||||
function handleMail(mail, done) {
|
function send(mail, done) {
|
||||||
// no unregistered users, go straight to send
|
|
||||||
if (mail.unregisteredUsers.length === 0) {
|
// check is email is to be sent encrypted or as plaintex
|
||||||
send(mail, done);
|
if (mail.encrypted === true) {
|
||||||
return;
|
// 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
var after = _.after(mail.unregisteredUsers.length, function() {
|
function onSend(err) {
|
||||||
// invite unregistered users if necessary
|
|
||||||
if (mail.unregisteredUsers.length > 0) {
|
|
||||||
unsentMails++;
|
|
||||||
self._invite({
|
|
||||||
sender: mail.from[0],
|
|
||||||
recipients: mail.unregisteredUsers
|
|
||||||
}, done);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// there are public keys available for the missing users,
|
|
||||||
// so let's re-encrypt the mail for them and send it
|
|
||||||
reencryptAndSend(mail, done);
|
|
||||||
});
|
|
||||||
|
|
||||||
// find out if the unregistered users have registered in the meantime
|
|
||||||
mail.unregisteredUsers.forEach(function(recipient) {
|
|
||||||
self._keychain.getReceiverPublicKey(recipient.address, function(err, key) {
|
|
||||||
var index;
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
self._outboxBusy = false;
|
|
||||||
callback(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key) {
|
|
||||||
// remove the newly joined users from the unregistered users
|
|
||||||
index = mail.unregisteredUsers.indexOf(recipient);
|
|
||||||
mail.unregisteredUsers.splice(index, 1);
|
|
||||||
mail.publicKeysArmored.push(key.publicKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
after();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// all the recipients have public keys available, so let's re-encrypt the mail
|
|
||||||
// to make it available for them, too
|
|
||||||
function reencryptAndSend(mail, done) {
|
|
||||||
self._emailDao.reEncrypt({
|
|
||||||
mail: mail,
|
|
||||||
publicKeysArmored: mail.publicKeysArmored
|
|
||||||
}, function(err) {
|
|
||||||
if (err) {
|
|
||||||
self._outboxBusy = false;
|
|
||||||
callback(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// stores the newly encrypted mail object to disk in case something funky
|
|
||||||
// happens during sending and we need do re-send the mail later.
|
|
||||||
// avoids doing the encryption twice...
|
|
||||||
self._devicestorage.storeList([mail], outboxDb, function(err) {
|
|
||||||
if (err) {
|
|
||||||
self._outboxBusy = false;
|
|
||||||
callback(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
send(mail, done);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// send the encrypted message
|
|
||||||
function send(mail, done) {
|
|
||||||
self._emailDao.sendEncrypted({
|
|
||||||
email: mail
|
|
||||||
}, function(err) {
|
|
||||||
if (err) {
|
if (err) {
|
||||||
self._outboxBusy = false;
|
self._outboxBusy = false;
|
||||||
if (err.code === 42) {
|
if (err.code === 42) {
|
||||||
@ -255,7 +205,7 @@ define(function(require) {
|
|||||||
if (typeof self.onSent === 'function') {
|
if (typeof self.onSent === 'function') {
|
||||||
self.onSent(mail);
|
self.onSent(mail);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// removes the mail object from disk after successfully sending it
|
// removes the mail object from disk after successfully sending it
|
||||||
@ -272,90 +222,5 @@ define(function(require) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends an invitation mail to an array of users that have no public key available yet
|
|
||||||
* @param {Array} recipients Array of objects with information on the sender (name, address)
|
|
||||||
* @param {Function} callback Invoked when the mail was sent
|
|
||||||
*/
|
|
||||||
OutboxBO.prototype._invite = function(options, callback) {
|
|
||||||
var self = this,
|
|
||||||
sender = options.sender;
|
|
||||||
|
|
||||||
var after = _.after(options.recipients.length, callback);
|
|
||||||
|
|
||||||
options.recipients.forEach(function(recipient) {
|
|
||||||
checkInvitationStatus(recipient, after);
|
|
||||||
});
|
|
||||||
|
|
||||||
// checks the invitation status. if an invitation is pending, we do not need to resend the invitation mail
|
|
||||||
function checkInvitationStatus(recipient, done) {
|
|
||||||
self._invitationDao.check({
|
|
||||||
recipient: recipient.address,
|
|
||||||
sender: sender.address
|
|
||||||
}, function(err, status) {
|
|
||||||
if (err) {
|
|
||||||
callback(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status === InvitationDAO.INVITE_PENDING) {
|
|
||||||
// the recipient is already invited, we're done here.
|
|
||||||
done();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
invite(recipient, done);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// let's invite the recipient and send him a mail to inform him to join whiteout
|
|
||||||
function invite(recipient, done) {
|
|
||||||
self._invitationDao.invite({
|
|
||||||
recipient: recipient.address,
|
|
||||||
sender: sender.address
|
|
||||||
}, function(err, status) {
|
|
||||||
if (err) {
|
|
||||||
callback(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (status !== InvitationDAO.INVITE_SUCCESS) {
|
|
||||||
callback({
|
|
||||||
errMsg: 'Could not successfully invite ' + recipient
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var invitationMail = {
|
|
||||||
from: [sender],
|
|
||||||
to: [recipient],
|
|
||||||
subject: str.invitationSubject,
|
|
||||||
body: 'Hi,\n\n' + str.invitationMessage + '\n\n'
|
|
||||||
};
|
|
||||||
|
|
||||||
// send invitation mail
|
|
||||||
self._emailDao.sendPlaintext({
|
|
||||||
email: invitationMail
|
|
||||||
}, function(err) {
|
|
||||||
if (err) {
|
|
||||||
if (err.code === 42) {
|
|
||||||
// offline try again later
|
|
||||||
done();
|
|
||||||
} else {
|
|
||||||
callback(err);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// fire sent notification
|
|
||||||
if (typeof self.onSent === 'function') {
|
|
||||||
self.onSent(invitationMail);
|
|
||||||
}
|
|
||||||
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return OutboxBO;
|
return OutboxBO;
|
||||||
});
|
});
|
@ -20,15 +20,6 @@ define(function(require) {
|
|||||||
// push handler
|
// push handler
|
||||||
if (emailDao) {
|
if (emailDao) {
|
||||||
emailDao.onIncomingMessage = function(email) {
|
emailDao.onIncomingMessage = function(email) {
|
||||||
if (!email.subject) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (email.subject.indexOf(str.subjectPrefix) === -1 ||
|
|
||||||
email.subject === str.subjectPrefix + str.verificationSubject) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// sync
|
// sync
|
||||||
$scope.synchronize(function() {
|
$scope.synchronize(function() {
|
||||||
// show notification
|
// show notification
|
||||||
@ -38,22 +29,6 @@ define(function(require) {
|
|||||||
chrome.notifications.onClicked.addListener(notificationClicked);
|
chrome.notifications.onClicked.addListener(notificationClicked);
|
||||||
}
|
}
|
||||||
|
|
||||||
function notificationClicked(uidString) {
|
|
||||||
var email, uid = parseInt(uidString, 10);
|
|
||||||
|
|
||||||
if (isNaN(uid)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
email = _.findWhere(getFolder().messages, {
|
|
||||||
uid: uid
|
|
||||||
});
|
|
||||||
|
|
||||||
if (email) {
|
|
||||||
$scope.select(email);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// scope functions
|
// scope functions
|
||||||
//
|
//
|
||||||
@ -131,7 +106,7 @@ define(function(require) {
|
|||||||
folder: getFolder().path
|
folder: getFolder().path
|
||||||
}, done);
|
}, done);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function done(err) {
|
function done(err) {
|
||||||
if (err && err.code === 409) {
|
if (err && err.code === 409) {
|
||||||
@ -249,6 +224,22 @@ define(function(require) {
|
|||||||
// helper functions
|
// helper functions
|
||||||
//
|
//
|
||||||
|
|
||||||
|
function notificationClicked(uidString) {
|
||||||
|
var email, uid = parseInt(uidString, 10);
|
||||||
|
|
||||||
|
if (isNaN(uid)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
email = _.findWhere(getFolder().messages, {
|
||||||
|
uid: uid
|
||||||
|
});
|
||||||
|
|
||||||
|
if (email) {
|
||||||
|
$scope.select(email);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function notificationForEmail(email) {
|
function notificationForEmail(email) {
|
||||||
chrome.notifications.create('' + email.uid, {
|
chrome.notifications.create('' + email.uid, {
|
||||||
type: 'basic',
|
type: 'basic',
|
||||||
|
@ -4,7 +4,8 @@ define(function(require) {
|
|||||||
var appController = require('js/app-controller'),
|
var appController = require('js/app-controller'),
|
||||||
download = require('js/util/download'),
|
download = require('js/util/download'),
|
||||||
angular = require('angular'),
|
angular = require('angular'),
|
||||||
emailDao, crypto, keychain;
|
str = require('js/app-config').string,
|
||||||
|
emailDao, invitationDao, outbox, crypto, keychain;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Controller
|
// Controller
|
||||||
@ -13,11 +14,13 @@ define(function(require) {
|
|||||||
var ReadCtrl = function($scope) {
|
var ReadCtrl = function($scope) {
|
||||||
|
|
||||||
emailDao = appController._emailDao;
|
emailDao = appController._emailDao;
|
||||||
|
invitationDao = appController._invitationDao;
|
||||||
|
outbox = appController._outboxBo;
|
||||||
crypto = appController._crypto;
|
crypto = appController._crypto;
|
||||||
keychain = appController._keychain;
|
keychain = appController._keychain;
|
||||||
|
|
||||||
// set default value so that the popover height is correct on init
|
// set default value so that the popover height is correct on init
|
||||||
$scope.keyId = 'XXXXXXXX';
|
$scope.keyId = 'No key found.';
|
||||||
|
|
||||||
$scope.state.read = {
|
$scope.state.read = {
|
||||||
open: false,
|
open: false,
|
||||||
@ -31,7 +34,7 @@ define(function(require) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
$scope.getKeyId = function(address) {
|
$scope.getKeyId = function(address) {
|
||||||
$scope.keyId = 'unknown user';
|
$scope.keyId = 'Searching...';
|
||||||
keychain.getReceiverPublicKey(address, function(err, pubkey) {
|
keychain.getReceiverPublicKey(address, function(err, pubkey) {
|
||||||
if (err) {
|
if (err) {
|
||||||
$scope.onError(err);
|
$scope.onError(err);
|
||||||
@ -39,13 +42,15 @@ define(function(require) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!pubkey) {
|
if (!pubkey) {
|
||||||
|
$scope.keyId = 'User has no key. Click to invite.';
|
||||||
|
$scope.$apply();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var fpr = crypto.getFingerprint(pubkey.publicKey);
|
var fpr = crypto.getFingerprint(pubkey.publicKey);
|
||||||
var formatted = fpr.slice(32);
|
var formatted = fpr.slice(32);
|
||||||
|
|
||||||
$scope.keyId = formatted;
|
$scope.keyId = 'PGP key: ' + formatted;
|
||||||
$scope.$apply();
|
$scope.$apply();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -112,6 +117,45 @@ define(function(require) {
|
|||||||
}, $scope.onError);
|
}, $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);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -105,6 +105,7 @@ define(function(require) {
|
|||||||
// set display to insecure while fetching keys
|
// set display to insecure while fetching keys
|
||||||
recipient.key = undefined;
|
recipient.key = undefined;
|
||||||
recipient.secure = false;
|
recipient.secure = false;
|
||||||
|
$scope.checkSendStatus();
|
||||||
|
|
||||||
// verify email address
|
// verify email address
|
||||||
if (!util.validateEmailAddress(recipient.address)) {
|
if (!util.validateEmailAddress(recipient.address)) {
|
||||||
@ -171,16 +172,21 @@ define(function(require) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// sender can invite only one use at a time
|
// only allow sending if receviers exist
|
||||||
if (!allSecure && numReceivers === 1) {
|
if (numReceivers < 1) {
|
||||||
$scope.sendBtnText = str.sendBtnInvite;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allSecure) {
|
||||||
|
// send encrypted if all secure
|
||||||
$scope.okToSend = true;
|
$scope.okToSend = true;
|
||||||
$scope.sendBtnSecure = false;
|
|
||||||
} else if (allSecure && numReceivers > 0) {
|
|
||||||
// all recipients are secure
|
|
||||||
$scope.sendBtnText = str.sendBtnSecure;
|
$scope.sendBtnText = str.sendBtnSecure;
|
||||||
$scope.okToSend = true;
|
|
||||||
$scope.sendBtnSecure = true;
|
$scope.sendBtnSecure = true;
|
||||||
|
} else {
|
||||||
|
// send plaintext
|
||||||
|
$scope.okToSend = true;
|
||||||
|
$scope.sendBtnText = str.sendBtnClear;
|
||||||
|
$scope.sendBtnSecure = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -461,7 +467,8 @@ define(function(require) {
|
|||||||
|
|
||||||
ngModule.directive('attachmentBtn', function() {
|
ngModule.directive('attachmentBtn', function() {
|
||||||
return function(scope, elm) {
|
return function(scope, elm) {
|
||||||
elm.on('click', function() {
|
elm.on('click touchstart', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
document.querySelector('#attachment-input').click();
|
document.querySelector('#attachment-input').click();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -566,11 +566,13 @@ define(function(require) {
|
|||||||
|
|
||||||
if (verificationMessage) {
|
if (verificationMessage) {
|
||||||
handleVerification(verificationMessage, function(err) {
|
handleVerification(verificationMessage, function(err) {
|
||||||
// TODO: show usable error when the verification failed
|
// eliminate the verification mail if the verification worked, otherwise display an error and it in the mail list
|
||||||
if (err) {
|
if (err) {
|
||||||
self._account.busy = false;
|
|
||||||
callback(err);
|
callback(err);
|
||||||
return;
|
} else {
|
||||||
|
messages = _.filter(messages, function(message) {
|
||||||
|
return message.subject !== (str.subjectPrefix + str.verificationSubject);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
storeHeaders();
|
storeHeaders();
|
||||||
@ -581,23 +583,12 @@ define(function(require) {
|
|||||||
storeHeaders();
|
storeHeaders();
|
||||||
|
|
||||||
function storeHeaders() {
|
function storeHeaders() {
|
||||||
// eliminate non-whiteout mails
|
|
||||||
messages = _.filter(messages, function(message) {
|
|
||||||
// we don't want to display "[whiteout] "-prefixed mails for now
|
|
||||||
return message.subject.indexOf(str.subjectPrefix) === 0 && message.subject !== (str.subjectPrefix + str.verificationSubject);
|
|
||||||
});
|
|
||||||
|
|
||||||
// no delta, we're done here
|
// no delta, we're done here
|
||||||
if (_.isEmpty(messages)) {
|
if (_.isEmpty(messages)) {
|
||||||
doDeltaF4();
|
doDeltaF4();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// filter out the "[whiteout] " prefix
|
|
||||||
messages.forEach(function(messages) {
|
|
||||||
messages.subject = messages.subject.replace(/^\[whiteout\] /, '');
|
|
||||||
});
|
|
||||||
|
|
||||||
// persist the encrypted message to the local storage
|
// persist the encrypted message to the local storage
|
||||||
self._localStoreMessages({
|
self._localStoreMessages({
|
||||||
folder: folder.path,
|
folder: folder.path,
|
||||||
@ -820,7 +811,11 @@ define(function(require) {
|
|||||||
self._imapDeleteMessage({
|
self._imapDeleteMessage({
|
||||||
folder: options.folder,
|
folder: options.folder,
|
||||||
uid: message.uid
|
uid: message.uid
|
||||||
}, localCallback);
|
}, function() {
|
||||||
|
// if we could successfully not delete the message or not doesn't matter.
|
||||||
|
// just don't show it in whiteout and keep quiet about it
|
||||||
|
localCallback();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1039,13 +1034,10 @@ define(function(require) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// add whiteout tag to subject
|
|
||||||
options.email.subject = str.subjectPrefix + options.email.subject;
|
|
||||||
|
|
||||||
// mime encode, sign, encrypt and send email via smtp
|
// mime encode, sign, encrypt and send email via smtp
|
||||||
self._pgpMailer.send({
|
self._pgpMailer.send({
|
||||||
encrypt: true,
|
encrypt: true,
|
||||||
cleartextMessage: str.message,
|
cleartextMessage: str.message + str.signature,
|
||||||
mail: options.email,
|
mail: options.email,
|
||||||
publicKeysArmored: options.email.publicKeysArmored
|
publicKeysArmored: options.email.publicKeysArmored
|
||||||
}, callback);
|
}, callback);
|
||||||
@ -1060,8 +1052,8 @@ define(function(require) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// add whiteout tag to subject
|
// append the signature to plaintext mails
|
||||||
options.email.subject = str.subjectPrefix + options.email.subject;
|
options.email.body += str.signature;
|
||||||
|
|
||||||
// mime encode, sign and send email via smtp
|
// mime encode, sign and send email via smtp
|
||||||
this._pgpMailer.send({
|
this._pgpMailer.send({
|
||||||
@ -1073,10 +1065,6 @@ define(function(require) {
|
|||||||
this._pgpbuilder.encrypt(options, callback);
|
this._pgpbuilder.encrypt(options, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
EmailDAO.prototype.reEncrypt = function(options, callback) {
|
|
||||||
this._pgpbuilder.reEncrypt(options, callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Internal API
|
// Internal API
|
||||||
//
|
//
|
||||||
@ -1177,8 +1165,7 @@ define(function(require) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var o = {
|
var o = {
|
||||||
path: options.folder,
|
path: options.folder
|
||||||
subject: str.subjectPrefix
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (typeof options.answered !== 'undefined') {
|
if (typeof options.answered !== 'undefined') {
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
background-image: none;
|
background-image: none;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
top: 1px;
|
top: 1px;
|
||||||
left: 1px;
|
right: -1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.disabled,
|
&.disabled,
|
||||||
@ -57,6 +57,21 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background-color: $label-primary-back-color;
|
||||||
|
color: $label-primary-color;
|
||||||
|
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAGCAYAAADgzO9IAAAAJUlEQVQIW2NkQABJIPM5lCvJCGMgC4LYIAkUlTAFMB0gjRQaBQCw8go5lVnl5wAAAABJRU5ErkJggg==);
|
||||||
|
|
||||||
|
@include respond-to(retina) {
|
||||||
|
background-size: 3px 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
background-color: lighten($label-primary-back-color, 5%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.btn-alt {
|
.btn-alt {
|
||||||
background-color: $color-grey-input;
|
background-color: $color-grey-input;
|
||||||
|
|
||||||
|
@ -70,14 +70,8 @@
|
|||||||
right: 0;
|
right: 0;
|
||||||
padding: em(7.5) em(7.5) em(4) em(7.5);
|
padding: em(7.5) em(7.5) em(4) em(7.5);
|
||||||
margin: em(5);
|
margin: em(5);
|
||||||
outline: none;
|
font-size: 1em;
|
||||||
color: $color-grey-lightest;
|
outline: 0;
|
||||||
background-color: $color-blue;
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
background-color: lighten($color-blue, 10%);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active,
|
&:active,
|
||||||
&.active {
|
&.active {
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
<input class="input-text input-search" type="search" results="5" ng-model="searchText" placeholder=" Filter..." focus-me="state.mailList.searching">
|
<input class="input-text input-search" type="search" results="5" ng-model="searchText" placeholder=" Filter..." focus-me="state.mailList.searching">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="list-wrapper" ng-iscroll="state.nav.currentFolder.messages.length">
|
<div class="list-wrapper" ng-iscroll="filteredMessages.length">
|
||||||
<ul class="mail-list">
|
<ul class="mail-list">
|
||||||
<li ng-class="{'mail-list-active': email === state.mailList.selected, 'mail-list-attachment': email.attachments !== undefined && email.attachments.length > 0, 'mail-list-unread': email.unread, 'mail-list-replied': !email.unread && email.answered}" ng-click="select(email)" ng-repeat="email in (filteredMessages = (state.nav.currentFolder.messages | orderBy:'uid':true | filter:searchText))">
|
<li ng-class="{'mail-list-active': email === state.mailList.selected, 'mail-list-attachment': email.attachments !== undefined && email.attachments.length > 0, 'mail-list-unread': email.unread, 'mail-list-replied': !email.unread && email.answered}" ng-click="select(email)" ng-repeat="email in (filteredMessages = (state.nav.currentFolder.messages | orderBy:'uid':true | filter:searchText))">
|
||||||
<h3>{{email.from[0].name || email.from[0].address}}</h3>
|
<h3>{{email.from[0].name || email.from[0].address}}</h3>
|
||||||
|
@ -9,14 +9,14 @@
|
|||||||
<p class="subject" ng-click="state.read.toggle(false)">{{(state.mailList.selected.subject) ? state.mailList.selected.subject : 'No subject'}}</p>
|
<p class="subject" ng-click="state.read.toggle(false)">{{(state.mailList.selected.subject) ? state.mailList.selected.subject : 'No subject'}}</p>
|
||||||
<p class="date">{{state.mailList.selected.sentDate | date:'EEEE, MMM d, yyyy h:mm a'}}</p>
|
<p class="date">{{state.mailList.selected.sentDate | date:'EEEE, MMM d, yyyy h:mm a'}}</p>
|
||||||
<p class="address">
|
<p class="address">
|
||||||
From: <span ng-repeat="u in state.mailList.selected.from" class="label" ng-class="{'label-primary': u.secure === false}" data-icon-append="{{(u.secure === false) ? '' : ''}}" ng-mouseover="getKeyId(u.address)" popover="#fingerprint-info">{{u.name || u.address}}</span>
|
From: <span ng-repeat="u in state.mailList.selected.from" class="label" ng-class="{'label-primary': u.secure === false}" data-icon-append="{{(u.secure === false) ? '' : ''}}" ng-mouseover="getKeyId(u.address)" ng-click="invite(u)" popover="#fingerprint-info">{{u.name || u.address}}</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="address">
|
<p class="address">
|
||||||
To: <span ng-repeat="u in state.mailList.selected.to" class="label" ng-class="{'label-primary': u.secure === false}" data-icon-append="{{(u.secure === false) ? '' : ''}}" ng-mouseover="getKeyId(u.address)" popover="#fingerprint-info">{{u.name || u.address}}</span>
|
To: <span ng-repeat="u in state.mailList.selected.to" class="label" ng-class="{'label-primary': u.secure === false}" data-icon-append="{{(u.secure === false) ? '' : ''}}" ng-mouseover="getKeyId(u.address)" ng-click="invite(u)" popover="#fingerprint-info">{{u.name || u.address}}</span>
|
||||||
</p>
|
</p>
|
||||||
<div ng-switch="state.mailList.selected.cc !== undefined">
|
<div ng-switch="state.mailList.selected.cc !== undefined">
|
||||||
<p class="address" ng-switch-when="true">
|
<p class="address" ng-switch-when="true">
|
||||||
Cc: <span ng-repeat="u in state.mailList.selected.cc" class="label" ng-class="{'label-primary': u.secure === false}" data-icon-append="{{(u.secure === false) ? '' : ''}}" ng-mouseover="getKeyId(u.address)" popover="#fingerprint-info">{{u.name || u.address}}</span>
|
Cc: <span ng-repeat="u in state.mailList.selected.cc" class="label" ng-class="{'label-primary': u.secure === false}" data-icon-append="{{(u.secure === false) ? '' : ''}}" ng-mouseover="getKeyId(u.address)" ng-click="invite(u)" popover="#fingerprint-info">{{u.name || u.address}}</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div><!--/.headers-->
|
</div><!--/.headers-->
|
||||||
@ -54,6 +54,6 @@
|
|||||||
<!-- popovers -->
|
<!-- popovers -->
|
||||||
<div id="fingerprint-info" class="popover right" ng-controller="PopoverCtrl">
|
<div id="fingerprint-info" class="popover right" ng-controller="PopoverCtrl">
|
||||||
<div class="arrow"></div>
|
<div class="arrow"></div>
|
||||||
<div class="popover-content"><b>PGP key:</b> {{keyId}}</div>
|
<div class="popover-content">{{keyId}}</div>
|
||||||
</div><!--/.popover-->
|
</div><!--/.popover-->
|
||||||
</div><!--/.view-read-->
|
</div><!--/.view-read-->
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
<input ng-model="subject" class="subject" spellcheck="true" tabindex="2" placeholder="Subject" ng-change="updatePreview()">
|
<input ng-model="subject" class="subject" spellcheck="true" tabindex="2" placeholder="Subject" ng-change="updatePreview()">
|
||||||
</div><!--/.subject-line-->
|
</div><!--/.subject-line-->
|
||||||
<input id="attachment-input" type="file" multiple attachment-input>
|
<input id="attachment-input" type="file" multiple attachment-input>
|
||||||
<button class="btn-attachment" attachment-btn>
|
<button class="btn btn-attachment" ng-class="{'btn-primary': sendBtnSecure === false}" attachment-btn>
|
||||||
<div data-icon=""></div>
|
<div data-icon=""></div>
|
||||||
</button><!--/.btn-attachment-->
|
</button><!--/.btn-attachment-->
|
||||||
</div><!--/.subject-box-->
|
</div><!--/.subject-box-->
|
||||||
@ -46,13 +46,13 @@
|
|||||||
<div class="body" focus-child>
|
<div class="body" focus-child>
|
||||||
<p ng-model="body" contentEditable="true" spellcheck="false" ng-change="updatePreview()" tabindex="3" focus-me="state.writer.open && writerTitle === 'Reply'"></p>
|
<p ng-model="body" contentEditable="true" spellcheck="false" ng-change="updatePreview()" tabindex="3" focus-me="state.writer.open && writerTitle === 'Reply'"></p>
|
||||||
|
|
||||||
<div class="encrypt-preview" ng-class="{'invisible': !ciphertextPreview}">
|
<div class="encrypt-preview" ng-class="{'invisible': !ciphertextPreview || !sendBtnSecure}">
|
||||||
<p>-----BEGIN ENCRYPTED PREVIEW-----<br>{{ciphertextPreview}}<br>-----END ENCRYPTED PREVIEW-----</p>
|
<p>-----BEGIN ENCRYPTED PREVIEW-----<br>{{ciphertextPreview}}<br>-----END ENCRYPTED PREVIEW-----</p>
|
||||||
</div><!--/.encrypt-preview-->
|
</div><!--/.encrypt-preview-->
|
||||||
</div><!--/.body-->
|
</div><!--/.body-->
|
||||||
|
|
||||||
<div class="send-control">
|
<div class="send-control">
|
||||||
<button ng-click="sendToOutbox()" class="btn" data-icon="{{(sendBtnSecure === false) ? '' : (sendBtnSecure === true) ? '' : ''}}" ng-disabled="!okToSend" tabindex="4">{{sendBtnText || 'Send'}}</button>
|
<button ng-click="sendToOutbox()" class="btn" ng-class="{'btn-primary': sendBtnSecure === false}" ng-disabled="!okToSend" tabindex="4">{{sendBtnText || 'Send'}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div><!--/.write-view-->
|
</div><!--/.write-view-->
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ define(function(require) {
|
|||||||
to: [{
|
to: [{
|
||||||
address: 'qwe@qwe.de'
|
address: 'qwe@qwe.de'
|
||||||
}],
|
}],
|
||||||
subject: '[whiteout] qweasd',
|
subject: 'qweasd',
|
||||||
body: '-----BEGIN PGP MESSAGE-----\nasd\n-----END PGP MESSAGE-----',
|
body: '-----BEGIN PGP MESSAGE-----\nasd\n-----END PGP MESSAGE-----',
|
||||||
unread: false,
|
unread: false,
|
||||||
answered: false
|
answered: false
|
||||||
@ -51,7 +51,7 @@ define(function(require) {
|
|||||||
}], // list of receivers
|
}], // list of receivers
|
||||||
subject: "[whiteout] New public key uploaded", // Subject line
|
subject: "[whiteout] New public key uploaded", // Subject line
|
||||||
body: 'yadda yadda bla blabla foo bar https://keys.whiteout.io/verify/' + verificationUuid, // plaintext body
|
body: 'yadda yadda bla blabla foo bar https://keys.whiteout.io/verify/' + verificationUuid, // plaintext body
|
||||||
unread: true,
|
unread: false,
|
||||||
answered: false
|
answered: false
|
||||||
};
|
};
|
||||||
corruptedVerificationUuid = 'OMFG_FUCKING_BASTARD_UUID_FROM_HELL!';
|
corruptedVerificationUuid = 'OMFG_FUCKING_BASTARD_UUID_FROM_HELL!';
|
||||||
@ -65,7 +65,7 @@ define(function(require) {
|
|||||||
}], // list of receivers
|
}], // list of receivers
|
||||||
subject: "[whiteout] New public key uploaded", // Subject line
|
subject: "[whiteout] New public key uploaded", // Subject line
|
||||||
body: 'yadda yadda bla blabla foo bar https://keys.whiteout.io/verify/' + corruptedVerificationUuid, // plaintext body
|
body: 'yadda yadda bla blabla foo bar https://keys.whiteout.io/verify/' + corruptedVerificationUuid, // plaintext body
|
||||||
unread: true,
|
unread: false,
|
||||||
answered: false
|
answered: false
|
||||||
};
|
};
|
||||||
dummyDecryptedMail = {
|
dummyDecryptedMail = {
|
||||||
@ -654,8 +654,7 @@ define(function(require) {
|
|||||||
var path = 'FOLDAAAA';
|
var path = 'FOLDAAAA';
|
||||||
|
|
||||||
imapClientStub.search.withArgs({
|
imapClientStub.search.withArgs({
|
||||||
path: path,
|
path: path
|
||||||
subject: '[whiteout] '
|
|
||||||
}).yields();
|
}).yields();
|
||||||
|
|
||||||
dao._imapSearch({
|
dao._imapSearch({
|
||||||
@ -667,7 +666,6 @@ define(function(require) {
|
|||||||
|
|
||||||
imapClientStub.search.withArgs({
|
imapClientStub.search.withArgs({
|
||||||
path: path,
|
path: path,
|
||||||
subject: '[whiteout] ',
|
|
||||||
answered: true
|
answered: true
|
||||||
}).yields();
|
}).yields();
|
||||||
|
|
||||||
@ -681,7 +679,6 @@ define(function(require) {
|
|||||||
|
|
||||||
imapClientStub.search.withArgs({
|
imapClientStub.search.withArgs({
|
||||||
path: path,
|
path: path,
|
||||||
subject: '[whiteout] ',
|
|
||||||
unread: true
|
unread: true
|
||||||
}).yields();
|
}).yields();
|
||||||
|
|
||||||
@ -1794,60 +1791,6 @@ define(function(require) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not fetch non-whitelisted mails', function(done) {
|
|
||||||
var invocations, folder, localListStub, imapSearchStub, imapListMessagesStub, localStoreStub;
|
|
||||||
|
|
||||||
invocations = 0;
|
|
||||||
folder = 'FOLDAAAA';
|
|
||||||
dao._account.folders = [{
|
|
||||||
type: 'Folder',
|
|
||||||
path: folder,
|
|
||||||
messages: []
|
|
||||||
}];
|
|
||||||
|
|
||||||
localListStub = sinon.stub(dao, '_localListMessages').yields(null, []);
|
|
||||||
imapSearchStub = sinon.stub(dao, '_imapSearch');
|
|
||||||
imapSearchStub.withArgs({
|
|
||||||
folder: folder
|
|
||||||
}).yields(null, [nonWhitelistedMail.uid]);
|
|
||||||
imapSearchStub.withArgs({
|
|
||||||
folder: folder,
|
|
||||||
unread: true
|
|
||||||
}).yields(null, []);
|
|
||||||
imapSearchStub.withArgs({
|
|
||||||
folder: folder,
|
|
||||||
answered: true
|
|
||||||
}).yields(null, []);
|
|
||||||
|
|
||||||
imapListMessagesStub = sinon.stub(dao, '_imapListMessages');
|
|
||||||
imapListMessagesStub.withArgs({
|
|
||||||
folder: folder,
|
|
||||||
firstUid: nonWhitelistedMail.uid,
|
|
||||||
lastUid: nonWhitelistedMail.uid
|
|
||||||
}).yields(null, [nonWhitelistedMail]);
|
|
||||||
|
|
||||||
localStoreStub = sinon.stub(dao, '_localStoreMessages');
|
|
||||||
|
|
||||||
dao.sync({
|
|
||||||
folder: folder
|
|
||||||
}, function(err) {
|
|
||||||
expect(err).to.not.exist;
|
|
||||||
|
|
||||||
if (invocations === 0) {
|
|
||||||
expect(dao._account.busy).to.be.true;
|
|
||||||
invocations++;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(dao._account.busy).to.be.false;
|
|
||||||
expect(dao._account.folders[0].messages).to.be.empty;
|
|
||||||
expect(localListStub.calledOnce).to.be.true;
|
|
||||||
expect(imapSearchStub.calledThrice).to.be.true;
|
|
||||||
expect(localStoreStub.called).to.be.false;
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should error while storing messages from the remote locally', function(done) {
|
it('should error while storing messages from the remote locally', function(done) {
|
||||||
var invocations, folder, localListStub, imapSearchStub, localStoreStub, imapListMessagesStub;
|
var invocations, folder, localListStub, imapSearchStub, localStoreStub, imapListMessagesStub;
|
||||||
|
|
||||||
@ -1896,7 +1839,7 @@ define(function(require) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should verify an authentication mail', function(done) {
|
it('should verify an authentication mail', function(done) {
|
||||||
var invocations, folder, localListStub, imapSearchStub, imapGetStub, imapListMessagesStub, imapDeleteStub;
|
var invocations, folder, localListStub, imapSearchStub, imapGetStub, imapListMessagesStub, imapDeleteStub, localStoreStub;
|
||||||
|
|
||||||
invocations = 0;
|
invocations = 0;
|
||||||
folder = 'FOLDAAAA';
|
folder = 'FOLDAAAA';
|
||||||
@ -1909,20 +1852,29 @@ define(function(require) {
|
|||||||
localListStub = sinon.stub(dao, '_localListMessages').yields(null, []);
|
localListStub = sinon.stub(dao, '_localListMessages').yields(null, []);
|
||||||
|
|
||||||
imapSearchStub = sinon.stub(dao, '_imapSearch');
|
imapSearchStub = sinon.stub(dao, '_imapSearch');
|
||||||
|
|
||||||
imapSearchStub.withArgs({
|
imapSearchStub.withArgs({
|
||||||
folder: folder
|
folder: folder
|
||||||
}).yields(null, [verificationMail.uid]);
|
}).yields(null, [verificationMail.uid]);
|
||||||
|
|
||||||
imapSearchStub.withArgs({
|
imapSearchStub.withArgs({
|
||||||
folder: folder,
|
folder: folder,
|
||||||
unread: true
|
unread: true
|
||||||
}).yields(null, []);
|
}).yields(null, []);
|
||||||
|
|
||||||
imapSearchStub.withArgs({
|
imapSearchStub.withArgs({
|
||||||
folder: folder,
|
folder: folder,
|
||||||
answered: true
|
answered: true
|
||||||
}).yields(null, []);
|
}).yields(null, []);
|
||||||
|
|
||||||
imapListMessagesStub = sinon.stub(dao, '_imapListMessages').yields(null, [verificationMail]);
|
imapListMessagesStub = sinon.stub(dao, '_imapListMessages').yields(null, [verificationMail]);
|
||||||
|
|
||||||
imapGetStub = sinon.stub(dao, '_imapStreamText').yields(null);
|
imapGetStub = sinon.stub(dao, '_imapStreamText').yields(null);
|
||||||
|
|
||||||
keychainStub.verifyPublicKey.withArgs(verificationUuid).yields();
|
keychainStub.verifyPublicKey.withArgs(verificationUuid).yields();
|
||||||
|
|
||||||
|
localStoreStub = sinon.stub(dao, '_localStoreMessages');
|
||||||
|
|
||||||
imapDeleteStub = sinon.stub(dao, '_imapDeleteMessage').withArgs({
|
imapDeleteStub = sinon.stub(dao, '_imapDeleteMessage').withArgs({
|
||||||
folder: folder,
|
folder: folder,
|
||||||
uid: verificationMail.uid
|
uid: verificationMail.uid
|
||||||
@ -1946,13 +1898,14 @@ define(function(require) {
|
|||||||
expect(imapGetStub.calledOnce).to.be.true;
|
expect(imapGetStub.calledOnce).to.be.true;
|
||||||
expect(keychainStub.verifyPublicKey.calledOnce).to.be.true;
|
expect(keychainStub.verifyPublicKey.calledOnce).to.be.true;
|
||||||
expect(imapDeleteStub.calledOnce).to.be.true;
|
expect(imapDeleteStub.calledOnce).to.be.true;
|
||||||
|
expect(localStoreStub.calledOnce).to.be.false;
|
||||||
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail during deletion of an authentication mail', function(done) {
|
it('should not care abouta failed deletion of an authentication mail', function(done) {
|
||||||
var invocations, folder, localListStub, imapSearchStub, imapGetStub, imapListMessagesStub, imapDeleteStub;
|
var invocations, folder, localListStub, localStoreStub, imapSearchStub, imapGetStub, imapListMessagesStub, imapDeleteStub;
|
||||||
|
|
||||||
invocations = 0;
|
invocations = 0;
|
||||||
folder = 'FOLDAAAA';
|
folder = 'FOLDAAAA';
|
||||||
@ -1981,6 +1934,12 @@ define(function(require) {
|
|||||||
keychainStub.verifyPublicKey.withArgs(verificationUuid).yields();
|
keychainStub.verifyPublicKey.withArgs(verificationUuid).yields();
|
||||||
imapDeleteStub = sinon.stub(dao, '_imapDeleteMessage').yields({});
|
imapDeleteStub = sinon.stub(dao, '_imapDeleteMessage').yields({});
|
||||||
|
|
||||||
|
localStoreStub = sinon.stub(dao, '_localStoreMessages');
|
||||||
|
localStoreStub.withArgs({
|
||||||
|
folder: folder,
|
||||||
|
emails: [verificationMail]
|
||||||
|
}).yields();
|
||||||
|
|
||||||
dao.sync({
|
dao.sync({
|
||||||
folder: folder
|
folder: folder
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
@ -1992,11 +1951,12 @@ define(function(require) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(err).to.exist;
|
expect(err).to.not.exist;
|
||||||
expect(dao._account.busy).to.be.false;
|
expect(dao._account.busy).to.be.false;
|
||||||
expect(dao._account.folders[0].messages).to.be.empty;
|
expect(dao._account.folders[0].messages).to.be.empty;
|
||||||
expect(localListStub.calledOnce).to.be.true;
|
expect(localListStub.calledOnce).to.be.true;
|
||||||
expect(imapSearchStub.calledOnce).to.be.true;
|
expect(localStoreStub.called).to.be.false;
|
||||||
|
expect(imapSearchStub.calledThrice).to.be.true;
|
||||||
expect(imapGetStub.calledOnce).to.be.true;
|
expect(imapGetStub.calledOnce).to.be.true;
|
||||||
expect(keychainStub.verifyPublicKey.calledOnce).to.be.true;
|
expect(keychainStub.verifyPublicKey.calledOnce).to.be.true;
|
||||||
expect(imapDeleteStub.calledOnce).to.be.true;
|
expect(imapDeleteStub.calledOnce).to.be.true;
|
||||||
@ -2006,7 +1966,7 @@ define(function(require) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should fail during verifying authentication', function(done) {
|
it('should fail during verifying authentication', function(done) {
|
||||||
var invocations, folder, localListStub, imapSearchStub, imapGetStub, imapListMessagesStub, imapDeleteStub;
|
var invocations, folder, localListStub, imapSearchStub, localStoreStub, imapGetStub, imapListMessagesStub, imapDeleteStub;
|
||||||
|
|
||||||
invocations = 0;
|
invocations = 0;
|
||||||
folder = 'FOLDAAAA';
|
folder = 'FOLDAAAA';
|
||||||
@ -2037,6 +1997,12 @@ define(function(require) {
|
|||||||
});
|
});
|
||||||
imapDeleteStub = sinon.stub(dao, '_imapDeleteMessage').yields({});
|
imapDeleteStub = sinon.stub(dao, '_imapDeleteMessage').yields({});
|
||||||
|
|
||||||
|
localStoreStub = sinon.stub(dao, '_localStoreMessages');
|
||||||
|
localStoreStub.withArgs({
|
||||||
|
folder: folder,
|
||||||
|
emails: [verificationMail]
|
||||||
|
}).yields();
|
||||||
|
|
||||||
dao.sync({
|
dao.sync({
|
||||||
folder: folder
|
folder: folder
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
@ -2048,11 +2014,18 @@ define(function(require) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(err).to.exist;
|
if (invocations === 1) {
|
||||||
|
expect(err).to.exist;
|
||||||
|
expect(dao._account.busy).to.be.true;
|
||||||
|
invocations++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(err).to.not.exist;
|
||||||
expect(dao._account.busy).to.be.false;
|
expect(dao._account.busy).to.be.false;
|
||||||
expect(dao._account.folders[0].messages).to.be.empty;
|
expect(dao._account.folders[0].messages).to.not.be.empty;
|
||||||
expect(localListStub.calledOnce).to.be.true;
|
expect(localListStub.calledOnce).to.be.true;
|
||||||
expect(imapSearchStub.calledOnce).to.be.true;
|
expect(imapSearchStub.calledThrice).to.be.true;
|
||||||
expect(imapGetStub.calledOnce).to.be.true;
|
expect(imapGetStub.calledOnce).to.be.true;
|
||||||
expect(keychainStub.verifyPublicKey.calledOnce).to.be.true;
|
expect(keychainStub.verifyPublicKey.calledOnce).to.be.true;
|
||||||
expect(imapDeleteStub.called).to.be.false;
|
expect(imapDeleteStub.called).to.be.false;
|
||||||
@ -2563,7 +2536,7 @@ define(function(require) {
|
|||||||
|
|
||||||
pgpMailerStub.send.withArgs({
|
pgpMailerStub.send.withArgs({
|
||||||
encrypt: true,
|
encrypt: true,
|
||||||
cleartextMessage: str.message,
|
cleartextMessage: str.message + str.signature,
|
||||||
mail: dummyDecryptedMail,
|
mail: dummyDecryptedMail,
|
||||||
publicKeysArmored: publicKeys
|
publicKeysArmored: publicKeys
|
||||||
}).yields();
|
}).yields();
|
||||||
@ -2606,17 +2579,6 @@ define(function(require) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('reEncrypt', function() {
|
|
||||||
it('should re-encrypt', function(done) {
|
|
||||||
pgpBuilderStub.reEncrypt.yields();
|
|
||||||
|
|
||||||
dao.reEncrypt({}, function() {
|
|
||||||
expect(pgpBuilderStub.reEncrypt.calledOnce).to.be.true;
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('syncOutbox', function() {
|
describe('syncOutbox', function() {
|
||||||
it('should sync the outbox', function(done) {
|
it('should sync the outbox', function(done) {
|
||||||
var folder = 'FOLDAAAA';
|
var folder = 'FOLDAAAA';
|
||||||
|
@ -5,13 +5,12 @@ define(function(require) {
|
|||||||
OutboxBO = require('js/bo/outbox'),
|
OutboxBO = require('js/bo/outbox'),
|
||||||
KeychainDAO = require('js/dao/keychain-dao'),
|
KeychainDAO = require('js/dao/keychain-dao'),
|
||||||
EmailDAO = require('js/dao/email-dao'),
|
EmailDAO = require('js/dao/email-dao'),
|
||||||
DeviceStorageDAO = require('js/dao/devicestorage-dao'),
|
DeviceStorageDAO = require('js/dao/devicestorage-dao');
|
||||||
InvitationDAO = require('js/dao/invitation-dao');
|
|
||||||
|
|
||||||
chai.Assertion.includeStack = true;
|
chai.Assertion.includeStack = true;
|
||||||
|
|
||||||
describe('Outbox Business Object unit test', function() {
|
describe('Outbox Business Object unit test', function() {
|
||||||
var outbox, emailDaoStub, devicestorageStub, invitationDaoStub, keychainStub,
|
var outbox, emailDaoStub, devicestorageStub, keychainStub,
|
||||||
dummyUser = 'spiderpig@springfield.com';
|
dummyUser = 'spiderpig@springfield.com';
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
@ -25,8 +24,7 @@ define(function(require) {
|
|||||||
};
|
};
|
||||||
devicestorageStub = sinon.createStubInstance(DeviceStorageDAO);
|
devicestorageStub = sinon.createStubInstance(DeviceStorageDAO);
|
||||||
keychainStub = sinon.createStubInstance(KeychainDAO);
|
keychainStub = sinon.createStubInstance(KeychainDAO);
|
||||||
invitationDaoStub = sinon.createStubInstance(InvitationDAO);
|
outbox = new OutboxBO(emailDaoStub, keychainStub, devicestorageStub);
|
||||||
outbox = new OutboxBO(emailDaoStub, keychainStub, devicestorageStub, invitationDaoStub);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {});
|
afterEach(function() {});
|
||||||
@ -46,7 +44,7 @@ define(function(require) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('put', function() {
|
describe('put', function() {
|
||||||
it('should encrypt and store a mail', function(done) {
|
it('should not encrypt and store a mail', function(done) {
|
||||||
var mail, senderKey, receiverKey;
|
var mail, senderKey, receiverKey;
|
||||||
|
|
||||||
senderKey = {
|
senderKey = {
|
||||||
@ -75,9 +73,51 @@ define(function(require) {
|
|||||||
keychainStub.getReceiverPublicKey.withArgs(mail.to[0].address).yieldsAsync(null, receiverKey);
|
keychainStub.getReceiverPublicKey.withArgs(mail.to[0].address).yieldsAsync(null, receiverKey);
|
||||||
keychainStub.getReceiverPublicKey.withArgs(mail.to[1].address).yieldsAsync();
|
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 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({
|
emailDaoStub.encrypt.withArgs({
|
||||||
mail: mail,
|
mail: mail,
|
||||||
publicKeysArmored: [senderKey.publicKey, receiverKey.publicKey]
|
publicKeysArmored: [senderKey.publicKey, receiverKey.publicKey, receiverKey.publicKey]
|
||||||
}).yieldsAsync();
|
}).yieldsAsync();
|
||||||
|
|
||||||
devicestorageStub.storeList.withArgs([mail]).yieldsAsync();
|
devicestorageStub.storeList.withArgs([mail]).yieldsAsync();
|
||||||
@ -85,8 +125,9 @@ define(function(require) {
|
|||||||
outbox.put(mail, function(error) {
|
outbox.put(mail, function(error) {
|
||||||
expect(error).to.not.exist;
|
expect(error).to.not.exist;
|
||||||
|
|
||||||
expect(mail.publicKeysArmored.length).to.equal(2);
|
expect(mail.publicKeysArmored.length).to.equal(3);
|
||||||
expect(mail.unregisteredUsers.length).to.equal(1);
|
expect(emailDaoStub.encrypt.calledOnce).to.be.true;
|
||||||
|
expect(devicestorageStub.storeList.calledOnce).to.be.true;
|
||||||
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@ -108,6 +149,7 @@ define(function(require) {
|
|||||||
name: 'member',
|
name: 'member',
|
||||||
address: 'member'
|
address: 'member'
|
||||||
}],
|
}],
|
||||||
|
encrypted: true,
|
||||||
publicKeysArmored: ['ARMORED KEY OF MEMBER'],
|
publicKeysArmored: ['ARMORED KEY OF MEMBER'],
|
||||||
unregisteredUsers: []
|
unregisteredUsers: []
|
||||||
};
|
};
|
||||||
@ -144,6 +186,7 @@ define(function(require) {
|
|||||||
name: 'newlyjoined',
|
name: 'newlyjoined',
|
||||||
address: 'newlyjoined'
|
address: 'newlyjoined'
|
||||||
}],
|
}],
|
||||||
|
encrypted: true,
|
||||||
publicKeysArmored: [],
|
publicKeysArmored: [],
|
||||||
unregisteredUsers: [{
|
unregisteredUsers: [{
|
||||||
name: 'newlyjoined',
|
name: 'newlyjoined',
|
||||||
@ -158,32 +201,8 @@ define(function(require) {
|
|||||||
|
|
||||||
devicestorageStub.listItems.yieldsAsync(null, dummyMails);
|
devicestorageStub.listItems.yieldsAsync(null, dummyMails);
|
||||||
|
|
||||||
keychainStub.getReceiverPublicKey.withArgs(invited.unregisteredUsers[0].address).yieldsAsync();
|
|
||||||
keychainStub.getReceiverPublicKey.withArgs(notinvited.unregisteredUsers[0].address).yieldsAsync();
|
|
||||||
keychainStub.getReceiverPublicKey.withArgs(newlyjoined.unregisteredUsers[0].address).yieldsAsync(null, newlyjoinedKey);
|
|
||||||
|
|
||||||
invitationDaoStub.check.withArgs({
|
|
||||||
recipient: invited.to[0].address,
|
|
||||||
sender: invited.from[0].address
|
|
||||||
}).yieldsAsync(null, InvitationDAO.INVITE_PENDING);
|
|
||||||
|
|
||||||
invitationDaoStub.check.withArgs({
|
|
||||||
recipient: notinvited.to[0].address,
|
|
||||||
sender: notinvited.from[0].address
|
|
||||||
}).yieldsAsync(null, InvitationDAO.INVITE_MISSING);
|
|
||||||
|
|
||||||
invitationDaoStub.invite.withArgs({
|
|
||||||
recipient: notinvited.to[0].address,
|
|
||||||
sender: notinvited.from[0].address
|
|
||||||
}).yieldsAsync(null, InvitationDAO.INVITE_SUCCESS);
|
|
||||||
|
|
||||||
emailDaoStub.sendPlaintext.yieldsAsync();
|
emailDaoStub.sendPlaintext.yieldsAsync();
|
||||||
|
|
||||||
emailDaoStub.reEncrypt.withArgs({
|
|
||||||
mail: newlyjoined,
|
|
||||||
publicKeysArmored: [newlyjoinedKey.publicKey]
|
|
||||||
}).yieldsAsync(null, newlyjoined);
|
|
||||||
|
|
||||||
emailDaoStub.sendEncrypted.withArgs({
|
emailDaoStub.sendEncrypted.withArgs({
|
||||||
email: newlyjoined
|
email: newlyjoined
|
||||||
}).yieldsAsync();
|
}).yieldsAsync();
|
||||||
@ -192,22 +211,18 @@ define(function(require) {
|
|||||||
email: member
|
email: member
|
||||||
}).yieldsAsync();
|
}).yieldsAsync();
|
||||||
|
|
||||||
devicestorageStub.storeList.withArgs([newlyjoined]).yieldsAsync();
|
devicestorageStub.removeList.yieldsAsync();
|
||||||
|
|
||||||
devicestorageStub.removeList.withArgs('email_OUTBOX_' + member.id).yieldsAsync();
|
|
||||||
devicestorageStub.removeList.withArgs('email_OUTBOX_' + newlyjoined.id).yieldsAsync();
|
|
||||||
|
|
||||||
function onOutboxUpdate(err, count) {
|
function onOutboxUpdate(err, count) {
|
||||||
expect(err).to.not.exist;
|
expect(err).to.not.exist;
|
||||||
expect(count).to.equal(2);
|
expect(count).to.equal(0);
|
||||||
|
|
||||||
expect(outbox._outboxBusy).to.be.false;
|
expect(outbox._outboxBusy).to.be.false;
|
||||||
expect(emailDaoStub.sendEncrypted.calledTwice).to.be.true;
|
expect(emailDaoStub.sendEncrypted.callCount).to.equal(2);
|
||||||
expect(emailDaoStub.reEncrypt.calledOnce).to.be.true;
|
expect(emailDaoStub.sendPlaintext.callCount).to.equal(2);
|
||||||
expect(emailDaoStub.sendPlaintext.calledOnce).to.be.true;
|
expect(devicestorageStub.listItems.callCount).to.equal(1);
|
||||||
expect(devicestorageStub.listItems.calledOnce).to.be.true;
|
expect(devicestorageStub.removeList.callCount).to.equal(4);
|
||||||
expect(keychainStub.getReceiverPublicKey.calledThrice).to.be.true;
|
expect(keychainStub.getReceiverPublicKey.callCount).to.equal(0);
|
||||||
expect(invitationDaoStub.check.calledTwice).to.be.true;
|
|
||||||
|
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
|
@ -4,12 +4,39 @@ define(function(require) {
|
|||||||
var expect = chai.expect,
|
var expect = chai.expect,
|
||||||
angular = require('angular'),
|
angular = require('angular'),
|
||||||
mocks = require('angularMocks'),
|
mocks = require('angularMocks'),
|
||||||
ReadCtrl = require('js/controller/read');
|
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');
|
||||||
|
|
||||||
describe('Read Controller unit test', function() {
|
describe('Read Controller unit test', function() {
|
||||||
var scope, ctrl;
|
var scope, ctrl,
|
||||||
|
origKeychain, keychainMock,
|
||||||
|
origInvitation, invitationMock,
|
||||||
|
origCrypto, cryptoMock,
|
||||||
|
origOutbox, outboxMock,
|
||||||
|
origEmailDao;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
|
origKeychain = appController._keychain;
|
||||||
|
appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||||
|
|
||||||
|
origInvitation = appController._invitationDao;
|
||||||
|
appController._invitationDao = invitationMock = sinon.createStubInstance(InvitationDAO);
|
||||||
|
|
||||||
|
origCrypto = appController._crypto;
|
||||||
|
appController._crypto = cryptoMock = sinon.createStubInstance(PGP);
|
||||||
|
|
||||||
|
origOutbox = appController._outboxBo;
|
||||||
|
appController._outboxBo = outboxMock = sinon.createStubInstance(OutboxBO);
|
||||||
|
|
||||||
|
origEmailDao = appController._emailDao;
|
||||||
|
appController._emailDao = {
|
||||||
|
_account: 'sender@example.com'
|
||||||
|
};
|
||||||
|
|
||||||
angular.module('readtest', []);
|
angular.module('readtest', []);
|
||||||
mocks.module('readtest');
|
mocks.module('readtest');
|
||||||
mocks.inject(function($rootScope, $controller) {
|
mocks.inject(function($rootScope, $controller) {
|
||||||
@ -21,7 +48,13 @@ define(function(require) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {});
|
afterEach(function() {
|
||||||
|
appController._keychain = origKeychain;
|
||||||
|
appController._invitationDao = origInvitation;
|
||||||
|
appController._crypto = origCrypto;
|
||||||
|
appController._outboxBo = origOutbox;
|
||||||
|
appController._emailDao = origEmailDao;
|
||||||
|
});
|
||||||
|
|
||||||
describe('scope variables', function() {
|
describe('scope variables', function() {
|
||||||
it('should be set correctly', function() {
|
it('should be set correctly', function() {
|
||||||
@ -40,5 +73,98 @@ define(function(require) {
|
|||||||
expect(scope.state.read.open).to.be.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'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
@ -169,7 +169,7 @@ define(function(require) {
|
|||||||
|
|
||||||
expect(recipient.key).to.be.undefined;
|
expect(recipient.key).to.be.undefined;
|
||||||
expect(recipient.secure).to.be.undefined;
|
expect(recipient.secure).to.be.undefined;
|
||||||
expect(scope.checkSendStatus.calledOnce).to.be.true;
|
expect(scope.checkSendStatus.callCount).to.equal(2);
|
||||||
expect(keychainMock.getReceiverPublicKey.called).to.be.false;
|
expect(keychainMock.getReceiverPublicKey.called).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -184,7 +184,7 @@ define(function(require) {
|
|||||||
scope.onError = function() {
|
scope.onError = function() {
|
||||||
expect(recipient.key).to.be.undefined;
|
expect(recipient.key).to.be.undefined;
|
||||||
expect(recipient.secure).to.be.false;
|
expect(recipient.secure).to.be.false;
|
||||||
expect(scope.checkSendStatus.called).to.be.false;
|
expect(scope.checkSendStatus.callCount).to.equal(1);
|
||||||
expect(keychainMock.getReceiverPublicKey.calledOnce).to.be.true;
|
expect(keychainMock.getReceiverPublicKey.calledOnce).to.be.true;
|
||||||
done();
|
done();
|
||||||
};
|
};
|
||||||
@ -205,7 +205,7 @@ define(function(require) {
|
|||||||
userId: 'asdf@example.com'
|
userId: 'asdf@example.com'
|
||||||
});
|
});
|
||||||
expect(recipient.secure).to.be.true;
|
expect(recipient.secure).to.be.true;
|
||||||
expect(scope.checkSendStatus.calledOnce).to.be.true;
|
expect(scope.checkSendStatus.callCount).to.equal(2);
|
||||||
expect(keychainMock.getReceiverPublicKey.calledOnce).to.be.true;
|
expect(keychainMock.getReceiverPublicKey.calledOnce).to.be.true;
|
||||||
done();
|
done();
|
||||||
};
|
};
|
||||||
@ -229,28 +229,29 @@ define(function(require) {
|
|||||||
expect(scope.sendBtnSecure).to.be.undefined;
|
expect(scope.sendBtnSecure).to.be.undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not be to invite 1 user', function() {
|
it('should be able to send plaintext', function() {
|
||||||
scope.to = [{
|
scope.to = [{
|
||||||
address: 'asdf@asdf.de'
|
address: 'asdf@asdf.de'
|
||||||
}];
|
}];
|
||||||
scope.checkSendStatus();
|
scope.checkSendStatus();
|
||||||
|
|
||||||
expect(scope.okToSend).to.be.true;
|
expect(scope.okToSend).to.be.true;
|
||||||
expect(scope.sendBtnText).to.equal('Invite & send securely');
|
expect(scope.sendBtnText).to.equal('Send');
|
||||||
expect(scope.sendBtnSecure).to.be.false;
|
expect(scope.sendBtnSecure).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not be able to invite multiple recipients', function() {
|
it('should send plaintext if one receiver is not secure', function() {
|
||||||
scope.to = [{
|
scope.to = [{
|
||||||
address: 'asdf@asdf.de'
|
address: 'asdf@asdf.de',
|
||||||
|
secure: true
|
||||||
}, {
|
}, {
|
||||||
address: 'asdf@asdfg.de'
|
address: 'asdf@asdfg.de'
|
||||||
}];
|
}];
|
||||||
scope.checkSendStatus();
|
scope.checkSendStatus();
|
||||||
|
|
||||||
expect(scope.okToSend).to.be.false;
|
expect(scope.okToSend).to.be.true;
|
||||||
expect(scope.sendBtnText).to.be.undefined;
|
expect(scope.sendBtnText).to.equal('Send');
|
||||||
expect(scope.sendBtnSecure).to.be.undefined;
|
expect(scope.sendBtnSecure).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to send securely to multiple recipients', function() {
|
it('should be able to send securely to multiple recipients', function() {
|
||||||
|
Loading…
Reference in New Issue
Block a user