1
0
mirror of https://github.com/moparisthebest/mail synced 2024-11-29 20:32:15 -05:00

[WO-18] introduce invitation email functionality

This commit is contained in:
Felix Hammerl 2013-11-20 19:13:39 +01:00
parent 8e8947e742
commit 93ddfb1c99
4 changed files with 159 additions and 23 deletions

View File

@ -38,6 +38,8 @@ define([], function() {
*/ */
app.string = { app.string = {
subjectPrefix: '[whiteout] ', subjectPrefix: '[whiteout] ',
invitationSubject: 'Invitation to your secure eMail experience',
invitationMessage: 'I would like to invite you to a private conversation. To read my encrypted messages, simply install Whiteout Mail for Chrome. The app is really easy to use and automatically encrypts sent emails, so that only the two of us can read them: https://chrome.google.com/webstore/detail/jjgghafhamholjigjoghcfcekhkonijg',
message: 'this is a private conversation. To read my encrypted message below, simply install Whiteout Mail for Chrome. The app is really easy to use and automatically encrypts sent emails, so that only the two of us can read them: https://chrome.google.com/webstore/detail/jjgghafhamholjigjoghcfcekhkonijg', message: 'this is a private conversation. To read my encrypted message below, simply install Whiteout Mail for Chrome. The app is really easy to use and automatically encrypts sent emails, so that only the two of us can read them: https://chrome.google.com/webstore/detail/jjgghafhamholjigjoghcfcekhkonijg',
cryptPrefix: '-----BEGIN PGP MESSAGE-----', cryptPrefix: '-----BEGIN PGP MESSAGE-----',
cryptSuffix: '-----END PGP MESSAGE-----', cryptSuffix: '-----END PGP MESSAGE-----',

View File

@ -1,7 +1,10 @@
define(function(require) { define(function(require) {
'use strict'; 'use strict';
var config = require('js/app-config').config, var _ = require('underscore'),
str = require('js/app-config').string,
config = require('js/app-config').config,
InvitationDAO = require('js/dao/invitation-dao'),
dbType = 'email_OUTBOX'; dbType = 'email_OUTBOX';
var OutboxBO = function(emailDao, invitationDao) { var OutboxBO = function(emailDao, invitationDao) {
@ -12,7 +15,7 @@ define(function(require) {
OutboxBO.prototype.startChecking = function(callback) { OutboxBO.prototype.startChecking = function(callback) {
// start periodic checking of outbox // start periodic checking of outbox
this._intervalId = setInterval(this._emptyOutbox.bind(this, callback), config.checkOutboxInterval); this._intervalId = setInterval(this._processOutbox.bind(this, callback), config.checkOutboxInterval);
}; };
OutboxBO.prototype.stopChecking = function() { OutboxBO.prototype.stopChecking = function() {
@ -24,7 +27,7 @@ define(function(require) {
delete this._intervalId; delete this._intervalId;
}; };
OutboxBO.prototype._emptyOutbox = function(callback) { OutboxBO.prototype._processOutbox = function(callback) {
var self = this, var self = this,
emails; emails;
@ -49,19 +52,115 @@ define(function(require) {
emails = pending; emails = pending;
// sending pending mails // sending pending mails
send(); processMails();
}); });
} }
function send() { function processMails() {
callback(null, emails.length); // in the navigation controller, this updates the folder count
if (emails.length === 0) { if (emails.length === 0) {
self._outboxBusy = false; self._outboxBusy = false;
callback(null, 0);
return; return;
} }
callback(null, emails.length);
var email = emails.shift(); var email = emails.shift();
checkReceivers(email);
}
function checkReceivers(email) {
var unregisteredUsers, receiverChecked;
unregisteredUsers = [];
receiverChecked = _.after(email.to.length, function() {
if (unregisteredUsers.length > 0) {
invite(unregisteredUsers);
return;
}
sendEncrypted(email);
});
email.to.forEach(function(recipient) {
self._emailDao._keychain.getReceiverPublicKey(recipient.address, function(err, key) {
if (err) {
// stop processing
}
if (!key) {
unregisteredUsers.push(recipient);
}
receiverChecked();
});
});
}
function invite(addresses) {
var sender = self._emailDao._account.emailAddress;
var invitationFinished = _.after(addresses.length, function() {
// after all of the invitations are checked and sent (if necessary),
//
processMails();
});
// send invite
addresses.forEach(function(recipient) {
var recipientAddress = recipient.address;
self._invitationDao.check({
recipient: recipientAddress,
sender: sender
}, function(err, status) {
if (status === InvitationDAO.INVITE_PENDING) {
// the recipient is already invited, we're done here.
invitationFinished();
return;
}
// the recipient is not yet invited, so let's do that
self._invitationDao.invite({
recipient: recipientAddress,
sender: sender
}, function(err, status) {
if (err) {
console.error(err.errMsg);
return;
}
if (status !== InvitationDAO.INVITE_SUCCESS) {
console.error('could not successfully invite ' + recipientAddress);
return;
}
sendInvitationMail(recipient, sender);
});
});
});
function sendInvitationMail(recipient, sender) {
var to = (recipient.name || recipient.address).split('@')[0].split('.')[0].split(' ')[0],
invitationMail = {
from: [sender],
to: [recipient],
subject: str.invitationSubject,
body: 'Hi ' + to + ',\n\n' + str.invitationMessage + '\n\n\n' + str.signature
};
// send invitation mail
self._emailDao.send(invitationMail, function(err) {
if (err) {
console.error(err.errMsg);
}
invitationFinished();
});
}
}
function sendEncrypted(email) {
self._emailDao.encryptedSend(email, function(err) { self._emailDao.encryptedSend(email, function(err) {
if (err) { if (err) {
self._outboxBusy = false; self._outboxBusy = false;
@ -91,7 +190,7 @@ define(function(require) {
return; return;
} }
send(); processMails();
}); });
} }
}; };

View File

@ -578,8 +578,6 @@ define(function(require) {
callback({ callback({
errMsg: 'User has no public key yet!' errMsg: 'User has no public key yet!'
}); });
// user hasn't registered a public key yet... invite
//self.encryptForNewUser(email, callback);
return; return;
} }

View File

@ -2,17 +2,24 @@ define(function(require) {
'use strict'; 'use strict';
var expect = chai.expect, var expect = chai.expect,
_ = require('underscore'),
OutboxBO = require('js/bo/outbox'), OutboxBO = require('js/bo/outbox'),
KeychainDAO = require('js/dao/keychain-dao'),
EmailDAO = require('js/dao/email-dao'), EmailDAO = require('js/dao/email-dao'),
DeviceStorageDAO = require('js/dao/devicestorage-dao'), DeviceStorageDAO = require('js/dao/devicestorage-dao'),
InvitationDAO = require('js/dao/invitation-dao'); InvitationDAO = require('js/dao/invitation-dao');
describe('Outbox Business Object unit test', function() { describe('Outbox Business Object unit test', function() {
var outbox, emailDaoStub, devicestorageStub, invitationDaoStub; var outbox, emailDaoStub, devicestorageStub, invitationDaoStub, keychainStub,
dummyUser = 'spiderpig@springfield.com';
beforeEach(function() { beforeEach(function() {
emailDaoStub = sinon.createStubInstance(EmailDAO); emailDaoStub = sinon.createStubInstance(EmailDAO);
emailDaoStub._account = {
emailAddress: dummyUser
};
emailDaoStub._devicestorage = devicestorageStub = sinon.createStubInstance(DeviceStorageDAO); emailDaoStub._devicestorage = devicestorageStub = sinon.createStubInstance(DeviceStorageDAO);
emailDaoStub._keychain = keychainStub = sinon.createStubInstance(KeychainDAO);
invitationDaoStub = sinon.createStubInstance(InvitationDAO); invitationDaoStub = sinon.createStubInstance(InvitationDAO);
outbox = new OutboxBO(emailDaoStub, invitationDaoStub); outbox = new OutboxBO(emailDaoStub, invitationDaoStub);
}); });
@ -42,25 +49,55 @@ define(function(require) {
}); });
}); });
describe('empty outbox', function() { describe('process outbox', function() {
it('should work', function(done) { it('should work', function(done) {
devicestorageStub.listItems.yields(null, [{ var dummyMails = [{
id: '12345' id: '123',
}]); to: [{
emailDaoStub.encryptedSend.yields(); name: 'member',
devicestorageStub.removeList.yields(); address: 'member@whiteout.io'
}]
}, {
id: '456',
to: [{
name: 'invited',
address: 'invited@whiteout.io'
}]
}, {
id: '789',
to: [{
name: 'notinvited',
address: 'notinvited@whiteout.io'
}]
}];
devicestorageStub.listItems.yieldsAsync(null, dummyMails);
emailDaoStub.encryptedSend.yieldsAsync();
emailDaoStub.send.yieldsAsync();
devicestorageStub.removeList.yieldsAsync();
invitationDaoStub.check.withArgs(sinon.match(function(o) { return o.recipient === 'invited@whiteout.io'; })).yieldsAsync(null, InvitationDAO.INVITE_PENDING);
invitationDaoStub.check.withArgs(sinon.match(function(o) { return o.recipient === 'notinvited@whiteout.io'; })).yieldsAsync(null, InvitationDAO.INVITE_MISSING);
invitationDaoStub.invite.withArgs(sinon.match(function(o) { return o.recipient === 'notinvited@whiteout.io'; })).yieldsAsync(null, InvitationDAO.INVITE_SUCCESS);
keychainStub.getReceiverPublicKey.withArgs(sinon.match(function(o) { return o === 'member@whiteout.io'; })).yieldsAsync(null, 'this is not the key you are looking for...');
keychainStub.getReceiverPublicKey.withArgs(sinon.match(function(o) { return o === 'invited@whiteout.io' || o === 'notinvited@whiteout.io'; })).yieldsAsync();
var check = _.after(dummyMails.length + 1, function() {
expect(devicestorageStub.listItems.callCount).to.equal(1);
expect(emailDaoStub.encryptedSend.callCount).to.equal(1);
expect(emailDaoStub.send.callCount).to.equal(1);
expect(devicestorageStub.removeList.callCount).to.equal(1);
expect(invitationDaoStub.check.callCount).to.equal(2);
expect(invitationDaoStub.invite.callCount).to.equal(1);
done();
});
function onOutboxUpdate(err, count) { function onOutboxUpdate(err, count) {
expect(err).to.not.exist; expect(err).to.not.exist;
if (count === 0) { expect(count).to.exist;
expect(devicestorageStub.listItems.callCount).to.equal(1); check();
expect(emailDaoStub.encryptedSend.callCount).to.equal(1);
expect(devicestorageStub.removeList.callCount).to.equal(1);
done();
}
} }
outbox._emptyOutbox(onOutboxUpdate); outbox._processOutbox(onOutboxUpdate);
}); });
}); });
}); });