import _ from '_'; import $ from '$'; import ko from 'ko'; import key from 'key'; import { inArray, pString, log, isUnd, trim, defautOptionsAfterRender } from 'Common/Utils'; import {Magics, KeyState} from 'Common/Enums'; import {i18n} from 'Common/Translator'; import PgpStore from 'Stores/User/Pgp'; import {EmailModel} from 'Model/Email'; import {popup, command} from 'Knoin/Knoin'; import {AbstractViewNext} from 'Knoin/AbstractViewNext'; const KEY_NAME_SUBSTR = -8; @popup({ name: 'View/Popup/ComposeOpenPgp', templateID: 'PopupsComposeOpenPgp' }) class ComposeOpenPgpPopupView extends AbstractViewNext { constructor() { super(); this.publicKeysOptionsCaption = i18n('PGP_NOTIFICATIONS/ADD_A_PUBLICK_KEY'); this.privateKeysOptionsCaption = i18n('PGP_NOTIFICATIONS/SELECT_A_PRIVATE_KEY'); this.notification = ko.observable(''); this.sign = ko.observable(false); this.encrypt = ko.observable(false); this.password = ko.observable(''); this.password.focus = ko.observable(false); this.buttonFocus = ko.observable(false); this.text = ko.observable(''); this.selectedPrivateKey = ko.observable(null); this.selectedPublicKey = ko.observable(null); this.signKey = ko.observable(null); this.encryptKeys = ko.observableArray([]); this.encryptKeysView = ko.computed( () => _.compact(_.map(this.encryptKeys(), (oKey) => (oKey ? oKey.key : null))) ); this.privateKeysOptions = ko.computed(() => { const opts = _.map(PgpStore.openpgpkeysPrivate(), (oKey, iIndex) => { if (this.signKey() && this.signKey().key.id === oKey.id) { return null; } return _.map(oKey.users, (user) => ({ 'id': oKey.guid, 'name': '(' + oKey.id.substr(KEY_NAME_SUBSTR).toUpperCase() + ') ' + user, 'key': oKey, 'class': iIndex % 2 ? 'odd' : 'even' })); }); return _.compact(_.flatten(opts, true)); }); this.publicKeysOptions = ko.computed(() => { const opts = _.map(PgpStore.openpgpkeysPublic(), (oKey, index) => { if (-1 < inArray(oKey, this.encryptKeysView())) { return null; } return _.map(oKey.users, (user) => ({ 'id': oKey.guid, 'name': '(' + oKey.id.substr(KEY_NAME_SUBSTR).toUpperCase() + ') ' + user, 'key': oKey, 'class': index % 2 ? 'odd' : 'even' })); }); return _.compact(_.flatten(opts, true)); }); this.submitRequest = ko.observable(false); this.resultCallback = null; this.selectedPrivateKey.subscribe((value) => { if (value) { this.selectCommand(); this.updateCommand(); } }); this.selectedPublicKey.subscribe((value) => { if (value) { this.addCommand(); } }); this.sDefaultKeyScope = KeyState.PopupComposeOpenPGP; this.defautOptionsAfterRender = defautOptionsAfterRender; this.addOptionClass = (domOption, item) => { this.defautOptionsAfterRender(domOption, item); if (item && !isUnd(item.class) && domOption) { $(domOption).addClass(item.class); } }; this.deletePublickKey = _.bind(this.deletePublickKey, this); } @command((self) => !self.submitRequest() && (self.sign() || self.encrypt())) doCommand() { let result = true, privateKey = null, aPublicKeys = []; this.submitRequest(true); if (result && this.sign()) { if (!this.signKey()) { this.notification(i18n('PGP_NOTIFICATIONS/NO_PRIVATE_KEY_FOUND')); result = false; } else if (!this.signKey().key) { this.notification(i18n('PGP_NOTIFICATIONS/NO_PRIVATE_KEY_FOUND_FOR', { 'EMAIL': this.signKey().email })); result = false; } if (result) { const privateKeys = this.signKey().key.getNativeKeys(); privateKey = privateKeys[0] || null; try { if (privateKey) { privateKey.decrypt(pString(this.password())); } } catch (e) { privateKey = null; } if (!privateKey) { this.notification(i18n('PGP_NOTIFICATIONS/NO_PRIVATE_KEY_FOUND')); result = false; } } } if (result && this.encrypt()) { if (0 === this.encryptKeys().length) { this.notification(i18n('PGP_NOTIFICATIONS/NO_PUBLIC_KEYS_FOUND')); result = false; } else if (this.encryptKeys()) { aPublicKeys = []; _.each(this.encryptKeys(), (oKey) => { if (oKey && oKey.key) { aPublicKeys = aPublicKeys.concat(_.compact(_.flatten(oKey.key.getNativeKeys()))); } else if (oKey && oKey.email) { this.notification(i18n('PGP_NOTIFICATIONS/NO_PUBLIC_KEYS_FOUND_FOR', { 'EMAIL': oKey.email })); result = false; } }); if (result && (0 === aPublicKeys.length || this.encryptKeys().length !== aPublicKeys.length)) { result = false; } } } if (result && this.resultCallback) { _.delay(() => { let pgpPromise = null; try { if (privateKey && 0 === aPublicKeys.length) { pgpPromise = PgpStore.openpgp.sign({ data: this.text(), privateKeys: [privateKey] }); } else if (privateKey && 0 < aPublicKeys.length) { pgpPromise = PgpStore.openpgp.encrypt({ data: this.text(), publicKeys: aPublicKeys, privateKeys: [privateKey] }); } else if (!privateKey && 0 < aPublicKeys.length) { pgpPromise = PgpStore.openpgp.encrypt({ data: this.text(), publicKeys: aPublicKeys }); } } catch (e) { log(e); this.notification(i18n('PGP_NOTIFICATIONS/PGP_ERROR', { 'ERROR': '' + e })); } if (pgpPromise) { try { pgpPromise.then((mData) => { this.resultCallback(mData.data); this.cancelCommand(); }).catch((e) => { this.notification(i18n('PGP_NOTIFICATIONS/PGP_ERROR', { 'ERROR': '' + e })); }); } catch (e) { this.notification(i18n('PGP_NOTIFICATIONS/PGP_ERROR', { 'ERROR': '' + e })); } } this.submitRequest(false); }, Magics.Time20ms); } else { this.submitRequest(false); } return result; } @command() selectCommand() { const keyId = this.selectedPrivateKey(), option = keyId ? _.find(this.privateKeysOptions(), (item) => item && keyId === item.id) : null; if (option) { this.signKey({ 'empty': !option.key, 'selected': ko.observable(!!option.key), 'users': option.key.users, 'hash': option.key.id.substr(KEY_NAME_SUBSTR).toUpperCase(), 'key': option.key }); } } @command() addCommand() { const keyId = this.selectedPublicKey(), keys = this.encryptKeys(), option = keyId ? _.find(this.publicKeysOptions(), (item) => (item && keyId === item.id)) : null; if (option) { keys.push({ 'empty': !option.key, 'selected': ko.observable(!!option.key), 'removable': ko.observable(!this.sign() || !this.signKey() || this.signKey().key.id !== option.key.id), 'users': option.key.users, 'hash': option.key.id.substr(KEY_NAME_SUBSTR).toUpperCase(), 'key': option.key }); this.encryptKeys(keys); } } @command() updateCommand() { _.each(this.encryptKeys(), (oKey) => { oKey.removable(!this.sign() || !this.signKey() || this.signKey().key.id !== oKey.key.id); }); } deletePublickKey(publicKey) { this.encryptKeys.remove(publicKey); } clearPopup() { this.notification(''); this.sign(false); this.encrypt(false); this.password(''); this.password.focus(false); this.buttonFocus(false); this.signKey(null); this.encryptKeys([]); this.text(''); this.resultCallback = null; } onBuild() { key('tab,shift+tab', KeyState.PopupComposeOpenPGP, () => { switch (true) { case this.password.focus(): this.buttonFocus(true); break; case this.buttonFocus(): this.password.focus(true); break; // no default } return false; }); } onHideWithDelay() { this.clearPopup(); } onShowWithDelay() { if (this.sign()) { this.password.focus(true); } else { this.buttonFocus(true); } } onShow(fCallback, sText, identity, sTo, sCc, sBcc) { this.clearPopup(); let rec = [], emailLine = ''; const email = new EmailModel(); this.resultCallback = fCallback; if ('' !== sTo) { rec.push(sTo); } if ('' !== sCc) { rec.push(sCc); } if ('' !== sBcc) { rec.push(sBcc); } rec = rec.join(', ').split(','); rec = _.compact(_.map(rec, (value) => { email.clear(); email.parse(trim(value)); return '' === email.email ? false : email.email; })); if (identity && identity.email()) { emailLine = identity.email(); rec.unshift(emailLine); const keys = PgpStore.findAllPrivateKeysByEmailNotNative(emailLine); if (keys && keys[0]) { this.signKey({ 'users': keys[0].users || [emailLine], 'hash': keys[0].id.substr(KEY_NAME_SUBSTR).toUpperCase(), 'key': keys[0] }); } } if (this.signKey()) { this.sign(true); } if (rec && 0 < rec.length) { this.encryptKeys(_.uniq(_.compact(_.flatten(_.map(rec, (recEmail) => { const keys = PgpStore.findAllPublicKeysByEmailNotNative(recEmail); return keys ? _.map(keys, (publicKey) => ({ 'empty': !publicKey, 'selected': ko.observable(!!publicKey), 'removable': ko.observable(!this.sign() || !this.signKey() || this.signKey().key.id !== publicKey.id), 'users': publicKey ? (publicKey.users || [recEmail]) : [recEmail], 'hash': publicKey ? publicKey.id.substr(KEY_NAME_SUBSTR).toUpperCase() : '', 'key': publicKey })) : []; }), true)), (encryptKey) => encryptKey.hash)); if (0 < this.encryptKeys().length) { this.encrypt(true); } } this.text(sText); } } export {ComposeOpenPgpPopupView, ComposeOpenPgpPopupView as default};