snappymail/dev/Stores/User/Pgp.js

438 lines
11 KiB
JavaScript
Raw Normal View History

2015-02-01 23:44:44 +08:00
(function () {
'use strict';
2015-02-03 07:58:58 +08:00
var
2015-02-22 06:00:51 +08:00
_ = require('_'),
ko = require('ko'),
$ = require('$'),
kn = require('Knoin/Knoin'),
Translator = require('Common/Translator'),
2015-02-22 06:00:51 +08:00
Utils = require('Common/Utils')
2015-02-03 07:58:58 +08:00
;
2015-02-01 23:44:44 +08:00
/**
* @constructor
*/
function PgpUserStore()
{
2015-02-03 07:58:58 +08:00
this.capaOpenPGP = ko.observable(false);
2015-02-01 23:44:44 +08:00
this.openpgp = null;
2015-02-03 07:58:58 +08:00
this.openpgpkeys = ko.observableArray([]);
this.openpgpKeyring = null;
2015-02-01 23:44:44 +08:00
2015-02-03 07:58:58 +08:00
this.openpgpkeysPublic = this.openpgpkeys.filter(function (oItem) {
return !!(oItem && !oItem.isPrivate);
});
2015-02-01 23:44:44 +08:00
2015-02-03 07:58:58 +08:00
this.openpgpkeysPrivate = this.openpgpkeys.filter(function (oItem) {
return !!(oItem && oItem.isPrivate);
});
}
2015-02-01 23:44:44 +08:00
/**
* @return {boolean}
*/
PgpUserStore.prototype.isSupported = function ()
{
return !!this.openpgp;
};
2015-02-22 06:00:51 +08:00
PgpUserStore.prototype.findPublicKeyByHex = function (sHash)
{
return _.find(this.openpgpkeysPublic(), function (oItem) {
return sHash && oItem && sHash === oItem.id;
2015-02-22 06:00:51 +08:00
});
};
PgpUserStore.prototype.findPrivateKeyByHex = function (sHash)
2015-02-22 06:00:51 +08:00
{
return _.find(this.openpgpkeysPrivate(), function (oItem) {
return sHash && oItem && sHash === oItem.id;
});
};
2015-02-22 06:00:51 +08:00
PgpUserStore.prototype.findPublicKeysByEmail = function (sEmail)
{
return _.compact(_.flatten(_.map(this.openpgpkeysPublic(), function (oItem) {
var oKey = oItem && -1 !== oItem.emails.indexOf(sEmail) ? oItem : null;
return oKey ? oKey.getNativeKeys() : [null];
}), true));
};
2015-02-22 06:00:51 +08:00
PgpUserStore.prototype.findPublicKeysBySigningKeyIds = function (aSigningKeyIds)
{
var self = this;
return _.compact(_.flatten(_.map(aSigningKeyIds, function (oId) {
var oKey = oId && oId.toHex ? self.findPublicKeyByHex(oId.toHex()) : null;
return oKey ? oKey.getNativeKeys() : [null];
}), true));
};
2015-02-22 06:00:51 +08:00
PgpUserStore.prototype.findPrivateKeysByEncryptionKeyIds = function (aEncryptionKeyIds, aRecipients, bReturnWrapKeys)
{
var self = this, aResult = [];
aResult = Utils.isArray(aEncryptionKeyIds) ? _.compact(_.flatten(_.map(aEncryptionKeyIds, function (oId) {
var oKey = oId && oId.toHex ? self.findPrivateKeyByHex(oId.toHex()) : null;
return oKey ? (bReturnWrapKeys ? [oKey] : oKey.getNativeKeys()) : [null];
}), true)) : [];
if (0 === aResult.length && Utils.isNonEmptyArray(aRecipients))
{
aResult = _.compact(_.flatten(_.map(aRecipients, function (sEmail) {
var oKey = sEmail ? self.findPrivateKeyByEmailNotNative(sEmail) : null;
return oKey ? (bReturnWrapKeys ? [oKey] : oKey.getNativeKeys()) : [null];
}), true));
}
return aResult;
2015-02-22 06:00:51 +08:00
};
2015-07-30 01:21:24 +08:00
/**
* @param {string} sEmail
* @return {?}
*/
PgpUserStore.prototype.findPublicKeyByEmailNotNative = function (sEmail)
{
return _.find(this.openpgpkeysPublic(), function (oItem) {
return oItem && -1 !== oItem.emails.indexOf(sEmail);
2015-07-30 01:21:24 +08:00
}) || null;
};
/**
* @param {string} sEmail
* @return {?}
*/
PgpUserStore.prototype.findPrivateKeyByEmailNotNative = function (sEmail)
{
return _.find(this.openpgpkeysPrivate(), function (oItem) {
return oItem && -1 !== oItem.emails.indexOf(sEmail);
2015-07-30 01:21:24 +08:00
}) || null;
};
2015-02-22 06:00:51 +08:00
/**
* @param {string} sEmail
* @param {string=} sPassword
2015-04-14 02:45:09 +08:00
* @return {?}
2015-02-22 06:00:51 +08:00
*/
PgpUserStore.prototype.findPrivateKeyByEmail = function (sEmail, sPassword)
{
var
oPrivateKeys = [],
2015-02-22 06:00:51 +08:00
oPrivateKey = null,
oKey = _.find(this.openpgpkeysPrivate(), function (oItem) {
return oItem && -1 !== oItem.emails.indexOf(sEmail);
2015-02-22 06:00:51 +08:00
})
;
if (oKey)
{
oPrivateKeys = oKey.getNativeKeys();
oPrivateKey = oPrivateKeys[0] || null;
2015-02-22 06:00:51 +08:00
try
{
if (oPrivateKey)
2015-02-22 06:00:51 +08:00
{
oPrivateKey.decrypt(Utils.pString(sPassword));
}
}
catch (e)
{
oPrivateKey = null;
}
}
return oPrivateKey;
};
/**
* @param {string=} sPassword
2015-04-14 02:45:09 +08:00
* @return {?}
2015-02-22 06:00:51 +08:00
*/
PgpUserStore.prototype.findSelfPrivateKey = function (sPassword)
{
return this.findPrivateKeyByEmail(require('Stores/User/Account').email(), sPassword);
};
PgpUserStore.prototype.decryptMessage = function (oMessage, aRecipients, fCallback)
{
var self = this, aPrivateKeys = [];
if (oMessage && oMessage.getEncryptionKeyIds)
{
aPrivateKeys = this.findPrivateKeysByEncryptionKeyIds(oMessage.getEncryptionKeyIds(), aRecipients, true);
if (aPrivateKeys && 0 < aPrivateKeys.length)
{
kn.showScreenPopup(require('View/Popup/MessageOpenPgp'), [function (oDecriptedKey) {
if (oDecriptedKey)
{
var oPrivateKey = null, oDecryptedMessage = null;
try
{
oDecryptedMessage = oMessage.decrypt(oDecriptedKey);
}
catch (e)
{
oDecryptedMessage = null;
}
if (oDecryptedMessage)
{
oPrivateKey = self.findPrivateKeyByHex(oDecriptedKey.primaryKey.keyid.toHex());
if (oPrivateKey)
{
self.verifyMessage(oDecryptedMessage, function (oValidKey, aSigningKeyIds) {
fCallback(oPrivateKey, oDecryptedMessage, oValidKey || null, aSigningKeyIds || null);
});
}
else
{
fCallback(oPrivateKey, oDecryptedMessage);
}
}
else
{
fCallback(oPrivateKey, oDecryptedMessage);
}
}
else
{
fCallback(null, null);
}
}, aPrivateKeys]);
return false;
}
}
fCallback(null, null);
return false;
};
PgpUserStore.prototype.verifyMessage = function (oMessage, fCallback)
{
var oValid = null, aResult = [], aPublicKeys = [], aSigningKeyIds = [];
if (oMessage && oMessage.getSigningKeyIds)
{
aSigningKeyIds = oMessage.getSigningKeyIds();
if (aSigningKeyIds && 0 < aSigningKeyIds.length)
{
aPublicKeys = this.findPublicKeysBySigningKeyIds(aSigningKeyIds);
if (aPublicKeys && 0 < aPublicKeys.length)
{
try
{
aResult = oMessage.verify(aPublicKeys);
oValid = _.find(_.isArray(aResult) ? aResult : [], function (oItem) {
return oItem && oItem.valid && oItem.keyid;
});
if (oValid && oValid.keyid && oValid.keyid && oValid.keyid.toHex)
{
fCallback(this.findPublicKeyByHex(oValid.keyid.toHex()));
return true;
}
}
2015-10-14 00:36:43 +08:00
catch (e)
{
Utils.log(e);
}
}
fCallback(null, aSigningKeyIds);
return false;
}
}
fCallback(null);
return false;
};
/**
* @param {*} mDom
*/
PgpUserStore.prototype.controlsHelper = function (mDom, oVerControl, bSuccess, sTitle, sText)
{
if (bSuccess)
{
mDom.removeClass('error').addClass('success').attr('title', sTitle);
oVerControl.removeClass('error').addClass('success').attr('title', sTitle);
}
else
{
mDom.removeClass('success').addClass('error').attr('title', sTitle);
oVerControl.removeClass('success').addClass('error').attr('title', sTitle);
}
if (!Utils.isUnd(sText))
{
mDom.text(Utils.trim(sText.replace(/(\u200C|\u0002)/g, '')));
}
};
/**
* @param {*} mDom
* @param {MessageModel} oRainLoopMessage
*/
PgpUserStore.prototype.initMessageBodyControls = function (mDom, oRainLoopMessage)
{
if (mDom && !mDom.hasClass('inited'))
{
mDom.addClass('inited');
var
self = this,
bEncrypted = mDom.hasClass('encrypted'),
bSigned = mDom.hasClass('signed'),
oVerControl = null,
2016-04-07 20:07:19 +08:00
aRecipients = oRainLoopMessage ? oRainLoopMessage.getEmails(['from', 'to', 'cc']) : [],
sData = ''
;
if (bEncrypted || bSigned)
{
sData = mDom.text();
mDom.data('openpgp-original', sData);
if (bEncrypted)
{
oVerControl = $('<div class="b-openpgp-control"><i class="icon-lock"></i></div>')
.attr('title', Translator.i18n('MESSAGE/PGP_ENCRYPTED_MESSAGE_DESC'));
oVerControl.on('click', function () {
if ($(this).hasClass('success'))
{
return false;
}
var oMessage = null;
try
{
oMessage = self.openpgp.message.readArmored(sData);
}
catch (e)
{
Utils.log(e);
2015-09-01 04:13:59 +08:00
}
if (oMessage && oMessage.getText && oMessage.verify && oMessage.decrypt)
{
self.decryptMessage(oMessage, aRecipients, function (oValidPrivateKey, oDecriptedMessage, oValidPublicKey, aSigningKeyIds) {
if (oDecriptedMessage)
{
if (oValidPublicKey)
{
self.controlsHelper(mDom, oVerControl, true, Translator.i18n('PGP_NOTIFICATIONS/GOOD_SIGNATURE', {
'USER': oValidPublicKey.user + ' (' + oValidPublicKey.id + ')'
}), oDecriptedMessage.getText());
}
else if (oValidPrivateKey)
{
var
aKeyIds = Utils.isNonEmptyArray(aSigningKeyIds) ? aSigningKeyIds : null,
sAdditional = aKeyIds ? _.compact(_.map(aKeyIds, function (oItem) {
return oItem && oItem.toHex ? oItem.toHex() : null;
})).join(', ') : ''
;
self.controlsHelper(mDom, oVerControl, false,
Translator.i18n('PGP_NOTIFICATIONS/UNVERIFIRED_SIGNATURE') +
(sAdditional ? ' (' + sAdditional + ')' : ''),
oDecriptedMessage.getText());
}
else
{
self.controlsHelper(mDom, oVerControl, false,
Translator.i18n('PGP_NOTIFICATIONS/DECRYPTION_ERROR'));
}
}
else
{
self.controlsHelper(mDom, oVerControl, false,
Translator.i18n('PGP_NOTIFICATIONS/DECRYPTION_ERROR'));
}
});
return false;
}
self.controlsHelper(mDom, oVerControl, false, Translator.i18n('PGP_NOTIFICATIONS/DECRYPTION_ERROR'));
return false;
});
}
else if (bSigned)
{
oVerControl = $('<div class="b-openpgp-control"><i class="icon-lock"></i></div>')
.attr('title', Translator.i18n('MESSAGE/PGP_SIGNED_MESSAGE_DESC'));
oVerControl.on('click', function () {
if ($(this).hasClass('success') || $(this).hasClass('error'))
{
return false;
}
var oMessage = null;
try
{
oMessage = self.openpgp.cleartext.readArmored(sData);
}
catch (e)
{
Utils.log(e);
2015-09-01 04:13:59 +08:00
}
if (oMessage && oMessage.getText && oMessage.verify)
{
self.verifyMessage(oMessage, function (oValidKey, aSigningKeyIds) {
if (oValidKey)
{
self.controlsHelper(mDom, oVerControl, true, Translator.i18n('PGP_NOTIFICATIONS/GOOD_SIGNATURE', {
'USER': oValidKey.user + ' (' + oValidKey.id + ')'
}), oMessage.getText());
}
else
{
var
aKeyIds = Utils.isNonEmptyArray(aSigningKeyIds) ? aSigningKeyIds : null,
sAdditional = aKeyIds ? _.compact(_.map(aKeyIds, function (oItem) {
return oItem && oItem.toHex ? oItem.toHex() : null;
})).join(', ') : ''
;
self.controlsHelper(mDom, oVerControl, false,
Translator.i18n('PGP_NOTIFICATIONS/UNVERIFIRED_SIGNATURE') +
(sAdditional ? ' (' + sAdditional + ')' : ''));
}
});
return false;
}
self.controlsHelper(mDom, oVerControl, false, Translator.i18n('PGP_NOTIFICATIONS/DECRYPTION_ERROR'));
return false;
});
}
if (oVerControl)
{
mDom.before(oVerControl).before('<div></div>');
}
}
}
};
2015-02-01 23:44:44 +08:00
module.exports = new PgpUserStore();
}());