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-08-16 14:50:47 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Inits all dependencies
|
|
|
|
*/
|
|
|
|
EmailDAO.prototype.init = function(account, password, callback) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
// init crypto
|
|
|
|
initCrypto(storedKeypair);
|
|
|
|
});
|
2013-08-16 14:50:47 -04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function initCrypto(storedKeypair) {
|
2013-09-26 07:26:57 -04:00
|
|
|
self._crypto.init({
|
2013-08-16 14:50:47 -04:00
|
|
|
emailAddress: emailAddress,
|
|
|
|
password: password,
|
2013-08-22 10:18:48 -04:00
|
|
|
keySize: self._account.symKeySize,
|
|
|
|
rsaKeySize: self._account.asymKeySize,
|
2013-08-16 14:50:47 -04:00
|
|
|
storedKeypair: storedKeypair
|
|
|
|
}, function(err, generatedKeypair) {
|
|
|
|
if (err) {
|
|
|
|
callback(err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (generatedKeypair) {
|
|
|
|
// persist newly generated keypair
|
|
|
|
self._keychain.putUserKeyPair(generatedKeypair, callback);
|
|
|
|
} else {
|
|
|
|
callback();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-08-20 07:30:35 -04:00
|
|
|
//
|
2013-08-22 10:18:48 -04:00
|
|
|
// IMAP/SMTP 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);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Send an email client side via STMP.
|
|
|
|
*/
|
|
|
|
EmailDAO.prototype.smtpSend = function(email, callback) {
|
2013-08-27 13:04:26 -04:00
|
|
|
var self = this,
|
|
|
|
invalidRecipient;
|
2013-08-20 07:30:35 -04:00
|
|
|
|
|
|
|
// validate the email input
|
|
|
|
if (!email.to || !email.from || !email.to[0].address || !email.from[0].address) {
|
|
|
|
callback({
|
|
|
|
errMsg: 'Invalid email object!'
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-08-27 12:23:27 -04:00
|
|
|
// validate email addresses
|
|
|
|
_.each(email.to, function(i) {
|
2013-08-30 05:42:32 -04:00
|
|
|
if (!util.validateEmailAddress(i.address)) {
|
2013-08-27 12:23:27 -04:00
|
|
|
invalidRecipient = i.address;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
if (invalidRecipient) {
|
|
|
|
callback({
|
|
|
|
errMsg: 'Invalid recipient: ' + invalidRecipient
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
2013-08-30 05:42:32 -04:00
|
|
|
if (!util.validateEmailAddress(email.from[0].address)) {
|
2013-08-27 12:23:27 -04:00
|
|
|
callback({
|
|
|
|
errMsg: 'Invalid sender: ' + email.from
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// only support single recipient for e-2-e encryption
|
|
|
|
// check if receiver has a public key
|
2013-08-27 13:04:26 -04:00
|
|
|
self._keychain.getReveiverPublicKey(email.to[0].address, function(err, receiverPubkey) {
|
2013-08-27 12:23:27 -04:00
|
|
|
if (err) {
|
|
|
|
callback(err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// validate public key
|
|
|
|
if (!receiverPubkey) {
|
2013-09-26 07:26:57 -04:00
|
|
|
callback({
|
|
|
|
errMsg: 'User has no public key yet!'
|
|
|
|
});
|
2013-08-29 13:32:34 -04:00
|
|
|
// user hasn't registered a public key yet... invite
|
2013-09-26 07:26:57 -04:00
|
|
|
//self.encryptForNewUser(email, callback);
|
2013-08-27 12:23:27 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// public key found... encrypt and send
|
2013-08-29 08:28:26 -04:00
|
|
|
self.encryptForUser(email, receiverPubkey, callback);
|
2013-08-27 12:23:27 -04:00
|
|
|
});
|
2013-08-29 08:28:26 -04:00
|
|
|
};
|
2013-08-27 12:23:27 -04:00
|
|
|
|
2013-08-29 08:28:26 -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-08-31 11:13:08 -04:00
|
|
|
ptItems = bundleForEncryption(email),
|
|
|
|
receiverPubkeys = [receiverPubkey];
|
2013-08-27 12:23:27 -04:00
|
|
|
|
2013-08-29 08:28:26 -04:00
|
|
|
// encrypt the email
|
2013-09-26 07:26:57 -04:00
|
|
|
self._crypto.encryptListForUser(ptItems, receiverPubkeys, function(err, encryptedList) {
|
2013-08-29 08:28:26 -04:00
|
|
|
if (err) {
|
|
|
|
callback(err);
|
|
|
|
return;
|
2013-08-28 08:12:39 -04:00
|
|
|
}
|
2013-08-27 12:23:27 -04:00
|
|
|
|
2013-08-31 11:13:08 -04:00
|
|
|
// bundle encrypted email together for sending
|
|
|
|
bundleEncryptedItems(email, encryptedList);
|
2013-08-27 12:23:27 -04:00
|
|
|
|
2013-08-31 11:13:08 -04:00
|
|
|
self.send(email, callback);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Encrypt an email symmetrically for a new user, write the secret one time key to the cloudstorage REST service, and send the email client side via SMTP.
|
|
|
|
*/
|
|
|
|
EmailDAO.prototype.encryptForNewUser = function(email, callback) {
|
|
|
|
var self = this,
|
|
|
|
ptItems = bundleForEncryption(email);
|
|
|
|
|
2013-09-26 07:26:57 -04:00
|
|
|
self._crypto.symEncryptList(ptItems, function(err, result) {
|
2013-08-31 11:13:08 -04:00
|
|
|
if (err) {
|
|
|
|
callback(err);
|
|
|
|
return;
|
2013-08-29 08:28:26 -04:00
|
|
|
}
|
2013-08-28 08:12:39 -04:00
|
|
|
|
2013-08-31 11:13:08 -04:00
|
|
|
// bundle encrypted email together for sending
|
|
|
|
bundleEncryptedItems(email, result.list);
|
|
|
|
|
|
|
|
// TODO: write result.key to REST endpoint
|
|
|
|
|
2013-08-29 08:28:26 -04:00
|
|
|
self.send(email, callback);
|
|
|
|
});
|
|
|
|
};
|
2013-08-27 12:23:27 -04:00
|
|
|
|
2013-08-29 13:32:34 -04:00
|
|
|
/**
|
2013-08-31 11:13:08 -04:00
|
|
|
* Give the email a newly generated UUID, remove its attachments, and bundle all plaintext items to a batchable array for encryption.
|
|
|
|
*/
|
|
|
|
|
|
|
|
function bundleForEncryption(email) {
|
|
|
|
var ptItems = [email];
|
|
|
|
|
|
|
|
// generate a new UUID for the new email
|
|
|
|
email.id = util.UUID();
|
|
|
|
|
|
|
|
// add attachment to encryption batch and remove from email object
|
|
|
|
if (email.attachments) {
|
|
|
|
email.attachments.forEach(function(attachment) {
|
|
|
|
attachment.id = email.id;
|
|
|
|
ptItems.push(attachment);
|
|
|
|
});
|
|
|
|
delete email.attachments;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ptItems;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Frame the encrypted email message and append the encrypted attachments.
|
2013-08-29 13:32:34 -04:00
|
|
|
*/
|
2013-08-31 11:13:08 -04:00
|
|
|
|
|
|
|
function bundleEncryptedItems(email, encryptedList) {
|
|
|
|
var i;
|
|
|
|
|
|
|
|
// replace body and subject of the email with encrypted versions
|
|
|
|
email = frameEncryptedMessage(email, encryptedList[0]);
|
|
|
|
|
|
|
|
// add encrypted attachments
|
|
|
|
if (encryptedList.length > 1) {
|
|
|
|
email.attachments = [];
|
|
|
|
}
|
|
|
|
for (i = 1; i < encryptedList.length; i++) {
|
|
|
|
email.attachments.push({
|
|
|
|
fileName: 'Encrypted Attachment ' + i,
|
|
|
|
contentType: 'application/octet-stream',
|
|
|
|
uint8Array: util.binStr2Uint8Arr(JSON.stringify(encryptedList[i]))
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Frames an encrypted message in base64 Format.
|
|
|
|
*/
|
|
|
|
|
|
|
|
function frameEncryptedMessage(email, ct) {
|
2013-08-29 13:32:34 -04:00
|
|
|
var to, greeting, ctBase64;
|
|
|
|
|
2013-09-15 13:00:35 -04:00
|
|
|
var SUBJECT = str.subject,
|
|
|
|
MESSAGE = str.message + '\n\n\n',
|
|
|
|
PREFIX = str.cryptPrefix + '\n',
|
|
|
|
SUFFIX = '\n' + str.cryptSuffix,
|
|
|
|
SIGNATURE = '\n\n\n' + str.signature + '\n' + str.webSite + '\n\n';
|
|
|
|
|
2013-08-29 13:32:34 -04:00
|
|
|
// 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
|
|
|
|
ctBase64 = btoa(JSON.stringify(ct));
|
|
|
|
email.body = greeting + MESSAGE + PREFIX + ctBase64 + SUFFIX + SIGNATURE;
|
|
|
|
email.subject = SUBJECT;
|
|
|
|
|
|
|
|
return email;
|
2013-08-31 11:13:08 -04:00
|
|
|
}
|
2013-08-29 13:32:34 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Send an actual message object via smtp
|
|
|
|
*/
|
2013-08-29 08:28:26 -04:00
|
|
|
EmailDAO.prototype.send = function(email, callback) {
|
|
|
|
var self = this;
|
2013-08-27 12:23:27 -04:00
|
|
|
|
2013-08-29 08:28:26 -04:00
|
|
|
self._smtpClient.send(email, callback);
|
2013-08-20 07:30:35 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* List the folders in the user's IMAP mailbox.
|
|
|
|
*/
|
|
|
|
EmailDAO.prototype.imapListFolders = function(callback) {
|
2013-08-20 11:22:08 -04:00
|
|
|
var self = this;
|
|
|
|
|
2013-09-20 12:44:14 -04:00
|
|
|
self._imapClient.listAllFolders(callback);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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,
|
2013-09-28 13:37:56 -04:00
|
|
|
displayList = [],
|
2013-09-26 07:26:57 -04:00
|
|
|
encryptedList = [];
|
|
|
|
|
|
|
|
// validate options
|
|
|
|
if (!options.folder || typeof options.offset === 'undefined' || typeof options.num === 'undefined') {
|
|
|
|
callback({
|
|
|
|
errMsg: 'Invalid options!'
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
if (emails.length === 0) {
|
|
|
|
callback(null, []);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// find encrypted items
|
|
|
|
emails.forEach(function(i) {
|
|
|
|
if (i.body.indexOf(str.cryptPrefix) !== -1 && i.body.indexOf(str.cryptSuffix) !== -1) {
|
2013-09-28 13:35:07 -04:00
|
|
|
// add item to plaintext list for display later
|
2013-09-28 13:37:56 -04:00
|
|
|
displayList.push(i);
|
2013-09-26 07:26:57 -04:00
|
|
|
// parse ct object from ascii armored message block
|
|
|
|
encryptedList.push(parseMessageBlock(i));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// decrypt items
|
|
|
|
decryptList(encryptedList, function(err, decryptedList) {
|
2013-09-28 13:35:07 -04:00
|
|
|
// replace encrypted subject and body
|
2013-09-28 13:37:56 -04:00
|
|
|
for (var j = 0; j < displayList.length; j++) {
|
|
|
|
displayList[j].subject = decryptedList[j].subject;
|
|
|
|
displayList[j].body = decryptedList[j].body;
|
2013-09-28 13:35:07 -04:00
|
|
|
}
|
|
|
|
|
2013-09-26 07:26:57 -04:00
|
|
|
// return only decrypted items
|
2013-09-28 13:37:56 -04:00
|
|
|
callback(null, displayList);
|
2013-09-26 07:26:57 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
function parseMessageBlock(email) {
|
|
|
|
var ctMessageBase64, ctMessageJson, ctMessage;
|
|
|
|
|
|
|
|
// parse email body for encrypted message block
|
|
|
|
try {
|
|
|
|
// get base64 encoded message block
|
|
|
|
ctMessageBase64 = email.body.split(str.cryptPrefix)[1].split(str.cryptSuffix)[0].trim();
|
|
|
|
// decode bae64
|
|
|
|
ctMessageJson = atob(ctMessageBase64);
|
|
|
|
// parse json string to get ciphertext object
|
|
|
|
ctMessage = JSON.parse(ctMessageJson);
|
|
|
|
} catch (e) {
|
|
|
|
callback({
|
|
|
|
errMsg: 'Error parsing encrypted message block!'
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ctMessage;
|
|
|
|
}
|
|
|
|
|
|
|
|
function decryptList(encryptedList, callback) {
|
|
|
|
var already, pubkeyIds = [];
|
|
|
|
|
|
|
|
// gather public key ids required to verify signatures
|
|
|
|
encryptedList.forEach(function(i) {
|
|
|
|
already = null;
|
|
|
|
already = _.findWhere(pubkeyIds, {
|
|
|
|
_id: i.senderPk
|
|
|
|
});
|
|
|
|
if (!already) {
|
|
|
|
pubkeyIds.push({
|
|
|
|
_id: i.senderPk
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// fetch public keys from keychain
|
|
|
|
self._keychain.getPublicKeys(pubkeyIds, function(err, senderPubkeys) {
|
|
|
|
if (err) {
|
|
|
|
callback(err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// verfiy signatures and re-encrypt item keys
|
|
|
|
self._crypto.decryptListForUser(encryptedList, senderPubkeys, function(err, decryptedList) {
|
|
|
|
if (err) {
|
|
|
|
callback(err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
callback(null, decryptedList);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
|
|
|
// validate options
|
|
|
|
if (!options.folder || typeof options.offset === 'undefined' || typeof options.num === 'undefined') {
|
|
|
|
callback({
|
|
|
|
errMsg: 'Invalid options!'
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
fetchList(options, function(emails) {
|
2013-09-28 13:04:15 -04:00
|
|
|
// 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, function(err) {
|
|
|
|
callback(err);
|
|
|
|
});
|
2013-09-26 07:26:57 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
function fetchList(folder, callback) {
|
2013-09-28 13:04:15 -04:00
|
|
|
var encryptedList = [];
|
|
|
|
|
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) {
|
|
|
|
console.log(err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-09-28 13:04:15 -04:00
|
|
|
// find encrypted messages by subject
|
|
|
|
emails.forEach(function(i) {
|
|
|
|
if (i.subject === str.subject) {
|
|
|
|
encryptedList.push(i);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2013-09-26 07:26:57 -04:00
|
|
|
// fetch message bodies
|
2013-09-28 13:04:15 -04:00
|
|
|
fetchBodies(encryptedList, folder, function(messages) {
|
2013-09-26 07:26:57 -04:00
|
|
|
callback(messages);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function fetchBodies(messageList, folder, callback) {
|
|
|
|
var emails = [];
|
|
|
|
|
2013-09-28 13:04:15 -04:00
|
|
|
if (messageList.length < 1) {
|
|
|
|
callback(emails);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-09-26 07:26:57 -04:00
|
|
|
var after = _.after(messageList.length, function() {
|
|
|
|
callback(emails);
|
|
|
|
});
|
|
|
|
|
|
|
|
_.each(messageList, function(messageItem) {
|
|
|
|
self.imapGetMessage({
|
|
|
|
folder: folder,
|
|
|
|
uid: messageItem.uid
|
|
|
|
}, function(err, message) {
|
|
|
|
if (err) {
|
|
|
|
console.log(err);
|
|
|
|
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'
|
|
|
|
messageItem.id = message.id;
|
|
|
|
messageItem.body = message.body;
|
|
|
|
messageItem.html = message.html;
|
|
|
|
messageItem.attachments = message.attachments;
|
|
|
|
|
|
|
|
emails.push(messageItem);
|
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;
|
|
|
|
|
|
|
|
// validate options
|
|
|
|
if (!options.folder || typeof options.offset === 'undefined' || typeof options.num === 'undefined') {
|
|
|
|
callback({
|
|
|
|
errMsg: 'Invalid options!'
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
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-08-21 07:43:19 -04:00
|
|
|
var self = this,
|
2013-08-21 10:07:59 -04:00
|
|
|
expectedItems,
|
|
|
|
itemCounter = 0,
|
2013-08-28 13:20:59 -04:00
|
|
|
message /*, attachments = []*/ ;
|
2013-08-21 07:43:19 -04:00
|
|
|
|
|
|
|
// validate options
|
|
|
|
if (!options.folder || !options.uid) {
|
|
|
|
callback({
|
|
|
|
errMsg: 'Invalid options!'
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-09-11 17:31:08 -04:00
|
|
|
function messageReady(err, gottenMessage) {
|
2013-08-21 07:43:19 -04:00
|
|
|
message = gottenMessage;
|
2013-08-21 10:07:59 -04:00
|
|
|
itemCounter++;
|
|
|
|
// remember how many items should be fetched before the callback fires
|
|
|
|
expectedItems = (message.attachments instanceof Array) ? message.attachments.length + 1 : 1;
|
2013-08-28 11:10:18 -04:00
|
|
|
|
2013-08-29 10:01:40 -04:00
|
|
|
// TODO: remove once attachments work again
|
|
|
|
if (itemCounter > 1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-09-26 07:26:57 -04:00
|
|
|
// return message
|
2013-08-28 11:10:18 -04:00
|
|
|
callback(null, message);
|
|
|
|
|
|
|
|
//check();
|
|
|
|
}
|
|
|
|
|
2013-08-28 13:20:59 -04:00
|
|
|
// function attachmentReady(err, gottenAttachment) {
|
|
|
|
// attachments.push(gottenAttachment);
|
|
|
|
// itemCounter++;
|
|
|
|
// check();
|
|
|
|
// }
|
|
|
|
|
|
|
|
// function check() {
|
|
|
|
// // go for another round you don't yet know how mich to fetch or you haven't fetch enough
|
|
|
|
// if (!expectedItems || itemCounter < expectedItems) {
|
|
|
|
// return;
|
|
|
|
// }
|
|
|
|
|
|
|
|
// // overwrite attachments array with the uint8array variant
|
|
|
|
// message.attachments = (attachments.length > 0) ? attachments : undefined;
|
|
|
|
// // cache message object in memory
|
|
|
|
// self.cacheItem(options.folder, message);
|
|
|
|
|
|
|
|
// callback(null, message);
|
|
|
|
// }
|
2013-08-21 07:43:19 -04:00
|
|
|
|
|
|
|
self._imapClient.getMessage({
|
|
|
|
path: options.folder,
|
2013-08-28 08:56:23 -04:00
|
|
|
uid: options.uid,
|
2013-09-11 17:31:08 -04:00
|
|
|
textOnly: true
|
|
|
|
}, messageReady);
|
2013-08-20 07:30:35 -04:00
|
|
|
};
|
|
|
|
|
2013-08-22 10:18:48 -04:00
|
|
|
/**
|
|
|
|
* Checks if an item is already cached and if not, cache it.
|
|
|
|
*/
|
|
|
|
EmailDAO.prototype.cacheItem = function(folderName, item) {
|
|
|
|
var self = this;
|
|
|
|
|
|
|
|
// check if account has a folders attribute
|
|
|
|
if (!self._account.folders) {
|
|
|
|
self._account.folders = {};
|
|
|
|
}
|
|
|
|
// create folder if not existant
|
|
|
|
if (!self._account.folders[folderName]) {
|
|
|
|
self._account.folders[folderName] = {};
|
|
|
|
}
|
|
|
|
|
|
|
|
// cache item
|
|
|
|
self._account.folders[folderName][item.uid] = item;
|
|
|
|
};
|
2013-08-20 07:30:35 -04:00
|
|
|
|
2013-08-16 14:50:47 -04:00
|
|
|
/**
|
2013-08-22 10:18:48 -04:00
|
|
|
* Fetch an item from the cache with the following id
|
2013-08-16 14:50:47 -04:00
|
|
|
*/
|
2013-08-22 10:18:48 -04:00
|
|
|
EmailDAO.prototype.readCache = function(folderName, itemId) {
|
2013-08-16 14:50:47 -04:00
|
|
|
var self = this;
|
|
|
|
|
2013-08-22 10:18:48 -04:00
|
|
|
// check if account has a folders attribute
|
|
|
|
if (!self._account.folders) {
|
2013-08-23 05:04:22 -04:00
|
|
|
return;
|
2013-08-22 10:18:48 -04:00
|
|
|
}
|
|
|
|
// check folder
|
|
|
|
if (!self._account.folders[folderName]) {
|
2013-08-23 05:04:22 -04:00
|
|
|
return;
|
2013-08-22 10:18:48 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return self._account.folders[folderName][itemId];
|
2013-08-16 14:50:47 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
return EmailDAO;
|
2013-08-28 04:31:53 -04:00
|
|
|
});
|