mail/src/js/dao/email-dao.js

220 lines
5.4 KiB
JavaScript
Raw Normal View History

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-03 13:57:15 -04:00
app.dao.EmailDAO = function(jsonDB, crypto, devicestorage, cloudstorage, util, keychain) {
'use strict';
2013-03-13 11:58:46 -04:00
/**
2013-04-02 09:02:57 -04:00
* Inits all dependencies
2013-03-13 11:58:46 -04:00
*/
2013-04-02 09:02:57 -04:00
this.init = function(account, password, callback) {
this.account = account;
2013-06-03 13:57:15 -04:00
// validate email address
var emailAddress = account.get('emailAddress');
if (!validateEmail(emailAddress)) {
callback({
errMsg: 'The user email address must be specified!'
});
return;
}
// init user's local database
jsonDB.init(emailAddress);
// call getUserKeyPair to read/sync keypair with devicestorage/cloud
2013-06-03 13:57:15 -04:00
keychain.getUserKeyPair(emailAddress, function(err, storedKeypair) {
2013-04-02 09:02:57 -04:00
if (err) {
callback(err);
return;
2013-04-02 09:02:57 -04:00
}
// init crypto
initCrypto(storedKeypair);
2013-04-02 09:02:57 -04:00
});
function initCrypto(storedKeypair) {
crypto.init({
2013-06-03 13:57:15 -04:00
emailAddress: emailAddress,
password: password,
2013-05-18 19:33:59 -04:00
keySize: account.get('symKeySize'),
rsaKeySize: account.get('asymKeySize'),
storedKeypair: storedKeypair
}, function(err, generatedKeypair) {
if (err) {
callback(err);
return;
}
if (generatedKeypair) {
// persist newly generated keypair
keychain.putUserKeyPair(generatedKeypair, callback);
} else {
callback();
}
2013-03-13 11:58:46 -04:00
});
2013-04-02 09:02:57 -04:00
}
};
2013-04-02 09:02:57 -04:00
/**
* Fetch an email with the following id
*/
this.getItem = function(folderName, itemId) {
var folder = this.account.get('folders').where({
name: folderName
})[0];
var mail = _.find(folder.get('items'), function(email) {
2013-04-02 09:02:57 -04:00
return email.id + '' === itemId + '';
});
return mail;
};
2013-04-02 09:02:57 -04:00
/**
* 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)
*/
this.listItems = function(folderName, offset, num, callback) {
var collection, folder, already, pubkeyIds = [],
self = this;
2013-04-02 09:02:57 -04:00
// check if items are in memory already (account.folders model)
folder = this.account.get('folders').where({
name: folderName
})[0];
if (!folder) {
// get encrypted items from storage
devicestorage.listEncryptedItems('email_' + folderName, offset, num, function(err, encryptedList) {
if (err) {
callback(err);
return;
}
2013-05-31 19:45:38 -04:00
if (encryptedList.length === 0) {
callback(null, []);
2013-05-31 19:45:38 -04:00
return;
}
// 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) {
if (err) {
callback(err);
return;
}
// 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-04-02 09:02:57 -04:00
});
});
});
2013-04-02 09:02:57 -04:00
} else {
// read items from memory
collection = folder.get('items');
callback(null, collection);
2013-04-02 09:02:57 -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'
*/
this.syncFromCloud = function(folderName, callback) {
var folder, self = this;
cloudstorage.listEncryptedItems('email', this.account.get('emailAddress'), folderName, function(err, data) {
2013-05-31 19:45:38 -04:00
// return if an error occured
if (err) {
callback({
2013-05-31 19:45:38 -04:00
errMsg: 'Syncing encrypted items from cloud failed!',
err: err
}); // error
2013-04-02 09:02:57 -04:00
return;
}
// TODO: remove old folder items from devicestorage
// persist encrypted list in device storage
devicestorage.storeEcryptedList(data, 'email_' + folderName, function() {
2013-04-02 09:02:57 -04:00
// 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-03-13 11:58:46 -04:00
};
/**
* Send a plaintext Email to the user's outbox in the cloud
*/
this.sendEmail = function(email, callback) {
var userId = this.account.get('emailAddress');
2013-05-04 07:02:17 -04:00
// validate email addresses
2013-05-04 09:28:10 -04:00
var invalidRecipient;
2013-06-06 14:41:25 -04:00
_.each(email.to, function(address) {
2013-05-04 07:02:17 -04:00
if (!validateEmail(address)) {
2013-05-04 09:28:10 -04:00
invalidRecipient = address;
2013-05-04 07:02:17 -04:00
}
});
2013-05-04 09:28:10 -04:00
if (invalidRecipient) {
callback({
errMsg: 'Invalid recipient: ' + invalidRecipient
});
return;
}
2013-06-06 14:41:25 -04:00
if (!validateEmail(email.from)) {
2013-05-04 07:02:17 -04:00
callback({
errMsg: 'Invalid sender: ' + email.from
});
return;
}
2013-05-03 10:09:13 -04:00
// generate a new UUID for the new email
2013-06-06 14:41:25 -04:00
email.id = util.UUID();
2013-05-03 10:09:13 -04:00
2013-05-04 07:02:17 -04:00
// send email to cloud service
cloudstorage.putEncryptedItem(email, 'email', userId, 'outbox', function(err) {
callback(err);
});
};
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-04-02 09:02:57 -04:00
};