Revision control

Copy as Markdown

Other Tools

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
import { Assert } from "resource://testing-common/Assert.sys.mjs";
import { BrowserTestUtils } from "resource://testing-common/BrowserTestUtils.sys.mjs";
import * as EventUtils from "resource://testing-common/mail/EventUtils.sys.mjs";
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
});
export const OpenPGPTestUtils = {
ACCEPTANCE_PERSONAL: "personal",
ACCEPTANCE_REJECTED: "rejected",
ACCEPTANCE_UNVERIFIED: "unverified",
ACCEPTANCE_VERIFIED: "verified",
ACCEPTANCE_UNDECIDED: "undecided",
ALICE_KEY_ID: "F231550C4F47E38E",
BOB_KEY_ID: "FBFCC82A015E7330",
CAROL_KEY_ID: "3099FF1238852B9F",
/**
* Given a compose message window, clicks on the "Digitally Sign This Message"
* menu item.
*/
async toggleMessageSigning(win) {
return clickToolbarButtonMenuItem(win, "#button-encryption-options", [
"#menu_securitySign_Toolbar",
]);
},
/**
* Given a compose message window, clicks on the "Encrypt Subject"
* menu item.
*/
async toggleMessageEncryptSubject(win) {
return clickToolbarButtonMenuItem(win, "#button-encryption-options", [
"#menu_securityEncryptSubject_Toolbar",
]);
},
/**
* Given a compose message window, clicks on the "Attach My Public Key"
* menu item.
*/
async toggleMessageKeyAttachment(win) {
return clickToolbarButtonMenuItem(win, "#button-attach", [
"#button-attachPopup_attachPublicKey",
]);
},
/**
* Given a compose message window, clicks on the "Require Encryption"
* menu item.
*/
async toggleMessageEncryption(win) {
// Note: doing it through #menu_securityEncryptRequire_Menubar won't work on
// mac since the native menu bar can't be clicked.
// Use the toolbar button to click Require encryption.
await clickToolbarButtonMenuItem(win, "#button-encryption-options", [
"#menu_securityEncrypt_Toolbar",
]);
},
/**
* For xpcshell-tests OpenPGP is not initialized automatically. This method
* should be called at the start of testing.
*/
async initOpenPGP() {
Assert.ok(await lazy.RNP.init(), "librnp did load");
await lazy.EnigmailCore.init();
lazy.EnigmailKeyRing.init();
await lazy.OpenPGPAlias.load();
},
/**
* Tests whether the signed icon's "src" attribute matches the provided state.
*
* @param {HTMLDocument} doc - The document of the message window.
* @param {"ok"|"unknown"|"verified"|"unverified"|"mismatch"} state - The
* state to test for.
* @returns {boolean}
*/
hasSignedIconState(doc, state) {
return !!doc.querySelector(`#signedHdrIcon[src*=message-signed-${state}]`);
},
/**
* Checks that the signed icon is hidden.
*
* @param {HTMLDocument} doc - The document of the message window.
* @returns {boolean}
*/
hasNoSignedIconState(doc) {
return !!doc.querySelector(`#signedHdrIcon[hidden]`);
},
/**
* Checks that the encrypted icon is hidden.
*
* @param {HTMLDocument} doc - The document of the message window.
* @returns {boolean}
*/
hasNoEncryptedIconState(doc) {
return !!doc.querySelector(`#encryptedHdrIcon[hidden]`);
},
/**
* Tests whether the encrypted icon's "src" attribute matches the provided
* state value.
*
* @param {HTMLDocument} doc - The document of the message window.
* @param {"ok"|"notok"} state - The state to test for.
* @returns {boolean}
*/
hasEncryptedIconState(doc, state) {
return !!doc.querySelector(
`#encryptedHdrIcon[src*=message-encrypted-${state}]`
);
},
/**
* Imports a public key into the keyring while also updating its acceptance.
*
* @param {nsIWindow} parent - The parent window.
* @param {nsIFile} file - A valid file containing a public OpenPGP key.
* @param {string} [acceptance] - The acceptance setting for the key.
* @returns {string[]} - List of imported key ids.
*/
async importPublicKey(
parent,
file,
acceptance = OpenPGPTestUtils.ACCEPTANCE_VERIFIED
) {
const ids = await OpenPGPTestUtils.importKey(parent, file, false);
if (!ids.length) {
throw new Error(`No public key imported from ${file.leafName}`);
}
return OpenPGPTestUtils.updateKeyIdAcceptance(ids, acceptance);
},
/**
* Imports a private key into the keyring while also updating its acceptance.
*
* @param {nsIWindow} parent - The parent window.
* @param {nsIFile} file - A valid file containing a private OpenPGP key.
* @param {string} [acceptance] - The acceptance setting for the key.
* @param {string} [passphrase] - The passphrase string that is required
* for unlocking the imported private key, or null, if no passphrase
* is necessary. The existing passphrase protection is kept.
* @param {boolean} [keepPassphrase] - true for keeping the existing
* passphrase. False for removing the existing passphrase and to
* set the automatic protection. If parameter passphrase is null
* then parameter keepPassphrase is ignored.
* @returns {string[]} - List of imported key ids.
*/
async importPrivateKey(
parent,
file,
acceptance = OpenPGPTestUtils.ACCEPTANCE_PERSONAL,
passphrase = null,
keepPassphrase = false
) {
const data = await IOUtils.read(file.path);
const pgpBlock = lazy.MailStringUtils.uint8ArrayToByteString(data);
function localPassphraseProvider(win, promptString, resultFlags) {
resultFlags.canceled = false;
return passphrase;
}
if (passphrase != null && keepPassphrase == undefined) {
throw new Error(
"must provide true of false for parameter keepPassphrase"
);
}
const result = await lazy.RNP.importSecKeyBlockImpl(
parent,
localPassphraseProvider,
passphrase != null && keepPassphrase,
pgpBlock,
false,
[]
);
if (!result || result.exitCode !== 0) {
throw new Error(
`EnigmailKeyRing.importKey failed with result "${result.errorMsg}"!`
);
}
if (!result.importedKeys || !result.importedKeys.length) {
throw new Error(`No private key imported from ${file.leafName}`);
}
lazy.EnigmailKeyRing.updateKeys(result.importedKeys);
lazy.EnigmailKeyRing.clearCache();
return OpenPGPTestUtils.updateKeyIdAcceptance(
result.importedKeys.slice(),
acceptance
);
},
/**
* Imports a key into the keyring.
*
* @param {nsIWindow} parent - The parent window.
* @param {nsIFile} file - A valid file containing an OpenPGP key.
* @param {boolean} [isBinary] - false for ASCII armored files
* @returns {Promise<string[]>} - A list of ids for the key(s) imported.
*/
async importKey(parent, file, isBinary) {
const data = await IOUtils.read(file.path);
const txt = lazy.MailStringUtils.uint8ArrayToByteString(data);
const errorObj = {};
const fingerPrintObj = {};
const result = lazy.EnigmailKeyRing.importKey(
parent,
false,
txt,
isBinary,
null,
errorObj,
fingerPrintObj,
false,
[]
);
if (result !== 0) {
console.debug(
`EnigmailKeyRing.importKey failed with result "${result}"!`
);
return [];
}
return fingerPrintObj.value.slice();
},
/**
* Updates the acceptance value of the provided key(s) in the database.
*
* @param {string|string[]} id - The id or list of ids to update.
* @param {string} acceptance - The new acceptance level for the key id.
* @returns {string[]} - A list of the key ids processed.
*/
async updateKeyIdAcceptance(id, acceptance) {
const ids = Array.isArray(id) ? id : [id];
for (const id of ids) {
const key = lazy.EnigmailKeyRing.getKeyById(id);
const email = lazy.EnigmailFuncs.getEmailFromUserID(key.userId);
await lazy.PgpSqliteDb2.updateAcceptance(key.fpr, [email], acceptance);
}
lazy.EnigmailKeyRing.clearCache();
return ids.slice();
},
getProtectedKeysCount() {
return lazy.RNP.getProtectedKeysCount();
},
/**
* Removes a key by its id, clearing its acceptance and refreshing the
* cache.
*
* @param {string|string[]} id - The id or list of ids to remove.
* @param {boolean} [deleteSecret=false] - If true, secret keys will be removed too.
*/
async removeKeyById(id, deleteSecret = false) {
const ids = Array.isArray(id) ? id : [id];
for (const id of ids) {
const key = lazy.EnigmailKeyRing.getKeyById(id);
await lazy.RNP.deleteKey(key.fpr, deleteSecret);
await lazy.PgpSqliteDb2.deleteAcceptance(key.fpr);
}
lazy.EnigmailKeyRing.clearCache();
},
};
/**
* Click a toolbar button to make it show the dropdown. Then click one of
* the menuitems in that popup.
*/
async function clickToolbarButtonMenuItem(
win,
buttonSelector,
menuitemSelectors
) {
const menupopup = win.document.querySelector(`${buttonSelector} > menupopup`);
const popupshown = BrowserTestUtils.waitForEvent(menupopup, "popupshown");
EventUtils.synthesizeMouseAtCenter(
win.document.querySelector(`${buttonSelector} > dropmarker`),
{},
win
);
await popupshown;
if (menuitemSelectors.length > 1) {
const submenuSelector = menuitemSelectors.shift();
menupopup.querySelector(submenuSelector).openMenu(true);
}
const popuphidden = BrowserTestUtils.waitForEvent(menupopup, "popuphidden");
menupopup.activateItem(win.document.querySelector(menuitemSelectors[0]));
await popuphidden;
}