diff --git a/extension/chrome/elements/pgp_pubkey.ts b/extension/chrome/elements/pgp_pubkey.ts
index bb163e98e3e..a919103f6e5 100644
--- a/extension/chrome/elements/pgp_pubkey.ts
+++ b/extension/chrome/elements/pgp_pubkey.ts
@@ -12,68 +12,62 @@ import { BrowserMsg } from '../../js/common/browser/browser-msg.js';
import { Assert } from '../../js/common/assert.js';
import { Xss } from '../../js/common/platform/xss.js';
import { Url } from '../../js/common/core/common.js';
+import { View } from '../../js/common/view.js';
declare const openpgp: typeof OpenPGP;
-Catch.try(async () => {
-
- // todo - this should use KeyImportUI for consistency. Needs general refactoring, hard to follow.
-
- Ui.event.protect();
-
- // minimized means I have to click to see details. Compact means the details take up very little space.
- const uncheckedUrlParams = Url.parse(['acctEmail', 'armoredPubkey', 'parentTabId', 'minimized', 'compact', 'frameId']);
- const acctEmail = Assert.urlParamRequire.string(uncheckedUrlParams, 'acctEmail');
- const parentTabId = Assert.urlParamRequire.string(uncheckedUrlParams, 'parentTabId');
- const armoredPubkey = Pgp.armor.normalize(Assert.urlParamRequire.string(uncheckedUrlParams, 'armoredPubkey'), 'publicKey');
- const frameId = Assert.urlParamRequire.string(uncheckedUrlParams, 'frameId');
- const compact = uncheckedUrlParams.compact === true;
- const minimized = uncheckedUrlParams.minimized === true;
-
- const { keys: pubs } = await openpgp.key.readArmored(armoredPubkey);
- const isUsableButExpired = await Pgp.key.usableButExpired(pubs[0]);
- const isExpired = await Pgp.key.expired(pubs[0]);
-
- const sendResizeMsg = () => {
- const desiredHeight = $('#pgp_block').height()! + (compact ? 10 : 30); // #pgp_block is defined in template
- BrowserMsg.send.setCss(parentTabId, { selector: `iframe#${frameId}`, css: { height: `${desiredHeight}px` } });
- };
-
- const setBtnText = async () => {
- if (pubs.length > 1) {
- $('.action_add_contact').text('import ' + pubs.length + ' public keys');
- } else {
- const [contact] = await Store.dbContactGet(undefined, [String($('.input_email').val())]);
- $('.action_add_contact')
- .text(contact?.has_pgp ? 'update key' : `import ${isExpired ? 'expired ' : ''}key`)
- .css('background-color', isExpired ? '#989898' : '');
- }
- };
-
- const render = async () => {
- $('.pubkey').text(armoredPubkey);
- if (compact) {
+// todo - this should use KeyImportUI for consistency.
+View.run(class PgpPubkeyView extends View {
+ private readonly acctEmail: string;
+ private readonly parentTabId: string;
+ private readonly armoredPubkey: string;
+ private readonly frameId: string;
+ private readonly compact: boolean; // means the details take up very little space.
+ private readonly minimized: boolean; // means I have to click to see details.
+ private publicKeys: OpenPGP.key.Key[] | undefined;
+ private primaryPubKey: OpenPGP.key.Key | undefined;
+ private isExpired: boolean | undefined;
+
+ constructor() {
+ super();
+ const uncheckedUrlParams = Url.parse(['acctEmail', 'armoredPubkey', 'parentTabId', 'minimized', 'compact', 'frameId']);
+ this.acctEmail = Assert.urlParamRequire.string(uncheckedUrlParams, 'acctEmail');
+ this.parentTabId = Assert.urlParamRequire.string(uncheckedUrlParams, 'parentTabId');
+ this.armoredPubkey = Pgp.armor.normalize(Assert.urlParamRequire.string(uncheckedUrlParams, 'armoredPubkey'), 'publicKey');
+ this.frameId = Assert.urlParamRequire.string(uncheckedUrlParams, 'frameId');
+ this.compact = uncheckedUrlParams.compact === true;
+ this.minimized = uncheckedUrlParams.minimized === true;
+ }
+
+ async render() {
+ Ui.event.protect();
+ this.publicKeys = (await openpgp.key.readArmored(this.armoredPubkey)).keys;
+ this.primaryPubKey = this.publicKeys[0];
+ this.isExpired = await Pgp.key.expired(this.primaryPubKey);
+ $('.pubkey').text(this.armoredPubkey);
+ if (this.compact) {
$('.hide_if_compact').remove();
$('body').css({ border: 'none', padding: 0 });
$('.line').removeClass('line');
}
- $('.line.fingerprints, .line.add_contact').css('display', minimized ? 'none' : 'block');
- if (pubs.length === 1) {
- $('.line.fingerprints .fingerprint').text(await Pgp.key.fingerprint(pubs[0], 'spaced') || '(fingerprint error)');
- $('.line.fingerprints .keywords').text(mnemonic(await Pgp.key.longid(pubs[0]) || '') || '(mnemonic error)');
+ $('.line.fingerprints, .line.add_contact').css('display', this.minimized ? 'none' : 'block');
+ if (this.publicKeys.length === 1) {
+ $('.line.fingerprints .fingerprint').text(await Pgp.key.fingerprint(this.primaryPubKey, 'spaced') || '(fingerprint error)');
+ $('.line.fingerprints .keywords').text(mnemonic(await Pgp.key.longid(this.primaryPubKey) || '') || '(mnemonic error)');
} else {
$('.line.fingerprints').css({ display: 'none' });
}
- if (typeof pubs[0] !== 'undefined') {
- if (!isUsableButExpired && ! await pubs[0].getEncryptionKey() && ! await pubs[0].getSigningKey()) {
- showKeyNotUsableError();
+ if (this.primaryPubKey) {
+ const isUsableButExpired = await Pgp.key.usableButExpired(this.primaryPubKey);
+ if (!isUsableButExpired && ! await this.primaryPubKey.getEncryptionKey() && ! await this.primaryPubKey.getSigningKey()) {
+ this.showKeyNotUsableError();
} else {
- if (compact) {
+ if (this.compact) {
$('.hide_if_compact_and_not_error').remove();
}
let emailText = '';
- if (pubs.length === 1) {
- const email = pubs[0].users[0].userId ? Str.parseEmail(pubs[0].users[0].userId ? pubs[0].users[0].userId!.userid : '').email : undefined;
+ if (this.publicKeys.length === 1) {
+ const email = Str.parseEmail(this.primaryPubKey.users[0].userId?.userid || '').email;
if (email) {
emailText = email;
$('.input_email').val(email); // checked above
@@ -82,30 +76,63 @@ Catch.try(async () => {
emailText = 'more than one person';
$('.input_email').css({ display: 'none' });
const pubToEmail = (pubkey: OpenPGP.key.Key) => Str.parseEmail(pubkey.users[0].userId ? pubkey.users[0].userId!.userid : '').email;
- Xss.sanitizeAppend('.add_contact', Xss.escape(' for ' + pubs.map(pubToEmail).filter(e => !!e).join(', ')));
+ Xss.sanitizeAppend('.add_contact', Xss.escape(' for ' + this.publicKeys.map(pubToEmail).filter(e => !!e).join(', ')));
}
Xss.sanitizePrepend('#pgp_block.pgp_pubkey .result', `This message includes a Public Key for ${Xss.escape(emailText)}.`);
$('.pubkey').addClass('good');
- setBtnText().catch(Catch.reportErr);
+ this.setBtnText().catch(Catch.reportErr);
}
} else {
- let fixed = armoredPubkey;
+ let fixed = this.armoredPubkey;
while (/\n> |\n>\n/.test(fixed)) {
fixed = fixed.replace(/\n> /g, '\n').replace(/\n>\n/g, '\n\n');
}
- if (fixed !== armoredPubkey) { // try to re-render it after un-quoting, (minimized because it is probably their own pubkey quoted by the other guy)
- window.location.href = Url.create('pgp_pubkey.htm', { armoredPubkey: fixed, minimized: true, acctEmail, parentTabId, frameId });
+ if (fixed !== this.armoredPubkey) { // try to re-render it after un-quoting, (minimized because it is probably their own pubkey quoted by the other guy)
+ window.location.href = Url.create('pgp_pubkey.htm', {
+ armoredPubkey: fixed, minimized: true,
+ acctEmail: this.acctEmail, parentTabId: this.parentTabId, frameId: this.frameId
+ });
} else {
- showKeyNotUsableError();
+ this.showKeyNotUsableError();
}
}
- };
+ this.sendResizeMsg();
+ }
+
+ setHandlers() {
+ $('.action_add_contact').click(this.setHandler(btn => this.addContactHandler(btn)));
+ $('.input_email').keyup(this.setHandler(() => this.setBtnText()));
+ $('.action_show_full').click(this.setHandler(btn => this.showFullKeyHandler(btn)));
+ }
+
+ private sendResizeMsg() {
+ const desiredHeight = $('#pgp_block').height()! + (this.compact ? 10 : 30); // #pgp_block is defined in template
+ BrowserMsg.send.setCss(this.parentTabId, { selector: `iframe#${this.frameId}`, css: { height: `${desiredHeight}px` } });
+ }
+
+ private async setBtnText() {
+ if (this.publicKeys!.length > 1) {
+ $('.action_add_contact').text('import ' + this.publicKeys!.length + ' public keys');
+ } else {
+ const [contact] = await Store.dbContactGet(undefined, [String($('.input_email').val())]);
+ $('.action_add_contact')
+ .text(contact?.has_pgp ? 'update key' : `import ${this.isExpired ? 'expired ' : ''}key`)
+ .css('background-color', this.isExpired ? '#989898' : '');
+ }
+ }
- $('.action_add_contact').click(Ui.event.handle(async target => {
- if (pubs.length > 1) {
+ private showKeyNotUsableError() {
+ $('.fingerprints, .add_contact').remove();
+ $('#pgp_block.pgp_pubkey .result')
+ .prepend('This OpenPGP key is not usable.'); // xss-direct
+ $('.pubkey').addClass('bad');
+ }
+
+ private async addContactHandler(addContactBtn: HTMLElement) {
+ if (this.publicKeys!.length > 1) {
const contacts: Contact[] = [];
- for (const pubkey of pubs) {
- const email = Str.parseEmail(pubkey.users[0].userId ? pubkey.users[0].userId!.userid : '').email;
+ for (const pubkey of this.publicKeys!) {
+ const email = Str.parseEmail(pubkey.users[0].userId?.userid || '').email;
if (email) {
contacts.push(await Store.dbContactObj({
email, client: 'pgp', pubkey: pubkey.armor(), lastUse: Date.now(), lastSig: await Pgp.key.lastSig(pubkey),
@@ -114,42 +141,29 @@ Catch.try(async () => {
}
}
await Store.dbContactSave(undefined, contacts);
- Xss.sanitizeReplace(target, 'added public keys');
- BrowserMsg.send.addToContacts(parentTabId);
+ Xss.sanitizeReplace(addContactBtn, 'added public keys');
+ BrowserMsg.send.addToContacts(this.parentTabId);
$('.input_email').remove();
- } else if (pubs.length) {
+ } else if (this.publicKeys!.length) {
if (Str.isEmailValid(String($('.input_email').val()))) {
const contact = await Store.dbContactObj({
- email: String($('.input_email').val()), client: 'pgp', pubkey: pubs[0].armor(), lastUse: Date.now(),
- lastSig: await Pgp.key.lastSig(pubs[0]), expiresOn: await Pgp.key.dateBeforeExpiration(pubs[0])
+ email: String($('.input_email').val()), client: 'pgp', pubkey: this.publicKeys![0].armor(), lastUse: Date.now(),
+ lastSig: await Pgp.key.lastSig(this.publicKeys![0]), expiresOn: await Pgp.key.dateBeforeExpiration(this.publicKeys![0])
});
await Store.dbContactSave(undefined, contact);
- BrowserMsg.send.addToContacts(parentTabId);
- Xss.sanitizeReplace(target, `${Xss.escape(String($('.input_email').val()))} added`);
+ BrowserMsg.send.addToContacts(this.parentTabId);
+ Xss.sanitizeReplace(addContactBtn, `${Xss.escape(String($('.input_email').val()))} added`);
$('.input_email').remove();
} else {
await Ui.modal.error('This email is invalid, please check for typos. Not added.');
$('.input_email').focus();
}
}
- }));
-
- const showKeyNotUsableError = () => {
- $('.fingerprints, .add_contact').remove();
- $('#pgp_block.pgp_pubkey .result')
- .prepend('This OpenPGP key is not usable.'); // xss-direct
- $('.pubkey').addClass('bad');
- };
+ }
- $('.input_email').keyup(() => setBtnText());
-
- $('.action_show_full').click(Ui.event.handle(target => {
- $(target).css('display', 'none');
+ private showFullKeyHandler(showFullBtn: HTMLElement) {
+ $(showFullBtn).css('display', 'none');
$('pre.pubkey, .line.fingerprints, .line.add_contact').css('display', 'block');
- sendResizeMsg();
- }));
-
- await render();
- sendResizeMsg();
-
-})();
+ this.sendResizeMsg();
+ }
+});