mirror of
https://github.com/moparisthebest/mail
synced 2024-12-01 13:22:16 -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": {
|
"dependencies": {
|
||||||
"crypto-lib": "https://github.com/whiteout-io/crypto-lib/tarball/v0.1.1",
|
"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",
|
"imap-client": "https://github.com/whiteout-io/imap-client/tarball/dev/WO-286",
|
||||||
"mailreader": "https://github.com/whiteout-io/mailreader/tarball/v0.2.2",
|
"mailreader": "https://github.com/whiteout-io/mailreader/tarball/dev/wo-286",
|
||||||
"pgpmailer": "https://github.com/whiteout-io/pgpmailer/tarball/v0.2.2",
|
"pgpmailer": "https://github.com/whiteout-io/pgpmailer/tarball/v0.2.2",
|
||||||
"pgpbuilder": "https://github.com/whiteout-io/pgpbuilder/tarball/v0.2.3",
|
"pgpbuilder": "https://github.com/whiteout-io/pgpbuilder/tarball/v0.2.3",
|
||||||
"requirejs": "2.1.10"
|
"requirejs": "2.1.10"
|
||||||
|
@ -46,7 +46,7 @@ define(function(require) {
|
|||||||
iconPath: '/img/icon.png',
|
iconPath: '/img/icon.png',
|
||||||
verificationUrl: '/verify/',
|
verificationUrl: '/verify/',
|
||||||
verificationUuidLength: 36,
|
verificationUuidLength: 36,
|
||||||
dbVersion: 1,
|
dbVersion: 2,
|
||||||
appVersion: appVersion
|
appVersion: appVersion
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ define(function(require) {
|
|||||||
self._keychain = keychain = new KeychainDAO(lawnchairDao, pubkeyDao);
|
self._keychain = keychain = new KeychainDAO(lawnchairDao, pubkeyDao);
|
||||||
self._crypto = pgp = new PGP();
|
self._crypto = pgp = new PGP();
|
||||||
self._pgpbuilder = pgpbuilder = new PgpBuilder();
|
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._emailDao = emailDao = new EmailDAO(keychain, pgp, userStorage, pgpbuilder, mailreader, emailSync);
|
||||||
self._outboxBo = new OutboxBO(emailDao, keychain, userStorage);
|
self._outboxBo = new OutboxBO(emailDao, keychain, userStorage);
|
||||||
self._updateHandler = new UpdateHandler(appConfigStore, userStorage);
|
self._updateHandler = new UpdateHandler(appConfigStore, userStorage);
|
||||||
@ -129,7 +129,7 @@ define(function(require) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pgpMailer = new PgpMailer(smtpOptions, self._pgpbuilder);
|
pgpMailer = new PgpMailer(smtpOptions, self._pgpbuilder);
|
||||||
imapClient = new ImapClient(imapOptions, mailreader);
|
imapClient = new ImapClient(imapOptions);
|
||||||
imapClient.onError = onImapError;
|
imapClient.onError = onImapError;
|
||||||
|
|
||||||
// connect to clients
|
// connect to clients
|
||||||
|
@ -274,123 +274,121 @@ define(function(require) {
|
|||||||
message = options.message,
|
message = options.message,
|
||||||
folder = options.folder;
|
folder = options.folder;
|
||||||
|
|
||||||
if (message.loadingBody) {
|
// the message either already has a body or is fetching it right now, so no need to become active here
|
||||||
return;
|
if (message.loadingBody || typeof message.body !== 'undefined') {
|
||||||
}
|
|
||||||
|
|
||||||
// the message already has a body, so no need to become active here
|
|
||||||
if (message.body) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
message.loadingBody = true;
|
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
|
retrieveContent();
|
||||||
function readFromDevice() {
|
|
||||||
|
function retrieveContent() {
|
||||||
|
// load the local message from memory
|
||||||
self._emailSync._localListMessages({
|
self._emailSync._localListMessages({
|
||||||
folder: folder,
|
folder: folder,
|
||||||
uid: message.uid
|
uid: message.uid
|
||||||
}, function(err, localMessages) {
|
}, function(err, localMessages) {
|
||||||
var localMessage;
|
if (err || localMessages.length === 0) {
|
||||||
|
done(err);
|
||||||
if (err) {
|
|
||||||
message.loadingBody = false;
|
|
||||||
callback(err);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
localMessage = localMessages[0];
|
var localMessage = localMessages[0];
|
||||||
|
|
||||||
if (!localMessage.body) {
|
// do we need to fetch content from the imap server?
|
||||||
streamFromImap();
|
var needsFetch = false;
|
||||||
return;
|
localMessage.bodyParts.forEach(function(part) {
|
||||||
}
|
needsFetch = (typeof part.content === 'undefined');
|
||||||
|
|
||||||
// attach the body to the mail object
|
|
||||||
message.body = localMessage.body;
|
|
||||||
handleEncryptedContent();
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
// if reading the message body from the device was unsuccessful,
|
if (!needsFetch) {
|
||||||
// stream the message from the imap server
|
// if we have all the content we need,
|
||||||
function streamFromImap() {
|
// we can extract the content
|
||||||
self._emailSync._imapStreamText({
|
message.bodyParts = localMessage.bodyParts;
|
||||||
folder: folder,
|
extractContent();
|
||||||
message: message
|
|
||||||
}, function(error) {
|
|
||||||
if (error) {
|
|
||||||
message.loadingBody = false;
|
|
||||||
callback(error);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
message.loadingBody = false;
|
// get the raw content from the imap server
|
||||||
|
self._emailSync._getBodyParts({
|
||||||
// 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({
|
|
||||||
folder: folder,
|
folder: folder,
|
||||||
uid: message.uid
|
uid: localMessage.uid,
|
||||||
}, function(error, storedMessages) {
|
bodyParts: localMessage.bodyParts
|
||||||
if (error) {
|
}, function(err, parsedBodyParts) {
|
||||||
callback(error);
|
if (err) {
|
||||||
|
done(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
storedMessages[0].body = message.body;
|
message.bodyParts = parsedBodyParts;
|
||||||
|
localMessage.bodyParts = parsedBodyParts;
|
||||||
|
|
||||||
|
// persist it to disk
|
||||||
self._emailSync._localStoreMessages({
|
self._emailSync._localStoreMessages({
|
||||||
folder: folder,
|
folder: folder,
|
||||||
emails: storedMessages
|
emails: [localMessage]
|
||||||
}, function(error) {
|
}, function(error) {
|
||||||
if (error) {
|
if (error) {
|
||||||
callback(error);
|
done(error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEncryptedContent();
|
// extract the content
|
||||||
|
extractContent();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleEncryptedContent() {
|
function extractContent() {
|
||||||
// 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
|
|
||||||
if (message.encrypted) {
|
if (message.encrypted) {
|
||||||
message.decrypted = false;
|
// show the encrypted message
|
||||||
extractCiphertext();
|
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;
|
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) {
|
EmailDAO.prototype.decryptBody = function(options, callback) {
|
||||||
var self = this,
|
var self = this,
|
||||||
message = options.message;
|
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) {
|
if (message.decryptingBody || !message.body || !message.encrypted || message.decrypted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -408,61 +406,56 @@ define(function(require) {
|
|||||||
if (!senderPublicKey) {
|
if (!senderPublicKey) {
|
||||||
// this should only happen if a mail from another channel is in the inbox
|
// this should only happen if a mail from another channel is in the inbox
|
||||||
message.body = 'Public key for sender not found!';
|
message.body = 'Public key for sender not found!';
|
||||||
message.decryptingBody = false;
|
done();
|
||||||
callback(null, message);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the receiver's public key to check the message signature
|
// get the receiver's public key to check the message signature
|
||||||
self._crypto.decrypt(message.body, senderPublicKey.publicKey, function(err, decrypted) {
|
var encryptedNode = self._emailSync.filterBodyParts(message.bodyParts, 'encrypted')[0];
|
||||||
// if an error occurs during decryption, display the error message as the message content
|
self._crypto.decrypt(encryptedNode.content, senderPublicKey.publicKey, function(err, decrypted) {
|
||||||
decrypted = decrypted || err.errMsg || 'Error occurred during decryption';
|
if (err || !decrypted) {
|
||||||
|
err = err || {
|
||||||
// this is a very primitive detection if we have PGP/MIME or PGP/INLINE
|
errMsg: 'Error occurred during decryption'
|
||||||
if (!self._mailreader.isRfc(decrypted)) {
|
};
|
||||||
message.body = decrypted;
|
done(err);
|
||||||
message.decrypted = true;
|
|
||||||
message.decryptingBody = false;
|
|
||||||
callback(null, message);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse the decrypted MIME message
|
// the mailparser works on the .raw property
|
||||||
self._imapParseMessageBlock({
|
encryptedNode.raw = decrypted;
|
||||||
message: message,
|
|
||||||
raw: decrypted
|
// parse the decrpyted raw content in the mailparser
|
||||||
}, function(error) {
|
self._mailreader.parse({
|
||||||
|
bodyParts: [encryptedNode]
|
||||||
|
}, function(error, parsedBodyParts) {
|
||||||
if (error) {
|
if (error) {
|
||||||
message.decryptingBody = false;
|
done(error);
|
||||||
callback(error);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
message.decrypted = true;
|
// we have successfully interpreted the descrypted message,
|
||||||
|
// so let's update the views on the message parts
|
||||||
|
|
||||||
|
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
|
// remove the pgp-signature from the attachments
|
||||||
message.attachments = _.reject(message.attachments, function(attmt) {
|
|
||||||
return attmt.mimeType === "application/pgp-signature";
|
return attmt.mimeType === "application/pgp-signature";
|
||||||
});
|
});
|
||||||
|
|
||||||
|
message.decrypted = true;
|
||||||
|
|
||||||
|
|
||||||
// we're done here!
|
// we're done here!
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function done(err) {
|
||||||
message.decryptingBody = false;
|
message.decryptingBody = false;
|
||||||
callback(null, message);
|
callback(err, err ? undefined : message);
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
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) {
|
EmailDAO.prototype.sendEncrypted = function(options, callback) {
|
||||||
@ -541,10 +534,6 @@ define(function(require) {
|
|||||||
this._imapClient.logout(callback);
|
this._imapClient.logout(callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
EmailDAO.prototype._imapParseMessageBlock = function(options, callback) {
|
|
||||||
this._mailreader.parseRfc(options, callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List the folders in the user's IMAP mailbox.
|
* List the folders in the user's IMAP mailbox.
|
||||||
*/
|
*/
|
||||||
|
@ -5,9 +5,10 @@ define(function(require) {
|
|||||||
config = require('js/app-config').config,
|
config = require('js/app-config').config,
|
||||||
str = require('js/app-config').string;
|
str = require('js/app-config').string;
|
||||||
|
|
||||||
var EmailSync = function(keychain, devicestorage) {
|
var EmailSync = function(keychain, devicestorage, mailreader) {
|
||||||
this._keychain = keychain;
|
this._keychain = keychain;
|
||||||
this._devicestorage = devicestorage;
|
this._devicestorage = devicestorage;
|
||||||
|
this._mailreader = mailreader;
|
||||||
};
|
};
|
||||||
|
|
||||||
EmailSync.prototype.init = function(options, callback) {
|
EmailSync.prototype.init = function(options, callback) {
|
||||||
@ -168,8 +169,8 @@ define(function(require) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
storedMessages.forEach(function(storedMessage) {
|
storedMessages.forEach(function(storedMessage) {
|
||||||
// remove the body to not load unnecessary data to memory
|
// remove the body parts to not load unnecessary data to memory
|
||||||
delete storedMessage.body;
|
delete storedMessage.bodyParts;
|
||||||
|
|
||||||
folder.messages.push(storedMessage);
|
folder.messages.push(storedMessage);
|
||||||
});
|
});
|
||||||
@ -619,10 +620,11 @@ define(function(require) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleVerification(message, localCallback) {
|
function handleVerification(message, localCallback) {
|
||||||
self._imapStreamText({
|
self._getBodyParts({
|
||||||
folder: options.folder,
|
folder: options.folder,
|
||||||
message: message
|
uid: message.uid,
|
||||||
}, function(error) {
|
bodyParts: message.bodyParts
|
||||||
|
}, function(error, parsedBodyParts) {
|
||||||
// we could not stream the text to determine if the verification was valid or not
|
// we could not stream the text to determine if the verification was valid or not
|
||||||
// so handle it as if it were valid
|
// so handle it as if it were valid
|
||||||
if (error) {
|
if (error) {
|
||||||
@ -630,8 +632,9 @@ define(function(require) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var verificationUrlPrefix = config.cloudUrl + config.verificationUrl,
|
var body = _.pluck(self.filterBodyParts(parsedBodyParts, 'text'), 'content').join('\n'),
|
||||||
uuid = message.body.split(verificationUrlPrefix).pop().substr(0, config.verificationUuidLength),
|
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);
|
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
|
// there's no valid uuid in the message, so forget about it
|
||||||
@ -707,12 +710,8 @@ define(function(require) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._imapClient.updateFlags({
|
options.path = options.folder;
|
||||||
path: options.folder,
|
this._imapClient.updateFlags(options, callback);
|
||||||
uid: options.uid,
|
|
||||||
unread: options.unread,
|
|
||||||
answered: options.answered
|
|
||||||
}, callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -731,18 +730,8 @@ define(function(require) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var o = {
|
options.path = options.folder;
|
||||||
path: options.folder
|
this._imapClient.search(options, callback);
|
||||||
};
|
|
||||||
|
|
||||||
if (typeof options.answered !== 'undefined') {
|
|
||||||
o.answered = options.answered;
|
|
||||||
}
|
|
||||||
if (typeof options.unread !== 'undefined') {
|
|
||||||
o.unread = options.unread;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._imapClient.search(o, callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
EmailSync.prototype._imapDeleteMessage = function(options, callback) {
|
EmailSync.prototype._imapDeleteMessage = function(options, callback) {
|
||||||
@ -754,10 +743,8 @@ define(function(require) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._imapClient.deleteMessage({
|
options.path = options.folder;
|
||||||
path: options.folder,
|
this._imapClient.deleteMessage(options, callback);
|
||||||
uid: options.uid
|
|
||||||
}, callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -778,20 +765,18 @@ define(function(require) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self._imapClient.listMessagesByUid({
|
options.path = options.folder;
|
||||||
path: options.folder,
|
self._imapClient.listMessages(options, callback);
|
||||||
firstUid: options.firstUid,
|
|
||||||
lastUid: options.lastUid
|
|
||||||
}, callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stream an email messsage's body
|
* Stream an email messsage's body
|
||||||
* @param {String} options.folder The folder
|
* @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
|
* @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;
|
var self = this;
|
||||||
|
|
||||||
if (!this._account.online) {
|
if (!this._account.online) {
|
||||||
@ -802,11 +787,37 @@ define(function(require) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self._imapClient.getBody({
|
options.path = options.folder;
|
||||||
path: options.folder,
|
self._imapClient.getBodyParts(options, function(err) {
|
||||||
message: options.message
|
if (err) {
|
||||||
}, callback);
|
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;
|
return EmailSync;
|
||||||
});
|
});
|
@ -2,7 +2,8 @@ define(function(require) {
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var cfg = require('js/app-config').config,
|
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
|
* Handles database migration
|
||||||
@ -10,7 +11,7 @@ define(function(require) {
|
|||||||
var UpdateHandler = function(appConfigStorage, userStorage) {
|
var UpdateHandler = function(appConfigStorage, userStorage) {
|
||||||
this._appConfigStorage = appConfigStorage;
|
this._appConfigStorage = appConfigStorage;
|
||||||
this._userStorage = userStorage;
|
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() {
|
describe('_imapLogin', function() {
|
||||||
it('should fail when disconnected', function(done) {
|
it('should fail when disconnected', function(done) {
|
||||||
dao.onDisconnect(null, function(err) {
|
dao.onDisconnect(null, function(err) {
|
||||||
@ -616,6 +605,10 @@ define(function(require) {
|
|||||||
|
|
||||||
|
|
||||||
describe('getBody', function() {
|
describe('getBody', function() {
|
||||||
|
var folder = 'asdasdasdasdasd',
|
||||||
|
uid = 1234,
|
||||||
|
localListStub, localStoreStub, imapGetStub;
|
||||||
|
|
||||||
it('should not do anything if the message already has content', function() {
|
it('should not do anything if the message already has content', function() {
|
||||||
var message = {
|
var message = {
|
||||||
body: 'bender is great!'
|
body: 'bender is great!'
|
||||||
@ -629,11 +622,9 @@ define(function(require) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should read an unencrypted body from the device', function(done) {
|
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!';
|
body = 'bender is great! bender is great!';
|
||||||
uid = 1234;
|
|
||||||
message = {
|
message = {
|
||||||
uid: uid
|
uid: uid
|
||||||
};
|
};
|
||||||
@ -642,10 +633,12 @@ define(function(require) {
|
|||||||
folder: folder,
|
folder: folder,
|
||||||
uid: uid
|
uid: uid
|
||||||
}).yieldsAsync(null, [{
|
}).yieldsAsync(null, [{
|
||||||
body: body
|
bodyParts: [{
|
||||||
|
type: 'text',
|
||||||
|
content: body
|
||||||
|
}]
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
|
|
||||||
dao.getBody({
|
dao.getBody({
|
||||||
message: message,
|
message: message,
|
||||||
folder: folder
|
folder: folder
|
||||||
@ -653,8 +646,7 @@ define(function(require) {
|
|||||||
expect(err).to.not.exist;
|
expect(err).to.not.exist;
|
||||||
|
|
||||||
expect(msg).to.equal(message);
|
expect(msg).to.equal(message);
|
||||||
expect(msg.body).to.not.be.empty;
|
expect(msg.body).to.equal(body);
|
||||||
expect(msg.encrypted).to.be.false;
|
|
||||||
expect(msg.loadingBody).to.be.false;
|
expect(msg.loadingBody).to.be.false;
|
||||||
|
|
||||||
expect(localListStub.calledOnce).to.be.true;
|
expect(localListStub.calledOnce).to.be.true;
|
||||||
@ -665,20 +657,26 @@ define(function(require) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should read an encrypted body from the device', function(done) {
|
it('should read an encrypted body from the device', function(done) {
|
||||||
var message, uid, folder, body, localListStub;
|
var message, ct, pt;
|
||||||
|
|
||||||
folder = 'asdasdasdasdasd';
|
pt = 'bender is great!';
|
||||||
body = '-----BEGIN PGP MESSAGE-----asdasdasd-----END PGP MESSAGE-----';
|
ct = '-----BEGIN PGP MESSAGE-----asdasdasd-----END PGP MESSAGE-----';
|
||||||
uid = 1234;
|
|
||||||
message = {
|
message = {
|
||||||
uid: uid
|
uid: uid,
|
||||||
|
encrypted: true
|
||||||
};
|
};
|
||||||
|
|
||||||
localListStub = sinon.stub(emailSync, '_localListMessages').withArgs({
|
localListStub = sinon.stub(emailSync, '_localListMessages').withArgs({
|
||||||
folder: folder,
|
folder: folder,
|
||||||
uid: uid
|
uid: uid
|
||||||
}).yieldsAsync(null, [{
|
}).yieldsAsync(null, [{
|
||||||
body: body
|
bodyParts: [{
|
||||||
|
type: 'text',
|
||||||
|
content: pt
|
||||||
|
}, {
|
||||||
|
type: 'encrypted',
|
||||||
|
content: ct
|
||||||
|
}]
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
dao.getBody({
|
dao.getBody({
|
||||||
@ -688,9 +686,8 @@ define(function(require) {
|
|||||||
expect(err).to.not.exist;
|
expect(err).to.not.exist;
|
||||||
|
|
||||||
expect(msg).to.equal(message);
|
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.encrypted).to.be.true;
|
||||||
expect(msg.decrypted).to.be.false;
|
|
||||||
expect(message.loadingBody).to.be.false;
|
expect(message.loadingBody).to.be.false;
|
||||||
|
|
||||||
expect(localListStub.calledOnce).to.be.true;
|
expect(localListStub.calledOnce).to.be.true;
|
||||||
@ -700,14 +697,17 @@ define(function(require) {
|
|||||||
expect(message.loadingBody).to.be.true;
|
expect(message.loadingBody).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should stream an unencrypted body from imap', function(done) {
|
it('should stream from imap and set plain text body', function(done) {
|
||||||
var message, uid, folder, body, localListStub, localStoreStub, imapStreamStub;
|
var message, body;
|
||||||
|
|
||||||
folder = 'asdasdasdasdasd';
|
folder = 'asdasdasdasdasd';
|
||||||
body = 'bender is great! bender is great!';
|
body = 'bender is great! bender is great!';
|
||||||
uid = 1234;
|
uid = 1234;
|
||||||
message = {
|
message = {
|
||||||
uid: uid
|
uid: uid,
|
||||||
|
bodyParts: [{
|
||||||
|
type: 'text'
|
||||||
|
}]
|
||||||
};
|
};
|
||||||
|
|
||||||
localListStub = sinon.stub(emailSync, '_localListMessages').withArgs({
|
localListStub = sinon.stub(emailSync, '_localListMessages').withArgs({
|
||||||
@ -720,16 +720,14 @@ define(function(require) {
|
|||||||
emails: [message]
|
emails: [message]
|
||||||
}).yieldsAsync();
|
}).yieldsAsync();
|
||||||
|
|
||||||
imapStreamStub = sinon.stub(emailSync, '_imapStreamText', function(opts, cb) {
|
imapGetStub = sinon.stub(emailSync, '_getBodyParts').withArgs({
|
||||||
expect(opts).to.deep.equal({
|
|
||||||
folder: folder,
|
folder: folder,
|
||||||
message: message
|
uid: message.uid,
|
||||||
});
|
bodyParts: message.bodyParts
|
||||||
|
}).yieldsAsync(null, [{
|
||||||
message.body = body;
|
type: 'text',
|
||||||
cb();
|
content: body
|
||||||
});
|
}]);
|
||||||
|
|
||||||
|
|
||||||
dao.getBody({
|
dao.getBody({
|
||||||
message: message,
|
message: message,
|
||||||
@ -738,12 +736,11 @@ define(function(require) {
|
|||||||
expect(err).to.not.exist;
|
expect(err).to.not.exist;
|
||||||
|
|
||||||
expect(msg).to.equal(message);
|
expect(msg).to.equal(message);
|
||||||
expect(msg.body).to.not.be.empty;
|
expect(msg.body).to.equal(body);
|
||||||
expect(msg.encrypted).to.be.false;
|
|
||||||
expect(msg.loadingBody).to.be.false;
|
expect(msg.loadingBody).to.be.false;
|
||||||
|
|
||||||
expect(localListStub.calledTwice).to.be.true;
|
expect(localListStub.calledOnce).to.be.true;
|
||||||
expect(imapStreamStub.calledOnce).to.be.true;
|
expect(imapGetStub.calledOnce).to.be.true;
|
||||||
expect(localStoreStub.calledOnce).to.be.true;
|
expect(localStoreStub.calledOnce).to.be.true;
|
||||||
|
|
||||||
done();
|
done();
|
||||||
@ -751,14 +748,19 @@ define(function(require) {
|
|||||||
expect(message.loadingBody).to.be.true;
|
expect(message.loadingBody).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should stream an encrypted body from imap', function(done) {
|
it('should stream from imap and set encrypted body', function(done) {
|
||||||
var message, uid, folder, body, localListStub, localStoreStub, imapStreamStub;
|
var message, ct, pt;
|
||||||
|
|
||||||
folder = 'asdasdasdasdasd';
|
pt = 'bender is great';
|
||||||
body = '-----BEGIN PGP MESSAGE-----asdasdasd-----END PGP MESSAGE-----';
|
ct = '-----BEGIN PGP MESSAGE-----asdasdasd-----END PGP MESSAGE-----';
|
||||||
uid = 1234;
|
|
||||||
message = {
|
message = {
|
||||||
uid: uid
|
uid: uid,
|
||||||
|
encrypted: true,
|
||||||
|
bodyParts: [{
|
||||||
|
type: 'text'
|
||||||
|
}, {
|
||||||
|
type: 'encrypted'
|
||||||
|
}]
|
||||||
};
|
};
|
||||||
|
|
||||||
localListStub = sinon.stub(emailSync, '_localListMessages').withArgs({
|
localListStub = sinon.stub(emailSync, '_localListMessages').withArgs({
|
||||||
@ -771,15 +773,17 @@ define(function(require) {
|
|||||||
emails: [message]
|
emails: [message]
|
||||||
}).yieldsAsync();
|
}).yieldsAsync();
|
||||||
|
|
||||||
imapStreamStub = sinon.stub(emailSync, '_imapStreamText', function(opts, cb) {
|
imapGetStub = sinon.stub(emailSync, '_getBodyParts').withArgs({
|
||||||
expect(opts).to.deep.equal({
|
|
||||||
folder: folder,
|
folder: folder,
|
||||||
message: message
|
uid: message.uid,
|
||||||
});
|
bodyParts: message.bodyParts
|
||||||
|
}).yieldsAsync(null, [{
|
||||||
message.body = body;
|
type: 'text',
|
||||||
cb();
|
content: pt
|
||||||
});
|
}, {
|
||||||
|
type: 'encrypted',
|
||||||
|
content: ct
|
||||||
|
}]);
|
||||||
|
|
||||||
|
|
||||||
dao.getBody({
|
dao.getBody({
|
||||||
@ -789,13 +793,12 @@ define(function(require) {
|
|||||||
expect(err).to.not.exist;
|
expect(err).to.not.exist;
|
||||||
|
|
||||||
expect(msg).to.equal(message);
|
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.encrypted).to.be.true;
|
||||||
expect(msg.decrypted).to.be.false;
|
|
||||||
expect(msg.loadingBody).to.be.false;
|
expect(msg.loadingBody).to.be.false;
|
||||||
|
|
||||||
expect(localListStub.calledTwice).to.be.true;
|
expect(localListStub.calledOnce).to.be.true;
|
||||||
expect(imapStreamStub.calledOnce).to.be.true;
|
expect(imapGetStub.calledOnce).to.be.true;
|
||||||
expect(localStoreStub.calledOnce).to.be.true;
|
expect(localStoreStub.calledOnce).to.be.true;
|
||||||
|
|
||||||
done();
|
done();
|
||||||
@ -804,22 +807,19 @@ define(function(require) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('fail to stream from imap due to error when persisting', function(done) {
|
it('fail to stream from imap due to error when persisting', function(done) {
|
||||||
var message, uid, folder, body, localListStub, localStoreStub, imapStreamStub;
|
var message = {
|
||||||
|
uid: uid,
|
||||||
folder = 'asdasdasdasdasd';
|
bodyParts: [{
|
||||||
body = 'THIS IS THE BODY';
|
type: 'text'
|
||||||
uid = 1234;
|
}]
|
||||||
message = {
|
|
||||||
uid: uid
|
|
||||||
};
|
};
|
||||||
|
|
||||||
localListStub = sinon.stub(emailSync, '_localListMessages').yieldsAsync(null, [message]);
|
localListStub = sinon.stub(emailSync, '_localListMessages').yieldsAsync(null, [message]);
|
||||||
localStoreStub = sinon.stub(emailSync, '_localStoreMessages').yieldsAsync({});
|
localStoreStub = sinon.stub(emailSync, '_localStoreMessages').yieldsAsync({});
|
||||||
|
imapGetStub = sinon.stub(emailSync, '_getBodyParts').yieldsAsync(null, [{
|
||||||
imapStreamStub = sinon.stub(emailSync, '_imapStreamText', function(opts, cb) {
|
type: 'text',
|
||||||
message.body = body;
|
content: 'bender is great! bender is great!'
|
||||||
cb();
|
}]);
|
||||||
});
|
|
||||||
|
|
||||||
dao.getBody({
|
dao.getBody({
|
||||||
message: message,
|
message: message,
|
||||||
@ -827,8 +827,8 @@ define(function(require) {
|
|||||||
}, function(err, msg) {
|
}, function(err, msg) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
expect(msg).to.not.exist;
|
expect(msg).to.not.exist;
|
||||||
expect(localListStub.calledTwice).to.be.true;
|
expect(localListStub.calledOnce).to.be.true;
|
||||||
expect(imapStreamStub.calledOnce).to.be.true;
|
expect(imapGetStub.calledOnce).to.be.true;
|
||||||
expect(localStoreStub.calledOnce).to.be.true;
|
expect(localStoreStub.calledOnce).to.be.true;
|
||||||
|
|
||||||
expect(message.loadingBody).to.be.false;
|
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) {
|
it('fail to stream from imap due to stream error', function(done) {
|
||||||
var message, uid, folder, body, localListStub, localStoreStub, imapStreamStub;
|
var message = {
|
||||||
|
uid: uid,
|
||||||
folder = 'asdasdasdasdasd';
|
bodyParts: [{
|
||||||
uid = 1234;
|
type: 'text'
|
||||||
message = {
|
}]
|
||||||
uid: uid
|
|
||||||
};
|
};
|
||||||
|
|
||||||
localListStub = sinon.stub(emailSync, '_localListMessages').yields(null, [{}]);
|
localListStub = sinon.stub(emailSync, '_localListMessages').yields(null, [message]);
|
||||||
|
imapGetStub = sinon.stub(emailSync, '_getBodyParts').yieldsAsync({});
|
||||||
imapStreamStub = sinon.stub(emailSync, '_imapStreamText', function(opts, cb) {
|
|
||||||
message.body = body;
|
|
||||||
cb({});
|
|
||||||
});
|
|
||||||
|
|
||||||
localStoreStub = sinon.stub(emailSync, '_localStoreMessages');
|
localStoreStub = sinon.stub(emailSync, '_localStoreMessages');
|
||||||
|
|
||||||
dao.getBody({
|
dao.getBody({
|
||||||
@ -862,7 +856,7 @@ define(function(require) {
|
|||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
expect(msg).to.not.exist;
|
expect(msg).to.not.exist;
|
||||||
expect(localListStub.calledOnce).to.be.true;
|
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(localStoreStub.called).to.be.false;
|
||||||
|
|
||||||
expect(message.loadingBody).to.be.false;
|
expect(message.loadingBody).to.be.false;
|
||||||
@ -873,55 +867,89 @@ define(function(require) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('decryptBody', function() {
|
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 = {
|
var message = {
|
||||||
encrypted: false
|
encrypted: false,
|
||||||
|
decrypted: true,
|
||||||
|
body: 'asd'
|
||||||
};
|
};
|
||||||
|
|
||||||
dao.decryptBody({
|
dao.decryptBody({
|
||||||
message: message
|
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 = {
|
var message = {
|
||||||
encrypted: true,
|
encrypted: true,
|
||||||
decrypted: true
|
decrypted: true,
|
||||||
|
body: 'asd'
|
||||||
};
|
};
|
||||||
|
|
||||||
dao.decryptBody({
|
dao.decryptBody({
|
||||||
message: message
|
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) {
|
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 = {
|
message = {
|
||||||
from: [{
|
from: [{
|
||||||
address: 'asdasdasd'
|
address: 'asdasdasd'
|
||||||
}],
|
}],
|
||||||
|
body: ct,
|
||||||
encrypted: true,
|
encrypted: true,
|
||||||
decrypted: false,
|
bodyParts: [{
|
||||||
body: '-----BEGIN PGP MESSAGE-----asdasdasd-----END PGP MESSAGE-----'
|
type: 'encrypted',
|
||||||
|
content: ct
|
||||||
|
}]
|
||||||
};
|
};
|
||||||
|
|
||||||
mimeBody = 'Content-Type: asdasdasd';
|
|
||||||
parsedBody = 'body? yes.';
|
|
||||||
|
|
||||||
keychainStub.getReceiverPublicKey.withArgs(message.from[0].address).yieldsAsync(null, mockKeyPair.publicKey);
|
keychainStub.getReceiverPublicKey.withArgs(message.from[0].address).yieldsAsync(null, mockKeyPair.publicKey);
|
||||||
pgpStub.decrypt.withArgs(message.body, mockKeyPair.publicKey.publicKey).yieldsAsync(null, mimeBody);
|
pgpStub.decrypt.withArgs(ct, mockKeyPair.publicKey.publicKey).yieldsAsync(null, pt);
|
||||||
parseStub = sinon.stub(dao, '_imapParseMessageBlock', function(o, cb) {
|
parseStub = sinon.stub(mailreader, 'parse').withArgs({
|
||||||
expect(o.message).to.equal(message);
|
bodyParts: [{
|
||||||
expect(o.raw).to.equal(mimeBody);
|
type: 'encrypted',
|
||||||
|
content: ct,
|
||||||
o.message.body = parsedBody;
|
raw: pt
|
||||||
cb(null, o.message);
|
}]
|
||||||
});
|
}).yieldsAsync(null, [{
|
||||||
|
type: 'encrypted',
|
||||||
|
content: [{
|
||||||
|
type: 'text',
|
||||||
|
content: parsed
|
||||||
|
}]
|
||||||
|
}]);
|
||||||
|
|
||||||
dao.decryptBody({
|
dao.decryptBody({
|
||||||
message: message
|
message: message
|
||||||
@ -929,110 +957,71 @@ define(function(require) {
|
|||||||
expect(error).to.not.exist;
|
expect(error).to.not.exist;
|
||||||
|
|
||||||
expect(msg).to.equal(message);
|
expect(msg).to.equal(message);
|
||||||
expect(msg.decrypted).to.be.true;
|
expect(message.decrypted).to.be.true;
|
||||||
expect(msg.body).to.equal(parsedBody);
|
expect(message.body).to.equal(parsed);
|
||||||
expect(msg.decryptingBody).to.be.false;
|
expect(message.decryptingBody).to.be.false;
|
||||||
|
|
||||||
expect(keychainStub.getReceiverPublicKey.calledOnce).to.be.true;
|
expect(keychainStub.getReceiverPublicKey.calledOnce).to.be.true;
|
||||||
expect(pgpStub.decrypt.calledOnce).to.be.true;
|
expect(pgpStub.decrypt.calledOnce).to.be.true;
|
||||||
expect(parseStub.calledOnce).to.be.true;
|
expect(parseStub.calledOnce).to.be.true;
|
||||||
|
|
||||||
|
mailreader.parse.restore();
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(message.decryptingBody).to.be.true;
|
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) {
|
it('should fail during decryption message', function(done) {
|
||||||
var message, plaintextBody, parseStub, errMsg;
|
var message = {
|
||||||
|
|
||||||
message = {
|
|
||||||
from: [{
|
from: [{
|
||||||
address: 'asdasdasd'
|
address: 'asdasdasd'
|
||||||
}],
|
}],
|
||||||
|
body: 'asdjafuad',
|
||||||
encrypted: true,
|
encrypted: true,
|
||||||
decrypted: false,
|
bodyParts: [{
|
||||||
body: '-----BEGIN PGP MESSAGE-----asdasdasd-----END PGP MESSAGE-----'
|
type: 'encrypted',
|
||||||
|
content: '-----BEGIN PGP MESSAGE-----asdasdasd-----END PGP MESSAGE-----'
|
||||||
|
}]
|
||||||
};
|
};
|
||||||
|
|
||||||
plaintextBody = 'body? yes.';
|
var parseStub = sinon.spy(mailreader, 'parse');
|
||||||
errMsg = 'yaddayadda';
|
keychainStub.getReceiverPublicKey.yields(null, mockKeyPair.publicKey);
|
||||||
|
|
||||||
keychainStub.getReceiverPublicKey.withArgs(message.from[0].address).yields(null, mockKeyPair.publicKey);
|
|
||||||
pgpStub.decrypt.yields({
|
pgpStub.decrypt.yields({
|
||||||
errMsg: errMsg
|
errMsg: 'asd'
|
||||||
});
|
});
|
||||||
parseStub = sinon.stub(dao, '_imapParseMessageBlock');
|
|
||||||
|
|
||||||
dao.decryptBody({
|
dao.decryptBody({
|
||||||
message: message
|
message: message
|
||||||
}, function(error, msg) {
|
}, function(error, msg) {
|
||||||
expect(error).to.not.exist;
|
expect(error).to.exist;
|
||||||
|
expect(msg).to.not.exist;
|
||||||
expect(msg).to.equal(message);
|
expect(message.decryptingBody).to.be.false;
|
||||||
expect(msg.decrypted).to.be.true;
|
|
||||||
expect(msg.body).to.equal(errMsg);
|
|
||||||
expect(msg.decryptingBody).to.be.false;
|
|
||||||
|
|
||||||
expect(keychainStub.getReceiverPublicKey.calledOnce).to.be.true;
|
expect(keychainStub.getReceiverPublicKey.calledOnce).to.be.true;
|
||||||
expect(pgpStub.decrypt.calledOnce).to.be.true;
|
expect(pgpStub.decrypt.calledOnce).to.be.true;
|
||||||
expect(parseStub.called).to.be.false;
|
expect(parseStub.called).to.be.false;
|
||||||
|
|
||||||
|
mailreader.parse.restore();
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail during key export', function(done) {
|
it('should fail during key export', function(done) {
|
||||||
var message, parseStub;
|
var message = {
|
||||||
|
|
||||||
message = {
|
|
||||||
from: [{
|
from: [{
|
||||||
address: 'asdasdasd'
|
address: 'asdasdasd'
|
||||||
}],
|
}],
|
||||||
encrypted: true,
|
encrypted: true,
|
||||||
decrypted: false,
|
body: 'asdjafuad',
|
||||||
body: '-----BEGIN PGP MESSAGE-----asdasdasd-----END PGP MESSAGE-----'
|
bodyParts: [{
|
||||||
|
type: 'encrypted',
|
||||||
|
content: '-----BEGIN PGP MESSAGE-----asdasdasd-----END PGP MESSAGE-----'
|
||||||
|
}]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var parseStub = sinon.spy(mailreader, 'parse');
|
||||||
keychainStub.getReceiverPublicKey.yields({});
|
keychainStub.getReceiverPublicKey.yields({});
|
||||||
parseStub = sinon.stub(dao, '_imapParseMessageBlock');
|
|
||||||
|
|
||||||
dao.decryptBody({
|
dao.decryptBody({
|
||||||
message: message
|
message: message
|
||||||
@ -1041,13 +1030,13 @@ define(function(require) {
|
|||||||
|
|
||||||
expect(msg).to.not.exist;
|
expect(msg).to.not.exist;
|
||||||
|
|
||||||
expect(message.decrypted).to.be.false;
|
|
||||||
expect(message.decryptingBody).to.be.false;
|
expect(message.decryptingBody).to.be.false;
|
||||||
|
|
||||||
expect(keychainStub.getReceiverPublicKey.calledOnce).to.be.true;
|
expect(keychainStub.getReceiverPublicKey.calledOnce).to.be.true;
|
||||||
expect(pgpStub.decrypt.called).to.be.false;
|
expect(pgpStub.decrypt.called).to.be.false;
|
||||||
expect(parseStub.called).to.be.false;
|
expect(parseStub.called).to.be.false;
|
||||||
|
|
||||||
|
mailreader.parse.restore();
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -3,6 +3,7 @@ define(function(require) {
|
|||||||
|
|
||||||
var EmailSync = require('js/dao/email-sync'),
|
var EmailSync = require('js/dao/email-sync'),
|
||||||
KeychainDAO = require('js/dao/keychain-dao'),
|
KeychainDAO = require('js/dao/keychain-dao'),
|
||||||
|
mailreader = require('mailreader'),
|
||||||
ImapClient = require('imap-client'),
|
ImapClient = require('imap-client'),
|
||||||
DeviceStorageDAO = require('js/dao/devicestorage-dao'),
|
DeviceStorageDAO = require('js/dao/devicestorage-dao'),
|
||||||
expect = chai.expect;
|
expect = chai.expect;
|
||||||
@ -13,8 +14,7 @@ define(function(require) {
|
|||||||
var emailSync, keychainStub, imapClientStub, devicestorageStub;
|
var emailSync, keychainStub, imapClientStub, devicestorageStub;
|
||||||
|
|
||||||
var emailAddress, mockkeyId, dummyEncryptedMail,
|
var emailAddress, mockkeyId, dummyEncryptedMail,
|
||||||
dummyDecryptedMail, mockKeyPair, account, verificationMail, verificationUuid,
|
dummyDecryptedMail, mockKeyPair, account, verificationMail, verificationUuid, corruptedVerificationUuid,
|
||||||
corruptedVerificationMail, corruptedVerificationUuid,
|
|
||||||
nonWhitelistedMail;
|
nonWhitelistedMail;
|
||||||
|
|
||||||
beforeEach(function(done) {
|
beforeEach(function(done) {
|
||||||
@ -29,7 +29,9 @@ define(function(require) {
|
|||||||
address: 'qwe@qwe.de'
|
address: 'qwe@qwe.de'
|
||||||
}],
|
}],
|
||||||
subject: 'qweasd',
|
subject: 'qweasd',
|
||||||
body: '-----BEGIN PGP MESSAGE-----\nasd\n-----END PGP MESSAGE-----',
|
bodyParts: [{
|
||||||
|
type: 'encrypted'
|
||||||
|
}],
|
||||||
unread: false,
|
unread: false,
|
||||||
answered: false
|
answered: false
|
||||||
};
|
};
|
||||||
@ -43,24 +45,13 @@ define(function(require) {
|
|||||||
address: 'safewithme.testuser@gmail.com'
|
address: 'safewithme.testuser@gmail.com'
|
||||||
}], // list of receivers
|
}], // list of receivers
|
||||||
subject: "[whiteout] New public key uploaded", // Subject line
|
subject: "[whiteout] New public key uploaded", // Subject line
|
||||||
body: 'yadda yadda bla blabla foo bar https://keys.whiteout.io/verify/' + verificationUuid, // plaintext body
|
bodyParts: [{
|
||||||
|
type: 'text'
|
||||||
|
}],
|
||||||
unread: false,
|
unread: false,
|
||||||
answered: false
|
answered: false
|
||||||
};
|
};
|
||||||
corruptedVerificationUuid = 'OMFG_FUCKING_BASTARD_UUID_FROM_HELL!';
|
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 = {
|
dummyDecryptedMail = {
|
||||||
uid: 1234,
|
uid: 1234,
|
||||||
from: [{
|
from: [{
|
||||||
@ -70,7 +61,9 @@ define(function(require) {
|
|||||||
address: 'qwe@qwe.de'
|
address: 'qwe@qwe.de'
|
||||||
}],
|
}],
|
||||||
subject: 'qweasd',
|
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,
|
unread: false,
|
||||||
answered: false,
|
answered: false,
|
||||||
};
|
};
|
||||||
@ -83,7 +76,9 @@ define(function(require) {
|
|||||||
address: 'qwe@qwe.de'
|
address: 'qwe@qwe.de'
|
||||||
}],
|
}],
|
||||||
subject: 'qweasd',
|
subject: 'qweasd',
|
||||||
body: 'asd'
|
bodyParts: [{
|
||||||
|
type: 'text'
|
||||||
|
}],
|
||||||
};
|
};
|
||||||
mockKeyPair = {
|
mockKeyPair = {
|
||||||
publicKey: {
|
publicKey: {
|
||||||
@ -106,10 +101,11 @@ define(function(require) {
|
|||||||
imapClientStub = sinon.createStubInstance(ImapClient);
|
imapClientStub = sinon.createStubInstance(ImapClient);
|
||||||
devicestorageStub = sinon.createStubInstance(DeviceStorageDAO);
|
devicestorageStub = sinon.createStubInstance(DeviceStorageDAO);
|
||||||
|
|
||||||
emailSync = new EmailSync(keychainStub, devicestorageStub);
|
emailSync = new EmailSync(keychainStub, devicestorageStub, mailreader);
|
||||||
|
|
||||||
expect(emailSync._keychain).to.equal(keychainStub);
|
expect(emailSync._keychain).to.equal(keychainStub);
|
||||||
expect(emailSync._devicestorage).to.equal(devicestorageStub);
|
expect(emailSync._devicestorage).to.equal(devicestorageStub);
|
||||||
|
expect(emailSync._mailreader).to.equal(mailreader);
|
||||||
|
|
||||||
// init
|
// init
|
||||||
emailSync.init({
|
emailSync.init({
|
||||||
@ -154,6 +150,8 @@ define(function(require) {
|
|||||||
|
|
||||||
|
|
||||||
describe('_imapSearch', function() {
|
describe('_imapSearch', function() {
|
||||||
|
var path = 'FOLDAAAA';
|
||||||
|
|
||||||
it('should fail when disconnected', function(done) {
|
it('should fail when disconnected', function(done) {
|
||||||
// this is set in the emailDao.onDisconnect
|
// this is set in the emailDao.onDisconnect
|
||||||
emailSync._account.online = false;
|
emailSync._account.online = false;
|
||||||
@ -164,10 +162,9 @@ define(function(require) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work', function(done) {
|
it('should list all uids', function(done) {
|
||||||
var path = 'FOLDAAAA';
|
|
||||||
|
|
||||||
imapClientStub.search.withArgs({
|
imapClientStub.search.withArgs({
|
||||||
|
folder: path,
|
||||||
path: path
|
path: path
|
||||||
}).yields();
|
}).yields();
|
||||||
|
|
||||||
@ -175,10 +172,10 @@ define(function(require) {
|
|||||||
folder: path
|
folder: path
|
||||||
}, done);
|
}, done);
|
||||||
});
|
});
|
||||||
it('should work', function(done) {
|
|
||||||
var path = 'FOLDAAAA';
|
|
||||||
|
|
||||||
|
it('should list answered uids', function(done) {
|
||||||
imapClientStub.search.withArgs({
|
imapClientStub.search.withArgs({
|
||||||
|
folder: path,
|
||||||
path: path,
|
path: path,
|
||||||
answered: true
|
answered: true
|
||||||
}).yields();
|
}).yields();
|
||||||
@ -188,10 +185,10 @@ define(function(require) {
|
|||||||
answered: true
|
answered: true
|
||||||
}, done);
|
}, done);
|
||||||
});
|
});
|
||||||
it('should work', function(done) {
|
|
||||||
var path = 'FOLDAAAA';
|
|
||||||
|
|
||||||
|
it('should list unread uids', function(done) {
|
||||||
imapClientStub.search.withArgs({
|
imapClientStub.search.withArgs({
|
||||||
|
folder: path,
|
||||||
path: path,
|
path: path,
|
||||||
unread: true
|
unread: true
|
||||||
}).yields();
|
}).yields();
|
||||||
@ -204,6 +201,9 @@ define(function(require) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('_imapDeleteMessage', function() {
|
describe('_imapDeleteMessage', function() {
|
||||||
|
var path = 'FOLDAAAA',
|
||||||
|
uid = 1337;
|
||||||
|
|
||||||
it('should fail when disconnected', function(done) {
|
it('should fail when disconnected', function(done) {
|
||||||
// this is set in the emailDao.onDisconnect
|
// this is set in the emailDao.onDisconnect
|
||||||
emailSync._account.online = false;
|
emailSync._account.online = false;
|
||||||
@ -215,11 +215,9 @@ define(function(require) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should work', function(done) {
|
it('should work', function(done) {
|
||||||
var path = 'FOLDAAAA',
|
|
||||||
uid = 1337;
|
|
||||||
|
|
||||||
imapClientStub.deleteMessage.withArgs({
|
imapClientStub.deleteMessage.withArgs({
|
||||||
path: path,
|
path: path,
|
||||||
|
folder: path,
|
||||||
uid: uid
|
uid: uid
|
||||||
}).yields();
|
}).yields();
|
||||||
|
|
||||||
@ -231,13 +229,15 @@ define(function(require) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('_imapListMessages', function() {
|
describe('_imapListMessages', function() {
|
||||||
it('should work', function(done) {
|
|
||||||
var path = 'FOLDAAAA',
|
var path = 'FOLDAAAA',
|
||||||
firstUid = 1337,
|
firstUid = 1337,
|
||||||
lastUid = 1339;
|
lastUid = 1339;
|
||||||
|
|
||||||
imapClientStub.listMessagesByUid.withArgs({
|
|
||||||
|
it('should work', function(done) {
|
||||||
|
imapClientStub.listMessages.withArgs({
|
||||||
path: path,
|
path: path,
|
||||||
|
folder: path,
|
||||||
firstUid: firstUid,
|
firstUid: firstUid,
|
||||||
lastUid: lastUid
|
lastUid: lastUid
|
||||||
}).yields(null, []);
|
}).yields(null, []);
|
||||||
@ -250,18 +250,14 @@ define(function(require) {
|
|||||||
expect(err).to.not.exist;
|
expect(err).to.not.exist;
|
||||||
expect(msgs).to.exist;
|
expect(msgs).to.exist;
|
||||||
|
|
||||||
expect(imapClientStub.listMessagesByUid.calledOnce).to.be.true;
|
expect(imapClientStub.listMessages.calledOnce).to.be.true;
|
||||||
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not work when listMessagesByUid fails', function(done) {
|
it('should not work when listMessages fails', function(done) {
|
||||||
var path = 'FOLDAAAA',
|
imapClientStub.listMessages.yields({});
|
||||||
firstUid = 1337,
|
|
||||||
lastUid = 1339;
|
|
||||||
|
|
||||||
imapClientStub.listMessagesByUid.yields({});
|
|
||||||
|
|
||||||
emailSync._imapListMessages({
|
emailSync._imapListMessages({
|
||||||
folder: path,
|
folder: path,
|
||||||
@ -271,7 +267,7 @@ define(function(require) {
|
|||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
expect(msgs).to.not.exist;
|
expect(msgs).to.not.exist;
|
||||||
|
|
||||||
expect(imapClientStub.listMessagesByUid.calledOnce).to.be.true;
|
expect(imapClientStub.listMessages.calledOnce).to.be.true;
|
||||||
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@ -288,42 +284,50 @@ define(function(require) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('_imapStreamText', function() {
|
describe('_getBodyParts', function() {
|
||||||
|
var path = 'FOLDAAAA',
|
||||||
|
parseStub;
|
||||||
|
|
||||||
it('should work', function(done) {
|
it('should work', function(done) {
|
||||||
var path = 'FOLDAAAA';
|
var o = {
|
||||||
|
|
||||||
imapClientStub.getBody.withArgs({
|
|
||||||
path: path,
|
|
||||||
message: {}
|
|
||||||
}).yields(null, {});
|
|
||||||
|
|
||||||
emailSync._imapStreamText({
|
|
||||||
folder: path,
|
folder: path,
|
||||||
message: {}
|
uid: 123,
|
||||||
}, function(err, msg) {
|
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(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();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not work when getBody fails', function(done) {
|
it('should not work when getBody fails', function(done) {
|
||||||
var path = 'FOLDAAAA';
|
var o = {
|
||||||
|
|
||||||
imapClientStub.getBody.yields({});
|
|
||||||
|
|
||||||
emailSync._imapStreamText({
|
|
||||||
folder: path,
|
folder: path,
|
||||||
message: {}
|
uid: 123,
|
||||||
}, function(err, msg) {
|
bodyParts: []
|
||||||
|
};
|
||||||
|
|
||||||
|
imapClientStub.getBodyParts.yields({});
|
||||||
|
parseStub = sinon.spy(mailreader, 'parse');
|
||||||
|
|
||||||
|
emailSync._getBodyParts(o, function(err, msg) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
expect(msg).to.not.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();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -332,7 +336,7 @@ define(function(require) {
|
|||||||
// this is set in the emailDao.onDisconnect
|
// this is set in the emailDao.onDisconnect
|
||||||
emailSync._account.online = false;
|
emailSync._account.online = false;
|
||||||
|
|
||||||
emailSync._imapStreamText({}, function(err) {
|
emailSync._getBodyParts({}, function(err) {
|
||||||
expect(err.code).to.equal(42);
|
expect(err.code).to.equal(42);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@ -950,13 +954,12 @@ define(function(require) {
|
|||||||
}).yields(null, []);
|
}).yields(null, []);
|
||||||
|
|
||||||
imapListMessagesStub = sinon.stub(emailSync, '_imapListMessages').yields(null, [verificationMail]);
|
imapListMessagesStub = sinon.stub(emailSync, '_imapListMessages').yields(null, [verificationMail]);
|
||||||
|
imapGetStub = sinon.stub(emailSync, '_getBodyParts').yields(null, [{
|
||||||
imapGetStub = sinon.stub(emailSync, '_imapStreamText').yields(null);
|
type: 'text',
|
||||||
|
content: 'yadda yadda bla blabla foo bar https://keys.whiteout.io/verify/' + verificationUuid,
|
||||||
|
}]);
|
||||||
keychainStub.verifyPublicKey.withArgs(verificationUuid).yields();
|
keychainStub.verifyPublicKey.withArgs(verificationUuid).yields();
|
||||||
|
|
||||||
localStoreStub = sinon.stub(emailSync, '_localStoreMessages');
|
localStoreStub = sinon.stub(emailSync, '_localStoreMessages');
|
||||||
|
|
||||||
imapDeleteStub = sinon.stub(emailSync, '_imapDeleteMessage').withArgs({
|
imapDeleteStub = sinon.stub(emailSync, '_imapDeleteMessage').withArgs({
|
||||||
folder: folder,
|
folder: folder,
|
||||||
uid: verificationMail.uid
|
uid: verificationMail.uid
|
||||||
@ -1012,7 +1015,10 @@ define(function(require) {
|
|||||||
answered: true
|
answered: true
|
||||||
}).yields(null, []);
|
}).yields(null, []);
|
||||||
imapListMessagesStub = sinon.stub(emailSync, '_imapListMessages').yields(null, [verificationMail]);
|
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();
|
keychainStub.verifyPublicKey.withArgs(verificationUuid).yields();
|
||||||
imapDeleteStub = sinon.stub(emailSync, '_imapDeleteMessage').yields({});
|
imapDeleteStub = sinon.stub(emailSync, '_imapDeleteMessage').yields({});
|
||||||
|
|
||||||
@ -1073,7 +1079,10 @@ define(function(require) {
|
|||||||
answered: true
|
answered: true
|
||||||
}).yields(null, []);
|
}).yields(null, []);
|
||||||
imapListMessagesStub = sinon.stub(emailSync, '_imapListMessages').yields(null, [verificationMail]);
|
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({
|
keychainStub.verifyPublicKey.withArgs(verificationUuid).yields({
|
||||||
errMsg: 'fubar'
|
errMsg: 'fubar'
|
||||||
});
|
});
|
||||||
@ -1134,7 +1143,7 @@ define(function(require) {
|
|||||||
imapSearchStub = sinon.stub(emailSync, '_imapSearch');
|
imapSearchStub = sinon.stub(emailSync, '_imapSearch');
|
||||||
imapSearchStub.withArgs({
|
imapSearchStub.withArgs({
|
||||||
folder: folder
|
folder: folder
|
||||||
}).yields(null, [corruptedVerificationMail.uid]);
|
}).yields(null, [verificationMail.uid]);
|
||||||
imapSearchStub.withArgs({
|
imapSearchStub.withArgs({
|
||||||
folder: folder,
|
folder: folder,
|
||||||
unread: true
|
unread: true
|
||||||
@ -1146,12 +1155,15 @@ define(function(require) {
|
|||||||
|
|
||||||
localStoreStub = sinon.stub(emailSync, '_localStoreMessages').withArgs({
|
localStoreStub = sinon.stub(emailSync, '_localStoreMessages').withArgs({
|
||||||
folder: folder,
|
folder: folder,
|
||||||
emails: [corruptedVerificationMail]
|
emails: [verificationMail]
|
||||||
}).yields();
|
}).yields();
|
||||||
|
|
||||||
|
|
||||||
imapListMessagesStub = sinon.stub(emailSync, '_imapListMessages').yields(null, [corruptedVerificationMail]);
|
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/' + corruptedVerificationUuid,
|
||||||
|
}]);
|
||||||
keychainStub.verifyPublicKey.withArgs(corruptedVerificationUuid).yields({
|
keychainStub.verifyPublicKey.withArgs(corruptedVerificationUuid).yields({
|
||||||
errMsg: 'fubar'
|
errMsg: 'fubar'
|
||||||
});
|
});
|
||||||
@ -1602,19 +1614,16 @@ define(function(require) {
|
|||||||
|
|
||||||
describe('mark', function() {
|
describe('mark', function() {
|
||||||
it('should work', function(done) {
|
it('should work', function(done) {
|
||||||
imapClientStub.updateFlags.withArgs({
|
var o = {
|
||||||
path: 'asdf',
|
|
||||||
uid: 1,
|
|
||||||
unread: false,
|
|
||||||
answered: false
|
|
||||||
}).yields();
|
|
||||||
|
|
||||||
emailSync._imapMark({
|
|
||||||
folder: 'asdf',
|
folder: 'asdf',
|
||||||
uid: 1,
|
uid: 1,
|
||||||
unread: false,
|
unread: false,
|
||||||
answered: false
|
answered: false
|
||||||
}, function(err) {
|
};
|
||||||
|
|
||||||
|
imapClientStub.updateFlags.withArgs(o).yields();
|
||||||
|
|
||||||
|
emailSync._imapMark(o, function(err) {
|
||||||
expect(imapClientStub.updateFlags.calledOnce).to.be.true;
|
expect(imapClientStub.updateFlags.calledOnce).to.be.true;
|
||||||
expect(err).to.not.exist;
|
expect(err).to.not.exist;
|
||||||
done();
|
done();
|
||||||
|
@ -6,11 +6,11 @@ define(function(require) {
|
|||||||
UpdateHandler = require('js/util/update/update-handler'),
|
UpdateHandler = require('js/util/update/update-handler'),
|
||||||
expect = chai.expect;
|
expect = chai.expect;
|
||||||
|
|
||||||
chai.Assertion.includeStack = true;
|
|
||||||
|
|
||||||
describe('UpdateHandler', function() {
|
describe('UpdateHandler', function() {
|
||||||
var updateHandler, appConfigStorageStub, userStorageStub, origDbVersion;
|
var updateHandler, appConfigStorageStub, userStorageStub, origDbVersion;
|
||||||
|
|
||||||
|
chai.Assertion.includeStack = true;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
origDbVersion = cfg.dbVersion;
|
origDbVersion = cfg.dbVersion;
|
||||||
appConfigStorageStub = sinon.createStubInstance(DeviceStorageDAO);
|
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