mirror of
https://github.com/moparisthebest/mail
synced 2024-12-23 07:48:48 -05:00
Merge pull request #16 from whiteout-io/dev/attachments-ui
Dev/attachments ui
This commit is contained in:
commit
0c2fd7f052
10
Gruntfile.js
10
Gruntfile.js
@ -137,13 +137,11 @@ module.exports = function(grunt) {
|
||||
'imap-client/node_modules/mimelib/node_modules/addressparser/src/addressparser.js',
|
||||
'imap-client/node_modules/mimelib/node_modules/encoding/src/encoding.js',
|
||||
'imap-client/node_modules/mimelib/node_modules/encoding/node_modules/iconv-lite/src/*.js',
|
||||
'imap-client/node_modules/mimelib/node_modules/encoding/node_modules/mime/src/*.js',
|
||||
'imap-client/node_modules/mailparser/src/*.js',
|
||||
'imap-client/node_modules/mailparser/node_modules/mime/src/mime.js',
|
||||
'smtp-client/src/*.js',
|
||||
'smtp-client/node_modules/mailcomposer/src/*',
|
||||
'smtp-client/node_modules/nodemailer/src/*',
|
||||
'smtp-client/node_modules/nodemailer/node_modules/simplesmtp/src/*',
|
||||
'imap-client/node_modules/mime/src/mime.js',
|
||||
'pgpmailer/src/*.js',
|
||||
'pgpmailer/node_modules/simplesmtp/src/*',
|
||||
'pgpmailer/node_modules/mailbuilder/src/*.js'
|
||||
],
|
||||
dest: 'src/lib/'
|
||||
},
|
||||
|
@ -12,7 +12,7 @@
|
||||
"dependencies": {
|
||||
"crypto-lib": "https://github.com/whiteout-io/crypto-lib/tarball/master",
|
||||
"imap-client": "https://github.com/whiteout-io/imap-client/tarball/master",
|
||||
"smtp-client": "https://github.com/whiteout-io/smtp-client/tarball/master",
|
||||
"pgpmailer": "https://github.com/whiteout-io/pgpmailer/tarball/master",
|
||||
"requirejs": "2.1.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -54,7 +54,7 @@ define(function(require) {
|
||||
fallbackSubject: '(no subject)',
|
||||
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',
|
||||
message: 'this 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',
|
||||
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',
|
||||
cryptPrefix: '-----BEGIN PGP MESSAGE-----',
|
||||
cryptSuffix: '-----END PGP MESSAGE-----',
|
||||
signature: 'Sent securely from Whiteout Mail',
|
||||
|
@ -6,7 +6,7 @@ define(function(require) {
|
||||
|
||||
var $ = require('jquery'),
|
||||
ImapClient = require('imap-client'),
|
||||
SmtpClient = require('smtp-client'),
|
||||
PgpMailer = require('pgpmailer'),
|
||||
EmailDAO = require('js/dao/email-dao'),
|
||||
RestDAO = require('js/dao/rest-dao'),
|
||||
PublicKeyDAO = require('js/dao/publickey-dao'),
|
||||
@ -89,7 +89,7 @@ define(function(require) {
|
||||
});
|
||||
|
||||
function initClients(oauth, certificate) {
|
||||
var auth, imapOptions, imapClient, smtpOptions, smtpClient;
|
||||
var auth, imapOptions, imapClient, smtpOptions, pgpMailer;
|
||||
|
||||
auth = {
|
||||
XOAuth2: {
|
||||
@ -106,15 +106,18 @@ define(function(require) {
|
||||
ca: [certificate]
|
||||
};
|
||||
smtpOptions = {
|
||||
secure: config.gmail.smtp.secure,
|
||||
secureConnection: config.gmail.smtp.secure,
|
||||
port: config.gmail.smtp.port,
|
||||
host: config.gmail.smtp.host,
|
||||
auth: auth,
|
||||
tls: {
|
||||
ca: [certificate]
|
||||
},
|
||||
onError: console.error
|
||||
};
|
||||
|
||||
imapClient = new ImapClient(imapOptions);
|
||||
smtpClient = new SmtpClient(smtpOptions);
|
||||
pgpMailer = new PgpMailer(smtpOptions);
|
||||
|
||||
imapClient.onError = function(err) {
|
||||
console.log('IMAP error.', err);
|
||||
@ -138,7 +141,7 @@ define(function(require) {
|
||||
// connect to clients
|
||||
self._emailDao.onConnect({
|
||||
imapClient: imapClient,
|
||||
smtpClient: smtpClient
|
||||
pgpMailer: pgpMailer
|
||||
}, callback);
|
||||
}
|
||||
};
|
||||
|
@ -96,7 +96,7 @@ define(function(require) {
|
||||
self._outboxBusy = true;
|
||||
|
||||
// get last item from outbox
|
||||
self._emailDao.list(function(err, pending) {
|
||||
self._emailDao.listForOutbox(function(err, pending) {
|
||||
if (err) {
|
||||
self._outboxBusy = false;
|
||||
callback(err);
|
||||
|
@ -277,7 +277,14 @@ define(function(require) {
|
||||
this.to = [{
|
||||
address: 'max.musterman@gmail.com'
|
||||
}]; // list of receivers
|
||||
this.attachments = (attachments) ? [true] : undefined;
|
||||
if (attachments) {
|
||||
// body structure with three attachments
|
||||
this.bodystructure = {"1": {"part": "1","type": "text/plain","parameters": {"charset": "us-ascii"},"encoding": "7bit","size": 9,"lines": 2},"2": {"part": "2","type": "application/octet-stream","parameters": {"name": "a.md"},"encoding": "7bit","size": 123,"disposition": [{"type": "attachment","filename": "a.md"}]},"3": {"part": "3","type": "application/octet-stream","parameters": {"name": "b.md"},"encoding": "7bit","size": 456,"disposition": [{"type": "attachment","filename": "b.md"}]},"4": {"part": "4","type": "application/octet-stream","parameters": {"name": "c.md"},"encoding": "7bit","size": 789,"disposition": [{"type": "attachment","filename": "c.md"}]},"type": "multipart/mixed"};
|
||||
this.attachments = [{"filename": "a.md","filesize": 123,"mimeType": "text/x-markdown","part": "2","content": null}, {"filename": "b.md","filesize": 456,"mimeType": "text/x-markdown","part": "3","content": null}, {"filename": "c.md","filesize": 789,"mimeType": "text/x-markdown","part": "4","content": null}];
|
||||
} else {
|
||||
this.bodystructure = {"part": "1","type": "text/plain","parameters": {"charset": "us-ascii"},"encoding": "7bit","size": 9,"lines": 2};
|
||||
this.attachments = [];
|
||||
}
|
||||
this.unread = unread;
|
||||
this.answered = answered;
|
||||
this.html = html;
|
||||
@ -286,7 +293,7 @@ define(function(require) {
|
||||
this.body = 'Here are a few pointers to help you get started with Whiteout Mail.\n\n# Write encrypted message\n- You can compose a message by clicking on the compose button on the upper right (keyboard shortcut is "n" for a new message or "r" to reply).\n- When typing the recipient\'s email address, secure recipients are marked with a blue label and insecure recipients are red.\n- When sending an email to insecure recipients, the default behavior for Whiteout Mail is to invite them to the service and only send the message content in an encrypted form, once they have joined.\n\n# Advanced features\n- To verify a recipient\'s PGP key, you can hover over the blue label containing their email address and their key fingerprint will be displayed.\n- To view your own key fingerprint, open the account view in the navigation bar on the left. You can compare these with your correspondants over a second channel such as a phonecall.\n\nWe hope this helped you to get started with Whiteout Mail.\n\nYour Whiteout Networks team'; // plaintext body
|
||||
};
|
||||
|
||||
var dummys = [new Email(true, true), new Email(true, false, true, true), new Email(false, true, true), new Email(false), new Email(false), new Email(false), new Email(false), new Email(false), new Email(false), new Email(false), new Email(false), new Email(false), new Email(false), new Email(false), new Email(false), new Email(false), new Email(false), new Email(false)];
|
||||
var dummys = [new Email(true, true), new Email(true, false, true, true), new Email(false, true, true), new Email(false, true), new Email(false), new Email(false), new Email(false), new Email(false), new Email(false), new Email(false), new Email(false), new Email(false), new Email(false), new Email(false), new Email(false), new Email(false), new Email(false), new Email(false)];
|
||||
|
||||
return dummys;
|
||||
}
|
||||
|
@ -2,14 +2,17 @@ define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var appController = require('js/app-controller'),
|
||||
download = require('js/util/download'),
|
||||
angular = require('angular'),
|
||||
crypto, keychain;
|
||||
emailDao, crypto, keychain;
|
||||
|
||||
//
|
||||
// Controller
|
||||
//
|
||||
|
||||
var ReadCtrl = function($scope) {
|
||||
|
||||
emailDao = appController._emailDao;
|
||||
crypto = appController._crypto;
|
||||
keychain = appController._keychain;
|
||||
|
||||
@ -74,6 +77,38 @@ define(function(require) {
|
||||
$scope.$apply();
|
||||
});
|
||||
}
|
||||
|
||||
$scope.download = function(attachment) {
|
||||
// download file to disk if content is available
|
||||
if (attachment.content) {
|
||||
saveToDisk(attachment);
|
||||
return;
|
||||
}
|
||||
|
||||
var folder = $scope.state.nav.currentFolder;
|
||||
var email = $scope.state.mailList.selected;
|
||||
|
||||
emailDao.getAttachment({
|
||||
path: folder.path,
|
||||
uid: email.uid,
|
||||
attachment: attachment
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
saveToDisk(attachment);
|
||||
});
|
||||
|
||||
function saveToDisk(attachment) {
|
||||
download.createDownload({
|
||||
content: attachment.content,
|
||||
filename: attachment.filename,
|
||||
contentType: attachment.mimeType
|
||||
}, $scope.onError);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
//
|
||||
|
@ -56,6 +56,7 @@ define(function(require) {
|
||||
$scope.subject = '';
|
||||
$scope.body = '';
|
||||
$scope.ciphertextPreview = '';
|
||||
$scope.attachments = [];
|
||||
}
|
||||
|
||||
function fillFields(re) {
|
||||
@ -79,7 +80,7 @@ define(function(require) {
|
||||
|
||||
// only display non html mails in reply part
|
||||
if (!re.html) {
|
||||
body += re.body.split('\n').join('\n> ');
|
||||
body += re.body.trim().split('\n').join('\n> ');
|
||||
$scope.body = body;
|
||||
}
|
||||
}
|
||||
@ -182,6 +183,14 @@ define(function(require) {
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Editing attachments
|
||||
//
|
||||
|
||||
$scope.remove = function(attachment) {
|
||||
$scope.attachments.splice($scope.attachments.indexOf(attachment), 1);
|
||||
};
|
||||
|
||||
//
|
||||
// Editing email body
|
||||
//
|
||||
@ -239,8 +248,13 @@ define(function(require) {
|
||||
});
|
||||
}
|
||||
|
||||
// add attachment to email object
|
||||
if ($scope.attachments.length > 0) {
|
||||
email.attachments = $scope.attachments;
|
||||
}
|
||||
|
||||
// persist the email locally for later smtp transmission
|
||||
emailDao.store(email, function(err) {
|
||||
emailDao.storeForOutbox(email, function(err) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
@ -440,5 +454,36 @@ define(function(require) {
|
||||
};
|
||||
});
|
||||
|
||||
ngModule.directive('attachmentInput', function() {
|
||||
return function(scope, elm) {
|
||||
elm.on('change', function(e) {
|
||||
for (var i = 0; i < e.target.files.length; i++) {
|
||||
addAttachment(e.target.files.item(i));
|
||||
}
|
||||
});
|
||||
|
||||
function addAttachment(file) {
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
scope.attachments.push({
|
||||
filename: file.name,
|
||||
mimeType: file.type,
|
||||
content: new Uint8Array(e.target.result)
|
||||
});
|
||||
scope.$apply();
|
||||
};
|
||||
reader.readAsArrayBuffer(file);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
ngModule.directive('attachmentBtn', function() {
|
||||
return function(scope, elm) {
|
||||
elm.on('click', function() {
|
||||
document.querySelector('#attachment-input').click();
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
return WriteCtrl;
|
||||
});
|
@ -73,7 +73,11 @@ define(function(require) {
|
||||
var self = this;
|
||||
|
||||
self._imapClient = options.imapClient;
|
||||
self._smtpClient = options.smtpClient;
|
||||
self._pgpMailer = options.pgpMailer;
|
||||
// set private key
|
||||
if (self._crypto && self._crypto._privateKey) {
|
||||
self._pgpMailer._privateKey = self._crypto._privateKey;
|
||||
}
|
||||
|
||||
// delegation-esque pattern to mitigate between node-style events and plain js
|
||||
self._imapClient.onIncomingMessage = function(message) {
|
||||
@ -82,6 +86,9 @@ define(function(require) {
|
||||
}
|
||||
};
|
||||
|
||||
// connect the pgpmailer
|
||||
self._pgpmailerLogin();
|
||||
|
||||
// connect to newly created imap client
|
||||
self._imapLogin(function(err) {
|
||||
if (err) {
|
||||
@ -116,7 +123,7 @@ define(function(require) {
|
||||
// set status to online
|
||||
this._account.online = false;
|
||||
this._imapClient = undefined;
|
||||
this._smtpClient = undefined;
|
||||
self._pgpMailer = undefined;
|
||||
|
||||
callback();
|
||||
};
|
||||
@ -131,6 +138,8 @@ define(function(require) {
|
||||
privateKeyArmored: options.keypair.privateKey.encryptedKey,
|
||||
publicKeyArmored: options.keypair.publicKey.publicKey
|
||||
}, callback);
|
||||
// set decrypted privateKey to pgpMailer
|
||||
self._pgpMailer._privateKey = self._crypto._privateKey;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -805,7 +814,8 @@ define(function(require) {
|
||||
|
||||
if (!senderPubkey) {
|
||||
// this should only happen if a mail from another channel is in the inbox
|
||||
setBodyAndContinue('Public key for sender not found!');
|
||||
email.body = 'Public key for sender not found!';
|
||||
localCallback(null, email);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -817,7 +827,23 @@ define(function(require) {
|
||||
|
||||
// set encrypted flag
|
||||
email.encrypted = true;
|
||||
setBodyAndContinue(decrypted);
|
||||
|
||||
// does our message block even need to be parsed?
|
||||
// this is a very primitive detection if we have a mime node or plain text
|
||||
// taking this out breaks compatibility to clients < 0.5
|
||||
if (decrypted.indexOf('Content-Transfer-Encoding:') === -1 &&
|
||||
decrypted.indexOf('Content-Type:') === -1) {
|
||||
// decrypted message is plain text and not a well-formed email
|
||||
email.body = decrypted;
|
||||
localCallback(null, email);
|
||||
return;
|
||||
}
|
||||
|
||||
// parse decrypted message
|
||||
self._imapParseMessageBlock({
|
||||
message: email,
|
||||
block: decrypted
|
||||
}, localCallback);
|
||||
});
|
||||
});
|
||||
|
||||
@ -828,11 +854,6 @@ define(function(require) {
|
||||
// parse email body for encrypted message block
|
||||
email.body = email.body.substring(start, end);
|
||||
}
|
||||
|
||||
function setBodyAndContinue(text) {
|
||||
email.body = text;
|
||||
localCallback(null, email);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -869,6 +890,18 @@ define(function(require) {
|
||||
}, callback);
|
||||
};
|
||||
|
||||
EmailDAO.prototype.getAttachment = function(options, callback) {
|
||||
if (!this._account.online) {
|
||||
callback({
|
||||
errMsg: 'Client is currently offline!',
|
||||
code: 42
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this._imapClient.getAttachment(options, callback);
|
||||
};
|
||||
|
||||
EmailDAO.prototype.sendEncrypted = function(options, callback) {
|
||||
var self = this,
|
||||
email = options.email;
|
||||
@ -889,17 +922,26 @@ define(function(require) {
|
||||
return;
|
||||
}
|
||||
|
||||
// public key found... encrypt and send
|
||||
self._encrypt({
|
||||
email: email,
|
||||
keys: email.receiverKeys // this Array is set in writer controller
|
||||
}, function(err, email) {
|
||||
// get own public key so send message can be read
|
||||
self._crypto.exportKeys(function(err, ownKeys) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
self._smtpClient.send(email, callback);
|
||||
// add own public key to receiver list
|
||||
email.receiverKeys.push(ownKeys.publicKeyArmored);
|
||||
|
||||
// add whiteout tag to subject
|
||||
email.subject = str.subjectPrefix + email.subject;
|
||||
|
||||
// mime encode, sign, encrypt and send email via smtp
|
||||
self._pgpMailer.send({
|
||||
encrypt: true,
|
||||
cleartextMessage: str.message,
|
||||
mail: email,
|
||||
publicKeysArmored: email.receiverKeys
|
||||
}, callback);
|
||||
});
|
||||
};
|
||||
|
||||
@ -912,56 +954,19 @@ define(function(require) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._smtpClient.send(options.email, callback);
|
||||
// add whiteout tag to subject
|
||||
options.email.subject = str.subjectPrefix + options.email.subject;
|
||||
|
||||
// mime encode, sign and send email via smtp
|
||||
this._pgpMailer.send({
|
||||
mail: options.email
|
||||
}, callback);
|
||||
};
|
||||
|
||||
//
|
||||
// Internal API
|
||||
//
|
||||
|
||||
// Encryption API
|
||||
|
||||
EmailDAO.prototype._encrypt = function(options, callback) {
|
||||
var self = this,
|
||||
pt = options.email.body;
|
||||
|
||||
options.keys = options.keys || [];
|
||||
|
||||
// get own public key so send message can be read
|
||||
self._crypto.exportKeys(function(err, ownKeys) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// add own public key to receiver list
|
||||
options.keys.push(ownKeys.publicKeyArmored);
|
||||
// encrypt the email
|
||||
self._crypto.encrypt(pt, options.keys, function(err, ct) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// bundle encrypted email together for sending
|
||||
frameEncryptedMessage(options.email, ct);
|
||||
callback(null, options.email);
|
||||
});
|
||||
});
|
||||
|
||||
function frameEncryptedMessage(email, ct) {
|
||||
var greeting,
|
||||
message = str.message + '\n\n\n',
|
||||
signature = '\n\n' + str.signature + '\n\n';
|
||||
|
||||
greeting = 'Hi,\n\n';
|
||||
|
||||
// build encrypted text body
|
||||
email.body = greeting + message + ct + signature;
|
||||
email.subject = str.subjectPrefix + email.subject;
|
||||
}
|
||||
};
|
||||
|
||||
// Local Storage API
|
||||
|
||||
EmailDAO.prototype._localListMessages = function(options, callback) {
|
||||
@ -989,6 +994,16 @@ define(function(require) {
|
||||
};
|
||||
|
||||
|
||||
// PGP Mailer API
|
||||
|
||||
/**
|
||||
* Login the smtp client
|
||||
*/
|
||||
EmailDAO.prototype._pgpmailerLogin = function() {
|
||||
this._pgpMailer.login();
|
||||
};
|
||||
|
||||
|
||||
// IMAP API
|
||||
|
||||
/**
|
||||
@ -1068,6 +1083,10 @@ define(function(require) {
|
||||
}, callback);
|
||||
};
|
||||
|
||||
EmailDAO.prototype._imapParseMessageBlock = function(options, callback) {
|
||||
this._imapClient.parseDecryptedMessageBlock(options, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get an email messsage including the email body from imap
|
||||
* @param {String} options.messageId The
|
||||
@ -1161,29 +1180,47 @@ define(function(require) {
|
||||
}
|
||||
};
|
||||
|
||||
// to be removed and solved with IMAP!
|
||||
EmailDAO.prototype.store = function(email, callback) {
|
||||
/**
|
||||
* Persists an email object for the outbox, encrypted with the user's public key
|
||||
* @param {Object} email The email object
|
||||
* @param {Function} callback(error) Invoked when the email was encrypted and persisted, contains information in case of an error
|
||||
*/
|
||||
EmailDAO.prototype.storeForOutbox = function(email, callback) {
|
||||
var self = this,
|
||||
dbType = 'email_OUTBOX';
|
||||
dbType = 'email_OUTBOX',
|
||||
plaintext = email.body;
|
||||
|
||||
// give the email a random identifier (used for storage)
|
||||
email.id = util.UUID();
|
||||
|
||||
// encrypt
|
||||
self._encrypt({
|
||||
email: email
|
||||
}, function(err, email) {
|
||||
// get own public key so send message can be read
|
||||
self._crypto.exportKeys(function(err, ownKeys) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// encrypt the email with the user's public key
|
||||
self._crypto.encrypt(plaintext, [ownKeys.publicKeyArmored], function(err, ciphertext) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// replace plaintext body with pgp message
|
||||
email.body = ciphertext;
|
||||
|
||||
// store to local storage
|
||||
self._devicestorage.storeList([email], dbType, callback);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// to be removed and solved with IMAP!
|
||||
EmailDAO.prototype.list = function(callback) {
|
||||
/**
|
||||
* Reads and decrypts persisted email objects for the outbox
|
||||
* @param {Function} callback(error, emails) Invoked when the email was encrypted and persisted, contains information in case of an error
|
||||
*/
|
||||
EmailDAO.prototype.listForOutbox = function(callback) {
|
||||
var self = this,
|
||||
dbType = 'email_OUTBOX';
|
||||
|
||||
@ -1209,15 +1246,12 @@ define(function(require) {
|
||||
});
|
||||
|
||||
mails.forEach(function(mail) {
|
||||
mail.body = str.cryptPrefix + mail.body.split(str.cryptPrefix)[1].split(str.cryptSuffix)[0] + str.cryptSuffix;
|
||||
mail.subject = mail.subject.split(str.subjectPrefix)[1];
|
||||
self._crypto.decrypt(mail.body, ownKeys.publicKeyArmored, function(err, decrypted) {
|
||||
mail.body = err ? err.errMsg : decrypted;
|
||||
after();
|
||||
});
|
||||
mail.encrypted = true;
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
};
|
||||
|
@ -5,6 +5,8 @@
|
||||
color: $color-grey-dark;
|
||||
|
||||
.headers {
|
||||
margin-bottom: 1em;
|
||||
|
||||
p {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
@ -36,15 +38,47 @@
|
||||
}
|
||||
}
|
||||
|
||||
.attachments {
|
||||
position: relative;
|
||||
width: inherit;
|
||||
border: 1px;
|
||||
border-style: solid;
|
||||
border-color: $color-grey-lighter;
|
||||
min-height: em(44);
|
||||
|
||||
.attachment {
|
||||
height: 32px;
|
||||
border-radius: 16px;
|
||||
vertical-align: middle;
|
||||
margin: 5px 0 5px 5px;
|
||||
padding: 5px 10px 5px 10px;
|
||||
border: 1px;
|
||||
border-style: solid;
|
||||
border-color: $color-grey-lighter;
|
||||
display: inline-block;
|
||||
|
||||
span {
|
||||
font-size: 14px;
|
||||
color: $color-grey-input;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: darken($color-white, 3%);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.seperator-line {
|
||||
height: 1px;
|
||||
color: $color-grey-lighter;
|
||||
background-color: $color-grey-lighter;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1.75em;
|
||||
}
|
||||
|
||||
.body {
|
||||
margin-top: 1.75em;
|
||||
cursor: text;
|
||||
padding-bottom: 200px;
|
||||
line-height: 1.5em;
|
||||
|
@ -38,7 +38,8 @@
|
||||
}
|
||||
|
||||
.subject-box {
|
||||
margin: 20px 0;
|
||||
position: relative;
|
||||
margin: 20px 0 7px 0;
|
||||
width: inherit;
|
||||
border: 1px;
|
||||
border-style: solid;
|
||||
@ -57,16 +58,76 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
input[type=file] {
|
||||
visibility: hidden;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.btn-attachment {
|
||||
float: right;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
padding: em(7.5) em(7.5) em(4) em(7.5);
|
||||
margin: em(5);
|
||||
outline: none;
|
||||
//color: $color-grey-lightest;
|
||||
//background-color: $color-blue;
|
||||
color: $btn-disabled-color;
|
||||
background-color: $btn-disabled-back-color;
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAGCAYAAADgzO9IAAAAJUlEQVQIW2NkQABJIPM5lCvJCGMgC4LYIAkUlTAFMB0gjRQaBQCw8go5lVnl5wAAAABJRU5ErkJggg==);
|
||||
color: $color-grey-lightest;
|
||||
background-color: $color-blue;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: lighten($color-blue, 10%);
|
||||
}
|
||||
|
||||
&:active,
|
||||
&.active {
|
||||
top: 1px;
|
||||
right: -1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.attachments-box {
|
||||
position: relative;
|
||||
margin: 0 0 5px 0;
|
||||
width: inherit;
|
||||
border: 1px;
|
||||
border-style: solid;
|
||||
border-color: $color-grey-lighter;
|
||||
min-height: em(44);
|
||||
|
||||
.attachment {
|
||||
height: 32px;
|
||||
border-radius: 16px;
|
||||
vertical-align: middle;
|
||||
margin: 5px 0 5px 5px;
|
||||
padding: 5px 5px 5px 10px;
|
||||
border: 1px;
|
||||
border-style: solid;
|
||||
border-color: $color-grey-lighter;
|
||||
cursor: default;
|
||||
display: inline-block;
|
||||
|
||||
span {
|
||||
font-size: 14px;
|
||||
color: $color-grey-input;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.close {
|
||||
margin-left: 5px;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: darken($color-grey, 10%);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: darken($color-white, 2%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,20 @@
|
||||
</div>
|
||||
</div><!--/.headers-->
|
||||
|
||||
<div ng-switch="state.mailList.selected.attachments !== undefined && state.mailList.selected.attachments.length > 0">
|
||||
<div ng-switch-when="true">
|
||||
<div class="attachments">
|
||||
<span class="attachment" ng-repeat="attachment in state.mailList.selected.attachments" ng-click="download(attachment)">
|
||||
<span data-icon=""></span>
|
||||
{{attachment.filename}}
|
||||
</span><!--/.attachment-->
|
||||
</div><!--/.attachments-->
|
||||
</div>
|
||||
<div ng-switch-default>
|
||||
<div class="seperator-line"></div>
|
||||
</div>
|
||||
</div><!--/.ng-switch-->
|
||||
|
||||
|
||||
<div class="body">
|
||||
<!-- Render lines of a text-email in divs for easier styling -->
|
||||
|
@ -24,12 +24,25 @@
|
||||
<div class="subject-box">
|
||||
<div class="subject-line">
|
||||
<input ng-model="subject" class="subject" spellcheck="true" tabindex="2" placeholder="Subject" ng-change="updatePreview()">
|
||||
</div>
|
||||
<button class="btn-attachment">
|
||||
</div><!--/.subject-line-->
|
||||
<input id="attachment-input" type="file" multiple attachment-input>
|
||||
<button class="btn-attachment" attachment-btn>
|
||||
<div data-icon=""></div>
|
||||
</button>
|
||||
</button><!--/.btn-attachment-->
|
||||
</div><!--/.subject-box-->
|
||||
|
||||
<div ng-switch="attachments.length > 0">
|
||||
<div ng-switch-when="true">
|
||||
<div class="attachments-box">
|
||||
<span ng-repeat="attachment in attachments" class="attachment">
|
||||
<span data-icon=""></span>
|
||||
{{attachment.filename}}
|
||||
<span class="close" data-icon="" ng-click="remove(attachment)"></span>
|
||||
</span><!--/.attachment-->
|
||||
</div><!--/.attachments-box-->
|
||||
</div><!--/ng-switch-when-->
|
||||
</div><!--/ng-switch-->
|
||||
|
||||
<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>
|
||||
|
||||
|
@ -50,13 +50,20 @@ define(function(require) {
|
||||
mocks.module('addaccounttest');
|
||||
mocks.inject(function($controller, $rootScope, $location) {
|
||||
location = $location;
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
|
||||
sinon.stub(location, 'path', function(path) {
|
||||
expect(path).to.equal('/login');
|
||||
expect(fetchOAuthTokenStub.calledOnce).to.be.true;
|
||||
|
||||
location.path.restore();
|
||||
scope.$apply.restore();
|
||||
done();
|
||||
});
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
|
||||
sinon.stub(scope, '$apply', function() {});
|
||||
|
||||
ctrl = $controller(AddAccountCtrl, {
|
||||
$location: location,
|
||||
$scope: scope
|
||||
|
@ -4,17 +4,18 @@ define(function(require) {
|
||||
var EmailDAO = require('js/dao/email-dao'),
|
||||
KeychainDAO = require('js/dao/keychain-dao'),
|
||||
ImapClient = require('imap-client'),
|
||||
SmtpClient = require('smtp-client'),
|
||||
PgpMailer = require('pgpmailer'),
|
||||
PGP = require('js/crypto/pgp'),
|
||||
DeviceStorageDAO = require('js/dao/devicestorage-dao'),
|
||||
str = require('js/app-config').string,
|
||||
expect = chai.expect;
|
||||
|
||||
|
||||
describe('Email DAO unit tests', function() {
|
||||
var dao, keychainStub, imapClientStub, smtpClientStub, pgpStub, devicestorageStub;
|
||||
var dao, keychainStub, imapClientStub, pgpMailerStub, pgpStub, devicestorageStub;
|
||||
|
||||
var emailAddress, passphrase, asymKeySize, mockkeyId, dummyEncryptedMail,
|
||||
dummyDecryptedMail, mockKeyPair, account, publicKey, verificationMail, verificationUuid,
|
||||
dummyDecryptedMail, dummyLegacyDecryptedMail, mockKeyPair, account, publicKey, verificationMail, verificationUuid,
|
||||
corruptedVerificationMail, corruptedVerificationUuid,
|
||||
nonWhitelistedMail;
|
||||
|
||||
@ -65,6 +66,20 @@ define(function(require) {
|
||||
answered: false
|
||||
};
|
||||
dummyDecryptedMail = {
|
||||
uid: 1234,
|
||||
from: [{
|
||||
address: 'asd@asd.de'
|
||||
}],
|
||||
to: [{
|
||||
address: 'qwe@qwe.de'
|
||||
}],
|
||||
subject: 'qweasd',
|
||||
body: 'Content-Type: multipart/signed;\r\n boundary="Apple-Mail=_1D8756C0-F347-4D7A-A8DB-7869CBF14FD2";\r\n protocol="application/pgp-signature";\r\n micalg=pgp-sha512\r\n\r\n\r\n--Apple-Mail=_1D8756C0-F347-4D7A-A8DB-7869CBF14FD2\r\nContent-Type: multipart/mixed;\r\n boundary="Apple-Mail=_8ED7DC84-6AD9-4A08-8327-80B62D6BCBFA"\r\n\r\n\r\n--Apple-Mail=_8ED7DC84-6AD9-4A08-8327-80B62D6BCBFA\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: text/plain;\r\n charset=us-ascii\r\n\r\nasdasd \r\n--Apple-Mail=_8ED7DC84-6AD9-4A08-8327-80B62D6BCBFA\r\nContent-Disposition: attachment;\r\n filename=dummy.txt\r\nContent-Type: text/plain;\r\n name="dummy.txt"\r\nContent-Transfer-Encoding: 7bit\r\n\r\noaudbcoaurbvosuabvlasdjbfalwubjvawvb\r\n--Apple-Mail=_8ED7DC84-6AD9-4A08-8327-80B62D6BCBFA--\r\n\r\n--Apple-Mail=_1D8756C0-F347-4D7A-A8DB-7869CBF14FD2\r\nContent-Transfer-Encoding: 7bit\r\nContent-Disposition: attachment;\r\n filename=signature.asc\r\nContent-Type: application/pgp-signature;\r\n name=signature.asc\r\nContent-Description: Message signed with OpenPGP using GPGMail\r\n\r\n-----BEGIN PGP SIGNATURE-----\r\nComment: GPGTools - https://gpgtools.org\r\n\r\niQEcBAEBCgAGBQJS2kO1AAoJEDzmUwH7XO/cP+YH/2PSBxX1ZZd83Uf9qBGDY807\r\niHOdgPFXm64YjSnohO7XsPcnmihqP1ipS2aaCXFC3/Vgb9nc4isQFS+i1VdPwfuR\r\n1Pd2l3dC4/nD4xO9h/W6JW7Yd24NS5TJD5cA7LYwQ8LF+rOzByMatiTMmecAUCe8\r\nEEalEjuogojk4IacA8dg/bfLqQu9E+0GYUJBcI97dx/0jZ0qMOxbWOQLsJ3DnUnV\r\nOad7pAIbHEO6T0EBsH7TyTj4RRHkP6SKE0mm6ZYUC7KCk2Z3MtkASTxUrnqW5qZ5\r\noaXUO9GEc8KZcmbCdhZY2Y5h+dmucaO0jpbeSKkvtYyD4KZrSvt7NTb/0dSLh4Y=\r\n=G8km\r\n-----END PGP SIGNATURE-----\r\n\r\n--Apple-Mail=_1D8756C0-F347-4D7A-A8DB-7869CBF14FD2--\r\n',
|
||||
unread: false,
|
||||
answered: false,
|
||||
receiverKeys: ['-----BEGIN PGP PUBLIC KEY-----\nasd\n-----END PGP PUBLIC KEY-----']
|
||||
};
|
||||
dummyLegacyDecryptedMail = {
|
||||
uid: 1234,
|
||||
from: [{
|
||||
address: 'asd@asd.de'
|
||||
@ -110,7 +125,7 @@ define(function(require) {
|
||||
|
||||
keychainStub = sinon.createStubInstance(KeychainDAO);
|
||||
imapClientStub = sinon.createStubInstance(ImapClient);
|
||||
smtpClientStub = sinon.createStubInstance(SmtpClient);
|
||||
pgpMailerStub = sinon.createStubInstance(PgpMailer);
|
||||
pgpStub = sinon.createStubInstance(PGP);
|
||||
devicestorageStub = sinon.createStubInstance(DeviceStorageDAO);
|
||||
|
||||
@ -130,7 +145,7 @@ define(function(require) {
|
||||
|
||||
dao.onConnect({
|
||||
imapClient: imapClientStub,
|
||||
smtpClient: smtpClientStub
|
||||
pgpMailer: pgpMailerStub
|
||||
}, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(dao._account.online).to.be.true;
|
||||
@ -275,11 +290,12 @@ define(function(require) {
|
||||
});
|
||||
|
||||
describe('onConnect', function() {
|
||||
var imapLoginStub, imapListFoldersStub;
|
||||
var imapLoginStub, imapListFoldersStub, pgpmailerLoginStub;
|
||||
|
||||
beforeEach(function(done) {
|
||||
// imap login
|
||||
imapLoginStub = sinon.stub(dao, '_imapLogin');
|
||||
pgpmailerLoginStub = sinon.stub(dao, '_pgpmailerLogin');
|
||||
imapListFoldersStub = sinon.stub(dao, '_imapListFolders');
|
||||
|
||||
dao.onDisconnect(null, function(err) {
|
||||
@ -293,17 +309,20 @@ define(function(require) {
|
||||
|
||||
afterEach(function() {
|
||||
imapLoginStub.restore();
|
||||
pgpmailerLoginStub.restore();
|
||||
imapListFoldersStub.restore();
|
||||
});
|
||||
|
||||
it('should fail due to error in imap login', function(done) {
|
||||
imapLoginStub.yields({});
|
||||
pgpmailerLoginStub.returns();
|
||||
|
||||
dao.onConnect({
|
||||
imapClient: imapClientStub,
|
||||
smtpClient: smtpClientStub
|
||||
pgpMailer: pgpMailerStub
|
||||
}, function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(pgpmailerLoginStub.calledOnce).to.be.true;
|
||||
expect(imapLoginStub.calledOnce).to.be.true;
|
||||
expect(dao._account.online).to.be.false;
|
||||
done();
|
||||
@ -313,10 +332,11 @@ define(function(require) {
|
||||
it('should work when folder already initiated', function(done) {
|
||||
dao._account.folders = [];
|
||||
imapLoginStub.yields();
|
||||
pgpmailerLoginStub.returns();
|
||||
|
||||
dao.onConnect({
|
||||
imapClient: imapClientStub,
|
||||
smtpClient: smtpClientStub
|
||||
pgpMailer: pgpMailerStub
|
||||
}, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(dao._account.online).to.be.true;
|
||||
@ -329,11 +349,12 @@ define(function(require) {
|
||||
it('should work when folder not yet initiated', function(done) {
|
||||
var folders = [];
|
||||
imapLoginStub.yields();
|
||||
pgpmailerLoginStub.returns();
|
||||
imapListFoldersStub.yields(null, folders);
|
||||
|
||||
dao.onConnect({
|
||||
imapClient: imapClientStub,
|
||||
smtpClient: smtpClientStub
|
||||
pgpMailer: pgpMailerStub
|
||||
}, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(dao._account.online).to.be.true;
|
||||
@ -471,6 +492,33 @@ define(function(require) {
|
||||
});
|
||||
});
|
||||
|
||||
describe('_imapParseMessageBlock', function() {
|
||||
it('should parse a message', function(done) {
|
||||
imapClientStub.parseDecryptedMessageBlock.yields(null, {});
|
||||
|
||||
dao._imapParseMessageBlock(function(err, msg) {
|
||||
expect(err).to.not.exist;
|
||||
expect(msg).to.exist;
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('_pgpmailerLogin', function() {
|
||||
it('should work', function() {
|
||||
// called once in onConnect
|
||||
expect(pgpMailerStub.login.calledOnce).to.be.true;
|
||||
|
||||
pgpMailerStub.login.returns();
|
||||
|
||||
dao._pgpmailerLogin();
|
||||
|
||||
expect(pgpMailerStub.login.calledTwice).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('_imapLogin', function() {
|
||||
it('should fail when disconnected', function(done) {
|
||||
dao.onDisconnect(null, function(err) {
|
||||
@ -823,7 +871,7 @@ define(function(require) {
|
||||
|
||||
describe('sync', function() {
|
||||
it('should work initially', function(done) {
|
||||
var folder, localListStub, invocations, imapSearchStub;
|
||||
var folder, localListStub, invocations, imapSearchStub, imapParseStub;
|
||||
|
||||
invocations = 0;
|
||||
folder = 'FOLDAAAA';
|
||||
@ -852,6 +900,13 @@ define(function(require) {
|
||||
answered: true
|
||||
}).yields(null, []);
|
||||
|
||||
imapParseStub = sinon.stub(dao, '_imapParseMessageBlock');
|
||||
imapParseStub.withArgs({
|
||||
message: dummyEncryptedMail,
|
||||
block: dummyDecryptedMail.body
|
||||
}).yields(null, dummyDecryptedMail);
|
||||
|
||||
|
||||
dao.sync({
|
||||
folder: folder
|
||||
}, function(err) {
|
||||
@ -870,6 +925,7 @@ define(function(require) {
|
||||
expect(pgpStub.decrypt.calledOnce).to.be.true;
|
||||
expect(imapSearchStub.calledThrice).to.be.true;
|
||||
expect(dao._account.folders[0].count).to.equal(1);
|
||||
expect(imapParseStub.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
@ -903,7 +959,7 @@ define(function(require) {
|
||||
});
|
||||
|
||||
it('should initially sync downstream when storage is empty', function(done) {
|
||||
var folder, localListStub, localStoreStub, invocations, imapSearchStub, imapGetStub;
|
||||
var folder, localListStub, localStoreStub, invocations, imapSearchStub, imapGetStub, imapParseStub;
|
||||
|
||||
invocations = 0;
|
||||
folder = 'FOLDAAAA';
|
||||
@ -912,8 +968,8 @@ define(function(require) {
|
||||
path: folder
|
||||
}];
|
||||
|
||||
dummyEncryptedMail.unread = true;
|
||||
dummyEncryptedMail.answered = true;
|
||||
dummyDecryptedMail.unread = true;
|
||||
dummyDecryptedMail.answered = true;
|
||||
|
||||
localListStub = sinon.stub(dao, '_localListMessages').withArgs({
|
||||
folder: folder
|
||||
@ -938,6 +994,9 @@ define(function(require) {
|
||||
answered: true
|
||||
}).yields(null, [dummyEncryptedMail.uid]);
|
||||
|
||||
imapParseStub = sinon.stub(dao, '_imapParseMessageBlock');
|
||||
imapParseStub.yields(null, dummyDecryptedMail);
|
||||
|
||||
localStoreStub = sinon.stub(dao, '_localStoreMessages').yields();
|
||||
|
||||
dao.sync({
|
||||
@ -960,6 +1019,7 @@ define(function(require) {
|
||||
expect(keychainStub.getReceiverPublicKey.calledOnce).to.be.true;
|
||||
expect(pgpStub.decrypt.calledOnce).to.be.true;
|
||||
expect(dao._account.folders[0].count).to.equal(1);
|
||||
expect(imapParseStub.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
@ -1162,7 +1222,7 @@ define(function(require) {
|
||||
});
|
||||
});
|
||||
|
||||
it('should error whilte removing messages from local', function(done) {
|
||||
it('should error while removing messages from local', function(done) {
|
||||
var invocations, folder, localListStub, imapSearchStub, localDeleteStub, imapDeleteStub;
|
||||
|
||||
invocations = 0;
|
||||
@ -1316,7 +1376,7 @@ define(function(require) {
|
||||
});
|
||||
});
|
||||
|
||||
it('should fetch messages downstream from the remote', function(done) {
|
||||
it('should fetch legacy messages downstream from the remote', function(done) {
|
||||
var invocations, folder, localListStub, imapSearchStub, imapGetStub, localStoreStub;
|
||||
|
||||
invocations = 0;
|
||||
@ -1348,10 +1408,75 @@ define(function(require) {
|
||||
uid: dummyEncryptedMail.uid
|
||||
}).yields(null, dummyEncryptedMail);
|
||||
|
||||
localStoreStub = sinon.stub(dao, '_localStoreMessages').yields();
|
||||
keychainStub.getReceiverPublicKey.withArgs(dummyEncryptedMail.from[0].address).yields(null, mockKeyPair);
|
||||
pgpStub.decrypt.withArgs(dummyEncryptedMail.body, mockKeyPair.publicKey).yields(null, dummyLegacyDecryptedMail.body);
|
||||
|
||||
|
||||
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.not.be.empty;
|
||||
expect(localListStub.calledOnce).to.be.true;
|
||||
expect(imapSearchStub.calledThrice).to.be.true;
|
||||
expect(imapGetStub.calledOnce).to.be.true;
|
||||
expect(localStoreStub.calledOnce).to.be.true;
|
||||
expect(keychainStub.getReceiverPublicKey.calledOnce).to.be.true;
|
||||
expect(pgpStub.decrypt.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fetch valid pgp messages downstream from the remote', function(done) {
|
||||
var invocations, folder, localListStub, imapSearchStub, imapGetStub, localStoreStub, imapParseStub;
|
||||
|
||||
invocations = 0;
|
||||
folder = 'FOLDAAAA';
|
||||
dao._account.folders = [{
|
||||
type: 'Folder',
|
||||
path: folder,
|
||||
messages: []
|
||||
}];
|
||||
|
||||
localListStub = sinon.stub(dao, '_localListMessages').withArgs({
|
||||
folder: folder
|
||||
}).yields(null, []);
|
||||
imapSearchStub = sinon.stub(dao, '_imapSearch');
|
||||
imapSearchStub.withArgs({
|
||||
folder: folder
|
||||
}).yields(null, [dummyEncryptedMail.uid]);
|
||||
imapSearchStub.withArgs({
|
||||
folder: folder,
|
||||
unread: true
|
||||
}).yields(null, []);
|
||||
imapSearchStub.withArgs({
|
||||
folder: folder,
|
||||
answered: true
|
||||
}).yields(null, []);
|
||||
|
||||
imapGetStub = sinon.stub(dao, '_imapGetMessage').withArgs({
|
||||
folder: folder,
|
||||
uid: dummyEncryptedMail.uid
|
||||
}).yields(null, dummyEncryptedMail);
|
||||
|
||||
localStoreStub = sinon.stub(dao, '_localStoreMessages').yields();
|
||||
keychainStub.getReceiverPublicKey.withArgs(dummyEncryptedMail.from[0].address).yields(null, mockKeyPair);
|
||||
pgpStub.decrypt.withArgs(dummyEncryptedMail.body, mockKeyPair.publicKey).yields(null, dummyDecryptedMail.body);
|
||||
|
||||
imapParseStub = sinon.stub(dao, '_imapParseMessageBlock');
|
||||
imapParseStub.withArgs({
|
||||
message: dummyEncryptedMail,
|
||||
block: dummyDecryptedMail.body
|
||||
}).yields(null, dummyDecryptedMail);
|
||||
|
||||
dao.sync({
|
||||
folder: folder
|
||||
@ -1372,6 +1497,7 @@ define(function(require) {
|
||||
expect(localStoreStub.calledOnce).to.be.true;
|
||||
expect(keychainStub.getReceiverPublicKey.calledOnce).to.be.true;
|
||||
expect(pgpStub.decrypt.calledOnce).to.be.true;
|
||||
expect(imapParseStub.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
@ -2354,13 +2480,27 @@ define(function(require) {
|
||||
|
||||
describe('sendPlaintext', function() {
|
||||
it('should work', function(done) {
|
||||
smtpClientStub.send.withArgs(dummyEncryptedMail).yields();
|
||||
pgpMailerStub.send.withArgs({
|
||||
mail: dummyEncryptedMail
|
||||
}).yields();
|
||||
|
||||
dao.sendPlaintext({
|
||||
email: dummyEncryptedMail
|
||||
}, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(smtpClientStub.send.calledOnce).to.be.true;
|
||||
expect(pgpMailerStub.send.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not work when pgpmailer fails', function(done) {
|
||||
pgpMailerStub.send.yields({});
|
||||
|
||||
dao.sendPlaintext({
|
||||
email: dummyEncryptedMail
|
||||
}, function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(pgpMailerStub.send.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
@ -2368,101 +2508,76 @@ define(function(require) {
|
||||
|
||||
describe('sendEncrypted', function() {
|
||||
it('should work', function(done) {
|
||||
var encryptStub = sinon.stub(dao, '_encrypt').yields(null, {});
|
||||
|
||||
smtpClientStub.send.yields();
|
||||
|
||||
dao.sendEncrypted({
|
||||
email: dummyDecryptedMail
|
||||
}, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
|
||||
expect(encryptStub.calledOnce).to.be.true;
|
||||
expect(smtpClientStub.send.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should not work when encryption fails', function(done) {
|
||||
var encryptStub = sinon.stub(dao, '_encrypt').yields({});
|
||||
|
||||
dao.sendEncrypted({
|
||||
email: dummyDecryptedMail
|
||||
}, function(err) {
|
||||
expect(err).to.exist;
|
||||
|
||||
expect(encryptStub.calledOnce).to.be.true;
|
||||
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;
|
||||
|
||||
dao.sendEncrypted({
|
||||
email: dummyDecryptedMail
|
||||
}, function(err) {
|
||||
expect(err).to.exist;
|
||||
|
||||
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');
|
||||
delete dummyDecryptedMail.from;
|
||||
|
||||
dao.sendEncrypted({
|
||||
email: dummyDecryptedMail
|
||||
}, function(err) {
|
||||
expect(err).to.exist;
|
||||
|
||||
expect(encryptStub.called).to.be.false;
|
||||
expect(smtpClientStub.send.called).to.be.false;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('_encrypt', function() {
|
||||
it('should work without attachments', function(done) {
|
||||
var ct = 'OMGSOENCRYPTED';
|
||||
|
||||
pgpStub.exportKeys.yields(null, {
|
||||
privateKeyArmored: mockKeyPair.privateKey.encryptedKey,
|
||||
publicKeyArmored: mockKeyPair.publicKey.publicKey
|
||||
});
|
||||
pgpStub.encrypt.yields(null, ct);
|
||||
|
||||
dao._encrypt({
|
||||
pgpMailerStub.send.withArgs({
|
||||
encrypt: true,
|
||||
cleartextMessage: str.message,
|
||||
mail: dummyDecryptedMail,
|
||||
publicKeysArmored: dummyDecryptedMail.receiverKeys
|
||||
}).yields();
|
||||
|
||||
dao.sendEncrypted({
|
||||
email: dummyDecryptedMail
|
||||
}, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
|
||||
expect(pgpStub.exportKeys.calledOnce).to.be.true;
|
||||
expect(pgpStub.encrypt.calledOnce).to.be.true;
|
||||
expect(dummyDecryptedMail.body).to.contain(ct);
|
||||
expect(pgpMailerStub.send.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should not work when pgpmailer fails', function(done) {
|
||||
pgpStub.exportKeys.yields(null, {
|
||||
privateKeyArmored: mockKeyPair.privateKey.encryptedKey,
|
||||
publicKeyArmored: mockKeyPair.publicKey.publicKey
|
||||
});
|
||||
pgpMailerStub.send.yields({});
|
||||
|
||||
dao.sendEncrypted({
|
||||
email: dummyDecryptedMail
|
||||
}, function(err) {
|
||||
expect(err).to.exist;
|
||||
|
||||
expect(pgpStub.exportKeys.calledOnce).to.be.true;
|
||||
expect(pgpMailerStub.send.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should not work when sender key export fails', function(done) {
|
||||
pgpStub.exportKeys.yields({});
|
||||
|
||||
dao.sendEncrypted({
|
||||
email: dummyDecryptedMail
|
||||
}, function(err) {
|
||||
expect(err).to.exist;
|
||||
|
||||
expect(pgpStub.exportKeys.calledOnce).to.be.true;
|
||||
expect(pgpMailerStub.send.calledOnce).to.be.false;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('store', function() {
|
||||
describe('storeForOutbox', function() {
|
||||
it('should work', function(done) {
|
||||
pgpStub.exportKeys.yields(null, {
|
||||
publicKeyArmored: 'omgsocrypto'
|
||||
});
|
||||
pgpStub.encrypt.yields(null, 'asdfasfd');
|
||||
devicestorageStub.storeList.yields();
|
||||
var key = 'omgsocrypto';
|
||||
|
||||
dao.store(dummyDecryptedMail, function(err) {
|
||||
pgpStub.exportKeys.yields(null, {
|
||||
publicKeyArmored: key
|
||||
});
|
||||
pgpStub.encrypt.withArgs(dummyDecryptedMail.body, [key]).yields(null, 'asdfasfd');
|
||||
devicestorageStub.storeList.withArgs([dummyDecryptedMail], 'email_OUTBOX').yields();
|
||||
|
||||
dao.storeForOutbox(dummyDecryptedMail, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
|
||||
expect(pgpStub.exportKeys.calledOnce).to.be.true;
|
||||
expect(pgpStub.encrypt.calledOnce).to.be.true;
|
||||
expect(devicestorageStub.storeList.calledOnce).to.be.true;
|
||||
@ -2470,17 +2585,72 @@ define(function(require) {
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('list', function() {
|
||||
it('should work', function(done) {
|
||||
devicestorageStub.listItems.yields(null, [dummyEncryptedMail]);
|
||||
it('should work when store fails', function(done) {
|
||||
var key = 'omgsocrypto';
|
||||
|
||||
pgpStub.exportKeys.yields(null, {
|
||||
publicKeyArmored: 'omgsocrypto'
|
||||
publicKeyArmored: key
|
||||
});
|
||||
pgpStub.decrypt.yields(null, dummyDecryptedMail.body);
|
||||
pgpStub.encrypt.yields(null, 'asdfasfd');
|
||||
devicestorageStub.storeList.yields({});
|
||||
|
||||
dao.list(function(err, mails) {
|
||||
dao.storeForOutbox(dummyDecryptedMail, function(err) {
|
||||
expect(err).to.exist;
|
||||
|
||||
expect(pgpStub.exportKeys.calledOnce).to.be.true;
|
||||
expect(pgpStub.encrypt.calledOnce).to.be.true;
|
||||
expect(devicestorageStub.storeList.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work when encryption fails', function(done) {
|
||||
var key = 'omgsocrypto';
|
||||
|
||||
pgpStub.exportKeys.yields(null, {
|
||||
publicKeyArmored: key
|
||||
});
|
||||
pgpStub.encrypt.yields({});
|
||||
|
||||
dao.storeForOutbox(dummyDecryptedMail, function(err) {
|
||||
expect(err).to.exist;
|
||||
|
||||
expect(pgpStub.exportKeys.calledOnce).to.be.true;
|
||||
expect(pgpStub.encrypt.calledOnce).to.be.true;
|
||||
expect(devicestorageStub.storeList.called).to.be.false;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work when key export fails', function(done) {
|
||||
pgpStub.exportKeys.yields({});
|
||||
|
||||
dao.storeForOutbox(dummyDecryptedMail, function(err) {
|
||||
expect(err).to.exist;
|
||||
|
||||
expect(pgpStub.exportKeys.calledOnce).to.be.true;
|
||||
expect(pgpStub.encrypt.called).to.be.false;
|
||||
expect(devicestorageStub.storeList.called).to.be.false;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('listForOutbox', function() {
|
||||
it('should work', function(done) {
|
||||
var key = 'omgsocrypto';
|
||||
|
||||
devicestorageStub.listItems.withArgs('email_OUTBOX', 0, null).yields(null, [dummyEncryptedMail]);
|
||||
pgpStub.exportKeys.yields(null, {
|
||||
publicKeyArmored: key
|
||||
});
|
||||
pgpStub.decrypt.withArgs(dummyEncryptedMail.body, key).yields(null, dummyDecryptedMail.body);
|
||||
|
||||
dao.listForOutbox(function(err, mails) {
|
||||
expect(err).to.not.exist;
|
||||
|
||||
expect(devicestorageStub.listItems.calledOnce).to.be.true;
|
||||
@ -2488,12 +2658,63 @@ define(function(require) {
|
||||
expect(pgpStub.decrypt.calledOnce).to.be.true;
|
||||
expect(mails.length).to.equal(1);
|
||||
expect(mails[0].body).to.equal(dummyDecryptedMail.body);
|
||||
expect(mails[0].subject).to.equal(dummyDecryptedMail.subject);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not work when decryption fails', function(done) {
|
||||
var key = 'omgsocrypto',
|
||||
errMsg = 'THIS IS AN ERROR!';
|
||||
|
||||
devicestorageStub.listItems.yields(null, [dummyEncryptedMail]);
|
||||
pgpStub.exportKeys.yields(null, {
|
||||
publicKeyArmored: key
|
||||
});
|
||||
pgpStub.decrypt.yields({errMsg: errMsg});
|
||||
|
||||
dao.listForOutbox(function(err, mails) {
|
||||
expect(err).to.not.exist;
|
||||
expect(mails[0].body).to.equal(errMsg);
|
||||
|
||||
expect(devicestorageStub.listItems.calledOnce).to.be.true;
|
||||
expect(pgpStub.exportKeys.calledOnce).to.be.true;
|
||||
expect(pgpStub.decrypt.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not work when key export fails', function(done) {
|
||||
devicestorageStub.listItems.yields(null, [dummyEncryptedMail]);
|
||||
pgpStub.exportKeys.yields({});
|
||||
|
||||
dao.listForOutbox(function(err, mails) {
|
||||
expect(err).to.exist;
|
||||
expect(mails).to.not.exist;
|
||||
|
||||
expect(devicestorageStub.listItems.calledOnce).to.be.true;
|
||||
expect(pgpStub.exportKeys.calledOnce).to.be.true;
|
||||
expect(pgpStub.decrypt.called).to.be.false;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not work when list from storage fails', function(done) {
|
||||
devicestorageStub.listItems.yields({});
|
||||
|
||||
dao.listForOutbox(function(err, mails) {
|
||||
expect(err).to.exist;
|
||||
expect(mails).to.not.exist;
|
||||
|
||||
expect(devicestorageStub.listItems.calledOnce).to.be.true;
|
||||
expect(pgpStub.exportKeys.called).to.be.false;
|
||||
expect(pgpStub.decrypt.called).to.be.false;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
@ -85,7 +85,7 @@ define(function(require) {
|
||||
};
|
||||
dummyMails = [member, invited, notinvited];
|
||||
|
||||
emailDaoStub.list.yieldsAsync(null, dummyMails);
|
||||
emailDaoStub.listForOutbox.yieldsAsync(null, dummyMails);
|
||||
emailDaoStub.sendEncrypted.withArgs(sinon.match(function(opts) {
|
||||
return typeof opts.email !== 'undefined' && opts.email.to.address === member.to.address;
|
||||
})).yieldsAsync();
|
||||
@ -111,7 +111,7 @@ define(function(require) {
|
||||
expect(outbox._outboxBusy).to.be.false;
|
||||
|
||||
expect(unsentCount).to.equal(2);
|
||||
expect(emailDaoStub.list.callCount).to.equal(1);
|
||||
expect(emailDaoStub.listForOutbox.callCount).to.equal(1);
|
||||
expect(emailDaoStub.sendEncrypted.callCount).to.equal(1);
|
||||
expect(emailDaoStub.sendPlaintext.callCount).to.equal(1);
|
||||
expect(devicestorageStub.removeList.callCount).to.equal(1);
|
||||
@ -136,7 +136,7 @@ define(function(require) {
|
||||
|
||||
it('should not process outbox in offline mode', function(done) {
|
||||
emailDaoStub._account.online = false;
|
||||
emailDaoStub.list.yieldsAsync(null, [{
|
||||
emailDaoStub.listForOutbox.yieldsAsync(null, [{
|
||||
id: '123',
|
||||
to: [{
|
||||
name: 'member',
|
||||
@ -147,7 +147,7 @@ define(function(require) {
|
||||
outbox._processOutbox(function(err, count) {
|
||||
expect(err).to.not.exist;
|
||||
expect(count).to.equal(1);
|
||||
expect(emailDaoStub.list.callCount).to.equal(1);
|
||||
expect(emailDaoStub.listForOutbox.callCount).to.equal(1);
|
||||
expect(outbox._outboxBusy).to.be.false;
|
||||
done();
|
||||
});
|
||||
|
@ -277,13 +277,13 @@ define(function(require) {
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(scope.state.writer.open).to.be.false;
|
||||
expect(emailDaoMock.store.calledOnce).to.be.true;
|
||||
expect(emailDaoMock.storeForOutbox.calledOnce).to.be.true;
|
||||
expect(emailDaoMock.sync.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
};
|
||||
|
||||
emailDaoMock.store.yields();
|
||||
emailDaoMock.storeForOutbox.yields();
|
||||
emailDaoMock.sync.yields({
|
||||
code: 42
|
||||
});
|
||||
@ -310,13 +310,13 @@ define(function(require) {
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(scope.state.writer.open).to.be.false;
|
||||
expect(emailDaoMock.store.calledOnce).to.be.true;
|
||||
expect(emailDaoMock.storeForOutbox.calledOnce).to.be.true;
|
||||
expect(emailDaoMock.sync.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
};
|
||||
|
||||
emailDaoMock.store.yields();
|
||||
emailDaoMock.storeForOutbox.yields();
|
||||
emailDaoMock.sync.yields();
|
||||
|
||||
scope.state.writer.write(re);
|
||||
@ -341,13 +341,13 @@ define(function(require) {
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(scope.state.writer.open).to.be.false;
|
||||
expect(emailDaoMock.store.calledOnce).to.be.true;
|
||||
expect(emailDaoMock.storeForOutbox.calledOnce).to.be.true;
|
||||
expect(emailDaoMock.sync.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
};
|
||||
|
||||
emailDaoMock.store.yields();
|
||||
emailDaoMock.storeForOutbox.yields();
|
||||
emailDaoMock.sync.yields({});
|
||||
|
||||
scope.state.writer.write(re);
|
||||
@ -367,7 +367,7 @@ define(function(require) {
|
||||
scope.subject = 'yaddablabla';
|
||||
scope.toKey = 'Public Key';
|
||||
|
||||
emailDaoMock.store.withArgs(sinon.match(function(mail) {
|
||||
emailDaoMock.storeForOutbox.withArgs(sinon.match(function(mail) {
|
||||
return mail.from[0].address === emailAddress && mail.to.length === 1 && mail.receiverKeys.length === 1;
|
||||
})).yields({
|
||||
errMsg: 'snafu'
|
||||
@ -376,7 +376,7 @@ define(function(require) {
|
||||
scope.onError = function(err) {
|
||||
expect(err).to.exist;
|
||||
expect(scope.state.writer.open).to.be.true;
|
||||
expect(emailDaoMock.store.calledOnce).to.be.true;
|
||||
expect(emailDaoMock.storeForOutbox.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
scope.sendToOutbox();
|
||||
|
Loading…
Reference in New Issue
Block a user