2013-11-19 10:14:48 -05:00
|
|
|
define(function(require) {
|
|
|
|
'use strict';
|
|
|
|
|
2013-11-20 13:13:39 -05:00
|
|
|
var _ = require('underscore'),
|
2014-02-24 04:14:07 -05:00
|
|
|
util = require('cryptoLib/util'),
|
2013-11-20 13:13:39 -05:00
|
|
|
config = require('js/app-config').config,
|
2014-02-24 04:14:07 -05:00
|
|
|
outboxDb = 'email_OUTBOX';
|
2013-11-19 10:14:48 -05:00
|
|
|
|
2013-11-21 04:57:22 -05:00
|
|
|
/**
|
2013-11-21 05:37:18 -05:00
|
|
|
* High level business object that orchestrates the local outbox.
|
|
|
|
* The local outbox takes care of the emails before they are being sent.
|
2013-11-21 04:57:22 -05:00
|
|
|
* It also checks periodically if there are any mails in the local device storage to be sent.
|
|
|
|
*/
|
2014-02-27 09:23:33 -05:00
|
|
|
var OutboxBO = function(emailDao, keychain, devicestorage) {
|
2013-11-21 11:37:07 -05:00
|
|
|
/** @private */
|
2013-12-02 09:48:59 -05:00
|
|
|
this._emailDao = emailDao;
|
2013-11-21 11:37:07 -05:00
|
|
|
|
|
|
|
/** @private */
|
|
|
|
this._keychain = keychain;
|
|
|
|
|
|
|
|
/** @private */
|
|
|
|
this._devicestorage = devicestorage;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Semaphore-esque flag to avoid 'concurrent' calls to _processOutbox when the timeout fires, but a call is still in process.
|
|
|
|
* @private */
|
2013-11-19 10:14:48 -05:00
|
|
|
this._outboxBusy = false;
|
2013-12-04 11:13:45 -05:00
|
|
|
};
|
|
|
|
|
2013-11-21 04:57:22 -05:00
|
|
|
/**
|
|
|
|
* This function activates the periodic checking of the local device storage for pending mails.
|
|
|
|
* @param {Function} callback(error, pendingMailsCount) Callback that informs you about the count of pending mails.
|
|
|
|
*/
|
2013-11-19 10:14:48 -05:00
|
|
|
OutboxBO.prototype.startChecking = function(callback) {
|
2014-02-27 12:14:38 -05:00
|
|
|
// remember global callback
|
2014-02-28 10:27:11 -05:00
|
|
|
this._onUpdate = callback;
|
2013-11-19 10:14:48 -05:00
|
|
|
// start periodic checking of outbox
|
2014-02-28 10:27:11 -05:00
|
|
|
this._intervalId = setInterval(this._processOutbox.bind(this, this._onUpdate), config.checkOutboxInterval);
|
2013-11-19 10:14:48 -05:00
|
|
|
};
|
|
|
|
|
2013-11-21 04:57:22 -05:00
|
|
|
/**
|
|
|
|
* Outbox stops the periodic checking of the local device storage for pending mails.
|
|
|
|
*/
|
2013-11-19 10:14:48 -05:00
|
|
|
OutboxBO.prototype.stopChecking = function() {
|
|
|
|
if (!this._intervalId) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
clearInterval(this._intervalId);
|
|
|
|
delete this._intervalId;
|
|
|
|
};
|
|
|
|
|
2013-12-05 09:02:41 -05:00
|
|
|
/**
|
2014-02-24 04:14:07 -05:00
|
|
|
* Put a email dto in the outbox for sending when ready
|
|
|
|
* @param {Object} mail The Email DTO
|
|
|
|
* @param {Function} callback Invoked when the object was encrypted and persisted to disk
|
2013-12-05 09:02:41 -05:00
|
|
|
*/
|
2014-02-24 04:14:07 -05:00
|
|
|
OutboxBO.prototype.put = function(mail, callback) {
|
|
|
|
var self = this,
|
|
|
|
allReaders = mail.from.concat(mail.to.concat(mail.cc.concat(mail.bcc))); // all the users that should be able to read the mail
|
|
|
|
|
|
|
|
mail.publicKeysArmored = []; // gather the public keys
|
2014-05-23 08:23:50 -04:00
|
|
|
mail.uid = mail.id = util.UUID(); // the mail needs a random id & uid for storage in the database
|
2014-02-24 04:14:07 -05:00
|
|
|
|
2014-05-16 07:09:55 -04:00
|
|
|
// do not encrypt mails with a bcc recipient, due to a possible privacy leak
|
|
|
|
if (mail.bcc.length > 0) {
|
|
|
|
storeAndForward(mail);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-02-24 04:14:07 -05:00
|
|
|
checkRecipients(allReaders);
|
|
|
|
|
|
|
|
// check if there are unregistered recipients
|
|
|
|
function checkRecipients(recipients) {
|
|
|
|
var after = _.after(recipients.length, function() {
|
2014-02-27 12:14:38 -05:00
|
|
|
checkEncrypt();
|
2014-02-24 04:14:07 -05:00
|
|
|
});
|
|
|
|
|
|
|
|
// find out if there are unregistered users
|
|
|
|
recipients.forEach(function(recipient) {
|
|
|
|
self._keychain.getReceiverPublicKey(recipient.address, function(err, key) {
|
|
|
|
if (err) {
|
|
|
|
callback(err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if a public key is available, add the recipient's key to the armored public keys,
|
|
|
|
// otherwise remember the recipient as unregistered for later sending
|
|
|
|
if (key) {
|
|
|
|
mail.publicKeysArmored.push(key.publicKey);
|
|
|
|
}
|
|
|
|
|
|
|
|
after();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2014-02-27 12:14:38 -05:00
|
|
|
function checkEncrypt() {
|
2014-02-27 09:23:33 -05:00
|
|
|
// only encrypt if all recipients have public keys
|
|
|
|
if (mail.publicKeysArmored.length < allReaders.length) {
|
2014-02-27 12:14:38 -05:00
|
|
|
storeAndForward(mail);
|
2014-02-27 09:23:33 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// encrypts the body and attachments and persists the mail object
|
2014-02-24 04:14:07 -05:00
|
|
|
self._emailDao.encrypt({
|
|
|
|
mail: mail,
|
|
|
|
publicKeysArmored: mail.publicKeysArmored
|
|
|
|
}, function(err) {
|
|
|
|
if (err) {
|
|
|
|
callback(err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-02-27 12:14:38 -05:00
|
|
|
storeAndForward(mail);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function storeAndForward(mail) {
|
|
|
|
// store in outbox
|
|
|
|
self._devicestorage.storeList([mail], outboxDb, function(err) {
|
|
|
|
if (err) {
|
|
|
|
callback(err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
callback();
|
|
|
|
// don't wait for next round
|
2014-02-28 10:27:11 -05:00
|
|
|
self._processOutbox(self._onUpdate);
|
2014-02-24 04:14:07 -05:00
|
|
|
});
|
2013-12-05 09:02:41 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-11-21 04:57:22 -05:00
|
|
|
/**
|
|
|
|
* Checks the local device storage for pending mails.
|
|
|
|
* @param {Function} callback(error, pendingMailsCount) Callback that informs you about the count of pending mails.
|
|
|
|
*/
|
2013-11-20 13:13:39 -05:00
|
|
|
OutboxBO.prototype._processOutbox = function(callback) {
|
2013-11-19 10:14:48 -05:00
|
|
|
var self = this,
|
2014-02-24 04:14:07 -05:00
|
|
|
unsentMails = 0;
|
2013-11-19 10:14:48 -05:00
|
|
|
|
2014-02-24 04:14:07 -05:00
|
|
|
// also, if a _processOutbox call is still in progress, ignore it.
|
2013-11-19 10:14:48 -05:00
|
|
|
if (self._outboxBusy) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-02-24 04:14:07 -05:00
|
|
|
self._outboxBusy = true;
|
2013-11-19 10:14:48 -05:00
|
|
|
|
2014-02-24 04:14:07 -05:00
|
|
|
// get pending mails from the outbox
|
|
|
|
self._devicestorage.listItems(outboxDb, 0, null, function(err, pendingMails) {
|
|
|
|
// error, we're done here
|
|
|
|
if (err) {
|
|
|
|
self._outboxBusy = false;
|
|
|
|
callback(err);
|
|
|
|
return;
|
|
|
|
}
|
2013-11-19 10:14:48 -05:00
|
|
|
|
2014-02-24 04:14:07 -05:00
|
|
|
// if we're not online, don't even bother sending mails.
|
|
|
|
if (!self._emailDao._account.online || _.isEmpty(pendingMails)) {
|
|
|
|
self._outboxBusy = false;
|
|
|
|
callback(null, pendingMails.length);
|
|
|
|
return;
|
|
|
|
}
|
2013-11-21 11:37:07 -05:00
|
|
|
|
2014-02-24 04:14:07 -05:00
|
|
|
// we're done after all the mails have been handled
|
|
|
|
// update the outbox count...
|
|
|
|
var after = _.after(pendingMails.length, function() {
|
|
|
|
self._outboxBusy = false;
|
|
|
|
callback(null, unsentMails);
|
|
|
|
});
|
2014-01-20 05:03:01 -05:00
|
|
|
|
2014-02-24 04:14:07 -05:00
|
|
|
// send pending mails if possible
|
|
|
|
pendingMails.forEach(function(mail) {
|
2014-02-27 09:23:33 -05:00
|
|
|
send(mail, after);
|
2013-11-19 10:14:48 -05:00
|
|
|
});
|
2014-02-24 04:14:07 -05:00
|
|
|
});
|
2013-11-19 10:14:48 -05:00
|
|
|
|
2014-02-27 09:23:33 -05:00
|
|
|
// send the message
|
|
|
|
function send(mail, done) {
|
2013-11-21 05:37:18 -05:00
|
|
|
|
2014-02-27 09:23:33 -05:00
|
|
|
// check is email is to be sent encrypted or as plaintex
|
|
|
|
if (mail.encrypted === true) {
|
|
|
|
// email was already encrypted before persisting in outbox, tell pgpmailer to send encrypted and not encrypt again
|
|
|
|
self._emailDao.sendEncrypted({
|
|
|
|
email: mail
|
|
|
|
}, onSend);
|
|
|
|
} else {
|
|
|
|
// send email as plaintext
|
|
|
|
self._emailDao.sendPlaintext({
|
|
|
|
email: mail
|
|
|
|
}, onSend);
|
|
|
|
}
|
2014-02-24 04:14:07 -05:00
|
|
|
|
2014-02-27 09:23:33 -05:00
|
|
|
function onSend(err) {
|
2014-02-24 04:14:07 -05:00
|
|
|
if (err) {
|
|
|
|
self._outboxBusy = false;
|
|
|
|
if (err.code === 42) {
|
|
|
|
// offline try again later
|
|
|
|
done();
|
|
|
|
} else {
|
|
|
|
self._outboxBusy = false;
|
|
|
|
callback(err);
|
2013-11-20 13:13:39 -05:00
|
|
|
}
|
2014-02-24 04:14:07 -05:00
|
|
|
return;
|
|
|
|
}
|
2013-11-21 05:37:18 -05:00
|
|
|
|
2014-02-24 04:14:07 -05:00
|
|
|
// remove the pending mail from the storage
|
|
|
|
removeFromStorage(mail, done);
|
2013-11-20 13:13:39 -05:00
|
|
|
|
2014-02-24 04:14:07 -05:00
|
|
|
// fire sent notification
|
|
|
|
if (typeof self.onSent === 'function') {
|
|
|
|
self.onSent(mail);
|
|
|
|
}
|
2014-02-27 09:23:33 -05:00
|
|
|
}
|
2014-02-24 04:14:07 -05:00
|
|
|
}
|
2013-11-20 13:13:39 -05:00
|
|
|
|
2014-02-24 04:14:07 -05:00
|
|
|
// removes the mail object from disk after successfully sending it
|
|
|
|
function removeFromStorage(mail, done) {
|
2014-05-23 08:23:50 -04:00
|
|
|
self._devicestorage.removeList(outboxDb + '_' + mail.uid, function(err) {
|
2014-02-24 04:14:07 -05:00
|
|
|
if (err) {
|
|
|
|
self._outboxBusy = false;
|
|
|
|
callback(err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-11-19 10:14:48 -05:00
|
|
|
return OutboxBO;
|
|
|
|
});
|