Skip to content
This repository was archived by the owner on Jul 10, 2019. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/js/app-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ module.exports = appCfg;
* Global app configurations
*/
appCfg.config = {
pgpComment: 'Whiteout Mail - https://whiteout.io',
keyServerUrl: 'https://keys.whiteout.io',
hkpUrl: 'http://keyserver.ubuntu.com',
privkeyServerUrl: 'https://keychain.whiteout.io',
Expand Down
18 changes: 4 additions & 14 deletions src/js/controller/app/read.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@

var ReadCtrl = function($scope, $location, $q, email, invitation, outbox, pgp, keychain, appConfig, download, auth, dialog, status) {

var str = appConfig.string;

//
// scope state
//
Expand Down Expand Up @@ -158,18 +156,10 @@ var ReadCtrl = function($scope, $location, $q, email, invitation, outbox, pgp, k
});

}).then(function() {
var invitationMail = {
from: [{
address: sender
}],
to: [{
address: recipient
}],
cc: [],
bcc: [],
subject: str.invitationSubject,
body: str.invitationMessage
};
var invitationMail = invitation.createMail({
sender: sender,
recipient: recipient
});
// send invitation mail
return outbox.put(invitationMail);

Expand Down
59 changes: 58 additions & 1 deletion src/js/controller/app/write.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ var util = require('crypto-lib').util;
// Controller
//

var WriteCtrl = function($scope, $window, $filter, $q, appConfig, auth, keychain, pgp, email, outbox, dialog, axe, status) {
var WriteCtrl = function($scope, $window, $filter, $q, appConfig, auth, keychain, pgp, email, outbox, dialog, axe, status, invitation) {

var str = appConfig.string;
var cfg = appConfig.config;
Expand Down Expand Up @@ -52,6 +52,8 @@ var WriteCtrl = function($scope, $window, $filter, $q, appConfig, auth, keychain
$scope.body = '';
$scope.attachments = [];
$scope.addressBookCache = undefined;
$scope.showInvite = undefined;
$scope.invited = [];
}

function reportBug() {
Expand Down Expand Up @@ -248,6 +250,9 @@ var WriteCtrl = function($scope, $window, $filter, $q, appConfig, auth, keychain
recipient.key = key;
recipient.secure = true;
}
} else {
// show invite dialog if no key found
$scope.showInvite = true;
}
$scope.checkSendStatus();

Expand Down Expand Up @@ -286,6 +291,7 @@ var WriteCtrl = function($scope, $window, $filter, $q, appConfig, auth, keychain

// only allow sending if receviers exist
if (numReceivers < 1) {
$scope.showInvite = false;
return;
}

Expand All @@ -299,6 +305,7 @@ var WriteCtrl = function($scope, $window, $filter, $q, appConfig, auth, keychain
$scope.okToSend = true;
$scope.sendBtnText = str.sendBtnSecure;
$scope.sendBtnSecure = true;
$scope.showInvite = false;
} else {
// send plaintext
$scope.okToSend = true;
Expand All @@ -315,6 +322,56 @@ var WriteCtrl = function($scope, $window, $filter, $q, appConfig, auth, keychain
$scope.attachments.splice($scope.attachments.indexOf(attachment), 1);
};

/**
* Invite all users without a public key
*/
$scope.invite = function() {
var sender = auth.emailAddress,
sendJobs = [],
invitees = [];

$scope.showInvite = false;

// get recipients with no keys
$scope.to.forEach(check);
$scope.cc.forEach(check);
$scope.bcc.forEach(check);

function check(recipient) {
if (util.validateEmailAddress(recipient.address) && !recipient.secure && $scope.invited.indexOf(recipient.address) === -1) {
invitees.push(recipient.address);
}
}

return $q(function(resolve) {
resolve();

}).then(function() {
invitees.forEach(function(recipientAddress) {
var invitationMail = invitation.createMail({
sender: sender,
recipient: recipientAddress
});
// send invitation mail
var promise = outbox.put(invitationMail).then(function() {
return invitation.invite({
recipient: recipientAddress,
sender: sender
});
});
sendJobs.push(promise);
// remember already invited users to prevent spamming
$scope.invited.push(recipientAddress);
});

return Promise.all(sendJobs);

}).catch(function(err) {
$scope.showInvite = true;
return dialog.error(err);
});
};

//
// Editing email body
//
Expand Down
2 changes: 1 addition & 1 deletion src/js/crypto/pgp.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ var util = openpgp.util,
* High level crypto api that handles all calls to OpenPGP.js
*/
function PGP() {
openpgp.config.commentstring = 'Whiteout Mail - https://whiteout.io';
openpgp.config.commentstring = config.pgpComment;
openpgp.config.prefer_hash_algorithm = openpgp.enums.hash.sha256;
openpgp.initWorker(config.workerPath + '/openpgp.worker.min.js');
}
Expand Down
6 changes: 6 additions & 0 deletions src/js/email/outbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ Outbox.prototype.put = function(mail) {
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

if (mail.to.concat(mail.cc.concat(mail.bcc)).length === 0) {
return new Promise(function() {
throw new Error('Message has no recipients!');
});
}

mail.publicKeysArmored = []; // gather the public keys
mail.uid = mail.id = util.UUID(); // the mail needs a random id & uid for storage in the database

Expand Down
28 changes: 24 additions & 4 deletions src/js/service/invitation.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,33 @@ module.exports = Invitation;
* The Invitation is a high level Data Access Object that access the invitation service REST endpoint.
* @param {Object} restDao The REST Data Access Object abstraction
*/
function Invitation(invitationRestDao) {
function Invitation(invitationRestDao, appConfig) {
this._restDao = invitationRestDao;
this._appConfig = appConfig;
}

//
// API
//
/**
* Create the invitation mail object
* @param {String} options.sender The sender's email address
* @param {String} options.recipient The recipient's email address
* @return {Object} The mail object
*/
Invitation.prototype.createMail = function(options) {
var str = this._appConfig.string;

return {
from: [{
address: options.sender
}],
to: [{
address: options.recipient
}],
cc: [],
bcc: [],
subject: str.invitationSubject,
body: str.invitationMessage
};
};

/**
* Notes an invite for the recipient by the sender in the invitation web service
Expand Down
27 changes: 27 additions & 0 deletions src/sass/blocks/views/_write.scss
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,33 @@
margin-top: 0.5em;
}
}
&__invite {
position: relative;
margin-top: 1.3em;
border: 1px solid $color-red-light;

p {
color: $color-red-light;
margin: 0.7em 1em;

svg {
width: 1em;
height: 1em;
fill: $color-red-light;

// for better valignment
position: relative;
top: 0.15em;
margin-right: 0.3em;
}
}

.btn {
position: absolute;
top: 5px;
right: 5px;
}
}
&__subject {
position: relative;
margin-top: 1.3em;
Expand Down
16 changes: 14 additions & 2 deletions src/tpl/write.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,18 @@ <h2>{{writerTitle}}</h2>
</tags-input>
</div>

<div class="write__invite" ng-show="showInvite">
<p>
<svg role="presentation"><use xlink:href="#icon-decrypted"/></svg>
<strong>Key not found!</strong>
<span class="u-hidden-sm">Invite user to encrypt.</span>
</p>
<button class="btn btn--light" wo-touch="invite()" title="Invite to Whiteout Mail">
<svg role="presentation"><use xlink:href="#icon-add_contact"/></svg>
Invite
</button>
</div>

<div class="write__subject">
<input class="input-text" ng-model="subject" spellcheck="true" tabindex="2" placeholder="Subject" ng-change="updatePreview()">
<input id="attachment-input" type="file" multiple attachment-input>
Expand All @@ -61,10 +73,10 @@ <h2>{{writerTitle}}</h2>
</header>

<textarea class="write__body" ng-model="body" spellcheck="true" wo-focus-me="state.lightbox === 'write' && writerTitle === 'Reply'" tabindex="3"></textarea>
</div><!--/write-->

</div>
<footer class="lightbox__controls">
<button wo-touch="sendToOutbox()" class="btn" ng-class="{'btn--invalid': sendBtnSecure === false}"
ng-disabled="!okToSend" tabindex="4">{{sendBtnText || 'Send'}}</button>
</footer>
</div>
</div><!--/lightbox__body-->
Loading