1
0
mirror of https://github.com/moparisthebest/mail synced 2024-08-13 16:43:47 -04:00

Merge remote-tracking branch 'origin/dev/fix-cleartext-leak'

This commit is contained in:
Tankred Hase 2013-12-06 16:50:21 +01:00
commit 18fe55af7a
2 changed files with 113 additions and 68 deletions

View File

@ -149,20 +149,20 @@ define(function(require) {
* delta2: memory > storage => we added messages, push to remote <<< not supported yet
*
* Second, we check the delta for the flags
* deltaF1: memory > storage => we changed flags, sync them to the remote and memory
* deltaF2: memory > storage => we changed flags, sync them to the remote and memory
*
* Third, we go on to sync between imap and memory, again based on uid
* delta3: memory > imap => we deleted messages directly from the remote, remove from memory and storage
* delta4: imap > memory => we have new messages available, fetch to memory and storage
*
* Fourth, we pull changes in the flags downstream
* deltaF2: imap > memory => we changed flags directly on the remote, sync them to the storage and memory
* deltaF4: imap > memory => we changed flags directly on the remote, sync them to the storage and memory
*/
var self = this,
folder,
delta1 /*, delta2 */ , delta3, delta4, //message
deltaF1, deltaF2,
deltaF2, deltaF4,
isFolderInitialized;
@ -202,27 +202,27 @@ define(function(require) {
folder.messages = [];
self._localListMessages({
folder: folder.path
}, function(err, messages) {
}, function(err, storedMessages) {
if (err) {
self._account.busy = false;
callback(err);
return;
}
if (_.isEmpty(messages)) {
if (_.isEmpty(storedMessages)) {
// if there's nothing here, we're good
callback();
doImapDelta();
return;
}
var after = _.after(messages.length, function() {
var after = _.after(storedMessages.length, function() {
callback();
doImapDelta();
});
messages.forEach(function(message) {
handleMessage(message, function(err, cleartextMessage) {
storedMessages.forEach(function(storedMessage) {
handleMessage(storedMessage, function(err, cleartextMessage) {
if (err) {
self._account.busy = false;
callback(err);
@ -239,7 +239,7 @@ define(function(require) {
function doLocalDelta() {
self._localListMessages({
folder: folder.path
}, function(err, messages) {
}, function(err, storedMessages) {
if (err) {
self._account.busy = false;
callback(err);
@ -249,28 +249,29 @@ define(function(require) {
/*
* delta1: storage > memory => we deleted messages, remove from remote
* delta2: memory > storage => we added messages, push to remote
* deltaF1: memory > storage => we changed flags, sync them to the remote and memory
* deltaF2: memory > storage => we changed flags, sync them to the remote and memory
*/
delta1 = checkDelta(messages, folder.messages);
// delta2 = checkDelta(folder.messages, messages); // not supported yet
deltaF1 = checkFlags(folder.messages, messages);
delta1 = checkDelta(storedMessages, folder.messages);
// delta2 = checkDelta(folder.messages, storedMessages); // not supported yet
deltaF2 = checkFlags(folder.messages, storedMessages);
doDelta1();
function doDelta1() {
if (_.isEmpty(delta1)) {
doDeltaF1();
doDeltaF2();
return;
}
var after = _.after(delta1.length, function() {
doDeltaF1();
doDeltaF2();
});
delta1.forEach(function(message) {
// deltaF2 contains references to the in-memory messages
delta1.forEach(function(inMemoryMessage) {
var deleteMe = {
folder: folder.path,
uid: message.uid
uid: inMemoryMessage.uid
};
self._imapDeleteMessage(deleteMe, function(err) {
@ -293,24 +294,25 @@ define(function(require) {
});
}
function doDeltaF1() {
if (_.isEmpty(deltaF1)) {
function doDeltaF2() {
if (_.isEmpty(deltaF2)) {
callback();
doImapDelta();
return;
}
var after = _.after(deltaF1.length, function() {
var after = _.after(deltaF2.length, function() {
callback();
doImapDelta();
});
deltaF1.forEach(function(message) {
// deltaF2 contains references to the in-memory messages
deltaF2.forEach(function(inMemoryMessage) {
self._imapMark({
folder: folder.path,
uid: message.uid,
unread: message.unread,
answered: message.answered
uid: inMemoryMessage.uid,
unread: inMemoryMessage.unread,
answered: inMemoryMessage.answered
}, function(err) {
if (err) {
self._account.busy = false;
@ -318,9 +320,16 @@ define(function(require) {
return;
}
var storedMessage = _.findWhere(storedMessages, {
uid: inMemoryMessage.uid
});
storedMessage.unread = inMemoryMessage.unread;
storedMessage.answered = inMemoryMessage.answered;
self._localStoreMessages({
folder: folder.path,
emails: [message]
emails: [storedMessage]
}, function(err) {
if (err) {
self._account.busy = false;
@ -357,11 +366,11 @@ define(function(require) {
/*
* delta3: memory > imap => we deleted messages directly from the remote, remove from memory and storage
* delta4: imap > memory => we have new messages available, fetch to memory and storage
* deltaF2: imap > memory => we changed flags directly on the remote, sync them to the storage and memory
* deltaF4: imap > memory => we changed flags directly on the remote, sync them to the storage and memory
*/
delta3 = checkDelta(folder.messages, headers);
delta4 = checkDelta(headers, folder.messages);
deltaF2 = checkFlags(headers, folder.messages);
deltaF4 = checkFlags(headers, folder.messages);
doDelta3();
@ -377,15 +386,12 @@ define(function(require) {
doDelta4();
});
delta3.forEach(function(header) {
// remove delta3 from memory
var idx = folder.messages.indexOf(header);
folder.messages.splice(idx, 1);
// delta3 contains references to the in-memory messages that have been deleted from the remote
delta3.forEach(function(inMemoryMessage) {
// remove delta3 from local storage
self._localDeleteMessage({
folder: folder.path,
uid: header.uid
uid: inMemoryMessage.uid
}, function(err) {
if (err) {
self._account.busy = false;
@ -393,6 +399,10 @@ define(function(require) {
return;
}
// remove delta3 from memory
var idx = folder.messages.indexOf(inMemoryMessage);
folder.messages.splice(idx, 1);
after();
});
});
@ -403,19 +413,20 @@ define(function(require) {
function doDelta4() {
// no delta, we're done here
if (_.isEmpty(delta4)) {
doDeltaF2();
doDeltaF4();
return;
}
var after = _.after(delta4.length, function() {
doDeltaF2();
doDeltaF4();
});
delta4.forEach(function(header) {
// delta4 contains the headers that are newly available on the remote
delta4.forEach(function(imapHeader) {
// get the whole message
self._imapGetMessage({
folder: folder.path,
uid: header.uid
uid: imapHeader.uid
}, function(err, message) {
if (err) {
self._account.busy = false;
@ -438,15 +449,15 @@ define(function(require) {
// create a bastard child of smtp and imap.
// before thinking this is stupid, talk to the guys who wrote this.
header.id = message.id;
header.body = message.body;
header.html = message.html;
header.attachments = message.attachments;
imapHeader.id = message.id;
imapHeader.body = message.body;
imapHeader.html = message.html;
imapHeader.attachments = message.attachments;
// add the encrypted message to the local storage
self._localStoreMessages({
folder: folder.path,
emails: [header]
emails: [imapHeader]
}, function(err) {
if (err) {
self._account.busy = false;
@ -455,7 +466,7 @@ define(function(require) {
}
// decrypt and add to folder in memory
handleMessage(header, function(err, cleartextMessage) {
handleMessage(imapHeader, function(err, cleartextMessage) {
if (err) {
self._account.busy = false;
callback(err);
@ -472,37 +483,55 @@ define(function(require) {
// we have a mismatch concerning flags between imap and memory.
// pull changes from imap.
function doDeltaF2() {
if (_.isEmpty(deltaF2)) {
function doDeltaF4() {
if (_.isEmpty(deltaF4)) {
self._account.busy = false;
callback();
return;
}
var after = _.after(deltaF2.length, function() {
var after = _.after(deltaF4.length, function() {
self._account.busy = false;
callback();
});
deltaF2.forEach(function(header) {
// we don't work on the header, we work on the live object
var msg = _.findWhere(folder.messages, {
uid: header.uid
});
msg.unread = header.unread;
msg.answered = header.answered;
self._localStoreMessages({
// deltaF4 contains the imap headers that have changed flags
deltaF4.forEach(function(imapHeader) {
// do a short round trip to the database to avoid re-encrypting,
// instead use the encrypted object in the storage
self._localListMessages({
folder: folder.path,
emails: [msg]
}, function(err) {
uid: imapHeader.uid
}, function(err, storedMessages) {
if (err) {
self._account.busy = false;
callback(err);
return;
}
after();
var storedMessage = storedMessages[0];
storedMessage.unread = imapHeader.unread;
storedMessage.answered = imapHeader.answered;
self._localStoreMessages({
folder: folder.path,
emails: [storedMessage]
}, function(err) {
if (err) {
self._account.busy = false;
callback(err);
return;
}
// after the metadata of the encrypted object has changed, proceed with the live object
var inMemoryMessage = _.findWhere(folder.messages, {
uid: imapHeader.uid
});
inMemoryMessage.unread = imapHeader.unread;
inMemoryMessage.answered = imapHeader.answered;
after();
});
});
});
}
@ -796,6 +825,9 @@ define(function(require) {
EmailDAO.prototype._localListMessages = function(options, callback) {
var dbType = 'email_' + options.folder;
if (typeof options.uid !== 'undefined') {
dbType = dbType + '_' + options.uid;
}
this._devicestorage.listItems(dbType, 0, null, callback);
};

View File

@ -532,7 +532,7 @@ define(function(require) {
});
describe('_localListMessages', function() {
it('should work', function(done) {
it('should work without uid', function(done) {
var folder = 'FOLDAAAA';
devicestorageStub.listItems.withArgs('email_' + folder, 0, null).yields();
@ -540,6 +540,17 @@ define(function(require) {
folder: folder
}, done);
});
it('should work with uid', function(done) {
var folder = 'FOLDAAAA',
uid = 123;
devicestorageStub.listItems.withArgs('email_' + folder + '_' + uid, 0, null).yields();
dao._localListMessages({
folder: folder,
uid: uid
}, done);
});
});
describe('_localStoreMessages', function() {
@ -1007,7 +1018,7 @@ define(function(require) {
expect(err).to.exist;
expect(dao._account.busy).to.be.false;
expect(dao._account.folders[0].messages).to.be.empty;
expect(dao._account.folders[0].messages).to.not.be.empty;
expect(localListStub.calledOnce).to.be.true;
expect(imapListStub.calledOnce).to.be.true;
expect(localDeleteStub.calledOnce).to.be.true;
@ -1517,7 +1528,7 @@ define(function(require) {
}).yields();
localStoreStub = sinon.stub(dao, '_localStoreMessages').withArgs({
folder: folder,
emails: [dummyDecryptedMail]
emails: [inStorage]
}).yields();
dao.sync({
@ -1538,6 +1549,9 @@ define(function(require) {
expect(markStub.calledOnce).to.be.true;
expect(localStoreStub.calledOnce).to.be.true;
expect(inStorage.unread).to.equal(dummyDecryptedMail.unread);
expect(inStorage.answered).to.equal(dummyDecryptedMail.answered);
done();
});
});
@ -1633,10 +1647,7 @@ define(function(require) {
localListStub = sinon.stub(dao, '_localListMessages').yields(null, [inStorage]);
imapListStub = sinon.stub(dao, '_imapListMessages').yields(null, [inImap]);
markStub = sinon.stub(dao, '_imapMark');
localStoreStub = sinon.stub(dao, '_localStoreMessages').withArgs({
folder: folder,
emails: [dummyDecryptedMail]
}).yields();
localStoreStub = sinon.stub(dao, '_localStoreMessages').yields();
dao.sync({
folder: folder
@ -1651,12 +1662,13 @@ define(function(require) {
expect(dao._account.busy).to.be.false;
expect(dao._account.folders[0]).to.not.be.empty;
expect(localListStub.calledOnce).to.be.true;
expect(localListStub.calledTwice).to.be.true;
expect(imapListStub.calledOnce).to.be.true;
expect(markStub.called).to.be.false;
expect(localStoreStub.calledOnce).to.be.true;
expect(dummyDecryptedMail.unread).to.equal(inImap.unread);
expect(inStorage.unread).to.equal(inImap.unread);
done();
});
@ -1697,12 +1709,13 @@ define(function(require) {
expect(err).to.exist;
expect(dao._account.busy).to.be.false;
expect(dao._account.folders[0]).to.not.be.empty;
expect(localListStub.calledOnce).to.be.true;
expect(localListStub.calledTwice).to.be.true;
expect(imapListStub.calledOnce).to.be.true;
expect(markStub.called).to.be.false;
expect(localStoreStub.calledOnce).to.be.true;
expect(dummyDecryptedMail.unread).to.equal(inImap.unread);
expect(inStorage.unread).to.equal(inImap.unread);
expect(dummyDecryptedMail.unread).to.equal(true); // the live object has not been touched!
done();
});