mirror of
https://github.com/moparisthebest/mail
synced 2024-11-22 17:02:17 -05:00
introduce email dao 2
This commit is contained in:
parent
81a56a77c0
commit
1f89219353
724
src/js/dao/email-dao-2.js
Normal file
724
src/js/dao/email-dao-2.js
Normal file
@ -0,0 +1,724 @@
|
|||||||
|
define(function(require) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var util = require('cryptoLib/util');
|
||||||
|
// _ = require('underscore'),
|
||||||
|
// str = require('js/app-config').string,
|
||||||
|
// config = require('js/app-config').config;
|
||||||
|
|
||||||
|
var EmailDAO = function(keychain, imapClient, smtpClient, crypto, devicestorage) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
self._keychain = keychain;
|
||||||
|
self._imapClient = imapClient;
|
||||||
|
self._smtpClient = smtpClient;
|
||||||
|
self._crypto = crypto;
|
||||||
|
self._devicestorage = devicestorage;
|
||||||
|
|
||||||
|
// delegation-esque pattern to mitigate between node-style events and plain js
|
||||||
|
self._imapClient.onIncomingMessage = function(message) {
|
||||||
|
if (typeof self.onIncomingMessage === 'function') {
|
||||||
|
self.onIncomingMessage(message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Housekeeping APIs
|
||||||
|
//
|
||||||
|
|
||||||
|
EmailDAO.prototype.init = function(options, callback) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
self._account = options.account;
|
||||||
|
|
||||||
|
// validate email address
|
||||||
|
var emailAddress = self._account.emailAddress;
|
||||||
|
if (!util.validateEmailAddress(emailAddress)) {
|
||||||
|
callback({
|
||||||
|
errMsg: 'The user email address must be specified!'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init keychain and then crypto module
|
||||||
|
initKeychain();
|
||||||
|
|
||||||
|
function initKeychain() {
|
||||||
|
// init user's local database
|
||||||
|
self._devicestorage.init(emailAddress, function() {
|
||||||
|
// call getUserKeyPair to read/sync keypair with devicestorage/cloud
|
||||||
|
self._keychain.getUserKeyPair(emailAddress, function(err, storedKeypair) {
|
||||||
|
if (err) {
|
||||||
|
callback(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callback(null, storedKeypair);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
EmailDAO.prototype.unlock = function(options, callback) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
if (options.keypair) {
|
||||||
|
// import existing key pair into crypto module
|
||||||
|
self._crypto.importKeys({
|
||||||
|
passphrase: options.passphrase,
|
||||||
|
privateKeyArmored: options.keypair.privateKey.encryptedKey,
|
||||||
|
publicKeyArmored: options.keypair.publicKey.publicKey
|
||||||
|
}, callback);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// no keypair for is stored for the user... generate a new one
|
||||||
|
self._crypto.generateKeys({
|
||||||
|
emailAddress: self._account.emailAddress,
|
||||||
|
keySize: self._account.asymKeySize,
|
||||||
|
passphrase: options.passphrase
|
||||||
|
}, function(err, generatedKeypair) {
|
||||||
|
if (err) {
|
||||||
|
callback(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// import the new key pair into crypto module
|
||||||
|
self._crypto.importKeys({
|
||||||
|
passphrase: options.passphrase,
|
||||||
|
privateKeyArmored: generatedKeypair.privateKeyArmored,
|
||||||
|
publicKeyArmored: generatedKeypair.publicKeyArmored
|
||||||
|
}, function(err) {
|
||||||
|
if (err) {
|
||||||
|
callback(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// persist newly generated keypair
|
||||||
|
var newKeypair = {
|
||||||
|
publicKey: {
|
||||||
|
_id: generatedKeypair.keyId,
|
||||||
|
userId: self._account.emailAddress,
|
||||||
|
publicKey: generatedKeypair.publicKeyArmored
|
||||||
|
},
|
||||||
|
privateKey: {
|
||||||
|
_id: generatedKeypair.keyId,
|
||||||
|
userId: self._account.emailAddress,
|
||||||
|
encryptedKey: generatedKeypair.privateKeyArmored
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self._keychain.putUserKeyPair(newKeypair, callback);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// //
|
||||||
|
// // IMAP Apis
|
||||||
|
// //
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Login the imap client
|
||||||
|
// */
|
||||||
|
// EmailDAO.prototype.imapLogin = function(callback) {
|
||||||
|
// var self = this;
|
||||||
|
|
||||||
|
// // login IMAP client if existent
|
||||||
|
// self._imapClient.login(callback);
|
||||||
|
// };
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Cleanup by logging the user off.
|
||||||
|
// */
|
||||||
|
// EmailDAO.prototype.destroy = function(callback) {
|
||||||
|
// var self = this;
|
||||||
|
|
||||||
|
// self._imapClient.logout(callback);
|
||||||
|
// };
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * List the folders in the user's IMAP mailbox.
|
||||||
|
// */
|
||||||
|
// EmailDAO.prototype.imapListFolders = function(callback) {
|
||||||
|
// var self = this,
|
||||||
|
// dbType = 'folders';
|
||||||
|
|
||||||
|
// // check local cache
|
||||||
|
// self._devicestorage.listItems(dbType, 0, null, function(err, stored) {
|
||||||
|
// if (err) {
|
||||||
|
// callback(err);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!stored || stored.length < 1) {
|
||||||
|
// // no folders cached... fetch from server
|
||||||
|
// fetchFromServer();
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// callback(null, stored[0]);
|
||||||
|
// });
|
||||||
|
|
||||||
|
// function fetchFromServer() {
|
||||||
|
// var folders;
|
||||||
|
|
||||||
|
// // fetch list from imap server
|
||||||
|
// self._imapClient.listWellKnownFolders(function(err, wellKnownFolders) {
|
||||||
|
// if (err) {
|
||||||
|
// callback(err);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// folders = [
|
||||||
|
// wellKnownFolders.inbox,
|
||||||
|
// wellKnownFolders.sent, {
|
||||||
|
// type: 'Outbox',
|
||||||
|
// path: 'OUTBOX'
|
||||||
|
// },
|
||||||
|
// wellKnownFolders.drafts,
|
||||||
|
// wellKnownFolders.trash
|
||||||
|
// ];
|
||||||
|
|
||||||
|
// // cache locally
|
||||||
|
// // persist encrypted list in device storage
|
||||||
|
// self._devicestorage.storeList([folders], dbType, function(err) {
|
||||||
|
// if (err) {
|
||||||
|
// callback(err);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// callback(null, folders);
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Get the number of unread message for a folder
|
||||||
|
// */
|
||||||
|
// EmailDAO.prototype.unreadMessages = function(path, callback) {
|
||||||
|
// var self = this;
|
||||||
|
|
||||||
|
// self._imapClient.unreadMessages(path, callback);
|
||||||
|
// };
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Fetch a list of emails from the device's local storage
|
||||||
|
// */
|
||||||
|
// EmailDAO.prototype.listMessages = function(options, callback) {
|
||||||
|
// var self = this,
|
||||||
|
// cleartextList = [];
|
||||||
|
|
||||||
|
// // validate options
|
||||||
|
// if (!options.folder) {
|
||||||
|
// callback({
|
||||||
|
// errMsg: 'Invalid options!'
|
||||||
|
// });
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// options.offset = (typeof options.offset === 'undefined') ? 0 : options.offset;
|
||||||
|
// options.num = (typeof options.num === 'undefined') ? null : options.num;
|
||||||
|
|
||||||
|
// // fetch items from device storage
|
||||||
|
// self._devicestorage.listItems('email_' + options.folder, options.offset, options.num, function(err, emails) {
|
||||||
|
// if (err) {
|
||||||
|
// callback(err);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (emails.length === 0) {
|
||||||
|
// callback(null, cleartextList);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// var after = _.after(emails.length, function() {
|
||||||
|
// callback(null, cleartextList);
|
||||||
|
// });
|
||||||
|
|
||||||
|
// _.each(emails, function(email) {
|
||||||
|
// handleMail(email, after);
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
|
// function handleMail(email, localCallback) {
|
||||||
|
// // remove subject filter prefix
|
||||||
|
// email.subject = email.subject.split(str.subjectPrefix)[1];
|
||||||
|
|
||||||
|
// // encrypted mail
|
||||||
|
// if (isPGPMail(email)) {
|
||||||
|
// email = parseMessageBlock(email);
|
||||||
|
// decrypt(email, localCallback);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // verification mail
|
||||||
|
// if (isVerificationMail(email)) {
|
||||||
|
// verify(email, localCallback);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // cleartext mail
|
||||||
|
// cleartextList.push(email);
|
||||||
|
// localCallback();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// function isPGPMail(email) {
|
||||||
|
// return typeof email.body === 'string' && email.body.indexOf(str.cryptPrefix) !== -1 && email.body.indexOf(str.cryptSuffix) !== -1;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// function isVerificationMail(email) {
|
||||||
|
// return email.subject === str.verificationSubject;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// function parseMessageBlock(email) {
|
||||||
|
// var messageBlock;
|
||||||
|
|
||||||
|
// // parse email body for encrypted message block
|
||||||
|
// // get ascii armored message block by prefix and suffix
|
||||||
|
// messageBlock = email.body.split(str.cryptPrefix)[1].split(str.cryptSuffix)[0];
|
||||||
|
// // add prefix and suffix again
|
||||||
|
// email.body = str.cryptPrefix + messageBlock + str.cryptSuffix;
|
||||||
|
|
||||||
|
// return email;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// function decrypt(email, localCallback) {
|
||||||
|
// // fetch public key required to verify signatures
|
||||||
|
// var sender = email.from[0].address;
|
||||||
|
// self._keychain.getReceiverPublicKey(sender, function(err, senderPubkey) {
|
||||||
|
// if (err) {
|
||||||
|
// callback(err);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!senderPubkey) {
|
||||||
|
// // this should only happen if a mail from another channel is in the inbox
|
||||||
|
// setBodyAndContinue('Public key for sender not found!');
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // decrypt and verfiy signatures
|
||||||
|
// self._pgp.decrypt(email.body, senderPubkey.publicKey, function(err, decrypted) {
|
||||||
|
// if (err) {
|
||||||
|
// decrypted = err.errMsg;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// setBodyAndContinue(decrypted);
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
|
// function setBodyAndContinue(text) {
|
||||||
|
// email.body = text;
|
||||||
|
// cleartextList.push(email);
|
||||||
|
// localCallback();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// function verify(email, localCallback) {
|
||||||
|
// var uuid, index,
|
||||||
|
// verifiyUrlPrefix = config.cloudUrl + config.verificationUrl;
|
||||||
|
|
||||||
|
// if (!email.unread) {
|
||||||
|
// // don't bother if the email was already marked as read
|
||||||
|
// localCallback();
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// index = email.body.indexOf(verifiyUrlPrefix);
|
||||||
|
// if (index === -1) {
|
||||||
|
// localCallback();
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// uuid = email.body.split(config.verificationUrl)[1];
|
||||||
|
// self._keychain.verifyPublicKey(uuid, function(err) {
|
||||||
|
// if (err) {
|
||||||
|
// callback({
|
||||||
|
// errMsg: 'Verifying your public key failed: ' + err.errMsg
|
||||||
|
// });
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // public key has been verified, mark the message as read, delete it, and ignore it in the future
|
||||||
|
// self.imapMarkMessageRead({
|
||||||
|
// folder: options.folder,
|
||||||
|
// uid: email.uid
|
||||||
|
// }, function(err) {
|
||||||
|
// if (err) {
|
||||||
|
// // if marking the mail as read failed, don't bother
|
||||||
|
// localCallback();
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// self.imapDeleteMessage({
|
||||||
|
// folder: options.folder,
|
||||||
|
// uid: email.uid
|
||||||
|
// }, function() {
|
||||||
|
// localCallback();
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * High level sync operation for the delta from the user's IMAP inbox
|
||||||
|
// */
|
||||||
|
// EmailDAO.prototype.imapSync = function(options, callback) {
|
||||||
|
// var self = this,
|
||||||
|
// dbType = 'email_' + options.folder;
|
||||||
|
|
||||||
|
// fetchList(function(err, emails) {
|
||||||
|
// if (err) {
|
||||||
|
// callback(err);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // delete old items from db
|
||||||
|
// self._devicestorage.removeList(dbType, function(err) {
|
||||||
|
// if (err) {
|
||||||
|
// callback(err);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // persist encrypted list in device storage
|
||||||
|
// self._devicestorage.storeList(emails, dbType, callback);
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
|
// function fetchList(callback) {
|
||||||
|
// var headers = [];
|
||||||
|
|
||||||
|
// // fetch imap folder's message list
|
||||||
|
// self.imapListMessages({
|
||||||
|
// folder: options.folder,
|
||||||
|
// offset: options.offset,
|
||||||
|
// num: options.num
|
||||||
|
// }, function(err, emails) {
|
||||||
|
// if (err) {
|
||||||
|
// callback(err);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // find encrypted messages by subject
|
||||||
|
// emails.forEach(function(i) {
|
||||||
|
// if (typeof i.subject === 'string' && i.subject.indexOf(str.subjectPrefix) !== -1) {
|
||||||
|
// headers.push(i);
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
// // fetch message bodies
|
||||||
|
// fetchBodies(headers, callback);
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// function fetchBodies(headers, callback) {
|
||||||
|
// var emails = [];
|
||||||
|
|
||||||
|
// if (headers.length < 1) {
|
||||||
|
// callback(null, emails);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// var after = _.after(headers.length, function() {
|
||||||
|
// callback(null, emails);
|
||||||
|
// });
|
||||||
|
|
||||||
|
// _.each(headers, function(header) {
|
||||||
|
// self.imapGetMessage({
|
||||||
|
// folder: options.folder,
|
||||||
|
// uid: header.uid
|
||||||
|
// }, function(err, message) {
|
||||||
|
// if (err) {
|
||||||
|
// callback(err);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // set gotten attributes like body to message object containing list meta data like 'unread' or 'replied'
|
||||||
|
// header.id = message.id;
|
||||||
|
// header.body = message.body;
|
||||||
|
// header.html = message.html;
|
||||||
|
// header.attachments = message.attachments;
|
||||||
|
|
||||||
|
// emails.push(header);
|
||||||
|
// after();
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * List messages from an imap folder. This will not yet fetch the email body.
|
||||||
|
// * @param {String} options.folderName The name of the imap folder.
|
||||||
|
// * @param {Number} options.offset The offset of items to fetch (0 is the last stored item)
|
||||||
|
// * @param {Number} options.num The number of items to fetch (null means fetch all)
|
||||||
|
// */
|
||||||
|
// EmailDAO.prototype.imapListMessages = function(options, callback) {
|
||||||
|
// var self = this;
|
||||||
|
|
||||||
|
// self._imapClient.listMessages({
|
||||||
|
// path: options.folder,
|
||||||
|
// offset: options.offset,
|
||||||
|
// length: options.num
|
||||||
|
// }, callback);
|
||||||
|
// };
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Get an email messsage including the email body from imap
|
||||||
|
// * @param {String} options.messageId The
|
||||||
|
// */
|
||||||
|
// EmailDAO.prototype.imapGetMessage = function(options, callback) {
|
||||||
|
// var self = this;
|
||||||
|
|
||||||
|
// self._imapClient.getMessagePreview({
|
||||||
|
// path: options.folder,
|
||||||
|
// uid: options.uid
|
||||||
|
// }, callback);
|
||||||
|
// };
|
||||||
|
|
||||||
|
// EmailDAO.prototype.imapMoveMessage = function(options, callback) {
|
||||||
|
// var self = this;
|
||||||
|
|
||||||
|
// self._imapClient.moveMessage({
|
||||||
|
// path: options.folder,
|
||||||
|
// uid: options.uid,
|
||||||
|
// destination: options.destination
|
||||||
|
// }, moved);
|
||||||
|
|
||||||
|
// function moved(err) {
|
||||||
|
// if (err) {
|
||||||
|
// callback(err);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // delete from local db
|
||||||
|
// self._devicestorage.removeList('email_' + options.folder + '_' + options.uid, callback);
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
// EmailDAO.prototype.imapDeleteMessage = function(options, callback) {
|
||||||
|
// var self = this;
|
||||||
|
|
||||||
|
// self._imapClient.deleteMessage({
|
||||||
|
// path: options.folder,
|
||||||
|
// uid: options.uid
|
||||||
|
// }, moved);
|
||||||
|
|
||||||
|
// function moved(err) {
|
||||||
|
// if (err) {
|
||||||
|
// callback(err);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // delete from local db
|
||||||
|
// self._devicestorage.removeList('email_' + options.folder + '_' + options.uid, callback);
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
// EmailDAO.prototype.imapMarkMessageRead = function(options, callback) {
|
||||||
|
// var self = this;
|
||||||
|
|
||||||
|
// self._imapClient.updateFlags({
|
||||||
|
// path: options.folder,
|
||||||
|
// uid: options.uid,
|
||||||
|
// unread: false
|
||||||
|
// }, callback);
|
||||||
|
// };
|
||||||
|
|
||||||
|
// EmailDAO.prototype.imapMarkAnswered = function(options, callback) {
|
||||||
|
// var self = this;
|
||||||
|
|
||||||
|
// self._imapClient.updateFlags({
|
||||||
|
// path: options.folder,
|
||||||
|
// uid: options.uid,
|
||||||
|
// answered: true
|
||||||
|
// }, callback);
|
||||||
|
// };
|
||||||
|
|
||||||
|
// //
|
||||||
|
// // SMTP Apis
|
||||||
|
// //
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Send an email client side via STMP.
|
||||||
|
// */
|
||||||
|
// EmailDAO.prototype.encryptedSend = function(email, callback) {
|
||||||
|
// var self = this,
|
||||||
|
// invalidRecipient;
|
||||||
|
|
||||||
|
// // validate the email input
|
||||||
|
// if (!email.to || !email.from || !email.to[0].address || !email.from[0].address) {
|
||||||
|
// callback({
|
||||||
|
// errMsg: 'Invalid email object!'
|
||||||
|
// });
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // validate email addresses
|
||||||
|
// _.each(email.to, function(i) {
|
||||||
|
// if (!util.validateEmailAddress(i.address)) {
|
||||||
|
// invalidRecipient = i.address;
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// if (invalidRecipient) {
|
||||||
|
// callback({
|
||||||
|
// errMsg: 'Invalid recipient: ' + invalidRecipient
|
||||||
|
// });
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// if (!util.validateEmailAddress(email.from[0].address)) {
|
||||||
|
// callback({
|
||||||
|
// errMsg: 'Invalid sender: ' + email.from
|
||||||
|
// });
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // only support single recipient for e-2-e encryption
|
||||||
|
// // check if receiver has a public key
|
||||||
|
// self._keychain.getReceiverPublicKey(email.to[0].address, function(err, receiverPubkey) {
|
||||||
|
// if (err) {
|
||||||
|
// callback(err);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // validate public key
|
||||||
|
// if (!receiverPubkey) {
|
||||||
|
// callback({
|
||||||
|
// errMsg: 'User has no public key yet!'
|
||||||
|
// });
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // public key found... encrypt and send
|
||||||
|
// self.encryptForUser({
|
||||||
|
// email: email,
|
||||||
|
// receiverPubkey: receiverPubkey.publicKey
|
||||||
|
// }, function(err, email) {
|
||||||
|
// if (err) {
|
||||||
|
// callback(err);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// self.send(email, callback);
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// };
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Encrypt an email asymmetrically for an exisiting user with their public key
|
||||||
|
// */
|
||||||
|
// EmailDAO.prototype.encryptForUser = function(options, callback) {
|
||||||
|
// var self = this,
|
||||||
|
// pt = options.email.body,
|
||||||
|
// receiverPubkeys = options.receiverPubkey ? [options.receiverPubkey] : [];
|
||||||
|
|
||||||
|
// // get own public key so send message can be read
|
||||||
|
// self._pgp.exportKeys(function(err, ownKeys) {
|
||||||
|
// if (err) {
|
||||||
|
// callback(err);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // add own public key to receiver list
|
||||||
|
// receiverPubkeys.push(ownKeys.publicKeyArmored);
|
||||||
|
// // encrypt the email
|
||||||
|
// self._pgp.encrypt(pt, receiverPubkeys, function(err, ct) {
|
||||||
|
// if (err) {
|
||||||
|
// callback(err);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // bundle encrypted email together for sending
|
||||||
|
// frameEncryptedMessage(options.email, ct);
|
||||||
|
// callback(null, options.email);
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// };
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Frames an encrypted message in base64 Format.
|
||||||
|
// */
|
||||||
|
// function frameEncryptedMessage(email, ct) {
|
||||||
|
// var to, greeting;
|
||||||
|
|
||||||
|
// var MESSAGE = str.message + '\n\n\n',
|
||||||
|
// SIGNATURE = '\n\n' + str.signature + '\n\n';
|
||||||
|
|
||||||
|
// // get first name of recipient
|
||||||
|
// to = (email.to[0].name || email.to[0].address).split('@')[0].split('.')[0].split(' ')[0];
|
||||||
|
// greeting = 'Hi ' + to + ',\n\n';
|
||||||
|
|
||||||
|
// // build encrypted text body
|
||||||
|
// email.body = greeting + MESSAGE + ct + SIGNATURE;
|
||||||
|
// email.subject = str.subjectPrefix + email.subject;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Send an actual message object via smtp
|
||||||
|
// */
|
||||||
|
// EmailDAO.prototype.send = function(email, callback) {
|
||||||
|
// var self = this;
|
||||||
|
|
||||||
|
// self._smtpClient.send(email, callback);
|
||||||
|
// };
|
||||||
|
|
||||||
|
// EmailDAO.prototype.store = function(email, callback) {
|
||||||
|
// var self = this,
|
||||||
|
// dbType = 'email_OUTBOX';
|
||||||
|
|
||||||
|
// email.id = util.UUID();
|
||||||
|
|
||||||
|
// // encrypt
|
||||||
|
// self.encryptForUser({
|
||||||
|
// email: email
|
||||||
|
// }, function(err, email) {
|
||||||
|
// if (err) {
|
||||||
|
// callback(err);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // store to local storage
|
||||||
|
// self._devicestorage.storeList([email], dbType, callback);
|
||||||
|
// });
|
||||||
|
// };
|
||||||
|
|
||||||
|
// EmailDAO.prototype.list = function(callback) {
|
||||||
|
// var self = this,
|
||||||
|
// dbType = 'email_OUTBOX';
|
||||||
|
|
||||||
|
// self._devicestorage.listItems(dbType, 0, null, function(err, mails) {
|
||||||
|
// if (err) {
|
||||||
|
// callback(err);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (mails.length === 0) {
|
||||||
|
// callback(null, []);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// self._pgp.exportKeys(function(err, ownKeys) {
|
||||||
|
// if (err) {
|
||||||
|
// callback(err);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// var after = _.after(mails.length, function() {
|
||||||
|
// callback(null, mails);
|
||||||
|
// });
|
||||||
|
|
||||||
|
// mails.forEach(function(mail) {
|
||||||
|
// mail.body = str.cryptPrefix + mail.body.split(str.cryptPrefix)[1].split(str.cryptSuffix)[0] + str.cryptSuffix;
|
||||||
|
// self._pgp.decrypt(mail.body, ownKeys.publicKeyArmored, function(err, decrypted) {
|
||||||
|
// mail.body = err ? err.errMsg : decrypted;
|
||||||
|
// mail.subject = mail.subject.split(str.subjectPrefix)[1];
|
||||||
|
// after();
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// };
|
||||||
|
|
||||||
|
return EmailDAO;
|
||||||
|
});
|
221
test/new-unit/email-dao-2-test.js
Normal file
221
test/new-unit/email-dao-2-test.js
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
define(function(require) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var EmailDAO = require('js/dao/email-dao-2'),
|
||||||
|
KeychainDAO = require('js/dao/keychain-dao'),
|
||||||
|
ImapClient = require('imap-client'),
|
||||||
|
SmtpClient = require('smtp-client'),
|
||||||
|
PGP = require('js/crypto/pgp'),
|
||||||
|
DeviceStorageDAO = require('js/dao/devicestorage-dao'),
|
||||||
|
expect = chai.expect;
|
||||||
|
|
||||||
|
|
||||||
|
describe('Email DAO 2 unit tests', function() {
|
||||||
|
var dao, keychainStub, imapClientStub, smtpClientStub, pgpStub, devicestorageStub;
|
||||||
|
|
||||||
|
var emailAddress = 'asdf@asdf.com',
|
||||||
|
passphrase = 'asdf',
|
||||||
|
asymKeySize = 2048,
|
||||||
|
mockkeyId = 1234,
|
||||||
|
mockKeyPair = {
|
||||||
|
publicKey: {
|
||||||
|
_id: mockkeyId,
|
||||||
|
userId: emailAddress,
|
||||||
|
publicKey: 'publicpublicpublicpublic'
|
||||||
|
},
|
||||||
|
privateKey: {
|
||||||
|
_id: mockkeyId,
|
||||||
|
userId: emailAddress,
|
||||||
|
encryptedKey: 'privateprivateprivateprivate'
|
||||||
|
}
|
||||||
|
}, account = {
|
||||||
|
emailAddress: emailAddress,
|
||||||
|
asymKeySize: asymKeySize,
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
keychainStub = sinon.createStubInstance(KeychainDAO);
|
||||||
|
imapClientStub = sinon.createStubInstance(ImapClient);
|
||||||
|
smtpClientStub = sinon.createStubInstance(SmtpClient);
|
||||||
|
pgpStub = sinon.createStubInstance(PGP);
|
||||||
|
devicestorageStub = sinon.createStubInstance(DeviceStorageDAO);
|
||||||
|
|
||||||
|
dao = new EmailDAO(keychainStub, imapClientStub, smtpClientStub, pgpStub, devicestorageStub);
|
||||||
|
dao._account = account;
|
||||||
|
|
||||||
|
expect(dao._keychain).to.equal(keychainStub);
|
||||||
|
expect(dao._imapClient).to.equal(imapClientStub);
|
||||||
|
expect(dao._smtpClient).to.equal(smtpClientStub);
|
||||||
|
expect(dao._crypto).to.equal(pgpStub);
|
||||||
|
expect(dao._devicestorage).to.equal(devicestorageStub);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function() {});
|
||||||
|
|
||||||
|
describe('init', function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
delete dao._account;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should init', function(done) {
|
||||||
|
devicestorageStub.init.withArgs(emailAddress).yields();
|
||||||
|
keychainStub.getUserKeyPair.yields(null, mockKeyPair);
|
||||||
|
|
||||||
|
dao.init({
|
||||||
|
account: account
|
||||||
|
}, function(err, keyPair) {
|
||||||
|
expect(err).to.not.exist;
|
||||||
|
expect(keyPair).to.equal(mockKeyPair);
|
||||||
|
|
||||||
|
expect(dao._account).to.equal(account);
|
||||||
|
expect(devicestorageStub.init.calledOnce).to.be.true;
|
||||||
|
expect(keychainStub.getUserKeyPair.calledOnce).to.be.true;
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail due to error in getUserKeyPair', function(done) {
|
||||||
|
devicestorageStub.init.yields();
|
||||||
|
keychainStub.getUserKeyPair.yields({});
|
||||||
|
|
||||||
|
dao.init({
|
||||||
|
account: account
|
||||||
|
}, function(err, keyPair) {
|
||||||
|
expect(err).to.exist;
|
||||||
|
expect(keyPair).to.not.exist;
|
||||||
|
|
||||||
|
expect(devicestorageStub.init.calledOnce).to.be.true;
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('unlock', function() {
|
||||||
|
it('should unlock', function(done) {
|
||||||
|
var importMatcher = sinon.match(function(o) {
|
||||||
|
expect(o.passphrase).to.equal(passphrase);
|
||||||
|
expect(o.privateKeyArmored).to.equal(mockKeyPair.privateKey.encryptedKey);
|
||||||
|
expect(o.publicKeyArmored).to.equal(mockKeyPair.publicKey.publicKey);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
pgpStub.importKeys.withArgs(importMatcher).yields();
|
||||||
|
|
||||||
|
dao.unlock({
|
||||||
|
passphrase: passphrase,
|
||||||
|
keypair: mockKeyPair
|
||||||
|
}, function(err) {
|
||||||
|
expect(err).to.not.exist;
|
||||||
|
|
||||||
|
expect(pgpStub.importKeys.calledOnce).to.be.true;
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate a keypair and unlock', function(done) {
|
||||||
|
var genKeysMatcher, persistKeysMatcher, importMatcher, keypair;
|
||||||
|
|
||||||
|
keypair = {
|
||||||
|
keyId: 123,
|
||||||
|
publicKeyArmored: mockKeyPair.publicKey.publicKey,
|
||||||
|
privateKeyArmored: mockKeyPair.privateKey.encryptedKey
|
||||||
|
};
|
||||||
|
genKeysMatcher = sinon.match(function(o) {
|
||||||
|
expect(o.emailAddress).to.equal(emailAddress);
|
||||||
|
expect(o.keySize).to.equal(asymKeySize);
|
||||||
|
expect(o.passphrase).to.equal(passphrase);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
importMatcher = sinon.match(function(o) {
|
||||||
|
expect(o.passphrase).to.equal(passphrase);
|
||||||
|
expect(o.privateKeyArmored).to.equal(mockKeyPair.privateKey.encryptedKey);
|
||||||
|
expect(o.publicKeyArmored).to.equal(mockKeyPair.publicKey.publicKey);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
persistKeysMatcher = sinon.match(function(o) {
|
||||||
|
expect(o).to.deep.equal(mockKeyPair);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
pgpStub.generateKeys.withArgs(genKeysMatcher).yields(null, keypair);
|
||||||
|
pgpStub.importKeys.withArgs(importMatcher).yields();
|
||||||
|
keychainStub.putUserKeyPair.withArgs().yields();
|
||||||
|
|
||||||
|
dao.unlock({
|
||||||
|
passphrase: passphrase
|
||||||
|
}, function(err) {
|
||||||
|
expect(err).to.not.exist;
|
||||||
|
|
||||||
|
expect(pgpStub.generateKeys.calledOnce).to.be.true;
|
||||||
|
expect(pgpStub.importKeys.calledOnce).to.be.true;
|
||||||
|
expect(keychainStub.putUserKeyPair.calledOnce).to.be.true;
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail when persisting fails', function(done) {
|
||||||
|
var keypair = {
|
||||||
|
keyId: 123,
|
||||||
|
publicKeyArmored: 'qwerty',
|
||||||
|
privateKeyArmored: 'asdfgh'
|
||||||
|
};
|
||||||
|
pgpStub.generateKeys.yields(null, keypair);
|
||||||
|
pgpStub.importKeys.withArgs().yields();
|
||||||
|
keychainStub.putUserKeyPair.yields({});
|
||||||
|
|
||||||
|
dao.unlock({
|
||||||
|
passphrase: passphrase
|
||||||
|
}, function(err) {
|
||||||
|
expect(err).to.exist;
|
||||||
|
|
||||||
|
expect(pgpStub.generateKeys.calledOnce).to.be.true;
|
||||||
|
expect(pgpStub.importKeys.calledOnce).to.be.true;
|
||||||
|
expect(keychainStub.putUserKeyPair.calledOnce).to.be.true;
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail when import fails', function(done) {
|
||||||
|
var keypair = {
|
||||||
|
keyId: 123,
|
||||||
|
publicKeyArmored: 'qwerty',
|
||||||
|
privateKeyArmored: 'asdfgh'
|
||||||
|
};
|
||||||
|
|
||||||
|
pgpStub.generateKeys.withArgs().yields(null, keypair);
|
||||||
|
pgpStub.importKeys.withArgs().yields({});
|
||||||
|
|
||||||
|
dao.unlock({
|
||||||
|
passphrase: passphrase
|
||||||
|
}, function(err) {
|
||||||
|
expect(err).to.exist;
|
||||||
|
|
||||||
|
expect(pgpStub.generateKeys.calledOnce).to.be.true;
|
||||||
|
expect(pgpStub.importKeys.calledOnce).to.be.true;
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail when generation fails', function(done) {
|
||||||
|
pgpStub.generateKeys.yields({});
|
||||||
|
|
||||||
|
dao.unlock({
|
||||||
|
passphrase: passphrase
|
||||||
|
}, function(err) {
|
||||||
|
expect(err).to.exist;
|
||||||
|
|
||||||
|
expect(pgpStub.generateKeys.calledOnce).to.be.true;
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -31,6 +31,7 @@ function startTests() {
|
|||||||
require(
|
require(
|
||||||
[
|
[
|
||||||
'test/new-unit/email-dao-test',
|
'test/new-unit/email-dao-test',
|
||||||
|
'test/new-unit/email-dao-2-test',
|
||||||
'test/new-unit/app-controller-test',
|
'test/new-unit/app-controller-test',
|
||||||
'test/new-unit/pgp-test',
|
'test/new-unit/pgp-test',
|
||||||
'test/new-unit/rest-dao-test',
|
'test/new-unit/rest-dao-test',
|
||||||
|
Loading…
Reference in New Issue
Block a user