2013-11-19 10:14:48 -05:00
|
|
|
define(function(require) {
|
|
|
|
'use strict';
|
|
|
|
|
2013-11-20 13:13:39 -05:00
|
|
|
var _ = require('underscore'),
|
|
|
|
str = require('js/app-config').string,
|
|
|
|
config = require('js/app-config').config,
|
|
|
|
InvitationDAO = require('js/dao/invitation-dao'),
|
2013-11-19 10:14:48 -05:00
|
|
|
dbType = 'email_OUTBOX';
|
|
|
|
|
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.
|
|
|
|
*/
|
2013-12-02 09:48:59 -05:00
|
|
|
var OutboxBO = function(emailDao, keychain, devicestorage, invitationDao) {
|
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;
|
|
|
|
|
|
|
|
|
|
|
|
/** @private */
|
2013-12-02 09:48:59 -05:00
|
|
|
this._invitationDao = invitationDao;
|
2013-11-21 11:37:07 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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-11-21 11:37:07 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Pending, unsent emails stored in the outbox. Updated on each call to _processOutbox
|
|
|
|
* @public */
|
|
|
|
this.pendingEmails = [];
|
2013-11-19 10:14:48 -05:00
|
|
|
};
|
|
|
|
|
2013-12-04 11:13:45 -05:00
|
|
|
OutboxBO.prototype.init = function() {
|
|
|
|
var outboxFolder = _.findWhere(this._emailDao._account.folders, {
|
|
|
|
type: 'Outbox'
|
|
|
|
});
|
|
|
|
outboxFolder.messages = this.pendingEmails;
|
|
|
|
};
|
|
|
|
|
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) {
|
|
|
|
// start periodic checking of outbox
|
2013-11-20 13:13:39 -05:00
|
|
|
this._intervalId = setInterval(this._processOutbox.bind(this, callback), 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
|
|
|
/**
|
|
|
|
* Private Api which is called whenever a message has been sent
|
|
|
|
* The public callback "onSent" can be set by the caller to get notified.
|
|
|
|
*/
|
|
|
|
OutboxBO.prototype._onSent = function(message) {
|
|
|
|
if (typeof this.onSent === 'function') {
|
|
|
|
this.onSent(message);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
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,
|
|
|
|
emails;
|
|
|
|
|
2013-11-21 05:37:18 -05:00
|
|
|
// if a _processOutbox call is still in progress when a new timeout kicks
|
|
|
|
// in, since sending mails might take time, ignore it. otherwise, mails
|
|
|
|
// could get sent multiple times
|
2013-11-19 10:14:48 -05:00
|
|
|
if (self._outboxBusy) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
checkStorage();
|
|
|
|
|
|
|
|
function checkStorage() {
|
|
|
|
self._outboxBusy = true;
|
|
|
|
|
|
|
|
// get last item from outbox
|
2014-02-06 05:55:24 -05:00
|
|
|
self._emailDao.listForOutbox(function(err, pending) {
|
2013-11-19 10:14:48 -05:00
|
|
|
if (err) {
|
|
|
|
self._outboxBusy = false;
|
|
|
|
callback(err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// update outbox folder count
|
|
|
|
emails = pending;
|
|
|
|
|
2013-12-04 11:13:45 -05:00
|
|
|
// fill all the pending mails into the pending mails array
|
|
|
|
self.pendingEmails.length = 0; //fastest way to empty an array
|
|
|
|
pending.forEach(function(i) {
|
|
|
|
self.pendingEmails.push(i);
|
|
|
|
});
|
2013-11-21 11:37:07 -05:00
|
|
|
|
2014-01-20 05:03:01 -05:00
|
|
|
// we're not online, don't even bother sending mails
|
|
|
|
if (!self._emailDao._account.online) {
|
|
|
|
self._outboxBusy = false;
|
|
|
|
callback(null, self.pendingEmails.length);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-11-19 10:14:48 -05:00
|
|
|
// sending pending mails
|
2013-11-20 13:13:39 -05:00
|
|
|
processMails();
|
2013-11-19 10:14:48 -05:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2013-11-21 05:37:18 -05:00
|
|
|
// process the next pending mail
|
2013-11-20 13:13:39 -05:00
|
|
|
function processMails() {
|
2013-11-19 10:14:48 -05:00
|
|
|
if (emails.length === 0) {
|
2013-11-21 05:37:18 -05:00
|
|
|
// in the navigation controller, this updates the folder count
|
2013-11-19 10:14:48 -05:00
|
|
|
self._outboxBusy = false;
|
2013-11-21 11:37:07 -05:00
|
|
|
callback(null, self.pendingEmails.length);
|
2013-11-19 10:14:48 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-11-21 05:37:18 -05:00
|
|
|
// in the navigation controller, this updates the folder count
|
2013-11-21 11:37:07 -05:00
|
|
|
callback(null, self.pendingEmails.length);
|
2013-11-19 10:14:48 -05:00
|
|
|
var email = emails.shift();
|
2013-11-20 13:13:39 -05:00
|
|
|
checkReceivers(email);
|
|
|
|
}
|
|
|
|
|
2013-11-21 05:37:18 -05:00
|
|
|
// check whether there are unregistered receivers, i.e. receivers without a public key
|
2013-11-20 13:13:39 -05:00
|
|
|
function checkReceivers(email) {
|
|
|
|
var unregisteredUsers, receiverChecked;
|
|
|
|
|
|
|
|
unregisteredUsers = [];
|
|
|
|
receiverChecked = _.after(email.to.length, function() {
|
2013-11-21 05:37:18 -05:00
|
|
|
// invite unregistered users if necessary
|
2013-11-20 13:13:39 -05:00
|
|
|
if (unregisteredUsers.length > 0) {
|
|
|
|
invite(unregisteredUsers);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sendEncrypted(email);
|
|
|
|
});
|
|
|
|
|
2013-11-21 05:37:18 -05:00
|
|
|
// find out if there are unregistered users
|
2013-11-20 13:13:39 -05:00
|
|
|
email.to.forEach(function(recipient) {
|
2013-11-21 11:37:07 -05:00
|
|
|
self._keychain.getReceiverPublicKey(recipient.address, function(err, key) {
|
2013-11-20 13:13:39 -05:00
|
|
|
if (err) {
|
2013-11-21 05:37:18 -05:00
|
|
|
self._outboxBusy = false;
|
|
|
|
callback(err);
|
|
|
|
return;
|
2013-11-20 13:13:39 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!key) {
|
|
|
|
unregisteredUsers.push(recipient);
|
|
|
|
}
|
|
|
|
|
|
|
|
receiverChecked();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2013-11-21 05:37:18 -05:00
|
|
|
// invite the unregistered receivers, if necessary
|
2013-11-20 13:13:39 -05:00
|
|
|
function invite(addresses) {
|
2013-12-02 09:48:59 -05:00
|
|
|
var sender = self._emailDao._account.emailAddress;
|
2013-11-20 13:13:39 -05:00
|
|
|
|
|
|
|
var invitationFinished = _.after(addresses.length, function() {
|
|
|
|
// after all of the invitations are checked and sent (if necessary),
|
|
|
|
processMails();
|
|
|
|
});
|
|
|
|
|
2013-11-21 05:37:18 -05:00
|
|
|
// check which of the adresses has pending invitations
|
2013-11-20 13:13:39 -05:00
|
|
|
addresses.forEach(function(recipient) {
|
|
|
|
var recipientAddress = recipient.address;
|
2013-11-21 05:37:18 -05:00
|
|
|
|
2013-12-02 09:48:59 -05:00
|
|
|
self._invitationDao.check({
|
2013-11-20 13:13:39 -05:00
|
|
|
recipient: recipientAddress,
|
|
|
|
sender: sender
|
|
|
|
}, function(err, status) {
|
2013-11-21 05:37:18 -05:00
|
|
|
if (err) {
|
|
|
|
self._outboxBusy = false;
|
|
|
|
callback(err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-11-20 13:13:39 -05:00
|
|
|
if (status === InvitationDAO.INVITE_PENDING) {
|
|
|
|
// the recipient is already invited, we're done here.
|
|
|
|
invitationFinished();
|
|
|
|
return;
|
|
|
|
}
|
2013-11-21 05:37:18 -05:00
|
|
|
|
2013-11-20 13:13:39 -05:00
|
|
|
// the recipient is not yet invited, so let's do that
|
2013-12-02 09:48:59 -05:00
|
|
|
self._invitationDao.invite({
|
2013-11-20 13:13:39 -05:00
|
|
|
recipient: recipientAddress,
|
|
|
|
sender: sender
|
|
|
|
}, function(err, status) {
|
|
|
|
if (err) {
|
2013-11-21 05:37:18 -05:00
|
|
|
self._outboxBusy = false;
|
|
|
|
callback(err);
|
2013-11-20 13:13:39 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (status !== InvitationDAO.INVITE_SUCCESS) {
|
2013-11-21 05:37:18 -05:00
|
|
|
self._outboxBusy = false;
|
|
|
|
callback({
|
|
|
|
errMsg: 'could not successfully invite ' + recipientAddress
|
|
|
|
});
|
2013-11-20 13:13:39 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sendInvitationMail(recipient, sender);
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2013-11-21 05:37:18 -05:00
|
|
|
// send an invitation to the unregistered user, aka the recipient
|
2013-11-20 13:13:39 -05:00
|
|
|
function sendInvitationMail(recipient, sender) {
|
2014-01-31 13:14:43 -05:00
|
|
|
var invitationMail = {
|
|
|
|
from: [sender],
|
|
|
|
to: [recipient],
|
|
|
|
subject: str.invitationSubject,
|
|
|
|
body: 'Hi,\n\n' + str.invitationMessage + '\n\n'
|
|
|
|
};
|
2013-11-20 13:13:39 -05:00
|
|
|
|
|
|
|
// send invitation mail
|
2013-12-11 10:46:29 -05:00
|
|
|
self._emailDao.sendPlaintext({
|
|
|
|
email: invitationMail
|
|
|
|
}, function(err) {
|
2013-11-20 13:13:39 -05:00
|
|
|
if (err) {
|
2013-11-21 05:37:18 -05:00
|
|
|
self._outboxBusy = false;
|
2013-12-09 13:21:52 -05:00
|
|
|
if (err.code === 42) {
|
|
|
|
// offline try again later
|
|
|
|
callback();
|
|
|
|
} else {
|
|
|
|
callback(err);
|
|
|
|
}
|
2013-11-21 05:37:18 -05:00
|
|
|
return;
|
2013-11-20 13:13:39 -05:00
|
|
|
}
|
2013-12-05 09:02:41 -05:00
|
|
|
|
|
|
|
// fire sent notification
|
|
|
|
self._onSent(invitationMail);
|
|
|
|
|
2013-11-20 13:13:39 -05:00
|
|
|
invitationFinished();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function sendEncrypted(email) {
|
2013-12-04 09:36:20 -05:00
|
|
|
self._emailDao.sendEncrypted({
|
|
|
|
email: email
|
|
|
|
}, function(err) {
|
2013-11-19 10:14:48 -05:00
|
|
|
if (err) {
|
|
|
|
self._outboxBusy = false;
|
2013-12-09 13:21:52 -05:00
|
|
|
if (err.code === 42) {
|
|
|
|
// offline try again later
|
|
|
|
callback();
|
|
|
|
} else {
|
|
|
|
callback(err);
|
|
|
|
}
|
2013-11-19 10:14:48 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-01-19 11:03:04 -05:00
|
|
|
// the email has been sent, remove from pending mails
|
|
|
|
removeFromPendingMails(email);
|
|
|
|
|
2013-12-05 09:02:41 -05:00
|
|
|
// fire sent notification
|
|
|
|
self._onSent(email);
|
|
|
|
|
2013-11-19 10:14:48 -05:00
|
|
|
removeFromStorage(email.id);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2013-11-21 11:37:07 -05:00
|
|
|
// update the member so that the outbox can visualize
|
|
|
|
function removeFromPendingMails(email) {
|
|
|
|
var i = self.pendingEmails.indexOf(email);
|
|
|
|
self.pendingEmails.splice(i, 1);
|
|
|
|
}
|
|
|
|
|
2013-11-19 10:14:48 -05:00
|
|
|
function removeFromStorage(id) {
|
|
|
|
if (!id) {
|
|
|
|
self._outboxBusy = false;
|
|
|
|
callback({
|
|
|
|
errMsg: 'Cannot remove email from storage without a valid id!'
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// delete email from local storage
|
|
|
|
var key = dbType + '_' + id;
|
2013-11-21 11:37:07 -05:00
|
|
|
self._devicestorage.removeList(key, function(err) {
|
2013-11-19 10:14:48 -05:00
|
|
|
if (err) {
|
|
|
|
self._outboxBusy = false;
|
|
|
|
callback(err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-11-20 13:13:39 -05:00
|
|
|
processMails();
|
2013-11-19 10:14:48 -05:00
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
return OutboxBO;
|
|
|
|
});
|