2013-08-19 15:13:32 -04:00
|
|
|
define(function(require) {
|
2013-08-16 14:50:47 -04:00
|
|
|
'use strict';
|
|
|
|
|
2013-08-19 15:13:32 -04:00
|
|
|
var _ = require('underscore'),
|
|
|
|
util = require('cryptoLib/util'),
|
2013-09-15 13:00:35 -04:00
|
|
|
str = require('js/app-config').string;
|
2013-08-27 13:17:06 -04:00
|
|
|
|
2013-08-19 15:13:32 -04:00
|
|
|
/**
|
|
|
|
* A high-level Data-Access Api for handling Email synchronization
|
|
|
|
* between the cloud service and the device's local storage
|
|
|
|
*/
|
2013-09-26 07:26:57 -04:00
|
|
|
var EmailDAO = function(keychain, imapClient, smtpClient, crypto, devicestorage) {
|
2013-08-16 14:50:47 -04:00
|
|
|
var self = this;
|
|
|
|
|
|
|
|
self._keychain = keychain;
|
|
|
|
self._imapClient = imapClient;
|
|
|
|
self._smtpClient = smtpClient;
|
2013-09-26 07:26:57 -04:00
|
|
|
self._crypto = crypto;
|
|
|
|
self._devicestorage = devicestorage;
|
2013-11-05 08:00:50 -05:00
|
|
|
|
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
};
|
2013-08-16 14:50:47 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Inits all dependencies
|
|
|
|
*/
|
2013-10-21 07:10:42 -04:00
|
|
|
EmailDAO.prototype.init = function(account, callback) {
|
2013-08-16 14:50:47 -04:00
|
|
|
var self = this;
|
|
|
|
|
2013-08-22 10:18:48 -04:00
|
|
|
self._account = account;
|
2013-08-16 14:50:47 -04:00
|
|
|
|
|
|
|
// validate email address
|
2013-08-22 10:18:48 -04:00
|
|
|
var emailAddress = self._account.emailAddress;
|
2013-08-30 05:42:32 -04:00
|
|
|
if (!util.validateEmailAddress(emailAddress)) {
|
2013-08-16 14:50:47 -04:00
|
|
|
callback({
|
|
|
|
errMsg: 'The user email address must be specified!'
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-09-26 11:37:56 -04:00
|
|
|
// init keychain and then crypto module
|
|
|
|
initKeychain();
|
2013-08-16 14:50:47 -04:00
|
|
|
|
|
|
|
function initKeychain() {
|
|
|
|
// init user's local database
|
2013-09-26 07:26:57 -04:00
|
|
|
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;
|
|
|
|
}
|
2013-10-21 07:10:42 -04:00
|
|
|
callback(null, storedKeypair);
|
2013-09-26 07:26:57 -04:00
|
|
|
});
|
2013-08-16 14:50:47 -04:00
|
|
|
});
|
|
|
|
}
|
2013-10-21 07:10:42 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
EmailDAO.prototype.unlock = function(keypair, passphrase, callback) {
|
|
|
|
var self = this;
|
2013-10-29 07:19:27 -04:00
|
|
|
|
2013-10-21 07:10:42 -04:00
|
|
|
if (keypair && keypair.privateKey && keypair.publicKey) {
|
|
|
|
// import existing key pair into crypto module
|
|
|
|
self._crypto.importKeys({
|
|
|
|
passphrase: passphrase,
|
|
|
|
privateKeyArmored: keypair.privateKey.encryptedKey,
|
|
|
|
publicKeyArmored: keypair.publicKey.publicKey
|
|
|
|
}, callback);
|
|
|
|
return;
|
|
|
|
}
|
2013-08-16 14:50:47 -04:00
|
|
|
|
2013-10-21 07:10:42 -04:00
|
|
|
// no keypair for is stored for the user... generate a new one
|
|
|
|
self._crypto.generateKeys({
|
|
|
|
emailAddress: self._account.emailAddress,
|
|
|
|
keySize: self._account.asymKeySize,
|
|
|
|
passphrase: passphrase
|
|
|
|
}, function(err, generatedKeypair) {
|
|
|
|
if (err) {
|
|
|
|
callback(err);
|
2013-10-11 21:19:01 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-10-21 07:10:42 -04:00
|
|
|
// import the new key pair into crypto module
|
|
|
|
self._crypto.importKeys({
|
|
|
|
passphrase: passphrase,
|
|
|
|
privateKeyArmored: generatedKeypair.privateKeyArmored,
|
|
|
|
publicKeyArmored: generatedKeypair.publicKeyArmored
|
|
|
|
}, function(err) {
|
2013-08-16 14:50:47 -04:00
|
|
|
if (err) {
|
|
|
|
callback(err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-10-21 07:10:42 -04:00
|
|
|
// 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
|
2013-10-11 21:19:01 -04:00
|
|
|
}
|
2013-10-21 07:10:42 -04:00
|
|
|
};
|
|
|
|
self._keychain.putUserKeyPair(newKeypair, callback);
|
2013-08-16 14:50:47 -04:00
|
|
|
});
|
2013-10-21 07:10:42 -04:00
|
|
|
});
|
2013-08-16 14:50:47 -04:00
|
|
|
};
|
|
|
|
|
2013-08-20 07:30:35 -04:00
|
|
|
//
|
2013-10-04 10:29:32 -04:00
|
|
|
// IMAP Apis
|
2013-08-20 07:30:35 -04:00
|
|
|
//
|
|
|
|
|
2013-09-26 11:37:56 -04:00
|
|
|
/**
|
|
|
|
* Login the imap client
|
|
|
|
*/
|
|
|
|
EmailDAO.prototype.imapLogin = function(callback) {
|
|
|
|
var self = this;
|
|
|
|
|
|
|
|
// login IMAP client if existent
|
|
|
|
self._imapClient.login(callback);
|
|
|
|
};
|
|
|
|
|
2013-08-20 07:30:35 -04:00
|
|
|
/**
|
|
|
|
* 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) {
|
2013-10-10 13:15:16 -04:00
|
|
|
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]);
|
|
|
|
});
|
2013-08-20 11:22:08 -04:00
|
|
|
|
2013-10-10 13:15:16 -04:00
|
|
|
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);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2013-09-20 12:44:14 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the number of unread message for a folder
|
|
|
|
*/
|
|
|
|
EmailDAO.prototype.unreadMessages = function(path, callback) {
|
|
|
|
var self = this;
|
|
|
|
|
|
|
|
self._imapClient.unreadMessages(path, callback);
|
2013-08-20 07:30:35 -04:00
|
|
|
};
|
|
|
|
|
2013-09-26 07:26:57 -04:00
|
|
|
/**
|
|
|
|
* Fetch a list of emails from the device's local storage
|
|
|
|
*/
|
|
|
|
EmailDAO.prototype.listMessages = function(options, callback) {
|
|
|
|
var self = this,
|
|
|
|
encryptedList = [];
|
|
|
|
|
|
|
|
// validate options
|
2013-10-12 13:39:09 -04:00
|
|
|
if (!options.folder) {
|
2013-09-26 07:26:57 -04:00
|
|
|
callback({
|
|
|
|
errMsg: 'Invalid options!'
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
2013-10-12 13:39:09 -04:00
|
|
|
options.offset = (typeof options.offset === 'undefined') ? 0 : options.offset;
|
|
|
|
options.num = (typeof options.num === 'undefined') ? null : options.num;
|
2013-09-26 07:26:57 -04:00
|
|
|
|
|
|
|
// fetch items from device storage
|
2013-09-26 09:48:32 -04:00
|
|
|
self._devicestorage.listItems('email_' + options.folder, options.offset, options.num, function(err, emails) {
|
2013-09-26 07:26:57 -04:00
|
|
|
if (err) {
|
|
|
|
callback(err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// find encrypted items
|
|
|
|
emails.forEach(function(i) {
|
2013-10-04 09:47:30 -04:00
|
|
|
if (typeof i.body === 'string' && i.body.indexOf(str.cryptPrefix) !== -1 && i.body.indexOf(str.cryptSuffix) !== -1) {
|
2013-09-26 07:26:57 -04:00
|
|
|
// parse ct object from ascii armored message block
|
|
|
|
encryptedList.push(parseMessageBlock(i));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2013-10-04 09:47:30 -04:00
|
|
|
if (encryptedList.length === 0) {
|
|
|
|
callback(null, []);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-09-26 07:26:57 -04:00
|
|
|
// decrypt items
|
2013-10-11 21:19:01 -04:00
|
|
|
decryptList(encryptedList, callback);
|
2013-09-26 07:26:57 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
function parseMessageBlock(email) {
|
2013-10-11 21:19:01 -04:00
|
|
|
var messageBlock;
|
2013-09-26 07:26:57 -04:00
|
|
|
|
|
|
|
// parse email body for encrypted message block
|
|
|
|
try {
|
2013-10-11 21:19:01 -04:00
|
|
|
// 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;
|
2013-10-29 10:22:19 -04:00
|
|
|
email.subject = email.subject.split(str.subjectPrefix)[1];
|
2013-09-26 07:26:57 -04:00
|
|
|
} catch (e) {
|
|
|
|
callback({
|
|
|
|
errMsg: 'Error parsing encrypted message block!'
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-10-11 21:19:01 -04:00
|
|
|
return email;
|
2013-09-26 07:26:57 -04:00
|
|
|
}
|
|
|
|
|
2013-10-11 21:19:01 -04:00
|
|
|
function decryptList(list, callback) {
|
|
|
|
var after = _.after(list.length, function() {
|
|
|
|
callback(null, list);
|
2013-09-26 07:26:57 -04:00
|
|
|
});
|
|
|
|
|
2013-10-11 21:19:01 -04:00
|
|
|
list.forEach(function(i) {
|
|
|
|
// gather public keys required to verify signatures
|
|
|
|
var sender = i.from[0].address;
|
2013-10-29 07:19:27 -04:00
|
|
|
self._keychain.getReceiverPublicKey(sender, function(err, senderPubkey) {
|
2013-09-26 07:26:57 -04:00
|
|
|
|
2013-10-11 21:19:01 -04:00
|
|
|
// decrypt and verfiy signatures
|
|
|
|
self._crypto.decrypt(i.body, senderPubkey.publicKey, function(err, decrypted) {
|
|
|
|
if (err) {
|
|
|
|
callback(err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-10-29 10:22:19 -04:00
|
|
|
i.body = decrypted;
|
2013-09-26 07:26:57 -04:00
|
|
|
|
2013-10-11 21:19:01 -04:00
|
|
|
after();
|
|
|
|
});
|
2013-09-26 07:26:57 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* High level sync operation for the delta from the user's IMAP inbox
|
|
|
|
*/
|
|
|
|
EmailDAO.prototype.imapSync = function(options, callback) {
|
2013-09-28 13:04:15 -04:00
|
|
|
var self = this,
|
|
|
|
dbType = 'email_' + options.folder;
|
2013-09-26 07:26:57 -04:00
|
|
|
|
2013-10-04 09:47:30 -04:00
|
|
|
fetchList(function(err, emails) {
|
|
|
|
if (err) {
|
|
|
|
callback(err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-09-28 13:04:15 -04:00
|
|
|
// delete old items from db
|
|
|
|
self._devicestorage.removeList(dbType, function(err) {
|
|
|
|
if (err) {
|
|
|
|
callback(err);
|
|
|
|
return;
|
|
|
|
}
|
2013-10-04 09:47:30 -04:00
|
|
|
|
2013-09-28 13:04:15 -04:00
|
|
|
// persist encrypted list in device storage
|
2013-10-04 09:47:30 -04:00
|
|
|
self._devicestorage.storeList(emails, dbType, callback);
|
2013-09-26 07:26:57 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2013-10-04 07:15:16 -04:00
|
|
|
function fetchList(callback) {
|
2013-10-29 10:22:19 -04:00
|
|
|
var headers = [];
|
2013-09-28 13:04:15 -04:00
|
|
|
|
2013-09-26 07:26:57 -04:00
|
|
|
// fetch imap folder's message list
|
|
|
|
self.imapListMessages({
|
|
|
|
folder: options.folder,
|
|
|
|
offset: options.offset,
|
|
|
|
num: options.num
|
|
|
|
}, function(err, emails) {
|
|
|
|
if (err) {
|
2013-10-04 09:47:30 -04:00
|
|
|
callback(err);
|
2013-09-26 07:26:57 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-09-28 13:04:15 -04:00
|
|
|
// find encrypted messages by subject
|
|
|
|
emails.forEach(function(i) {
|
2013-10-29 10:22:19 -04:00
|
|
|
if (i.subject.indexOf(str.subjectPrefix) !== -1) {
|
|
|
|
headers.push(i);
|
2013-09-28 13:04:15 -04:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2013-09-26 07:26:57 -04:00
|
|
|
// fetch message bodies
|
2013-10-29 10:22:19 -04:00
|
|
|
fetchBodies(headers, callback);
|
2013-09-26 07:26:57 -04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2013-10-29 10:22:19 -04:00
|
|
|
function fetchBodies(headers, callback) {
|
2013-09-26 07:26:57 -04:00
|
|
|
var emails = [];
|
|
|
|
|
2013-10-29 10:22:19 -04:00
|
|
|
if (headers.length < 1) {
|
2013-10-04 09:47:30 -04:00
|
|
|
callback(null, emails);
|
2013-09-28 13:04:15 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-10-29 10:22:19 -04:00
|
|
|
var after = _.after(headers.length, function() {
|
2013-10-04 09:47:30 -04:00
|
|
|
callback(null, emails);
|
2013-09-26 07:26:57 -04:00
|
|
|
});
|
|
|
|
|
2013-10-29 10:22:19 -04:00
|
|
|
_.each(headers, function(header) {
|
2013-09-26 07:26:57 -04:00
|
|
|
self.imapGetMessage({
|
2013-10-04 07:15:16 -04:00
|
|
|
folder: options.folder,
|
2013-10-29 10:22:19 -04:00
|
|
|
uid: header.uid
|
2013-09-26 07:26:57 -04:00
|
|
|
}, function(err, message) {
|
|
|
|
if (err) {
|
2013-10-04 09:47:30 -04:00
|
|
|
callback(err);
|
2013-09-26 07:26:57 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-10-29 10:22:19 -04:00
|
|
|
if (typeof message.body !== 'string' || message.body.indexOf(str.cryptPrefix) === -1 || message.body.indexOf(str.cryptSuffix) === -1) {
|
|
|
|
after();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-09-30 11:29:29 -04:00
|
|
|
// set gotten attributes like body to message object containing list meta data like 'unread' or 'replied'
|
2013-10-29 10:22:19 -04:00
|
|
|
header.id = message.id;
|
|
|
|
header.body = message.body;
|
|
|
|
header.html = message.html;
|
|
|
|
header.attachments = message.attachments;
|
2013-09-30 11:29:29 -04:00
|
|
|
|
2013-10-29 10:22:19 -04:00
|
|
|
emails.push(header);
|
2013-09-26 07:26:57 -04:00
|
|
|
after();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-08-20 07:30:35 -04:00
|
|
|
/**
|
|
|
|
* List messages from an imap folder. This will not yet fetch the email body.
|
|
|
|
* @param {String} options.folderName The name of the imap folder.
|
2013-08-20 13:48:49 -04:00
|
|
|
* @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)
|
2013-08-20 07:30:35 -04:00
|
|
|
*/
|
|
|
|
EmailDAO.prototype.imapListMessages = function(options, callback) {
|
2013-08-20 13:48:49 -04:00
|
|
|
var self = this;
|
|
|
|
|
|
|
|
self._imapClient.listMessages({
|
|
|
|
path: options.folder,
|
|
|
|
offset: options.offset,
|
|
|
|
length: options.num
|
|
|
|
}, callback);
|
2013-08-20 07:30:35 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get an email messsage including the email body from imap
|
|
|
|
* @param {String} options.messageId The
|
|
|
|
*/
|
|
|
|
EmailDAO.prototype.imapGetMessage = function(options, callback) {
|
2013-10-04 07:15:16 -04:00
|
|
|
var self = this;
|
2013-08-21 07:43:19 -04:00
|
|
|
|
2013-10-16 12:56:18 -04:00
|
|
|
self._imapClient.getMessagePreview({
|
|
|
|
path: options.folder,
|
|
|
|
uid: options.uid
|
|
|
|
}, callback);
|
|
|
|
};
|
2013-08-21 07:43:19 -04:00
|
|
|
|
2013-10-16 12:56:18 -04:00
|
|
|
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);
|
2013-08-29 10:01:40 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-10-16 12:56:18 -04:00
|
|
|
// delete from local db
|
|
|
|
self._devicestorage.removeList('email_' + options.folder + '_' + options.uid, callback);
|
2013-08-28 11:10:18 -04:00
|
|
|
}
|
2013-10-16 12:56:18 -04:00
|
|
|
};
|
2013-08-28 11:10:18 -04:00
|
|
|
|
2013-10-16 12:56:18 -04:00
|
|
|
EmailDAO.prototype.imapDeleteMessage = function(options, callback) {
|
|
|
|
var self = this;
|
|
|
|
|
|
|
|
self._imapClient.deleteMessage({
|
2013-08-21 07:43:19 -04:00
|
|
|
path: options.folder,
|
2013-10-04 11:21:09 -04:00
|
|
|
uid: options.uid
|
2013-10-16 12:56:18 -04:00
|
|
|
}, moved);
|
|
|
|
|
|
|
|
function moved(err) {
|
|
|
|
if (err) {
|
|
|
|
callback(err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// delete from local db
|
|
|
|
self._devicestorage.removeList('email_' + options.folder + '_' + options.uid, callback);
|
|
|
|
}
|
2013-08-20 07:30:35 -04:00
|
|
|
};
|
|
|
|
|
2013-10-04 11:02:27 -04:00
|
|
|
EmailDAO.prototype.imapMarkMessageRead = function(options, callback) {
|
|
|
|
var self = this;
|
|
|
|
|
|
|
|
self._imapClient.updateFlags({
|
|
|
|
path: options.folder,
|
|
|
|
uid: options.uid,
|
|
|
|
unread: false
|
|
|
|
}, callback);
|
|
|
|
};
|
|
|
|
|
2013-10-04 10:29:32 -04:00
|
|
|
//
|
|
|
|
// SMTP Apis
|
|
|
|
//
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Send an email client side via STMP.
|
|
|
|
*/
|
|
|
|
EmailDAO.prototype.smtpSend = 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
|
2013-10-29 07:19:27 -04:00
|
|
|
self._keychain.getReceiverPublicKey(email.to[0].address, function(err, receiverPubkey) {
|
2013-10-04 10:29:32 -04:00
|
|
|
if (err) {
|
|
|
|
callback(err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// validate public key
|
|
|
|
if (!receiverPubkey) {
|
|
|
|
callback({
|
|
|
|
errMsg: 'User has no public key yet!'
|
|
|
|
});
|
|
|
|
// user hasn't registered a public key yet... invite
|
|
|
|
//self.encryptForNewUser(email, callback);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// public key found... encrypt and send
|
2013-10-11 21:19:01 -04:00
|
|
|
self.encryptForUser(email, receiverPubkey.publicKey, callback);
|
2013-10-04 10:29:32 -04:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Encrypt an email asymmetrically for an exisiting user with their public key
|
|
|
|
*/
|
|
|
|
EmailDAO.prototype.encryptForUser = function(email, receiverPubkey, callback) {
|
|
|
|
var self = this,
|
2013-10-29 10:22:19 -04:00
|
|
|
pt = email.body,
|
2013-10-04 10:29:32 -04:00
|
|
|
receiverPubkeys = [receiverPubkey];
|
|
|
|
|
2013-10-11 21:19:01 -04:00
|
|
|
// get own public key so send message can be read
|
|
|
|
self._crypto.exportKeys(function(err, ownKeys) {
|
2013-10-04 10:29:32 -04:00
|
|
|
if (err) {
|
|
|
|
callback(err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-10-11 21:19:01 -04:00
|
|
|
// add own public key to receiver list
|
|
|
|
receiverPubkeys.push(ownKeys.publicKeyArmored);
|
|
|
|
// encrypt the email
|
|
|
|
self._crypto.encrypt(pt, receiverPubkeys, function(err, ct) {
|
|
|
|
if (err) {
|
|
|
|
callback(err);
|
|
|
|
return;
|
|
|
|
}
|
2013-10-04 10:29:32 -04:00
|
|
|
|
2013-10-11 21:19:01 -04:00
|
|
|
// bundle encrypted email together for sending
|
|
|
|
frameEncryptedMessage(email, ct);
|
2013-10-04 10:29:32 -04:00
|
|
|
|
2013-10-11 21:19:01 -04:00
|
|
|
self.send(email, callback);
|
|
|
|
});
|
2013-10-04 10:29:32 -04:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Frames an encrypted message in base64 Format.
|
|
|
|
*/
|
|
|
|
|
|
|
|
function frameEncryptedMessage(email, ct) {
|
2013-10-11 21:19:01 -04:00
|
|
|
var to, greeting;
|
2013-10-04 10:29:32 -04:00
|
|
|
|
2013-10-11 21:19:01 -04:00
|
|
|
var MESSAGE = str.message + '\n\n\n',
|
2013-10-04 10:29:32 -04:00
|
|
|
SIGNATURE = '\n\n\n' + str.signature + '\n' + str.webSite + '\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
|
2013-10-11 21:19:01 -04:00
|
|
|
email.body = greeting + MESSAGE + ct + SIGNATURE;
|
2013-10-29 10:22:19 -04:00
|
|
|
email.subject = str.subjectPrefix + email.subject;
|
2013-10-04 10:29:32 -04:00
|
|
|
|
|
|
|
return email;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Send an actual message object via smtp
|
|
|
|
*/
|
|
|
|
EmailDAO.prototype.send = function(email, callback) {
|
|
|
|
var self = this;
|
|
|
|
|
|
|
|
self._smtpClient.send(email, callback);
|
|
|
|
};
|
|
|
|
|
2013-08-16 14:50:47 -04:00
|
|
|
return EmailDAO;
|
2013-08-28 04:31:53 -04:00
|
|
|
});
|