mirror of
https://github.com/moparisthebest/mail
synced 2024-11-25 18:32:20 -05:00
[WO-286] adapt to changes in data model for use of signed msgs and html
This commit is contained in:
parent
f44db9d1bd
commit
a7efdf1125
@ -11,8 +11,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"crypto-lib": "https://github.com/whiteout-io/crypto-lib/tarball/v0.1.1",
|
||||
"imap-client": "https://github.com/whiteout-io/imap-client/tarball/v0.2.6",
|
||||
"mailreader": "https://github.com/whiteout-io/mailreader/tarball/v0.2.2",
|
||||
"imap-client": "https://github.com/whiteout-io/imap-client/tarball/dev/WO-286",
|
||||
"mailreader": "https://github.com/whiteout-io/mailreader/tarball/dev/wo-286",
|
||||
"pgpmailer": "https://github.com/whiteout-io/pgpmailer/tarball/v0.2.2",
|
||||
"pgpbuilder": "https://github.com/whiteout-io/pgpbuilder/tarball/v0.2.3",
|
||||
"requirejs": "2.1.10"
|
||||
|
@ -46,7 +46,7 @@ define(function(require) {
|
||||
iconPath: '/img/icon.png',
|
||||
verificationUrl: '/verify/',
|
||||
verificationUuidLength: 36,
|
||||
dbVersion: 1,
|
||||
dbVersion: 2,
|
||||
appVersion: appVersion
|
||||
};
|
||||
|
||||
|
@ -72,7 +72,7 @@ define(function(require) {
|
||||
self._keychain = keychain = new KeychainDAO(lawnchairDao, pubkeyDao);
|
||||
self._crypto = pgp = new PGP();
|
||||
self._pgpbuilder = pgpbuilder = new PgpBuilder();
|
||||
self._emailSync = emailSync = new EmailSync(keychain, userStorage);
|
||||
self._emailSync = emailSync = new EmailSync(keychain, userStorage, mailreader);
|
||||
self._emailDao = emailDao = new EmailDAO(keychain, pgp, userStorage, pgpbuilder, mailreader, emailSync);
|
||||
self._outboxBo = new OutboxBO(emailDao, keychain, userStorage);
|
||||
self._updateHandler = new UpdateHandler(appConfigStore, userStorage);
|
||||
@ -129,7 +129,7 @@ define(function(require) {
|
||||
};
|
||||
|
||||
pgpMailer = new PgpMailer(smtpOptions, self._pgpbuilder);
|
||||
imapClient = new ImapClient(imapOptions, mailreader);
|
||||
imapClient = new ImapClient(imapOptions);
|
||||
imapClient.onError = onImapError;
|
||||
|
||||
// connect to clients
|
||||
|
@ -274,123 +274,121 @@ define(function(require) {
|
||||
message = options.message,
|
||||
folder = options.folder;
|
||||
|
||||
if (message.loadingBody) {
|
||||
return;
|
||||
}
|
||||
|
||||
// the message already has a body, so no need to become active here
|
||||
if (message.body) {
|
||||
// the message either already has a body or is fetching it right now, so no need to become active here
|
||||
if (message.loadingBody || typeof message.body !== 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
message.loadingBody = true;
|
||||
|
||||
// the mail does not have its content in memory
|
||||
readFromDevice();
|
||||
/*
|
||||
* read this before inspecting the method!
|
||||
*
|
||||
* you will wonder about the round trip to the disk where we load the persisted object. there are two reasons for this behavior:
|
||||
* 1) if you work with a message that was loaded from the disk, we strip the message.bodyParts array,
|
||||
* because it is not really necessary to keep everything in memory
|
||||
* 2) the message in memory is polluted by angular. angular tracks ordering of a list by adding a property
|
||||
* to the model. this property is auto generated and must not be persisted.
|
||||
*/
|
||||
|
||||
// if possible, read the message body from the device
|
||||
function readFromDevice() {
|
||||
retrieveContent();
|
||||
|
||||
function retrieveContent() {
|
||||
// load the local message from memory
|
||||
self._emailSync._localListMessages({
|
||||
folder: folder,
|
||||
uid: message.uid
|
||||
}, function(err, localMessages) {
|
||||
var localMessage;
|
||||
|
||||
if (err) {
|
||||
message.loadingBody = false;
|
||||
callback(err);
|
||||
if (err || localMessages.length === 0) {
|
||||
done(err);
|
||||
return;
|
||||
}
|
||||
|
||||
localMessage = localMessages[0];
|
||||
var localMessage = localMessages[0];
|
||||
|
||||
if (!localMessage.body) {
|
||||
streamFromImap();
|
||||
// do we need to fetch content from the imap server?
|
||||
var needsFetch = false;
|
||||
localMessage.bodyParts.forEach(function(part) {
|
||||
needsFetch = (typeof part.content === 'undefined');
|
||||
});
|
||||
|
||||
if (!needsFetch) {
|
||||
// if we have all the content we need,
|
||||
// we can extract the content
|
||||
message.bodyParts = localMessage.bodyParts;
|
||||
extractContent();
|
||||
return;
|
||||
}
|
||||
|
||||
// attach the body to the mail object
|
||||
message.body = localMessage.body;
|
||||
handleEncryptedContent();
|
||||
});
|
||||
}
|
||||
|
||||
// if reading the message body from the device was unsuccessful,
|
||||
// stream the message from the imap server
|
||||
function streamFromImap() {
|
||||
self._emailSync._imapStreamText({
|
||||
folder: folder,
|
||||
message: message
|
||||
}, function(error) {
|
||||
if (error) {
|
||||
message.loadingBody = false;
|
||||
callback(error);
|
||||
return;
|
||||
}
|
||||
|
||||
message.loadingBody = false;
|
||||
|
||||
// do not write the object from the object used by angular to the disk, instead
|
||||
// do a short round trip and write back the unpolluted object
|
||||
self._emailSync._localListMessages({
|
||||
// get the raw content from the imap server
|
||||
self._emailSync._getBodyParts({
|
||||
folder: folder,
|
||||
uid: message.uid
|
||||
}, function(error, storedMessages) {
|
||||
if (error) {
|
||||
callback(error);
|
||||
uid: localMessage.uid,
|
||||
bodyParts: localMessage.bodyParts
|
||||
}, function(err, parsedBodyParts) {
|
||||
if (err) {
|
||||
done(err);
|
||||
return;
|
||||
}
|
||||
|
||||
storedMessages[0].body = message.body;
|
||||
message.bodyParts = parsedBodyParts;
|
||||
localMessage.bodyParts = parsedBodyParts;
|
||||
|
||||
// persist it to disk
|
||||
self._emailSync._localStoreMessages({
|
||||
folder: folder,
|
||||
emails: storedMessages
|
||||
emails: [localMessage]
|
||||
}, function(error) {
|
||||
if (error) {
|
||||
callback(error);
|
||||
done(error);
|
||||
return;
|
||||
}
|
||||
|
||||
handleEncryptedContent();
|
||||
// extract the content
|
||||
extractContent();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function handleEncryptedContent() {
|
||||
// normally, the imap-client should already have set the message.encrypted flag. problem: if we have pgp/inline,
|
||||
// we can't reliably determine if the message is encrypted before we have inspected the payload...
|
||||
message.encrypted = containsArmoredCiphertext(message);
|
||||
|
||||
// cleans the message body from everything but the ciphertext
|
||||
function extractContent() {
|
||||
if (message.encrypted) {
|
||||
message.decrypted = false;
|
||||
extractCiphertext();
|
||||
// show the encrypted message
|
||||
message.body = self._emailSync.filterBodyParts(message.bodyParts, 'encrypted')[0].content;
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
// for unencrypted messages, this is the array where the body parts are located
|
||||
var root = message.bodyParts;
|
||||
|
||||
if (message.signed) {
|
||||
var signedPart = self._emailSync.filterBodyParts(message.bodyParts, 'signed')[0];
|
||||
message.message = signedPart.message;
|
||||
message.signature = signedPart.signature;
|
||||
// TODO check integrity
|
||||
// in case of a signed message, you only want to show the signed content and ignore the rest
|
||||
root = signedPart.content;
|
||||
}
|
||||
|
||||
message.attachments = self._emailSync.filterBodyParts(root, 'attachment');
|
||||
message.body = _.pluck(self._emailSync.filterBodyParts(root, 'text'), 'content').join('\n');
|
||||
message.html = _.pluck(self._emailSync.filterBodyParts(root, 'html'), 'content').join('\n');
|
||||
|
||||
done();
|
||||
}
|
||||
|
||||
function done(err) {
|
||||
message.loadingBody = false;
|
||||
callback(null, message);
|
||||
callback(err, err ? undefined : message);
|
||||
}
|
||||
|
||||
function containsArmoredCiphertext() {
|
||||
return message.body.indexOf(str.cryptPrefix) !== -1 && message.body.indexOf(str.cryptSuffix) !== -1;
|
||||
}
|
||||
|
||||
function extractCiphertext() {
|
||||
var start = message.body.indexOf(str.cryptPrefix),
|
||||
end = message.body.indexOf(str.cryptSuffix) + str.cryptSuffix.length;
|
||||
|
||||
// parse message body for encrypted message block
|
||||
message.body = message.body.substring(start, end);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
EmailDAO.prototype.decryptBody = function(options, callback) {
|
||||
var self = this,
|
||||
message = options.message;
|
||||
|
||||
// the message has no body, is not encrypted or has already been decrypted
|
||||
// the message is decrypting has no body, is not encrypted or has already been decrypted
|
||||
if (message.decryptingBody || !message.body || !message.encrypted || message.decrypted) {
|
||||
return;
|
||||
}
|
||||
@ -408,61 +406,56 @@ define(function(require) {
|
||||
if (!senderPublicKey) {
|
||||
// this should only happen if a mail from another channel is in the inbox
|
||||
message.body = 'Public key for sender not found!';
|
||||
message.decryptingBody = false;
|
||||
callback(null, message);
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
// get the receiver's public key to check the message signature
|
||||
self._crypto.decrypt(message.body, senderPublicKey.publicKey, function(err, decrypted) {
|
||||
// if an error occurs during decryption, display the error message as the message content
|
||||
decrypted = decrypted || err.errMsg || 'Error occurred during decryption';
|
||||
|
||||
// this is a very primitive detection if we have PGP/MIME or PGP/INLINE
|
||||
if (!self._mailreader.isRfc(decrypted)) {
|
||||
message.body = decrypted;
|
||||
message.decrypted = true;
|
||||
message.decryptingBody = false;
|
||||
callback(null, message);
|
||||
var encryptedNode = self._emailSync.filterBodyParts(message.bodyParts, 'encrypted')[0];
|
||||
self._crypto.decrypt(encryptedNode.content, senderPublicKey.publicKey, function(err, decrypted) {
|
||||
if (err || !decrypted) {
|
||||
err = err || {
|
||||
errMsg: 'Error occurred during decryption'
|
||||
};
|
||||
done(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// parse the decrypted MIME message
|
||||
self._imapParseMessageBlock({
|
||||
message: message,
|
||||
raw: decrypted
|
||||
}, function(error) {
|
||||
// the mailparser works on the .raw property
|
||||
encryptedNode.raw = decrypted;
|
||||
|
||||
// parse the decrpyted raw content in the mailparser
|
||||
self._mailreader.parse({
|
||||
bodyParts: [encryptedNode]
|
||||
}, function(error, parsedBodyParts) {
|
||||
if (error) {
|
||||
message.decryptingBody = false;
|
||||
callback(error);
|
||||
done(error);
|
||||
return;
|
||||
}
|
||||
|
||||
message.decrypted = true;
|
||||
// we have successfully interpreted the descrypted message,
|
||||
// so let's update the views on the message parts
|
||||
|
||||
// remove the pgp-signature from the attachments
|
||||
message.attachments = _.reject(message.attachments, function(attmt) {
|
||||
message.body = _.pluck(self._emailSync.filterBodyParts(parsedBodyParts, 'text'), 'content').join('\n');
|
||||
message.html = _.pluck(self._emailSync.filterBodyParts(parsedBodyParts, 'html'), 'content').join('\n');
|
||||
message.attachments = _.reject(self._emailSync.filterBodyParts(parsedBodyParts, 'attachment'), function(attmt) {
|
||||
// remove the pgp-signature from the attachments
|
||||
return attmt.mimeType === "application/pgp-signature";
|
||||
});
|
||||
|
||||
message.decrypted = true;
|
||||
|
||||
|
||||
// we're done here!
|
||||
message.decryptingBody = false;
|
||||
callback(null, message);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
EmailDAO.prototype.getAttachment = function(options, callback) {
|
||||
if (!this._account.online) {
|
||||
callback({
|
||||
errMsg: 'Client is currently offline!',
|
||||
code: 42
|
||||
});
|
||||
return;
|
||||
function done(err) {
|
||||
message.decryptingBody = false;
|
||||
callback(err, err ? undefined : message);
|
||||
}
|
||||
|
||||
this._imapClient.getAttachment(options, callback);
|
||||
};
|
||||
|
||||
EmailDAO.prototype.sendEncrypted = function(options, callback) {
|
||||
@ -541,10 +534,6 @@ define(function(require) {
|
||||
this._imapClient.logout(callback);
|
||||
};
|
||||
|
||||
EmailDAO.prototype._imapParseMessageBlock = function(options, callback) {
|
||||
this._mailreader.parseRfc(options, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* List the folders in the user's IMAP mailbox.
|
||||
*/
|
||||
|
@ -5,9 +5,10 @@ define(function(require) {
|
||||
config = require('js/app-config').config,
|
||||
str = require('js/app-config').string;
|
||||
|
||||
var EmailSync = function(keychain, devicestorage) {
|
||||
var EmailSync = function(keychain, devicestorage, mailreader) {
|
||||
this._keychain = keychain;
|
||||
this._devicestorage = devicestorage;
|
||||
this._mailreader = mailreader;
|
||||
};
|
||||
|
||||
EmailSync.prototype.init = function(options, callback) {
|
||||
@ -168,8 +169,8 @@ define(function(require) {
|
||||
}
|
||||
|
||||
storedMessages.forEach(function(storedMessage) {
|
||||
// remove the body to not load unnecessary data to memory
|
||||
delete storedMessage.body;
|
||||
// remove the body parts to not load unnecessary data to memory
|
||||
delete storedMessage.bodyParts;
|
||||
|
||||
folder.messages.push(storedMessage);
|
||||
});
|
||||
@ -619,10 +620,11 @@ define(function(require) {
|
||||
}
|
||||
|
||||
function handleVerification(message, localCallback) {
|
||||
self._imapStreamText({
|
||||
self._getBodyParts({
|
||||
folder: options.folder,
|
||||
message: message
|
||||
}, function(error) {
|
||||
uid: message.uid,
|
||||
bodyParts: message.bodyParts
|
||||
}, function(error, parsedBodyParts) {
|
||||
// we could not stream the text to determine if the verification was valid or not
|
||||
// so handle it as if it were valid
|
||||
if (error) {
|
||||
@ -630,8 +632,9 @@ define(function(require) {
|
||||
return;
|
||||
}
|
||||
|
||||
var verificationUrlPrefix = config.cloudUrl + config.verificationUrl,
|
||||
uuid = message.body.split(verificationUrlPrefix).pop().substr(0, config.verificationUuidLength),
|
||||
var body = _.pluck(self.filterBodyParts(parsedBodyParts, 'text'), 'content').join('\n'),
|
||||
verificationUrlPrefix = config.cloudUrl + config.verificationUrl,
|
||||
uuid = body.split(verificationUrlPrefix).pop().substr(0, config.verificationUuidLength),
|
||||
isValidUuid = new RegExp('[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}').test(uuid);
|
||||
|
||||
// there's no valid uuid in the message, so forget about it
|
||||
@ -707,12 +710,8 @@ define(function(require) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._imapClient.updateFlags({
|
||||
path: options.folder,
|
||||
uid: options.uid,
|
||||
unread: options.unread,
|
||||
answered: options.answered
|
||||
}, callback);
|
||||
options.path = options.folder;
|
||||
this._imapClient.updateFlags(options, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -731,18 +730,8 @@ define(function(require) {
|
||||
return;
|
||||
}
|
||||
|
||||
var o = {
|
||||
path: options.folder
|
||||
};
|
||||
|
||||
if (typeof options.answered !== 'undefined') {
|
||||
o.answered = options.answered;
|
||||
}
|
||||
if (typeof options.unread !== 'undefined') {
|
||||
o.unread = options.unread;
|
||||
}
|
||||
|
||||
this._imapClient.search(o, callback);
|
||||
options.path = options.folder;
|
||||
this._imapClient.search(options, callback);
|
||||
};
|
||||
|
||||
EmailSync.prototype._imapDeleteMessage = function(options, callback) {
|
||||
@ -754,10 +743,8 @@ define(function(require) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._imapClient.deleteMessage({
|
||||
path: options.folder,
|
||||
uid: options.uid
|
||||
}, callback);
|
||||
options.path = options.folder;
|
||||
this._imapClient.deleteMessage(options, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -778,20 +765,18 @@ define(function(require) {
|
||||
return;
|
||||
}
|
||||
|
||||
self._imapClient.listMessagesByUid({
|
||||
path: options.folder,
|
||||
firstUid: options.firstUid,
|
||||
lastUid: options.lastUid
|
||||
}, callback);
|
||||
options.path = options.folder;
|
||||
self._imapClient.listMessages(options, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Stream an email messsage's body
|
||||
* @param {String} options.folder The folder
|
||||
* @param {Object} options.message The message, as retrieved by _imapListMessages
|
||||
* @param {String} options.uid the message's uid
|
||||
* @param {Object} options.bodyParts The message, as retrieved by _imapListMessages
|
||||
* @param {Function} callback (error, message) The callback when the imap client is done streaming message text content
|
||||
*/
|
||||
EmailSync.prototype._imapStreamText = function(options, callback) {
|
||||
EmailSync.prototype._getBodyParts = function(options, callback) {
|
||||
var self = this;
|
||||
|
||||
if (!this._account.online) {
|
||||
@ -802,11 +787,37 @@ define(function(require) {
|
||||
return;
|
||||
}
|
||||
|
||||
self._imapClient.getBody({
|
||||
path: options.folder,
|
||||
message: options.message
|
||||
}, callback);
|
||||
options.path = options.folder;
|
||||
self._imapClient.getBodyParts(options, function(err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
// interpret the raw content of the email
|
||||
self._mailreader.parse(options, callback);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function that recursively traverses the body parts tree. Looks for bodyParts that match the provided type and aggregates them
|
||||
* @param {[type]} bodyParts The bodyParts array
|
||||
* @param {[type]} type The type to look up
|
||||
* @param {undefined} result Leave undefined, only used for recursion
|
||||
*/
|
||||
EmailSync.prototype.filterBodyParts = function(bodyParts, type, result) {
|
||||
var self = this;
|
||||
|
||||
result = result || [];
|
||||
bodyParts.forEach(function(part) {
|
||||
if (part.type === type) {
|
||||
result.push(part);
|
||||
} else if (Array.isArray(part.content)) {
|
||||
self.filterBodyParts(part.content, type, result);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
return EmailSync;
|
||||
});
|
@ -2,7 +2,8 @@ define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var cfg = require('js/app-config').config,
|
||||
updateV1 = require('js/util/update/update-v1');
|
||||
updateV1 = require('js/util/update/update-v1'),
|
||||
updateV2 = require('js/util/update/update-v2');
|
||||
|
||||
/**
|
||||
* Handles database migration
|
||||
@ -10,7 +11,7 @@ define(function(require) {
|
||||
var UpdateHandler = function(appConfigStorage, userStorage) {
|
||||
this._appConfigStorage = appConfigStorage;
|
||||
this._userStorage = userStorage;
|
||||
this._updateScripts = [updateV1];
|
||||
this._updateScripts = [updateV1, updateV2];
|
||||
};
|
||||
|
||||
/**
|
||||
|
28
src/js/util/update/update-v2.js
Normal file
28
src/js/util/update/update-v2.js
Normal file
@ -0,0 +1,28 @@
|
||||
define(function() {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Update handler for transition database version 1 -> 2
|
||||
*
|
||||
* In database version 2, the stored email objects have to be purged, because the
|
||||
* new data model stores information about the email structure in the property 'bodyParts'.
|
||||
*/
|
||||
function updateV2(options, callback) {
|
||||
var emailDbType = 'email_',
|
||||
versionDbType = 'dbVersion',
|
||||
postUpdateDbVersion = 2;
|
||||
|
||||
// remove the emails
|
||||
options.userStorage.removeList(emailDbType, function(err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// update the database version to postUpdateDbVersion
|
||||
options.appConfigStorage.storeList([postUpdateDbVersion], versionDbType, callback);
|
||||
});
|
||||
}
|
||||
|
||||
return updateV2;
|
||||
});
|
@ -447,17 +447,6 @@ define(function(require) {
|
||||
});
|
||||
});
|
||||
|
||||
describe('_imapParseMessageBlock', function() {
|
||||
it('should parse a message', function(done) {
|
||||
var parseRfc = sinon.stub(mailreader, 'parseRfc').withArgs({}).yields();
|
||||
|
||||
dao._imapParseMessageBlock({}, function() {
|
||||
expect(parseRfc.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('_imapLogin', function() {
|
||||
it('should fail when disconnected', function(done) {
|
||||
dao.onDisconnect(null, function(err) {
|
||||
@ -616,6 +605,10 @@ define(function(require) {
|
||||
|
||||
|
||||
describe('getBody', function() {
|
||||
var folder = 'asdasdasdasdasd',
|
||||
uid = 1234,
|
||||
localListStub, localStoreStub, imapGetStub;
|
||||
|
||||
it('should not do anything if the message already has content', function() {
|
||||
var message = {
|
||||
body: 'bender is great!'
|
||||
@ -629,11 +622,9 @@ define(function(require) {
|
||||
});
|
||||
|
||||
it('should read an unencrypted body from the device', function(done) {
|
||||
var message, uid, folder, body, localListStub;
|
||||
var message, body;
|
||||
|
||||
folder = 'asdasdasdasdasd';
|
||||
body = 'bender is great! bender is great!';
|
||||
uid = 1234;
|
||||
message = {
|
||||
uid: uid
|
||||
};
|
||||
@ -642,10 +633,12 @@ define(function(require) {
|
||||
folder: folder,
|
||||
uid: uid
|
||||
}).yieldsAsync(null, [{
|
||||
body: body
|
||||
bodyParts: [{
|
||||
type: 'text',
|
||||
content: body
|
||||
}]
|
||||
}]);
|
||||
|
||||
|
||||
dao.getBody({
|
||||
message: message,
|
||||
folder: folder
|
||||
@ -653,8 +646,7 @@ define(function(require) {
|
||||
expect(err).to.not.exist;
|
||||
|
||||
expect(msg).to.equal(message);
|
||||
expect(msg.body).to.not.be.empty;
|
||||
expect(msg.encrypted).to.be.false;
|
||||
expect(msg.body).to.equal(body);
|
||||
expect(msg.loadingBody).to.be.false;
|
||||
|
||||
expect(localListStub.calledOnce).to.be.true;
|
||||
@ -665,20 +657,26 @@ define(function(require) {
|
||||
});
|
||||
|
||||
it('should read an encrypted body from the device', function(done) {
|
||||
var message, uid, folder, body, localListStub;
|
||||
var message, ct, pt;
|
||||
|
||||
folder = 'asdasdasdasdasd';
|
||||
body = '-----BEGIN PGP MESSAGE-----asdasdasd-----END PGP MESSAGE-----';
|
||||
uid = 1234;
|
||||
pt = 'bender is great!';
|
||||
ct = '-----BEGIN PGP MESSAGE-----asdasdasd-----END PGP MESSAGE-----';
|
||||
message = {
|
||||
uid: uid
|
||||
uid: uid,
|
||||
encrypted: true
|
||||
};
|
||||
|
||||
localListStub = sinon.stub(emailSync, '_localListMessages').withArgs({
|
||||
folder: folder,
|
||||
uid: uid
|
||||
}).yieldsAsync(null, [{
|
||||
body: body
|
||||
bodyParts: [{
|
||||
type: 'text',
|
||||
content: pt
|
||||
}, {
|
||||
type: 'encrypted',
|
||||
content: ct
|
||||
}]
|
||||
}]);
|
||||
|
||||
dao.getBody({
|
||||
@ -688,9 +686,8 @@ define(function(require) {
|
||||
expect(err).to.not.exist;
|
||||
|
||||
expect(msg).to.equal(message);
|
||||
expect(msg.body).to.not.be.empty;
|
||||
expect(msg.body).to.equal(ct);
|
||||
expect(msg.encrypted).to.be.true;
|
||||
expect(msg.decrypted).to.be.false;
|
||||
expect(message.loadingBody).to.be.false;
|
||||
|
||||
expect(localListStub.calledOnce).to.be.true;
|
||||
@ -700,14 +697,17 @@ define(function(require) {
|
||||
expect(message.loadingBody).to.be.true;
|
||||
});
|
||||
|
||||
it('should stream an unencrypted body from imap', function(done) {
|
||||
var message, uid, folder, body, localListStub, localStoreStub, imapStreamStub;
|
||||
it('should stream from imap and set plain text body', function(done) {
|
||||
var message, body;
|
||||
|
||||
folder = 'asdasdasdasdasd';
|
||||
body = 'bender is great! bender is great!';
|
||||
uid = 1234;
|
||||
message = {
|
||||
uid: uid
|
||||
uid: uid,
|
||||
bodyParts: [{
|
||||
type: 'text'
|
||||
}]
|
||||
};
|
||||
|
||||
localListStub = sinon.stub(emailSync, '_localListMessages').withArgs({
|
||||
@ -720,16 +720,14 @@ define(function(require) {
|
||||
emails: [message]
|
||||
}).yieldsAsync();
|
||||
|
||||
imapStreamStub = sinon.stub(emailSync, '_imapStreamText', function(opts, cb) {
|
||||
expect(opts).to.deep.equal({
|
||||
folder: folder,
|
||||
message: message
|
||||
});
|
||||
|
||||
message.body = body;
|
||||
cb();
|
||||
});
|
||||
|
||||
imapGetStub = sinon.stub(emailSync, '_getBodyParts').withArgs({
|
||||
folder: folder,
|
||||
uid: message.uid,
|
||||
bodyParts: message.bodyParts
|
||||
}).yieldsAsync(null, [{
|
||||
type: 'text',
|
||||
content: body
|
||||
}]);
|
||||
|
||||
dao.getBody({
|
||||
message: message,
|
||||
@ -738,12 +736,11 @@ define(function(require) {
|
||||
expect(err).to.not.exist;
|
||||
|
||||
expect(msg).to.equal(message);
|
||||
expect(msg.body).to.not.be.empty;
|
||||
expect(msg.encrypted).to.be.false;
|
||||
expect(msg.body).to.equal(body);
|
||||
expect(msg.loadingBody).to.be.false;
|
||||
|
||||
expect(localListStub.calledTwice).to.be.true;
|
||||
expect(imapStreamStub.calledOnce).to.be.true;
|
||||
expect(localListStub.calledOnce).to.be.true;
|
||||
expect(imapGetStub.calledOnce).to.be.true;
|
||||
expect(localStoreStub.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
@ -751,14 +748,19 @@ define(function(require) {
|
||||
expect(message.loadingBody).to.be.true;
|
||||
});
|
||||
|
||||
it('should stream an encrypted body from imap', function(done) {
|
||||
var message, uid, folder, body, localListStub, localStoreStub, imapStreamStub;
|
||||
it('should stream from imap and set encrypted body', function(done) {
|
||||
var message, ct, pt;
|
||||
|
||||
folder = 'asdasdasdasdasd';
|
||||
body = '-----BEGIN PGP MESSAGE-----asdasdasd-----END PGP MESSAGE-----';
|
||||
uid = 1234;
|
||||
pt = 'bender is great';
|
||||
ct = '-----BEGIN PGP MESSAGE-----asdasdasd-----END PGP MESSAGE-----';
|
||||
message = {
|
||||
uid: uid
|
||||
uid: uid,
|
||||
encrypted: true,
|
||||
bodyParts: [{
|
||||
type: 'text'
|
||||
}, {
|
||||
type: 'encrypted'
|
||||
}]
|
||||
};
|
||||
|
||||
localListStub = sinon.stub(emailSync, '_localListMessages').withArgs({
|
||||
@ -771,15 +773,17 @@ define(function(require) {
|
||||
emails: [message]
|
||||
}).yieldsAsync();
|
||||
|
||||
imapStreamStub = sinon.stub(emailSync, '_imapStreamText', function(opts, cb) {
|
||||
expect(opts).to.deep.equal({
|
||||
folder: folder,
|
||||
message: message
|
||||
});
|
||||
|
||||
message.body = body;
|
||||
cb();
|
||||
});
|
||||
imapGetStub = sinon.stub(emailSync, '_getBodyParts').withArgs({
|
||||
folder: folder,
|
||||
uid: message.uid,
|
||||
bodyParts: message.bodyParts
|
||||
}).yieldsAsync(null, [{
|
||||
type: 'text',
|
||||
content: pt
|
||||
}, {
|
||||
type: 'encrypted',
|
||||
content: ct
|
||||
}]);
|
||||
|
||||
|
||||
dao.getBody({
|
||||
@ -789,13 +793,12 @@ define(function(require) {
|
||||
expect(err).to.not.exist;
|
||||
|
||||
expect(msg).to.equal(message);
|
||||
expect(msg.body).to.not.be.empty;
|
||||
expect(msg.body).to.equal(ct);
|
||||
expect(msg.encrypted).to.be.true;
|
||||
expect(msg.decrypted).to.be.false;
|
||||
expect(msg.loadingBody).to.be.false;
|
||||
|
||||
expect(localListStub.calledTwice).to.be.true;
|
||||
expect(imapStreamStub.calledOnce).to.be.true;
|
||||
expect(localListStub.calledOnce).to.be.true;
|
||||
expect(imapGetStub.calledOnce).to.be.true;
|
||||
expect(localStoreStub.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
@ -804,22 +807,19 @@ define(function(require) {
|
||||
});
|
||||
|
||||
it('fail to stream from imap due to error when persisting', function(done) {
|
||||
var message, uid, folder, body, localListStub, localStoreStub, imapStreamStub;
|
||||
|
||||
folder = 'asdasdasdasdasd';
|
||||
body = 'THIS IS THE BODY';
|
||||
uid = 1234;
|
||||
message = {
|
||||
uid: uid
|
||||
var message = {
|
||||
uid: uid,
|
||||
bodyParts: [{
|
||||
type: 'text'
|
||||
}]
|
||||
};
|
||||
|
||||
localListStub = sinon.stub(emailSync, '_localListMessages').yieldsAsync(null, [message]);
|
||||
localStoreStub = sinon.stub(emailSync, '_localStoreMessages').yieldsAsync({});
|
||||
|
||||
imapStreamStub = sinon.stub(emailSync, '_imapStreamText', function(opts, cb) {
|
||||
message.body = body;
|
||||
cb();
|
||||
});
|
||||
imapGetStub = sinon.stub(emailSync, '_getBodyParts').yieldsAsync(null, [{
|
||||
type: 'text',
|
||||
content: 'bender is great! bender is great!'
|
||||
}]);
|
||||
|
||||
dao.getBody({
|
||||
message: message,
|
||||
@ -827,8 +827,8 @@ define(function(require) {
|
||||
}, function(err, msg) {
|
||||
expect(err).to.exist;
|
||||
expect(msg).to.not.exist;
|
||||
expect(localListStub.calledTwice).to.be.true;
|
||||
expect(imapStreamStub.calledOnce).to.be.true;
|
||||
expect(localListStub.calledOnce).to.be.true;
|
||||
expect(imapGetStub.calledOnce).to.be.true;
|
||||
expect(localStoreStub.calledOnce).to.be.true;
|
||||
|
||||
expect(message.loadingBody).to.be.false;
|
||||
@ -838,21 +838,15 @@ define(function(require) {
|
||||
});
|
||||
|
||||
it('fail to stream from imap due to stream error', function(done) {
|
||||
var message, uid, folder, body, localListStub, localStoreStub, imapStreamStub;
|
||||
|
||||
folder = 'asdasdasdasdasd';
|
||||
uid = 1234;
|
||||
message = {
|
||||
uid: uid
|
||||
var message = {
|
||||
uid: uid,
|
||||
bodyParts: [{
|
||||
type: 'text'
|
||||
}]
|
||||
};
|
||||
|
||||
localListStub = sinon.stub(emailSync, '_localListMessages').yields(null, [{}]);
|
||||
|
||||
imapStreamStub = sinon.stub(emailSync, '_imapStreamText', function(opts, cb) {
|
||||
message.body = body;
|
||||
cb({});
|
||||
});
|
||||
|
||||
localListStub = sinon.stub(emailSync, '_localListMessages').yields(null, [message]);
|
||||
imapGetStub = sinon.stub(emailSync, '_getBodyParts').yieldsAsync({});
|
||||
localStoreStub = sinon.stub(emailSync, '_localStoreMessages');
|
||||
|
||||
dao.getBody({
|
||||
@ -862,7 +856,7 @@ define(function(require) {
|
||||
expect(err).to.exist;
|
||||
expect(msg).to.not.exist;
|
||||
expect(localListStub.calledOnce).to.be.true;
|
||||
expect(imapStreamStub.calledOnce).to.be.true;
|
||||
expect(imapGetStub.calledOnce).to.be.true;
|
||||
expect(localStoreStub.called).to.be.false;
|
||||
|
||||
expect(message.loadingBody).to.be.false;
|
||||
@ -873,55 +867,89 @@ define(function(require) {
|
||||
});
|
||||
|
||||
describe('decryptBody', function() {
|
||||
it('should not do anything when the message is not encrypted', function() {
|
||||
it('should do nothing when the message is not encrypted', function() {
|
||||
var message = {
|
||||
encrypted: false
|
||||
encrypted: false,
|
||||
decrypted: true,
|
||||
body: 'asd'
|
||||
};
|
||||
|
||||
dao.decryptBody({
|
||||
message: message
|
||||
});
|
||||
|
||||
// should do nothing
|
||||
});
|
||||
|
||||
it('should not do anything when the message is already decrypted', function() {
|
||||
it('should do nothing when the message is already decrypted', function() {
|
||||
var message = {
|
||||
encrypted: true,
|
||||
decrypted: true
|
||||
decrypted: true,
|
||||
body: 'asd'
|
||||
};
|
||||
|
||||
dao.decryptBody({
|
||||
message: message
|
||||
});
|
||||
});
|
||||
|
||||
// should do nothing
|
||||
it('should do nothing when the message has no body', function() {
|
||||
var message = {
|
||||
encrypted: true,
|
||||
decrypted: false,
|
||||
body: ''
|
||||
};
|
||||
|
||||
dao.decryptBody({
|
||||
message: message
|
||||
});
|
||||
});
|
||||
|
||||
it('should do nothing when the message is decrypting', function() {
|
||||
var message = {
|
||||
encrypted: true,
|
||||
decrypted: false,
|
||||
body: 'asd',
|
||||
decryptingBody: true
|
||||
};
|
||||
|
||||
dao.decryptBody({
|
||||
message: message
|
||||
});
|
||||
});
|
||||
|
||||
it('decrypt a pgp/mime message', function(done) {
|
||||
var message, parsedBody, mimeBody, parseStub;
|
||||
var message, ct, pt, parsed, parseStub;
|
||||
|
||||
pt = 'bender is great';
|
||||
ct = '-----BEGIN PGP MESSAGE-----asdasdasd-----END PGP MESSAGE-----';
|
||||
parsed = 'bender! bender! bender!';
|
||||
message = {
|
||||
from: [{
|
||||
address: 'asdasdasd'
|
||||
}],
|
||||
body: ct,
|
||||
encrypted: true,
|
||||
decrypted: false,
|
||||
body: '-----BEGIN PGP MESSAGE-----asdasdasd-----END PGP MESSAGE-----'
|
||||
bodyParts: [{
|
||||
type: 'encrypted',
|
||||
content: ct
|
||||
}]
|
||||
};
|
||||
|
||||
mimeBody = 'Content-Type: asdasdasd';
|
||||
parsedBody = 'body? yes.';
|
||||
|
||||
keychainStub.getReceiverPublicKey.withArgs(message.from[0].address).yieldsAsync(null, mockKeyPair.publicKey);
|
||||
pgpStub.decrypt.withArgs(message.body, mockKeyPair.publicKey.publicKey).yieldsAsync(null, mimeBody);
|
||||
parseStub = sinon.stub(dao, '_imapParseMessageBlock', function(o, cb) {
|
||||
expect(o.message).to.equal(message);
|
||||
expect(o.raw).to.equal(mimeBody);
|
||||
|
||||
o.message.body = parsedBody;
|
||||
cb(null, o.message);
|
||||
});
|
||||
pgpStub.decrypt.withArgs(ct, mockKeyPair.publicKey.publicKey).yieldsAsync(null, pt);
|
||||
parseStub = sinon.stub(mailreader, 'parse').withArgs({
|
||||
bodyParts: [{
|
||||
type: 'encrypted',
|
||||
content: ct,
|
||||
raw: pt
|
||||
}]
|
||||
}).yieldsAsync(null, [{
|
||||
type: 'encrypted',
|
||||
content: [{
|
||||
type: 'text',
|
||||
content: parsed
|
||||
}]
|
||||
}]);
|
||||
|
||||
dao.decryptBody({
|
||||
message: message
|
||||
@ -929,110 +957,71 @@ define(function(require) {
|
||||
expect(error).to.not.exist;
|
||||
|
||||
expect(msg).to.equal(message);
|
||||
expect(msg.decrypted).to.be.true;
|
||||
expect(msg.body).to.equal(parsedBody);
|
||||
expect(msg.decryptingBody).to.be.false;
|
||||
expect(message.decrypted).to.be.true;
|
||||
expect(message.body).to.equal(parsed);
|
||||
expect(message.decryptingBody).to.be.false;
|
||||
|
||||
expect(keychainStub.getReceiverPublicKey.calledOnce).to.be.true;
|
||||
expect(pgpStub.decrypt.calledOnce).to.be.true;
|
||||
expect(parseStub.calledOnce).to.be.true;
|
||||
|
||||
mailreader.parse.restore();
|
||||
done();
|
||||
});
|
||||
|
||||
expect(message.decryptingBody).to.be.true;
|
||||
});
|
||||
|
||||
it('decrypt a pgp/inline message', function(done) {
|
||||
var message, plaintextBody, parseStub;
|
||||
|
||||
message = {
|
||||
from: [{
|
||||
address: 'asdasdasd'
|
||||
}],
|
||||
encrypted: true,
|
||||
decrypted: false,
|
||||
body: '-----BEGIN PGP MESSAGE-----asdasdasd-----END PGP MESSAGE-----'
|
||||
};
|
||||
|
||||
plaintextBody = 'body? yes.';
|
||||
|
||||
keychainStub.getReceiverPublicKey.withArgs(message.from[0].address).yieldsAsync(null, mockKeyPair.publicKey);
|
||||
pgpStub.decrypt.withArgs(message.body, mockKeyPair.publicKey.publicKey).yieldsAsync(null, plaintextBody);
|
||||
parseStub = sinon.stub(dao, '_imapParseMessageBlock');
|
||||
|
||||
dao.decryptBody({
|
||||
message: message
|
||||
}, function(error, msg) {
|
||||
expect(error).to.not.exist;
|
||||
|
||||
expect(msg).to.equal(message);
|
||||
expect(msg.decrypted).to.be.true;
|
||||
expect(msg.body).to.equal(plaintextBody);
|
||||
expect(msg.decryptingBody).to.be.false;
|
||||
|
||||
expect(keychainStub.getReceiverPublicKey.calledOnce).to.be.true;
|
||||
expect(pgpStub.decrypt.calledOnce).to.be.true;
|
||||
expect(parseStub.called).to.be.false;
|
||||
|
||||
done();
|
||||
});
|
||||
expect(message.decryptingBody).to.be.true;
|
||||
});
|
||||
|
||||
it('should fail during decryption message', function(done) {
|
||||
var message, plaintextBody, parseStub, errMsg;
|
||||
|
||||
message = {
|
||||
var message = {
|
||||
from: [{
|
||||
address: 'asdasdasd'
|
||||
}],
|
||||
body: 'asdjafuad',
|
||||
encrypted: true,
|
||||
decrypted: false,
|
||||
body: '-----BEGIN PGP MESSAGE-----asdasdasd-----END PGP MESSAGE-----'
|
||||
bodyParts: [{
|
||||
type: 'encrypted',
|
||||
content: '-----BEGIN PGP MESSAGE-----asdasdasd-----END PGP MESSAGE-----'
|
||||
}]
|
||||
};
|
||||
|
||||
plaintextBody = 'body? yes.';
|
||||
errMsg = 'yaddayadda';
|
||||
|
||||
keychainStub.getReceiverPublicKey.withArgs(message.from[0].address).yields(null, mockKeyPair.publicKey);
|
||||
var parseStub = sinon.spy(mailreader, 'parse');
|
||||
keychainStub.getReceiverPublicKey.yields(null, mockKeyPair.publicKey);
|
||||
pgpStub.decrypt.yields({
|
||||
errMsg: errMsg
|
||||
errMsg: 'asd'
|
||||
});
|
||||
parseStub = sinon.stub(dao, '_imapParseMessageBlock');
|
||||
|
||||
dao.decryptBody({
|
||||
message: message
|
||||
}, function(error, msg) {
|
||||
expect(error).to.not.exist;
|
||||
|
||||
expect(msg).to.equal(message);
|
||||
expect(msg.decrypted).to.be.true;
|
||||
expect(msg.body).to.equal(errMsg);
|
||||
expect(msg.decryptingBody).to.be.false;
|
||||
expect(error).to.exist;
|
||||
expect(msg).to.not.exist;
|
||||
expect(message.decryptingBody).to.be.false;
|
||||
|
||||
expect(keychainStub.getReceiverPublicKey.calledOnce).to.be.true;
|
||||
expect(pgpStub.decrypt.calledOnce).to.be.true;
|
||||
expect(parseStub.called).to.be.false;
|
||||
|
||||
mailreader.parse.restore();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail during key export', function(done) {
|
||||
var message, parseStub;
|
||||
|
||||
message = {
|
||||
var message = {
|
||||
from: [{
|
||||
address: 'asdasdasd'
|
||||
}],
|
||||
encrypted: true,
|
||||
decrypted: false,
|
||||
body: '-----BEGIN PGP MESSAGE-----asdasdasd-----END PGP MESSAGE-----'
|
||||
body: 'asdjafuad',
|
||||
bodyParts: [{
|
||||
type: 'encrypted',
|
||||
content: '-----BEGIN PGP MESSAGE-----asdasdasd-----END PGP MESSAGE-----'
|
||||
}]
|
||||
};
|
||||
|
||||
var parseStub = sinon.spy(mailreader, 'parse');
|
||||
keychainStub.getReceiverPublicKey.yields({});
|
||||
parseStub = sinon.stub(dao, '_imapParseMessageBlock');
|
||||
|
||||
dao.decryptBody({
|
||||
message: message
|
||||
@ -1041,13 +1030,13 @@ define(function(require) {
|
||||
|
||||
expect(msg).to.not.exist;
|
||||
|
||||
expect(message.decrypted).to.be.false;
|
||||
expect(message.decryptingBody).to.be.false;
|
||||
|
||||
expect(keychainStub.getReceiverPublicKey.calledOnce).to.be.true;
|
||||
expect(pgpStub.decrypt.called).to.be.false;
|
||||
expect(parseStub.called).to.be.false;
|
||||
|
||||
mailreader.parse.restore();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -3,6 +3,7 @@ define(function(require) {
|
||||
|
||||
var EmailSync = require('js/dao/email-sync'),
|
||||
KeychainDAO = require('js/dao/keychain-dao'),
|
||||
mailreader = require('mailreader'),
|
||||
ImapClient = require('imap-client'),
|
||||
DeviceStorageDAO = require('js/dao/devicestorage-dao'),
|
||||
expect = chai.expect;
|
||||
@ -13,8 +14,7 @@ define(function(require) {
|
||||
var emailSync, keychainStub, imapClientStub, devicestorageStub;
|
||||
|
||||
var emailAddress, mockkeyId, dummyEncryptedMail,
|
||||
dummyDecryptedMail, mockKeyPair, account, verificationMail, verificationUuid,
|
||||
corruptedVerificationMail, corruptedVerificationUuid,
|
||||
dummyDecryptedMail, mockKeyPair, account, verificationMail, verificationUuid, corruptedVerificationUuid,
|
||||
nonWhitelistedMail;
|
||||
|
||||
beforeEach(function(done) {
|
||||
@ -29,7 +29,9 @@ define(function(require) {
|
||||
address: 'qwe@qwe.de'
|
||||
}],
|
||||
subject: 'qweasd',
|
||||
body: '-----BEGIN PGP MESSAGE-----\nasd\n-----END PGP MESSAGE-----',
|
||||
bodyParts: [{
|
||||
type: 'encrypted'
|
||||
}],
|
||||
unread: false,
|
||||
answered: false
|
||||
};
|
||||
@ -43,24 +45,13 @@ define(function(require) {
|
||||
address: 'safewithme.testuser@gmail.com'
|
||||
}], // list of receivers
|
||||
subject: "[whiteout] New public key uploaded", // Subject line
|
||||
body: 'yadda yadda bla blabla foo bar https://keys.whiteout.io/verify/' + verificationUuid, // plaintext body
|
||||
bodyParts: [{
|
||||
type: 'text'
|
||||
}],
|
||||
unread: false,
|
||||
answered: false
|
||||
};
|
||||
corruptedVerificationUuid = 'OMFG_FUCKING_BASTARD_UUID_FROM_HELL!';
|
||||
corruptedVerificationMail = {
|
||||
from: [{
|
||||
name: 'Whiteout Test',
|
||||
address: 'whiteout.test@t-online.de'
|
||||
}], // sender address
|
||||
to: [{
|
||||
address: 'safewithme.testuser@gmail.com'
|
||||
}], // list of receivers
|
||||
subject: "[whiteout] New public key uploaded", // Subject line
|
||||
body: 'yadda yadda bla blabla foo bar https://keys.whiteout.io/verify/' + corruptedVerificationUuid, // plaintext body
|
||||
unread: false,
|
||||
answered: false
|
||||
};
|
||||
dummyDecryptedMail = {
|
||||
uid: 1234,
|
||||
from: [{
|
||||
@ -70,7 +61,9 @@ define(function(require) {
|
||||
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',
|
||||
bodyParts: [{
|
||||
type: 'text'
|
||||
}],
|
||||
unread: false,
|
||||
answered: false,
|
||||
};
|
||||
@ -83,7 +76,9 @@ define(function(require) {
|
||||
address: 'qwe@qwe.de'
|
||||
}],
|
||||
subject: 'qweasd',
|
||||
body: 'asd'
|
||||
bodyParts: [{
|
||||
type: 'text'
|
||||
}],
|
||||
};
|
||||
mockKeyPair = {
|
||||
publicKey: {
|
||||
@ -106,10 +101,11 @@ define(function(require) {
|
||||
imapClientStub = sinon.createStubInstance(ImapClient);
|
||||
devicestorageStub = sinon.createStubInstance(DeviceStorageDAO);
|
||||
|
||||
emailSync = new EmailSync(keychainStub, devicestorageStub);
|
||||
emailSync = new EmailSync(keychainStub, devicestorageStub, mailreader);
|
||||
|
||||
expect(emailSync._keychain).to.equal(keychainStub);
|
||||
expect(emailSync._devicestorage).to.equal(devicestorageStub);
|
||||
expect(emailSync._mailreader).to.equal(mailreader);
|
||||
|
||||
// init
|
||||
emailSync.init({
|
||||
@ -154,6 +150,8 @@ define(function(require) {
|
||||
|
||||
|
||||
describe('_imapSearch', function() {
|
||||
var path = 'FOLDAAAA';
|
||||
|
||||
it('should fail when disconnected', function(done) {
|
||||
// this is set in the emailDao.onDisconnect
|
||||
emailSync._account.online = false;
|
||||
@ -164,10 +162,9 @@ define(function(require) {
|
||||
});
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
var path = 'FOLDAAAA';
|
||||
|
||||
it('should list all uids', function(done) {
|
||||
imapClientStub.search.withArgs({
|
||||
folder: path,
|
||||
path: path
|
||||
}).yields();
|
||||
|
||||
@ -175,10 +172,10 @@ define(function(require) {
|
||||
folder: path
|
||||
}, done);
|
||||
});
|
||||
it('should work', function(done) {
|
||||
var path = 'FOLDAAAA';
|
||||
|
||||
it('should list answered uids', function(done) {
|
||||
imapClientStub.search.withArgs({
|
||||
folder: path,
|
||||
path: path,
|
||||
answered: true
|
||||
}).yields();
|
||||
@ -188,10 +185,10 @@ define(function(require) {
|
||||
answered: true
|
||||
}, done);
|
||||
});
|
||||
it('should work', function(done) {
|
||||
var path = 'FOLDAAAA';
|
||||
|
||||
it('should list unread uids', function(done) {
|
||||
imapClientStub.search.withArgs({
|
||||
folder: path,
|
||||
path: path,
|
||||
unread: true
|
||||
}).yields();
|
||||
@ -204,6 +201,9 @@ define(function(require) {
|
||||
});
|
||||
|
||||
describe('_imapDeleteMessage', function() {
|
||||
var path = 'FOLDAAAA',
|
||||
uid = 1337;
|
||||
|
||||
it('should fail when disconnected', function(done) {
|
||||
// this is set in the emailDao.onDisconnect
|
||||
emailSync._account.online = false;
|
||||
@ -215,11 +215,9 @@ define(function(require) {
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
var path = 'FOLDAAAA',
|
||||
uid = 1337;
|
||||
|
||||
imapClientStub.deleteMessage.withArgs({
|
||||
path: path,
|
||||
folder: path,
|
||||
uid: uid
|
||||
}).yields();
|
||||
|
||||
@ -231,13 +229,15 @@ define(function(require) {
|
||||
});
|
||||
|
||||
describe('_imapListMessages', function() {
|
||||
it('should work', function(done) {
|
||||
var path = 'FOLDAAAA',
|
||||
firstUid = 1337,
|
||||
lastUid = 1339;
|
||||
var path = 'FOLDAAAA',
|
||||
firstUid = 1337,
|
||||
lastUid = 1339;
|
||||
|
||||
imapClientStub.listMessagesByUid.withArgs({
|
||||
|
||||
it('should work', function(done) {
|
||||
imapClientStub.listMessages.withArgs({
|
||||
path: path,
|
||||
folder: path,
|
||||
firstUid: firstUid,
|
||||
lastUid: lastUid
|
||||
}).yields(null, []);
|
||||
@ -250,18 +250,14 @@ define(function(require) {
|
||||
expect(err).to.not.exist;
|
||||
expect(msgs).to.exist;
|
||||
|
||||
expect(imapClientStub.listMessagesByUid.calledOnce).to.be.true;
|
||||
expect(imapClientStub.listMessages.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not work when listMessagesByUid fails', function(done) {
|
||||
var path = 'FOLDAAAA',
|
||||
firstUid = 1337,
|
||||
lastUid = 1339;
|
||||
|
||||
imapClientStub.listMessagesByUid.yields({});
|
||||
it('should not work when listMessages fails', function(done) {
|
||||
imapClientStub.listMessages.yields({});
|
||||
|
||||
emailSync._imapListMessages({
|
||||
folder: path,
|
||||
@ -271,7 +267,7 @@ define(function(require) {
|
||||
expect(err).to.exist;
|
||||
expect(msgs).to.not.exist;
|
||||
|
||||
expect(imapClientStub.listMessagesByUid.calledOnce).to.be.true;
|
||||
expect(imapClientStub.listMessages.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
@ -288,42 +284,50 @@ define(function(require) {
|
||||
});
|
||||
});
|
||||
|
||||
describe('_imapStreamText', function() {
|
||||
describe('_getBodyParts', function() {
|
||||
var path = 'FOLDAAAA',
|
||||
parseStub;
|
||||
|
||||
it('should work', function(done) {
|
||||
var path = 'FOLDAAAA';
|
||||
|
||||
imapClientStub.getBody.withArgs({
|
||||
path: path,
|
||||
message: {}
|
||||
}).yields(null, {});
|
||||
|
||||
emailSync._imapStreamText({
|
||||
var o = {
|
||||
folder: path,
|
||||
message: {}
|
||||
}, function(err, msg) {
|
||||
uid: 123,
|
||||
bodyParts: []
|
||||
};
|
||||
|
||||
imapClientStub.getBodyParts.withArgs(o).yields(null, {});
|
||||
parseStub = sinon.stub(mailreader, 'parse').withArgs(o).yields(null, []);
|
||||
|
||||
emailSync._getBodyParts(o, function(err, parts) {
|
||||
expect(err).to.not.exist;
|
||||
expect(msg).to.exist;
|
||||
expect(parts).to.exist;
|
||||
|
||||
expect(imapClientStub.getBody.calledOnce).to.be.true;
|
||||
expect(imapClientStub.getBodyParts.calledOnce).to.be.true;
|
||||
expect(parseStub.calledOnce).to.be.true;
|
||||
|
||||
mailreader.parse.restore();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not work when getBody fails', function(done) {
|
||||
var path = 'FOLDAAAA';
|
||||
|
||||
imapClientStub.getBody.yields({});
|
||||
|
||||
emailSync._imapStreamText({
|
||||
var o = {
|
||||
folder: path,
|
||||
message: {}
|
||||
}, function(err, msg) {
|
||||
uid: 123,
|
||||
bodyParts: []
|
||||
};
|
||||
|
||||
imapClientStub.getBodyParts.yields({});
|
||||
parseStub = sinon.spy(mailreader, 'parse');
|
||||
|
||||
emailSync._getBodyParts(o, function(err, msg) {
|
||||
expect(err).to.exist;
|
||||
expect(msg).to.not.exist;
|
||||
|
||||
expect(imapClientStub.getBody.calledOnce).to.be.true;
|
||||
expect(imapClientStub.getBodyParts.calledOnce).to.be.true;
|
||||
expect(parseStub.called).to.be.false;
|
||||
|
||||
mailreader.parse.restore();
|
||||
done();
|
||||
});
|
||||
});
|
||||
@ -332,7 +336,7 @@ define(function(require) {
|
||||
// this is set in the emailDao.onDisconnect
|
||||
emailSync._account.online = false;
|
||||
|
||||
emailSync._imapStreamText({}, function(err) {
|
||||
emailSync._getBodyParts({}, function(err) {
|
||||
expect(err.code).to.equal(42);
|
||||
done();
|
||||
});
|
||||
@ -950,13 +954,12 @@ define(function(require) {
|
||||
}).yields(null, []);
|
||||
|
||||
imapListMessagesStub = sinon.stub(emailSync, '_imapListMessages').yields(null, [verificationMail]);
|
||||
|
||||
imapGetStub = sinon.stub(emailSync, '_imapStreamText').yields(null);
|
||||
|
||||
imapGetStub = sinon.stub(emailSync, '_getBodyParts').yields(null, [{
|
||||
type: 'text',
|
||||
content: 'yadda yadda bla blabla foo bar https://keys.whiteout.io/verify/' + verificationUuid,
|
||||
}]);
|
||||
keychainStub.verifyPublicKey.withArgs(verificationUuid).yields();
|
||||
|
||||
localStoreStub = sinon.stub(emailSync, '_localStoreMessages');
|
||||
|
||||
imapDeleteStub = sinon.stub(emailSync, '_imapDeleteMessage').withArgs({
|
||||
folder: folder,
|
||||
uid: verificationMail.uid
|
||||
@ -1012,7 +1015,10 @@ define(function(require) {
|
||||
answered: true
|
||||
}).yields(null, []);
|
||||
imapListMessagesStub = sinon.stub(emailSync, '_imapListMessages').yields(null, [verificationMail]);
|
||||
imapGetStub = sinon.stub(emailSync, '_imapStreamText').yields(null);
|
||||
imapGetStub = sinon.stub(emailSync, '_getBodyParts').yields(null, [{
|
||||
type: 'text',
|
||||
content: 'yadda yadda bla blabla foo bar https://keys.whiteout.io/verify/' + verificationUuid,
|
||||
}]);
|
||||
keychainStub.verifyPublicKey.withArgs(verificationUuid).yields();
|
||||
imapDeleteStub = sinon.stub(emailSync, '_imapDeleteMessage').yields({});
|
||||
|
||||
@ -1073,7 +1079,10 @@ define(function(require) {
|
||||
answered: true
|
||||
}).yields(null, []);
|
||||
imapListMessagesStub = sinon.stub(emailSync, '_imapListMessages').yields(null, [verificationMail]);
|
||||
imapGetStub = sinon.stub(emailSync, '_imapStreamText').yields(null);
|
||||
imapGetStub = sinon.stub(emailSync, '_getBodyParts').yields(null, [{
|
||||
type: 'text',
|
||||
content: 'yadda yadda bla blabla foo bar https://keys.whiteout.io/verify/' + verificationUuid,
|
||||
}]);
|
||||
keychainStub.verifyPublicKey.withArgs(verificationUuid).yields({
|
||||
errMsg: 'fubar'
|
||||
});
|
||||
@ -1134,7 +1143,7 @@ define(function(require) {
|
||||
imapSearchStub = sinon.stub(emailSync, '_imapSearch');
|
||||
imapSearchStub.withArgs({
|
||||
folder: folder
|
||||
}).yields(null, [corruptedVerificationMail.uid]);
|
||||
}).yields(null, [verificationMail.uid]);
|
||||
imapSearchStub.withArgs({
|
||||
folder: folder,
|
||||
unread: true
|
||||
@ -1146,12 +1155,15 @@ define(function(require) {
|
||||
|
||||
localStoreStub = sinon.stub(emailSync, '_localStoreMessages').withArgs({
|
||||
folder: folder,
|
||||
emails: [corruptedVerificationMail]
|
||||
emails: [verificationMail]
|
||||
}).yields();
|
||||
|
||||
|
||||
imapListMessagesStub = sinon.stub(emailSync, '_imapListMessages').yields(null, [corruptedVerificationMail]);
|
||||
imapGetStub = sinon.stub(emailSync, '_imapStreamText').yields(null);
|
||||
imapListMessagesStub = sinon.stub(emailSync, '_imapListMessages').yields(null, [verificationMail]);
|
||||
imapGetStub = sinon.stub(emailSync, '_getBodyParts').yields(null, [{
|
||||
type: 'text',
|
||||
content: 'yadda yadda bla blabla foo bar https://keys.whiteout.io/verify/' + corruptedVerificationUuid,
|
||||
}]);
|
||||
keychainStub.verifyPublicKey.withArgs(corruptedVerificationUuid).yields({
|
||||
errMsg: 'fubar'
|
||||
});
|
||||
@ -1602,19 +1614,16 @@ define(function(require) {
|
||||
|
||||
describe('mark', function() {
|
||||
it('should work', function(done) {
|
||||
imapClientStub.updateFlags.withArgs({
|
||||
path: 'asdf',
|
||||
uid: 1,
|
||||
unread: false,
|
||||
answered: false
|
||||
}).yields();
|
||||
|
||||
emailSync._imapMark({
|
||||
var o = {
|
||||
folder: 'asdf',
|
||||
uid: 1,
|
||||
unread: false,
|
||||
answered: false
|
||||
}, function(err) {
|
||||
};
|
||||
|
||||
imapClientStub.updateFlags.withArgs(o).yields();
|
||||
|
||||
emailSync._imapMark(o, function(err) {
|
||||
expect(imapClientStub.updateFlags.calledOnce).to.be.true;
|
||||
expect(err).to.not.exist;
|
||||
done();
|
||||
|
@ -6,11 +6,11 @@ define(function(require) {
|
||||
UpdateHandler = require('js/util/update/update-handler'),
|
||||
expect = chai.expect;
|
||||
|
||||
chai.Assertion.includeStack = true;
|
||||
|
||||
describe('UpdateHandler', function() {
|
||||
var updateHandler, appConfigStorageStub, userStorageStub, origDbVersion;
|
||||
|
||||
chai.Assertion.includeStack = true;
|
||||
|
||||
beforeEach(function() {
|
||||
origDbVersion = cfg.dbVersion;
|
||||
appConfigStorageStub = sinon.createStubInstance(DeviceStorageDAO);
|
||||
@ -160,6 +160,59 @@ define(function(require) {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('v1 -> v2', function() {
|
||||
var emailDbType = 'email_';
|
||||
|
||||
beforeEach(function() {
|
||||
cfg.dbVersion = 2; // app requires database version 2
|
||||
appConfigStorageStub.listItems.withArgs(versionDbType).yieldsAsync(null, [1]); // database version is 0
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// database version is only queried for version checking prior to the update script
|
||||
// so no need to check this in case-specific tests
|
||||
expect(appConfigStorageStub.listItems.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
userStorageStub.removeList.withArgs(emailDbType).yieldsAsync();
|
||||
appConfigStorageStub.storeList.withArgs([2], versionDbType).yieldsAsync();
|
||||
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.not.exist;
|
||||
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
||||
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail when persisting database version fails', function(done) {
|
||||
userStorageStub.removeList.yieldsAsync();
|
||||
appConfigStorageStub.storeList.yieldsAsync({});
|
||||
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
||||
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail when wiping emails from database fails', function(done) {
|
||||
userStorageStub.removeList.yieldsAsync({});
|
||||
|
||||
updateHandler.update(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
||||
expect(appConfigStorageStub.storeList.called).to.be.false;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user