2013-04-02 09:02:57 -04:00
|
|
|
/**
|
|
|
|
* A high-level Data-Access Api for handling Email synchronization
|
|
|
|
* between the cloud service and the device's local storage
|
|
|
|
*/
|
2013-06-10 17:50:26 -04:00
|
|
|
define(['underscore', 'cryptoLib/util', 'js/crypto/crypto', 'js/dao/lawnchair-dao',
|
2013-06-10 21:14:57 -04:00
|
|
|
'js/dao/devicestorage-dao', 'js/app-config', 'js/model/account-model'
|
|
|
|
], function(_, util, crypto, jsonDB, devicestorage, app) {
|
2013-04-01 18:12:15 -04:00
|
|
|
'use strict';
|
|
|
|
|
2013-06-10 17:50:26 -04:00
|
|
|
var EmailDAO = function(cloudstorage, keychain) {
|
|
|
|
var self = this;
|
2013-06-03 13:57:15 -04:00
|
|
|
|
2013-06-10 17:50:26 -04:00
|
|
|
/**
|
|
|
|
* Inits all dependencies
|
|
|
|
*/
|
|
|
|
self.init = function(account, password, callback) {
|
|
|
|
self.account = account;
|
2013-06-03 13:57:15 -04:00
|
|
|
|
2013-06-10 17:50:26 -04:00
|
|
|
// validate email address
|
|
|
|
var emailAddress = account.get('emailAddress');
|
|
|
|
if (!validateEmail(emailAddress)) {
|
|
|
|
callback({
|
|
|
|
errMsg: 'The user email address must be specified!'
|
|
|
|
});
|
2013-05-31 09:51:34 -04:00
|
|
|
return;
|
2013-04-02 09:02:57 -04:00
|
|
|
}
|
2013-05-07 09:10:51 -04:00
|
|
|
|
2013-06-10 17:50:26 -04:00
|
|
|
// init user's local database
|
|
|
|
jsonDB.init(emailAddress);
|
2013-04-01 18:12:15 -04:00
|
|
|
|
2013-06-10 17:50:26 -04:00
|
|
|
// call getUserKeyPair to read/sync keypair with devicestorage/cloud
|
|
|
|
keychain.getUserKeyPair(emailAddress, function(err, storedKeypair) {
|
2013-05-18 16:33:10 -04:00
|
|
|
if (err) {
|
|
|
|
callback(err);
|
|
|
|
return;
|
|
|
|
}
|
2013-06-10 17:50:26 -04:00
|
|
|
// init crypto
|
|
|
|
initCrypto(storedKeypair);
|
|
|
|
});
|
2013-05-18 16:33:10 -04:00
|
|
|
|
2013-06-10 17:50:26 -04:00
|
|
|
function initCrypto(storedKeypair) {
|
|
|
|
crypto.init({
|
|
|
|
emailAddress: emailAddress,
|
|
|
|
password: password,
|
|
|
|
keySize: account.get('symKeySize'),
|
|
|
|
rsaKeySize: account.get('asymKeySize'),
|
|
|
|
storedKeypair: storedKeypair
|
|
|
|
}, function(err, generatedKeypair) {
|
|
|
|
if (err) {
|
|
|
|
callback(err);
|
|
|
|
return;
|
2013-05-31 09:51:34 -04:00
|
|
|
}
|
|
|
|
|
2013-06-10 17:50:26 -04:00
|
|
|
if (generatedKeypair) {
|
|
|
|
// persist newly generated keypair
|
|
|
|
keychain.putUserKeyPair(generatedKeypair, callback);
|
|
|
|
} else {
|
|
|
|
callback();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fetch an email with the following id
|
|
|
|
*/
|
|
|
|
self.getItem = function(folderName, itemId) {
|
|
|
|
var folder = self.account.get('folders').where({
|
|
|
|
name: folderName
|
|
|
|
})[0];
|
|
|
|
var mail = _.find(folder.get('items'), function(email) {
|
|
|
|
return email.id + '' === itemId + '';
|
|
|
|
});
|
|
|
|
return mail;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fetch a list of emails from the device's local storage
|
|
|
|
* @param offset [Number] The offset of items to fetch (0 is the last stored item)
|
|
|
|
* @param num [Number] The number of items to fetch (null means fetch all)
|
|
|
|
*/
|
|
|
|
self.listItems = function(folderName, offset, num, callback) {
|
|
|
|
var collection, folder, already, pubkeyIds = [];
|
|
|
|
|
|
|
|
// check if items are in memory already (account.folders model)
|
|
|
|
folder = self.account.get('folders').where({
|
|
|
|
name: folderName
|
|
|
|
})[0];
|
|
|
|
|
|
|
|
if (!folder) {
|
|
|
|
// get encrypted items from storage
|
|
|
|
devicestorage.listEncryptedItems('email_' + folderName, offset, num, function(err, encryptedList) {
|
2013-05-31 09:51:34 -04:00
|
|
|
if (err) {
|
|
|
|
callback(err);
|
|
|
|
return;
|
|
|
|
}
|
2013-06-10 17:50:26 -04:00
|
|
|
if (encryptedList.length === 0) {
|
|
|
|
callback(null, []);
|
|
|
|
return;
|
|
|
|
}
|
2013-05-31 09:51:34 -04:00
|
|
|
|
2013-06-10 17:50:26 -04:00
|
|
|
// 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
|
|
|
|
keychain.getPublicKeys(pubkeyIds, function(err, senderPubkeys) {
|
2013-05-31 09:51:34 -04:00
|
|
|
if (err) {
|
|
|
|
callback(err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-06-10 17:50:26 -04:00
|
|
|
// decrypt list
|
|
|
|
crypto.decryptListForUser(encryptedList, senderPubkeys, function(err, decryptedList) {
|
|
|
|
if (err) {
|
|
|
|
callback(err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// cache collection in folder memory
|
|
|
|
if (decryptedList.length > 0) {
|
|
|
|
folder = new app.model.Folder({
|
|
|
|
name: folderName
|
|
|
|
});
|
|
|
|
folder.set('items', decryptedList);
|
|
|
|
self.account.get('folders').add(folder);
|
|
|
|
}
|
|
|
|
|
|
|
|
callback(null, decryptedList);
|
|
|
|
});
|
2013-05-31 09:51:34 -04:00
|
|
|
|
2013-04-02 09:02:57 -04:00
|
|
|
});
|
2013-05-31 09:51:34 -04:00
|
|
|
});
|
2013-04-02 09:02:57 -04:00
|
|
|
|
2013-06-10 17:50:26 -04:00
|
|
|
} else {
|
|
|
|
// read items from memory
|
|
|
|
collection = folder.get('items');
|
|
|
|
callback(null, collection);
|
2013-04-02 09:02:57 -04:00
|
|
|
}
|
2013-06-10 17:50:26 -04:00
|
|
|
};
|
2013-04-02 09:02:57 -04:00
|
|
|
|
2013-06-10 17:50:26 -04:00
|
|
|
/**
|
|
|
|
* Synchronize a folder's items from the cloud to the device-storage
|
|
|
|
* @param folderName [String] The name of the folder e.g. 'inbox'
|
|
|
|
*/
|
|
|
|
self.syncFromCloud = function(folderName, callback) {
|
|
|
|
var folder;
|
2013-04-02 09:02:57 -04:00
|
|
|
|
2013-06-10 17:50:26 -04:00
|
|
|
cloudstorage.listEncryptedItems('email', self.account.get('emailAddress'), folderName, function(err, data) {
|
|
|
|
// return if an error occured
|
|
|
|
if (err) {
|
|
|
|
callback({
|
|
|
|
errMsg: 'Syncing encrypted items from cloud failed!',
|
|
|
|
err: err
|
|
|
|
}); // error
|
|
|
|
return;
|
2013-04-02 09:02:57 -04:00
|
|
|
}
|
2013-04-01 18:12:15 -04:00
|
|
|
|
2013-06-10 17:50:26 -04:00
|
|
|
// TODO: remove old folder items from devicestorage
|
|
|
|
|
|
|
|
// persist encrypted list in device storage
|
|
|
|
devicestorage.storeEcryptedList(data, 'email_' + folderName, function() {
|
|
|
|
// remove cached folder in account model
|
|
|
|
folder = self.account.get('folders').where({
|
|
|
|
name: folderName
|
|
|
|
})[0];
|
|
|
|
if (folder) {
|
|
|
|
self.account.get('folders').remove(folder);
|
|
|
|
}
|
|
|
|
callback();
|
|
|
|
});
|
2013-05-04 09:28:10 -04:00
|
|
|
});
|
2013-06-10 17:50:26 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Send a plaintext Email to the user's outbox in the cloud
|
|
|
|
*/
|
|
|
|
self.sendEmail = function(email, callback) {
|
|
|
|
var userId = self.account.get('emailAddress');
|
|
|
|
|
|
|
|
// validate email addresses
|
|
|
|
var invalidRecipient;
|
|
|
|
_.each(email.to, function(address) {
|
|
|
|
if (!validateEmail(address)) {
|
|
|
|
invalidRecipient = address;
|
|
|
|
}
|
2013-05-04 07:02:17 -04:00
|
|
|
});
|
2013-06-10 17:50:26 -04:00
|
|
|
if (invalidRecipient) {
|
|
|
|
callback({
|
|
|
|
errMsg: 'Invalid recipient: ' + invalidRecipient
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!validateEmail(email.from)) {
|
|
|
|
callback({
|
|
|
|
errMsg: 'Invalid sender: ' + email.from
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
2013-05-04 07:02:17 -04:00
|
|
|
|
2013-06-10 17:50:26 -04:00
|
|
|
// generate a new UUID for the new email
|
|
|
|
email.id = util.UUID();
|
2013-05-03 10:09:13 -04:00
|
|
|
|
2013-06-10 17:50:26 -04:00
|
|
|
// send email to cloud service
|
|
|
|
cloudstorage.putEncryptedItem(email, 'email', userId, 'outbox', function(err) {
|
|
|
|
callback(err);
|
|
|
|
});
|
|
|
|
};
|
2013-05-02 12:49:22 -04:00
|
|
|
};
|
|
|
|
|
2013-06-10 17:50:26 -04:00
|
|
|
//
|
|
|
|
// helper functions
|
|
|
|
//
|
|
|
|
|
2013-06-03 13:57:15 -04:00
|
|
|
function validateEmail(email) {
|
|
|
|
var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
|
|
|
return re.test(email);
|
|
|
|
}
|
|
|
|
|
2013-06-10 17:50:26 -04:00
|
|
|
return EmailDAO;
|
|
|
|
});
|