From badea7ab8a1a516674afa30573a183f4096b99ed Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Thu, 27 Feb 2014 15:23:33 +0100 Subject: [PATCH] allow plaintext sending and remove whiteout tag from subject --- src/js/app-config.js | 8 +- src/js/app-controller.js | 10 +- src/js/bo/outbox.js | 202 ++++-------------------------- src/js/controller/read.js | 28 ++++- src/js/controller/write.js | 19 +-- src/js/dao/email-dao.js | 11 +- src/sass/components/_buttons.scss | 15 +++ src/tpl/write.html | 4 +- 8 files changed, 92 insertions(+), 205 deletions(-) diff --git a/src/js/app-config.js b/src/js/app-config.js index 8c66a00..7a56ba8 100644 --- a/src/js/app-config.js +++ b/src/js/app-config.js @@ -53,15 +53,15 @@ define(function(require) { subjectPrefix: '[whiteout] ', fallbackSubject: '(no subject)', invitationSubject: 'Invitation to a private conversation', - invitationMessage: 'I would like to invite you to a private conversation!\n\nIn order to read my encrypted message please install the Whiteout Mail application. This application is used to read and write messages securely with strong encryption applied.\n\nGo to the Whiteout Networks homepage to learn more and to download the application: https://whiteout.io', + invitationMessage: 'Hi,\n\nI would like to invite you to a private conversation!\n\nPlease install the Whiteout Mail application. This application is used to read and write messages securely with strong encryption applied.\n\nGo to the Whiteout Networks homepage to learn more and to download the application: https://whiteout.io\n\n', message: 'Hi,\n\nthis is a private conversation. To read my encrypted message below, simply open it in Whiteout Mail.\n\nOpen Whiteout Mail: https://chrome.google.com/webstore/detail/jjgghafhamholjigjoghcfcekhkonijg\n\n', cryptPrefix: '-----BEGIN PGP MESSAGE-----', cryptSuffix: '-----END PGP MESSAGE-----', - signature: 'Sent securely from Whiteout Mail', + signature: '\n\n\n--\nSent from Whiteout Mail - get the app for easy end-to-end encryption\nhttps://whiteout.io\n\n', webSite: 'http://whiteout.io', verificationSubject: 'New public key uploaded', - sendBtnInvite: 'Invite & send securely', - sendBtnSecure: 'Send securely' + sendBtnClear: 'Send', + sendBtnSecure: 'Secure send' }; return app; diff --git a/src/js/app-controller.js b/src/js/app-controller.js index 2c55d38..1e3139e 100644 --- a/src/js/app-controller.js +++ b/src/js/app-controller.js @@ -348,17 +348,15 @@ define(function(require) { // init objects and inject dependencies restDao = new RestDAO(); pubkeyDao = new PublicKeyDAO(restDao); - invitationDao = new InvitationDAO(restDao); lawnchairDao = new LawnchairDAO(); userStorage = new DeviceStorageDAO(lawnchairDao); - keychain = new KeychainDAO(lawnchairDao, pubkeyDao); - self._keychain = keychain; - pgp = new PGP(); - self._crypto = pgp; + self._invitationDao = invitationDao = new InvitationDAO(restDao); + self._keychain = keychain = new KeychainDAO(lawnchairDao, pubkeyDao); + self._crypto = pgp = new PGP(); self._pgpbuilder = pgpbuilder = new PgpBuilder(); self._emailDao = emailDao = new EmailDAO(keychain, pgp, userStorage, pgpbuilder, mailreader); - self._outboxBo = new OutboxBO(emailDao, keychain, userStorage, invitationDao); + self._outboxBo = new OutboxBO(emailDao, keychain, userStorage); }; /** diff --git a/src/js/bo/outbox.js b/src/js/bo/outbox.js index 69e7036..14f14a0 100644 --- a/src/js/bo/outbox.js +++ b/src/js/bo/outbox.js @@ -3,9 +3,7 @@ define(function(require) { var _ = require('underscore'), util = require('cryptoLib/util'), - str = require('js/app-config').string, config = require('js/app-config').config, - InvitationDAO = require('js/dao/invitation-dao'), outboxDb = 'email_OUTBOX'; /** @@ -13,7 +11,7 @@ define(function(require) { * The local outbox takes care of the emails before they are being sent. * It also checks periodically if there are any mails in the local device storage to be sent. */ - var OutboxBO = function(emailDao, keychain, devicestorage, invitationDao) { + var OutboxBO = function(emailDao, keychain, devicestorage) { /** @private */ this._emailDao = emailDao; @@ -23,10 +21,6 @@ define(function(require) { /** @private */ this._devicestorage = devicestorage; - - /** @private */ - this._invitationDao = invitationDao; - /** * Semaphore-esque flag to avoid 'concurrent' calls to _processOutbox when the timeout fires, but a call is still in process. * @private */ @@ -64,7 +58,6 @@ define(function(require) { 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 - mail.unregisteredUsers = []; // gather the recipients for which no public key is available mail.id = util.UUID(); // the mail needs a random uuid for storage in the database checkRecipients(allReaders); @@ -87,8 +80,6 @@ define(function(require) { // otherwise remember the recipient as unregistered for later sending if (key) { mail.publicKeysArmored.push(key.publicKey); - } else { - mail.unregisteredUsers.push(recipient); } after(); @@ -96,8 +87,15 @@ define(function(require) { }); } - // encrypts the body and attachments and persists the mail object + function encryptAndPersist() { + // only encrypt if all recipients have public keys + if (mail.publicKeysArmored.length < allReaders.length) { + self._devicestorage.storeList([mail], outboxDb, callback); + return; + } + + // encrypts the body and attachments and persists the mail object self._emailDao.encrypt({ mail: mail, publicKeysArmored: mail.publicKeysArmored @@ -152,90 +150,27 @@ define(function(require) { // send pending mails if possible pendingMails.forEach(function(mail) { - handleMail(mail, after); + send(mail, after); }); }); - // if we can send the mail, do that. otherwise check if there are users that need to be invited - function handleMail(mail, done) { - // no unregistered users, go straight to send - if (mail.unregisteredUsers.length === 0) { - send(mail, done); - return; + // send the message + function send(mail, done) { + + // 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); } - var after = _.after(mail.unregisteredUsers.length, function() { - // invite unregistered users if necessary - if (mail.unregisteredUsers.length > 0) { - unsentMails++; - self._invite({ - sender: mail.from[0], - recipients: mail.unregisteredUsers - }, done); - return; - } - - // there are public keys available for the missing users, - // so let's re-encrypt the mail for them and send it - reencryptAndSend(mail, done); - }); - - // find out if the unregistered users have registered in the meantime - mail.unregisteredUsers.forEach(function(recipient) { - self._keychain.getReceiverPublicKey(recipient.address, function(err, key) { - var index; - - if (err) { - self._outboxBusy = false; - callback(err); - return; - } - - if (key) { - // remove the newly joined users from the unregistered users - index = mail.unregisteredUsers.indexOf(recipient); - mail.unregisteredUsers.splice(index, 1); - mail.publicKeysArmored.push(key.publicKey); - } - - after(); - }); - }); - } - - // all the recipients have public keys available, so let's re-encrypt the mail - // to make it available for them, too - function reencryptAndSend(mail, done) { - self._emailDao.reEncrypt({ - mail: mail, - publicKeysArmored: mail.publicKeysArmored - }, function(err) { - if (err) { - self._outboxBusy = false; - callback(err); - return; - } - - // stores the newly encrypted mail object to disk in case something funky - // happens during sending and we need do re-send the mail later. - // avoids doing the encryption twice... - self._devicestorage.storeList([mail], outboxDb, function(err) { - if (err) { - self._outboxBusy = false; - callback(err); - return; - } - - send(mail, done); - }); - }); - } - - // send the encrypted message - function send(mail, done) { - self._emailDao.sendEncrypted({ - email: mail - }, function(err) { + function onSend(err) { if (err) { self._outboxBusy = false; if (err.code === 42) { @@ -255,7 +190,7 @@ define(function(require) { if (typeof self.onSent === 'function') { self.onSent(mail); } - }); + } } // removes the mail object from disk after successfully sending it @@ -272,90 +207,5 @@ define(function(require) { } }; - /** - * Sends an invitation mail to an array of users that have no public key available yet - * @param {Array} recipients Array of objects with information on the sender (name, address) - * @param {Function} callback Invoked when the mail was sent - */ - OutboxBO.prototype._invite = function(options, callback) { - var self = this, - sender = options.sender; - - var after = _.after(options.recipients.length, callback); - - options.recipients.forEach(function(recipient) { - checkInvitationStatus(recipient, after); - }); - - // checks the invitation status. if an invitation is pending, we do not need to resend the invitation mail - function checkInvitationStatus(recipient, done) { - self._invitationDao.check({ - recipient: recipient.address, - sender: sender.address - }, function(err, status) { - if (err) { - callback(err); - return; - } - - if (status === InvitationDAO.INVITE_PENDING) { - // the recipient is already invited, we're done here. - done(); - return; - } - - invite(recipient, done); - }); - } - - // let's invite the recipient and send him a mail to inform him to join whiteout - function invite(recipient, done) { - self._invitationDao.invite({ - recipient: recipient.address, - sender: sender.address - }, function(err, status) { - if (err) { - callback(err); - return; - } - if (status !== InvitationDAO.INVITE_SUCCESS) { - callback({ - errMsg: 'Could not successfully invite ' + recipient - }); - return; - } - - var invitationMail = { - from: [sender], - to: [recipient], - subject: str.invitationSubject, - body: 'Hi,\n\n' + str.invitationMessage + '\n\n' - }; - - // send invitation mail - self._emailDao.sendPlaintext({ - email: invitationMail - }, function(err) { - if (err) { - if (err.code === 42) { - // offline try again later - done(); - } else { - callback(err); - } - return; - } - - // fire sent notification - if (typeof self.onSent === 'function') { - self.onSent(invitationMail); - } - - done(); - }); - }); - } - }; - return OutboxBO; }); \ No newline at end of file diff --git a/src/js/controller/read.js b/src/js/controller/read.js index b5f90d2..ae062db 100644 --- a/src/js/controller/read.js +++ b/src/js/controller/read.js @@ -4,7 +4,8 @@ define(function(require) { var appController = require('js/app-controller'), download = require('js/util/download'), angular = require('angular'), - emailDao, crypto, keychain; + str = require('js/app-config').string, + emailDao, invitationDao, outbox, crypto, keychain; // // Controller @@ -13,6 +14,8 @@ define(function(require) { var ReadCtrl = function($scope) { emailDao = appController._emailDao; + invitationDao = appController._invitationDao; + outbox = appController._outboxBo; crypto = appController._crypto; keychain = appController._keychain; @@ -112,6 +115,29 @@ define(function(require) { }, $scope.onError); } }; + + $scope.inviteUser = function(address) { + invitationDao.invite({ + recipient: address, + sender: emailDao._account.emailAddress + }, function(err) { + if (err) { + $scope.onError(err); + return; + } + + var invitationMail = { + from: [emailDao._account.emailAddress], + to: [address], + subject: str.invitationSubject, + body: str.invitationMessage + }; + + // send invitation mail + outbox.put(invitationMail, $scope.onError); + }); + }; + }; // diff --git a/src/js/controller/write.js b/src/js/controller/write.js index 2c7dadc..9cea13c 100644 --- a/src/js/controller/write.js +++ b/src/js/controller/write.js @@ -171,16 +171,21 @@ define(function(require) { } } - // sender can invite only one use at a time - if (!allSecure && numReceivers === 1) { - $scope.sendBtnText = str.sendBtnInvite; + // only allow sending if receviers exist + if (numReceivers < 1) { + return; + } + + if (allSecure) { + // send encrypted if all secure $scope.okToSend = true; - $scope.sendBtnSecure = false; - } else if (allSecure && numReceivers > 0) { - // all recipients are secure $scope.sendBtnText = str.sendBtnSecure; - $scope.okToSend = true; $scope.sendBtnSecure = true; + } else { + // send plaintext + $scope.okToSend = true; + $scope.sendBtnText = str.sendBtnClear; + $scope.sendBtnSecure = false; } }; diff --git a/src/js/dao/email-dao.js b/src/js/dao/email-dao.js index a7047f9..8592a04 100644 --- a/src/js/dao/email-dao.js +++ b/src/js/dao/email-dao.js @@ -1034,9 +1034,6 @@ define(function(require) { return; } - // add whiteout tag to subject - options.email.subject = str.subjectPrefix + options.email.subject; - // mime encode, sign, encrypt and send email via smtp self._pgpMailer.send({ encrypt: true, @@ -1055,8 +1052,8 @@ define(function(require) { return; } - // add whiteout tag to subject - options.email.subject = str.subjectPrefix + options.email.subject; + // append the signature to plaintext mails + options.email.body += str.signature; // mime encode, sign and send email via smtp this._pgpMailer.send({ @@ -1068,10 +1065,6 @@ define(function(require) { this._pgpbuilder.encrypt(options, callback); }; - EmailDAO.prototype.reEncrypt = function(options, callback) { - this._pgpbuilder.reEncrypt(options, callback); - }; - // // Internal API // diff --git a/src/sass/components/_buttons.scss b/src/sass/components/_buttons.scss index 3f7b1bc..2a9a110 100755 --- a/src/sass/components/_buttons.scss +++ b/src/sass/components/_buttons.scss @@ -57,6 +57,21 @@ } } +.btn-primary { + background-color: $label-primary-back-color; + color: $label-primary-color; + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAGCAYAAADgzO9IAAAAJUlEQVQIW2NkQABJIPM5lCvJCGMgC4LYIAkUlTAFMB0gjRQaBQCw8go5lVnl5wAAAABJRU5ErkJggg==); + + @include respond-to(retina) { + background-size: 3px 3px; + } + + &:hover, + &:focus { + background-color: lighten($label-primary-back-color, 5%); + } +} + .btn-alt { background-color: $color-grey-input; diff --git a/src/tpl/write.html b/src/tpl/write.html index aeff5d8..da543f8 100644 --- a/src/tpl/write.html +++ b/src/tpl/write.html @@ -46,13 +46,13 @@

-
+

-----BEGIN ENCRYPTED PREVIEW-----
{{ciphertextPreview}}
-----END ENCRYPTED PREVIEW-----

- +