Bug 1616558, remove code for gnupg subprocess execution and gpg-agent interaction. r=PatrickBrunschwig

Differential Revision: https://phabricator.services.mozilla.com/D63325

--HG--
extra : rebase_source : 9edebe121f8e78abf35e71efff86b89d9906b950
This commit is contained in:
Kai Engert 2020-02-19 15:06:36 +01:00
Родитель 433e0d8fcd
Коммит 75eb1b1a91
54 изменённых файлов: 93 добавлений и 10156 удалений

Просмотреть файл

@ -53,15 +53,9 @@ var BondOpenPGP = {
if (!MailConstants.MOZ_OPENPGP) {
return;
}
const engine = Services.prefs.getIntPref("temp.openpgp.engine");
if (!engine) {
return;
}
if (this.initDone) {
return;
}
this.initDone = true;
console.log("loading OpenPGP");
try {

Просмотреть файл

@ -33,7 +33,6 @@ const EnigmailStreams = ChromeUtils.import("chrome://openpgp/content/modules/str
const EnigmailArmor = ChromeUtils.import("chrome://openpgp/content/modules/armor.jsm").EnigmailArmor;
const EnigmailData = ChromeUtils.import("chrome://openpgp/content/modules/data.jsm").EnigmailData;
const EnigmailRules = ChromeUtils.import("chrome://openpgp/content/modules/rules.jsm").EnigmailRules;
const EnigmailKeyEditor = ChromeUtils.import("chrome://openpgp/content/modules/keyEditor.jsm").EnigmailKeyEditor;
const EnigmailStdlib = ChromeUtils.import("chrome://openpgp/content/modules/stdlib.jsm").EnigmailStdlib;
const EnigmailPrefs = ChromeUtils.import("chrome://openpgp/content/modules/prefs.jsm").EnigmailPrefs;
const EnigmailConstants = ChromeUtils.import("chrome://openpgp/content/modules/constants.jsm").EnigmailConstants;

Просмотреть файл

@ -1,33 +0,0 @@
/*
* 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 https://mozilla.org/MPL/2.0/.
*/
"use strict";
var EXPORTED_SYMBOLS = ["EnigmailCard"];
const EnigmailLog = ChromeUtils.import("chrome://openpgp/content/modules/log.jsm").EnigmailLog;
const EnigmailExecution = ChromeUtils.import("chrome://openpgp/content/modules/execution.jsm").EnigmailExecution;
const EnigmailGpg = ChromeUtils.import("chrome://openpgp/content/modules/gpg.jsm").EnigmailGpg;
var EnigmailCard = {
getCardStatus: function(exitCodeObj, errorMsgObj) {
EnigmailLog.DEBUG("card.jsm: EnigmailCard.getCardStatus\n");
const GPG_ADDITIONAL_OPTIONS=["--no-verbose", "--status-fd", "2", "--fixed-list-mode", "--with-colons", "--card-status"];
const args = EnigmailGpg.getStandardArgs(false).concat(GPG_ADDITIONAL_OPTIONS);
const statusMsgObj = {};
const statusFlagsObj = {};
const outputTxt = EnigmailExecution.execCmd(EnigmailGpg.agentPath, args, "", exitCodeObj, statusFlagsObj, statusMsgObj, errorMsgObj);
if ((exitCodeObj.value === 0) && !outputTxt) {
exitCodeObj.value = -1;
return "";
}
return outputTxt;
}
};

Просмотреть файл

@ -13,13 +13,11 @@ const {
} = Components;
Cm.QueryInterface(Ci.nsIComponentRegistrar);
const subprocess = ChromeUtils.import("chrome://openpgp/content/modules/subprocess.jsm").subprocess;
const EnigmailLazy = ChromeUtils.import("chrome://openpgp/content/modules/lazy.jsm").EnigmailLazy;
const EnigmailConstants = ChromeUtils.import("chrome://openpgp/content/modules/constants.jsm").EnigmailConstants;
// load all modules lazily to avoid possible cross-reference errors
const getEnigmailConsole = EnigmailLazy.loader("enigmail/pipeConsole.jsm", "EnigmailConsole");
//const getEnigmailGpgAgent = EnigmailLazy.loader("enigmail/gpgAgent.jsm", "EnigmailGpgAgent");
const getEnigmailMimeEncrypt = EnigmailLazy.loader("enigmail/mimeEncrypt.jsm", "EnigmailMimeEncrypt");
const getEnigmailProtocolHandler = EnigmailLazy.loader("enigmail/protocolHandler.jsm", "EnigmailProtocolHandler");
const getEnigmailFiltersWrapper = EnigmailLazy.loader("enigmail/filtersWrapper.jsm", "EnigmailFiltersWrapper");
@ -122,7 +120,6 @@ var EnigmailCore = {
getEnigmailFiltersWrapper().onShutdown();
getEnigmailVerify().unregisterContentTypeHandler();
//getEnigmailGpgAgent().finalize();
getEnigmailLocale().shutdown();
getEnigmailLog().onShutdown();
@ -223,29 +220,6 @@ function initializeLogging(env) {
}
}
function initializeSubprocessLogging(env) {
const nspr_log_modules = env.get("NSPR_LOG_MODULES");
const matches = nspr_log_modules.match(/subprocess:(\d+)/);
subprocess.registerLogHandler(function(txt) {
getEnigmailLog().ERROR("subprocess.jsm: " + txt);
});
if (matches && matches.length > 1 && matches[1] > 2) {
subprocess.registerDebugHandler(function(txt) {
getEnigmailLog().DEBUG("subprocess.jsm: " + txt);
});
}
}
function initializeAgentInfo() {
return;
if (!getEnigmailOS().isDosLike && !getEnigmailGpgAgent().isDummy()) {
EnigmailCore.addToEnvList("GPG_AGENT_INFO=" + getEnigmailGpgAgent().gpgAgentInfo.envStr);
}
}
function failureOn(ex, status) {
status.initializationError = getEnigmailLocale().getString("enigmailNotAvailable");
getEnigmailLog().ERROR("core.jsm: Enigmail.initialize: Error - " + status.initializationError + "\n");
@ -340,7 +314,6 @@ Enigmail.prototype = {
this.environment = getEnvironment(this);
initializeSubprocessLogging(this.environment);
initializeEnvironment(this.environment);
try {
@ -349,11 +322,6 @@ Enigmail.prototype = {
failureOn(ex, this);
}
//getEnigmailGpgAgent().setAgentPath(domWindow, this, gPreferredGpgPath);
//getEnigmailGpgAgent().detectGpgAgent(domWindow, this);
//initializeAgentInfo();
//getEnigmailKeyRefreshService().start(getEnigmailKeyServer());
this.initialized = true;
@ -368,7 +336,6 @@ Enigmail.prototype = {
getEnigmailConsole().write("Reinitializing Enigmail service ...\n");
initializeEnvironment(this.environment);
//getEnigmailGpgAgent().setAgentPath(null, this, gPreferredGpgPath);
this.initialized = true;
},
@ -444,13 +411,6 @@ Enigmail.prototype = {
getEnigmailLog().DEBUG("core.jsm: getService: last used version: " + configuredVersion + "\n");
/*
if (firstInitialization && this.initialized &&
getEnigmailGpgAgent().agentType === "pgp") {
getEnigmailDialog().alert(win, getEnigmailLocale().getString("pgpNotSupported"));
}
*/
if (this.initialized && (getEnigmailApp().getVersion() != configuredVersion)) {
getEnigmailConfigure().configureEnigmail(win, startingPreferences);
}

Просмотреть файл

@ -4,30 +4,30 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
"use strict";
var EXPORTED_SYMBOLS = ["EnigmailCryptoAPI"];
var gCurrentApi = null;
var gGnuPGApi = null;
var Services = ChromeUtils.import("resource://gre/modules/Services.jsm").Services;
function EnigmailCryptoAPI() {
if (!gCurrentApi) {
const gOpenPGPEngine = Services.prefs.getIntPref("temp.openpgp.engine");
if (gOpenPGPEngine == 1) {
const {
getRNPAPI
} = ChromeUtils.import("chrome://openpgp/content/modules/cryptoAPI/RNPCryptoAPI.jsm");
gCurrentApi = getRNPAPI();
} else if (gOpenPGPEngine == 2) {
const {
getGnuPGAPI
} = ChromeUtils.import("chrome://openpgp/content/modules/cryptoAPI/GnuPGCryptoAPI.jsm");
gCurrentApi = getGnuPGAPI();
}
const {
getRNPAPI
} = ChromeUtils.import("chrome://openpgp/content/modules/cryptoAPI/RNPCryptoAPI.jsm");
gCurrentApi = getRNPAPI();
}
return gCurrentApi;
}
function EnigmailGnuPGAPI() {
if (!gGnuPGApi) {
const {
getGnuPGAPI
} = ChromeUtils.import("chrome://openpgp/content/modules/cryptoAPI/GnuPGCryptoAPI.jsm");
gGnuPGApi = getGnuPGAPI();
}
return gGnuPGApi;
}

Просмотреть файл

@ -19,19 +19,15 @@ Services.scriptloader.loadSubScript("chrome://openpgp/content/modules/cryptoAPI/
const EnigmailLog = ChromeUtils.import("chrome://openpgp/content/modules/log.jsm").EnigmailLog;
const EnigmailLazy = ChromeUtils.import("chrome://openpgp/content/modules/lazy.jsm").EnigmailLazy;
const EnigmailGpg = ChromeUtils.import("chrome://openpgp/content/modules/gpg.jsm").EnigmailGpg;
const EnigmailExecution = ChromeUtils.import("chrome://openpgp/content/modules/execution.jsm").EnigmailExecution;
const EnigmailFiles = ChromeUtils.import("chrome://openpgp/content/modules/files.jsm").EnigmailFiles;
const EnigmailConstants = ChromeUtils.import("chrome://openpgp/content/modules/constants.jsm").EnigmailConstants;
const EnigmailTime = ChromeUtils.import("chrome://openpgp/content/modules/time.jsm").EnigmailTime;
const EnigmailData = ChromeUtils.import("chrome://openpgp/content/modules/data.jsm").EnigmailData;
const EnigmailLocale = ChromeUtils.import("chrome://openpgp/content/modules/locale.jsm").EnigmailLocale;
const EnigmailPassword = ChromeUtils.import("chrome://openpgp/content/modules/passwords.jsm").EnigmailPassword;
const EnigmailErrorHandling = ChromeUtils.import("chrome://openpgp/content/modules/errorHandling.jsm").EnigmailErrorHandling;
const GnuPGDecryption = ChromeUtils.import("chrome://openpgp/content/modules/cryptoAPI/gnupg-decryption.jsm").GnuPGDecryption;
const {
obtainKeyList,
createKeyObj,
getPhotoFileFromGnuPG,
extractSignatures,
getGpgKeyData
@ -63,35 +59,6 @@ class GnuPGCryptoAPI extends CryptoAPI {
return keyList.keys;
}
/**
* Get groups defined in gpg.conf in the same structure as KeyObject
*
* @return {Array of KeyObject} with type = "grp"
*/
getGroups() {
let groups = EnigmailGpg.getGpgGroups();
let r = [];
for (var i = 0; i < groups.length; i++) {
let keyObj = createKeyObj(["grp"]);
keyObj.keyTrust = "g";
keyObj.userId = EnigmailData.convertGpgToUnicode(groups[i].alias).replace(/\\e3A/g, ":");
keyObj.keyId = keyObj.userId;
var grpMembers = EnigmailData.convertGpgToUnicode(groups[i].keylist).replace(/\\e3A/g, ":").split(/[,;]/);
for (var grpIdx = 0; grpIdx < grpMembers.length; grpIdx++) {
keyObj.userIds.push({
userId: grpMembers[grpIdx],
keyTrust: "q"
});
}
r.push(keyObj);
}
return r;
}
/**
* Obtain signatures for a given set of key IDs.
*
@ -102,28 +69,7 @@ class GnuPGCryptoAPI extends CryptoAPI {
*/
async getKeySignatures(keyId, ignoreUnknownUid = false) {
EnigmailLog.DEBUG(`gnupg.js: getKeySignatures: ${keyId}\n`);
const args = EnigmailGpg.getStandardArgs(true).concat(["--with-fingerprint", "--fixed-list-mode", "--with-colons", "--list-sig"]).concat(keyId.split(" "));
let res = await EnigmailExecution.execAsync(EnigmailGpg.agentPath, args, "");
if (!(res.statusFlags & EnigmailConstants.BAD_SIGNATURE)) {
// ignore exit code as recommended by GnuPG authors
res.exitCode = 0;
}
if (res.exitCode !== 0) {
if (res.errorMsg) {
res.errorMsg += "\n" + EnigmailFiles.formatCmdLine(EnigmailGpg.agentPath, args);
res.errorMsg += "\n" + res.errorMsg;
}
return "";
}
if (res.stdoutData.length > 0) {
return extractSignatures(res.stdoutData, ignoreUnknownUid);
}
return null;
throw new Error("Not implemented");
}
@ -143,51 +89,7 @@ class GnuPGCryptoAPI extends CryptoAPI {
*/
async getMinimalPubKey(fpr, email, subkeyDates) {
EnigmailLog.DEBUG(`gnupg.js: getMinimalPubKey: ${fpr}\n`);
let retObj = {
exitCode: 0,
errorMsg: "",
keyData: ""
};
let minimalKeyBlock = null;
let args = EnigmailGpg.getStandardArgs(true);
if (EnigmailGpg.getGpgFeature("export-specific-uid")) {
// Use GnuPG filters if possible
let dropSubkeyFilter = "usage!~e && usage!~s";
if (subkeyDates && subkeyDates.length > 0) {
dropSubkeyFilter = subkeyDates.map(x => `key_created!=${x}`).join(" && ");
}
args = args.concat(["--export-options", "export-minimal,no-export-attributes",
"--export-filter", "keep-uid=" + (email ? "mbox=" + email : "primary=1"),
"--export-filter", "drop-subkey=" + dropSubkeyFilter,
"--export", fpr
]);
} else {
args = args.concat(["--export-options", "export-minimal,no-export-attributes", "-a", "--export", fpr]);
}
const statusObj = {};
const exitCodeObj = {};
let res = await EnigmailExecution.execAsync(EnigmailGpg.agentPath, args);
let keyBlock = res.stdoutData;
// GnuPG 2.1.10+
if (!EnigmailGpg.getGpgFeature("export-result")) {
retObj.exitCode = 2;
retObj.errorMsg = EnigmailLocale.getString("failKeyExtract");
}
let r = new RegExp("^\\[GNUPG:\\] EXPORTED " + fpr, "m");
if (res.stderrData.search(r) < 0) {
retObj.exitCode = 2;
retObj.errorMsg = EnigmailLocale.getString("failKeyExtract");
}
retObj.keyData = btoa(keyBlock);
return retObj;
throw new Error("Not implemented");
}
/**
@ -254,20 +156,7 @@ class GnuPGCryptoAPI extends CryptoAPI {
async getFileName(byteData) {
EnigmailLog.DEBUG(`gnupg.js: getFileName()\n`);
const args = EnigmailGpg.getStandardArgs(true).concat(EnigmailPassword.command()).concat(["--decrypt"]);
let res = await EnigmailExecution.execAsync(EnigmailGpg.agentPath, args, byteData + "\n");
const matches = res.stderrData.match(/^(\[GNUPG:\] PLAINTEXT [0-9]+ [0-9]+ )(.*)$/m);
if (matches && (matches.length > 2)) {
var filename = matches[2];
if (filename.indexOf(" ") > 0) {
filename = filename.replace(/ .*$/, "");
}
return EnigmailData.convertToUnicode(unescape(filename), "utf-8");
} else {
return null;
}
throw new Error("Not implemented");
}
/**
@ -283,20 +172,7 @@ class GnuPGCryptoAPI extends CryptoAPI {
async verifyAttachment(filePath, sigPath) {
EnigmailLog.DEBUG(`gnupg.js: verifyAttachment()\n`);
const args = EnigmailGpg.getStandardArgs(true).concat(["--verify", sigPath, filePath]);
let result = await EnigmailExecution.execAsync(EnigmailGpg.agentPath, args);
const decrypted = {};
GnuPGDecryption.decryptMessageEnd(result.stderrData, result.exitCode, 1, true, true, EnigmailConstants.UI_INTERACTIVE, decrypted);
if (result.exitCode === 0) {
const detailArr = decrypted.sigDetails.split(/ /);
const dateTime = EnigmailTime.getDateTime(detailArr[2], true, true);
const msg1 = decrypted.errorMsg.split(/\n/)[0];
const msg2 = EnigmailLocale.getString("keyAndSigDate", ["0x" + decrypted.keyId, dateTime]);
const message = msg1 + "\n" + msg2;
return (message);
} else {
throw (decrypted.errorMsg);
}
throw new Error("Not implemented");
}
@ -313,14 +189,7 @@ class GnuPGCryptoAPI extends CryptoAPI {
async decryptAttachment(encrypted) {
EnigmailLog.DEBUG(`gnupg.js: decryptAttachment()\n`);
let args = EnigmailGpg.getStandardArgs(true);
args.push("--yes");
args = args.concat(EnigmailPassword.command());
args.push("-d");
let res = await EnigmailExecution.execAsync(EnigmailGpg.agentPath, args, encrypted);
return res;
throw new Error("Not implemented");
}
@ -338,26 +207,7 @@ class GnuPGCryptoAPI extends CryptoAPI {
async decrypt(encrypted, options) {
EnigmailLog.DEBUG(`gnupg.js: decrypt()\n`);
options.logFile = EnigmailErrorHandling.getTempLogFile();
const args = GnuPGDecryption.getDecryptionArgs(options);
let res = await EnigmailExecution.execAsync(EnigmailGpg.agentPath, args, encrypted);
EnigmailErrorHandling.appendLogFileToDebug(options.logFile);
if (res.statusFlags & EnigmailConstants.MISSING_PASSPHRASE) {
EnigmailLog.ERROR("decryption.jsm: decryptMessageStart: Error - no passphrase supplied\n");
throw {
errorMsg: EnigmailLocale.getString("noPassphrase")
};
}
const result = {
exitCode: res.exitCode,
decryptedData: res.stdoutData
};
GnuPGDecryption.decryptMessageEnd(res.stderrData, res.exitCode, res.stdoutData.length, options.verifyOnly, options.noOutput, options.uiFlags, result);
return result;
throw new Error("Not implemented");
}
/**

Просмотреть файл

@ -40,21 +40,6 @@ const EnigmailLocale = ChromeUtils.import(
"chrome://openpgp/content/modules/locale.jsm"
).EnigmailLocale;
/*
const {
obtainKeyList,
createKeyObj,
getPhotoFileFromGnuPG,
extractSignatures,
getGpgKeyData
} = ChromeUtils.import("chrome://openpgp/content/cryptoAPI/gnupg-keylist.jsm");
const {
GnuPG_importKeyFromFile,
GnuPG_extractSecretKey
} = ChromeUtils.import("chrome://openpgp/content/cryptoAPI/gnupg-key.jsm");
*/
/**
* RNP implementation of CryptoAPI
*/
@ -75,13 +60,6 @@ class RNPCryptoAPI extends CryptoAPI {
return RNP.getKeys(onlyKeys);
}
/**
* Get groups defined in gpg.conf in the same structure as KeyObject
*
* @return {Array of KeyObject} with type = "grp"
*/
getGroups() {}
/**
* Obtain signatures for a given set of key IDs.
*
@ -90,7 +68,9 @@ class RNPCryptoAPI extends CryptoAPI {
*
* @return {Promise<Array of Object>} - see extractSignatures()
*/
async getKeySignatures(keyId, ignoreUnknownUid = false) {}
async getKeySignatures(keyId, ignoreUnknownUid = false) {
throw new Error("Not implemented");
}
/**
* Export the minimum key for the public key object:
@ -106,7 +86,9 @@ class RNPCryptoAPI extends CryptoAPI {
* - errorMsg (if exitCode != 0)
* - keyData: BASE64-encded string of key data
*/
async getMinimalPubKey(fpr, email, subkeyDates) {}
async getMinimalPubKey(fpr, email, subkeyDates) {
throw new Error("Not implemented");
}
/**
* Extract a photo ID from a key, store it as file and return the file object.
@ -116,7 +98,9 @@ class RNPCryptoAPI extends CryptoAPI {
*
* @return {nsIFile} object or null in case no data / error.
*/
async getPhotoFile(keyId, photoNumber) {}
async getPhotoFile(keyId, photoNumber) {
throw new Error("Not implemented");
}
async importKeyBlock(keyBlock) {
// TODO: get status results
@ -137,7 +121,9 @@ class RNPCryptoAPI extends CryptoAPI {
* - {Number} importSum: total number of processed keys
* - {Number} importUnchanged: number of unchanged keys
*/
async importKeyFromFile(inputFile) {}
async importKeyFromFile(inputFile) {
throw new Error("Not implemented");
}
/**
* Export secret key(s) to a file
@ -151,7 +137,9 @@ class RNPCryptoAPI extends CryptoAPI {
* - {String} errorMsg: error message in case exitCode !== 0
*/
async extractSecretKey(keyId, minimalKey) {}
async extractSecretKey(keyId, minimalKey) {
throw new Error("Not implemented");
}
/**
*
@ -160,7 +148,9 @@ class RNPCryptoAPI extends CryptoAPI {
* @return {String or null} - the name of the attached file
*/
async getFileName(byteData) {}
async getFileName(byteData) {
throw new Error("Not implemented");
}
/**
*
@ -173,7 +163,9 @@ class RNPCryptoAPI extends CryptoAPI {
* The message will be an error message in this case.
*/
async verifyAttachment(filePath, sigPath) {}
async verifyAttachment(filePath, sigPath) {
throw new Error("Not implemented");
}
/**
*
@ -186,7 +178,9 @@ class RNPCryptoAPI extends CryptoAPI {
* retObj.errorMsg will be an error message in this case.
*/
async decryptAttachment(encrypted) {}
async decryptAttachment(encrypted) {
throw new Error("Not implemented");
}
/**
*

Просмотреть файл

@ -1,376 +0,0 @@
/*
* 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 https://mozilla.org/MPL/2.0/.
*/
"use strict";
var EXPORTED_SYMBOLS = ["GnuPGDecryption"];
const EnigmailLog = ChromeUtils.import("chrome://openpgp/content/modules/log.jsm").EnigmailLog;
const EnigmailLocale = ChromeUtils.import("chrome://openpgp/content/modules/locale.jsm").EnigmailLocale;
const EnigmailDialog = ChromeUtils.import("chrome://openpgp/content/modules/dialog.jsm").EnigmailDialog;
const EnigmailData = ChromeUtils.import("chrome://openpgp/content/modules/data.jsm").EnigmailData;
const EnigmailPrefs = ChromeUtils.import("chrome://openpgp/content/modules/prefs.jsm").EnigmailPrefs;
const EnigmailConstants = ChromeUtils.import("chrome://openpgp/content/modules/constants.jsm").EnigmailConstants;
const EnigmailExecution = ChromeUtils.import("chrome://openpgp/content/modules/execution.jsm").EnigmailExecution;
const EnigmailErrorHandling = ChromeUtils.import("chrome://openpgp/content/modules/errorHandling.jsm").EnigmailErrorHandling;
const EnigmailKey = ChromeUtils.import("chrome://openpgp/content/modules/key.jsm").EnigmailKey;
const EnigmailKeyRing = ChromeUtils.import("chrome://openpgp/content/modules/keyRing.jsm").EnigmailKeyRing;
const EnigmailGpg = ChromeUtils.import("chrome://openpgp/content/modules/gpg.jsm").EnigmailGpg;
const EnigmailFiles = ChromeUtils.import("chrome://openpgp/content/modules/files.jsm").EnigmailFiles;
const STATUS_ERROR = EnigmailConstants.BAD_SIGNATURE | EnigmailConstants.DECRYPTION_FAILED;
const STATUS_DECRYPTION_OK = EnigmailConstants.DECRYPTION_OKAY;
const STATUS_GOODSIG = EnigmailConstants.GOOD_SIGNATURE;
var GnuPGDecryption = {
/*
* options:
* - logFile (the actual file)
* - keyserver
* - keyserverProxy
* - fromAddr
* - noOutput
* - verifyOnly
* - mimeSignatureFile
* - maxOutputLength
*
*/
getDecryptionArgs: function(options) {
var args = EnigmailGpg.getStandardArgs(true);
args.push("--log-file");
args.push(EnigmailFiles.getEscapedFilename(EnigmailFiles.getFilePath(options.logFile)));
if (options.keyserver && options.keyserver !== "") {
var keyserver = options.keyserver.trim();
args.push("--keyserver-options");
var keySrvArgs = "auto-key-retrieve";
var srvProxy = options.keyserverProxy;
if (srvProxy) {
keySrvArgs += ",http-proxy=" + srvProxy;
}
args.push(keySrvArgs);
args.push("--keyserver");
args.push(keyserver);
}
if (EnigmailGpg.getGpgFeature("supports-sender") && options.fromAddr) {
args.push("--sender");
args.push(options.fromAddr.toLowerCase());
}
if (options.noOutput) {
args.push("--verify");
if (options.mimeSignatureFile) {
args.push(options.mimeSignatureFile);
args.push("-");
}
}
else {
if (options.maxOutputLength) {
args.push("--max-output");
args.push(String(options.maxOutputLength));
}
args.push("--decrypt");
}
return args;
},
decryptMessageEnd: function(stderrStr, exitCode, outputLen, verifyOnly, noOutput, uiFlags, retStatusObj) {
EnigmailLog.DEBUG("gnupg-decryption.jsm: decryptMessageEnd: uiFlags=" + uiFlags + ", verifyOnly=" + verifyOnly + ", noOutput=" + noOutput + "\n");
stderrStr = stderrStr.replace(/\r\n/g, "\n");
EnigmailLog.DEBUG("gnupg-decryption.jsm: decryptMessageEnd: stderrStr=\n" + stderrStr + "\n");
var interactive = uiFlags & EnigmailConstants.UI_INTERACTIVE;
var pgpMime = uiFlags & EnigmailConstants.UI_PGP_MIME;
var allowImport = uiFlags & EnigmailConstants.UI_ALLOW_KEY_IMPORT;
var unverifiedEncryptedOK = uiFlags & EnigmailConstants.UI_UNVERIFIED_ENC_OK;
var j;
retStatusObj.statusFlags = 0;
retStatusObj.errorMsg = "";
retStatusObj.blockSeparation = "";
var errorMsg = EnigmailErrorHandling.parseErrorOutput(stderrStr, retStatusObj);
if (retStatusObj.statusFlags & STATUS_ERROR) {
retStatusObj.errorMsg = errorMsg;
}
else {
retStatusObj.errorMsg = "";
}
if (pgpMime) {
retStatusObj.statusFlags |= verifyOnly ? EnigmailConstants.PGP_MIME_SIGNED : EnigmailConstants.PGP_MIME_ENCRYPTED;
}
var statusMsg = retStatusObj.statusMsg;
exitCode = EnigmailExecution.fixExitCode(exitCode, retStatusObj);
if ((exitCode === 0) && !noOutput && !outputLen &&
((retStatusObj.statusFlags & (STATUS_DECRYPTION_OK | STATUS_GOODSIG)) === 0)) {
exitCode = -1;
}
if (retStatusObj.statusFlags & EnigmailConstants.DISPLAY_MESSAGE && retStatusObj.extendedStatus.search(/\bdisp:/) >= 0) {
EnigmailDialog.alert(null, statusMsg);
return -1;
}
var errLines;
if (statusMsg) {
errLines = statusMsg.split(/\r?\n/);
}
else {
// should not really happen ...
errLines = stderrStr.split(/\r?\n/);
}
// possible STATUS Patterns (see GPG dod DETAILS.txt):
// one of these should be set for a signature:
var newsigPat = /^NEWSIG ?.*$/i;
var trustedsigPat = /^TRUST_(FULLY|ULTIMATE) ?.*$/i;
var goodsigPat = /^GOODSIG (\w{16}) (.*)$/i;
var badsigPat = /^BADSIG (\w{16}) (.*)$/i;
var expsigPat = /^EXPSIG (\w{16}) (.*)$/i;
var expkeysigPat = /^EXPKEYSIG (\w{16}) (.*)$/i;
var revkeysigPat = /^REVKEYSIG (\w{16}) (.*)$/i;
var errsigPat = /^ERRSIG (\w{16}) (.*)$/i;
// additional infos for good signatures:
var validSigPat = /^VALIDSIG (\w+) (.*) (\d+) (.*)/i;
// hint for a certain key id:
var userIdHintPat = /^USERID_HINT (\w{16}) (.*)$/i;
// to find out for which recipients the email was encrypted:
var encToPat = /^ENC_TO (\w{16}) (.*)$/i;
var matches;
var signed = false;
var goodOrExpOrRevSignature = false;
var sigKeyId = ""; // key of sender
var sigUserId = ""; // user ID of sender
var sigDetails = "";
var sigTrusted = false;
var encToDetails = "";
var encToArray = []; // collect ENC_TO lines here
for (j = 0; j < errLines.length; j++) {
EnigmailLog.DEBUG("gnupg-decryption.jsm: decryptMessageEnd: process: " + errLines[j] + "\n");
// ENC_TO entry
// - collect them for later processing to print details
matches = errLines[j].match(encToPat);
if (matches && (matches.length > 2)) {
encToArray.push("0x" + matches[1]);
}
// USERID_HINT entry
// - NOTE: NO END of loop
// ERROR: wrong to set userId because ecom is NOT the sender:
//matches = errLines[j].match(userIdHintPat);
//if (matches && (matches.length > 2)) {
// sigKeyId = matches[1];
// sigUserId = matches[2];
//}
// check for one of the possible SIG entries:
matches = errLines[j].match(newsigPat);
if (matches) {
if (signed) {
EnigmailLog.DEBUG("gnupg-decryption.jsm: decryptMessageEnd: multiple SIGN entries - ignoring previous signature\n");
}
signed = true;
goodOrExpOrRevSignature = false;
sigKeyId = "";
sigUserId = "";
sigDetails = "";
sigTrusted = false;
continue;
}
matches = errLines[j].match(trustedsigPat);
if (matches) {
sigTrusted = true;
continue;
}
matches = errLines[j].match(validSigPat);
if (matches && (matches.length > 4)) {
if (matches[4].length == 40) {
// in case of several subkeys refer to the main key ID.
// Only works with PGP V4 keys (Fingerprint length ==40)
sigKeyId = matches[4];
}
if (matches && (matches.length > 2)) {
sigDetails = errLines[j].substr(9);
}
continue;
}
// GOODSIG entry
matches = errLines[j].match(goodsigPat);
if (matches && (matches.length > 2)) {
if (signed) {
EnigmailLog.DEBUG("gnupg-decryption.jsm: decryptMessageEnd: OOPS: multiple SIGN entries\n");
}
signed = true;
goodOrExpOrRevSignature = true;
sigKeyId = matches[1];
sigUserId = matches[2];
}
else {
// BADSIG entry => signature found but bad
matches = errLines[j].match(badsigPat);
if (matches && (matches.length > 2)) {
if (signed) {
EnigmailLog.DEBUG("gnupg-decryption.jsm: decryptMessageEnd: OOPS: multiple SIGN entries\n");
}
signed = true;
goodOrExpOrRevSignature = false;
sigKeyId = matches[1];
sigUserId = matches[2];
}
else {
// EXPSIG entry => expired signature found
matches = errLines[j].match(expsigPat);
if (matches && (matches.length > 2)) {
if (signed) {
EnigmailLog.DEBUG("gnupg-decryption.jsm: decryptMessageEnd: OOPS: multiple SIGN entries\n");
}
signed = true;
goodOrExpOrRevSignature = true;
sigKeyId = matches[1];
sigUserId = matches[2];
}
else {
// EXPKEYSIG entry => signature found but key expired
matches = errLines[j].match(expkeysigPat);
if (matches && (matches.length > 2)) {
if (signed) {
EnigmailLog.DEBUG("gnupg-decryption.jsm: decryptMessageEnd: OOPS: multiple SIGN entries\n");
}
signed = true;
goodOrExpOrRevSignature = true;
sigKeyId = matches[1];
sigUserId = matches[2];
}
else {
// REVKEYSIG entry => signature found but key revoked
matches = errLines[j].match(revkeysigPat);
if (matches && (matches.length > 2)) {
if (signed) {
EnigmailLog.DEBUG("gnupg-decryption.jsm: decryptMessageEnd: OOPS: multiple SIGN entries\n");
}
signed = true;
goodOrExpOrRevSignature = true;
sigKeyId = matches[1];
sigUserId = matches[2];
}
else {
// ERRSIG entry => signature found but key not usable or unavailable
matches = errLines[j].match(errsigPat);
if (matches && (matches.length > 2)) {
if (signed) {
EnigmailLog.DEBUG("gnupg-decryption.jsm: decryptMessageEnd: OOPS: multiple SIGN entries\n");
}
signed = true;
goodOrExpOrRevSignature = false;
sigKeyId = matches[1];
// no user id with ecom istatus entry
}
}
}
}
}
}
} // end loop of processing errLines
if (sigTrusted) {
retStatusObj.statusFlags |= EnigmailConstants.TRUSTED_IDENTITY;
}
if (sigUserId && sigKeyId && EnigmailPrefs.getPref("displaySecondaryUid")) {
let keyObj = EnigmailKeyRing.getKeyById(sigKeyId);
if (keyObj) {
if (keyObj.photoAvailable) {
retStatusObj.statusFlags |= EnigmailConstants.PHOTO_AVAILABLE;
}
sigUserId = EnigmailKeyRing.getValidUids(sigKeyId).join("\n");
}
}
else if (sigUserId) {
sigUserId = EnigmailData.convertToUnicode(sigUserId, "UTF-8");
}
// add list of keys used for encryption if known (and their user IDs) if known
// Parsed status messages are something like (here the German version):
// [GNUPG:] ENC_TO AAAAAAAAAAAAAAAA 1 0
// [GNUPG:] ENC_TO 5B820D2D4553884F 16 0
// [GNUPG:] ENC_TO 37904DF2E631552F 1 0
// [GNUPG:] ENC_TO BBBBBBBBBBBBBBBB 1 0
// gpg: verschlüsselt mit 3072-Bit RSA Schlüssel, ID BBBBBBBB, erzeugt 2009-11-28
// "Joe Doo <joe.doo@domain.de>"
// [GNUPG:] NO_SECKEY E71712DF47BBCC40
// gpg: verschlüsselt mit RSA Schlüssel, ID AAAAAAAA
// [GNUPG:] NO_SECKEY AAAAAAAAAAAAAAAA
if (encToArray.length > 0) {
// for each key also show an associated user ID if known:
for (var encIdx = 0; encIdx < encToArray.length; ++encIdx) {
var localKeyId = encToArray[encIdx];
// except for ID 00000000, which signals hidden keys
if (localKeyId != "0x0000000000000000") {
let localKey = EnigmailKeyRing.getKeyById(localKeyId);
if (localKey) {
encToArray[encIdx] += " (" + localKey.userId + ")";
}
}
else {
encToArray[encIdx] = EnigmailLocale.getString("hiddenKey");
}
}
encToDetails = "\n " + encToArray.join(",\n ") + "\n";
}
retStatusObj.userId = sigUserId;
retStatusObj.keyId = sigKeyId;
retStatusObj.sigDetails = sigDetails;
retStatusObj.encToDetails = encToDetails;
if (signed) {
if (goodOrExpOrRevSignature) {
retStatusObj.errorMsg = EnigmailLocale.getString("prefGood", [sigUserId]);
/* + ", " + EnigmailLocale.getString("keyId") + " 0x" + sigKeyId.substring(8,16); */
}
else {
if (sigUserId.length > 0) {
retStatusObj.errorMsg = EnigmailLocale.getString("prefBad", [sigUserId]);
}
if (!exitCode)
exitCode = 1;
}
}
if (retStatusObj.statusFlags & EnigmailConstants.UNVERIFIED_SIGNATURE) {
retStatusObj.keyId = EnigmailKey.extractPubkey(statusMsg);
if (retStatusObj.statusFlags & EnigmailConstants.DECRYPTION_OKAY) {
exitCode = 0;
}
}
if (exitCode !== 0) {
// Error processing
EnigmailLog.DEBUG("gnupg-decryption.jsm: decryptMessageEnd: command execution exit code: " + exitCode + "\n");
}
return exitCode;
}
};

Просмотреть файл

@ -12,110 +12,15 @@
var EXPORTED_SYMBOLS = ["GnuPG_importKeyFromFile", "GnuPG_extractSecretKey"];
const EnigmailExecution = ChromeUtils.import("chrome://openpgp/content/modules/execution.jsm").EnigmailExecution;
const EnigmailLog = ChromeUtils.import("chrome://openpgp/content/modules/log.jsm").EnigmailLog;
const EnigmailGpg = ChromeUtils.import("chrome://openpgp/content/modules/gpg.jsm").EnigmailGpg;
const EnigmailFiles = ChromeUtils.import("chrome://openpgp/content/modules/files.jsm").EnigmailFiles;
const EnigmailLocale = ChromeUtils.import("chrome://openpgp/content/modules/files.jsm").EnigmailLocale;
async function GnuPG_importKeyFromFile(inputFile) {
EnigmailLog.DEBUG("gnupg-key.jsm: importKeysFromFile: fileName=" + inputFile.path + "\n");
var command = EnigmailGpg.agentPath;
var args = EnigmailGpg.getStandardArgs(false).concat(["--no-tty", "--batch", "--no-verbose", "--status-fd", "2", "--no-auto-check-trustdb", "--import"]);
var fileName = EnigmailFiles.getEscapedFilename((inputFile.QueryInterface(Ci.nsIFile)).path);
args.push(fileName);
let res = await EnigmailExecution.execAsync(command, args, "");
let statusMsg = res.statusMsg;
var keyList = [];
let importedKeys = [];
let importSum = 0;
let importUnchanged = 0;
// IMPORT_RES <count> <no_user_id> <imported> 0 <unchanged>
// <n_uids> <n_subk> <n_sigs> <n_revoc> <sec_read> <sec_imported> <sec_dups> <not_imported>
if (statusMsg) {
let import_res = statusMsg.match(/^IMPORT_RES ([0-9]+) ([0-9]+) ([0-9]+) 0 ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)/m);
if (import_res !== null) {
let secCount = parseInt(import_res[9], 10); // number of secret keys found
let secImported = parseInt(import_res[10], 10); // number of secret keys imported
let secDups = parseInt(import_res[11], 10); // number of secret keys already on the keyring
if (secCount !== secImported + secDups) {
res.errorMsg = EnigmailLocale.getString("import.secretKeyImportError");
res.exitCode = 1;
}
else {
importSum = parseInt(import_res[1], 10);
importUnchanged = parseInt(import_res[4], 10);
res.exitCode = 0;
var statusLines = statusMsg.split(/\r?\n/);
for (let j = 0; j < statusLines.length; j++) {
var matches = statusLines[j].match(/IMPORT_OK ([0-9]+) (\w+)/);
if (matches && (matches.length > 2)) {
if (typeof (keyList[matches[2]]) != "undefined") {
keyList[matches[2]] |= Number(matches[1]);
}
else
keyList[matches[2]] = Number(matches[1]);
importedKeys.push(matches[2]);
EnigmailLog.DEBUG("gnupg-key.jsm: importKeysFromFile: imported " + matches[2] + ":" + matches[1] + "\n");
}
}
}
}
}
return {
exitCode: res.exitCode,
errorMsg: res.errorMsg,
importedKeys: importedKeys,
importSum: importSum,
importUnchanged: importUnchanged
};
throw new Error("Not implemented");
}
async function GnuPG_extractSecretKey(userId, minimalKey) {
let args = EnigmailGpg.getStandardArgs(true);
let exitCode = -1,
errorMsg = "";
if (minimalKey) {
args.push("--export-options");
args.push("export-minimal,no-export-attributes");
}
args.push("-a");
args.push("--export-secret-keys");
if (userId) {
args = args.concat(userId.split(/[ ,\t]+/));
}
let res = await EnigmailExecution.execAsync(EnigmailGpg.agentPath, args, "");
if (res.stdoutData) {
exitCode = 0;
}
if (exitCode !== 0) {
if (res.errorMsg) {
errorMsg = EnigmailFiles.formatCmdLine(EnigmailGpg.agentPath, args);
errorMsg += "\n" + res.errorMsg;
}
}
return {
keyData: res.stdoutData,
exitCode: exitCode,
errorMsg: errorMsg
};
throw new Error("Not implemented");
}

Просмотреть файл

@ -10,13 +10,12 @@
"use strict";
var EXPORTED_SYMBOLS = ["obtainKeyList", "createKeyObj",
var EXPORTED_SYMBOLS = ["obtainKeyList",
"getPhotoFileFromGnuPG", "extractSignatures", "getGpgKeyData"
];
const EnigmailTime = ChromeUtils.import("chrome://openpgp/content/modules/time.jsm").EnigmailTime;
const EnigmailGpg = ChromeUtils.import("chrome://openpgp/content/modules/gpg.jsm").EnigmailGpg;
const EnigmailExecution = ChromeUtils.import("chrome://openpgp/content/modules/execution.jsm").EnigmailExecution;
const EnigmailLog = ChromeUtils.import("chrome://openpgp/content/modules/log.jsm").EnigmailLog;
const EnigmailTrust = ChromeUtils.import("chrome://openpgp/content/modules/trust.jsm").EnigmailTrust;
const EnigmailData = ChromeUtils.import("chrome://openpgp/content/modules/data.jsm").EnigmailData;
@ -70,40 +69,7 @@ const NS_LOCALFILEOUTPUTSTREAM_CONTRACTID = "@mozilla.org/network/file-output-st
*/
async function obtainKeyList(onlyKeys = null) {
EnigmailLog.DEBUG("gnupg-keylist.jsm: obtainKeyList()\n");
let secKeyList = [],
pubKeyList = [];
let commonArgs = EnigmailGpg.getStandardArgs(true);
commonArgs = commonArgs.concat(["--with-fingerprint", "--fixed-list-mode", "--with-colons"]);
let args = commonArgs.concat(["--list-keys"]);
if (onlyKeys) {
args = args.concat(onlyKeys);
}
let res = await EnigmailExecution.execAsync(EnigmailGpg.agentPath, args, "");
pubKeyList = res.stdoutData.split(/\n/);
let keyList = {
keys: [],
index: []
};
EnigmailLog.DEBUG(`gnupg-keylist.jsm: obtainKeyList: #lines: ${pubKeyList.length}\n`);
if (pubKeyList.length > 0) {
appendKeyItems(pubKeyList, keyList);
args = commonArgs.concat(["--list-secret-keys"]);
if (onlyKeys) {
args = args.concat(onlyKeys);
}
res = await EnigmailExecution.execAsync(EnigmailGpg.agentPath, args, "");
secKeyList = res.stdoutData.split(/\n/);
appendKeyItems(secKeyList, keyList);
}
return keyList;
throw new Error("Not implemented");
}
@ -262,82 +228,7 @@ function appendUnkownSecretKey(keyId, aKeyList, startIndex, keyList) {
*/
async function getPhotoFileFromGnuPG(keyId, photoNumber) {
EnigmailLog.DEBUG(`gnupg-keylist.jsm: getPhotoFileFromGnuPG, keyId=${keyId} photoNumber=${photoNumber}\n`);
const GPG_ADDITIONAL_OPTIONS = ["--no-secmem-warning", "--no-verbose", "--no-auto-check-trustdb",
"--batch", "--no-tty", "--no-verbose", "--status-fd", "1", "--attribute-fd", "2",
"--fixed-list-mode", "--list-keys", keyId
];
const args = EnigmailGpg.getStandardArgs(false).concat(GPG_ADDITIONAL_OPTIONS);
let res = await EnigmailExecution.execAsync(EnigmailGpg.agentPath, args);
let photoData = res.stderrData;
let outputTxt = res.stdoutData;
if (!outputTxt || !photoData) {
return null;
}
if (EnigmailOS.isDosLike && EnigmailGpg.getGpgFeature("windows-photoid-bug")) {
// workaround for error in gpg
photoData = photoData.replace(/\r\n/g, "\n");
}
// [GNUPG:] ATTRIBUTE A053069284158FC1E6770BDB57C9EB602B0717E2 2985
let foundPicture = -1;
let skipData = 0;
let imgSize = -1;
const statusLines = outputTxt.split(/[\n\r+]/);
for (let i = 0; i < statusLines.length; i++) {
const matches = statusLines[i].match(/\[GNUPG:\] ATTRIBUTE ([A-F\d]+) (\d+) (\d+) (\d+) (\d+) (\d+) (\d+) (\d+)/);
if (matches && matches[3] == "1") {
// attribute is an image
foundPicture++;
if (foundPicture === photoNumber) {
imgSize = Number(matches[2]);
break;
} else {
skipData += Number(matches[2]);
}
}
}
if (foundPicture >= 0 && foundPicture === photoNumber) {
if (photoData.search(/^gpg: /) === 0) {
// skip disturbing gpg output
let i = photoData.search(/\n/) + 1;
skipData += i;
}
const pictureData = photoData.substr(16 + skipData, imgSize);
if (!pictureData.length) {
return null;
}
try {
const flags = NS_WRONLY | NS_CREATE_FILE | NS_TRUNCATE;
const picFile = EnigmailFiles.getTempDirObj();
picFile.append(keyId + ".jpg");
picFile.createUnique(picFile.NORMAL_FILE_TYPE, STANDARD_FILE_PERMS);
const fileStream = Cc[NS_LOCALFILEOUTPUTSTREAM_CONTRACTID].createInstance(Ci.nsIFileOutputStream);
fileStream.init(picFile, flags, STANDARD_FILE_PERMS, 0);
if (fileStream.write(pictureData, pictureData.length) !== pictureData.length) {
fileStream.close();
throw Components.results.NS_ERROR_FAILURE;
}
fileStream.flush();
fileStream.close();
// delete picFile upon exit
let extAppLauncher = Cc["@mozilla.org/mime;1"].getService(Ci.nsPIExternalAppLauncher);
extAppLauncher.deleteTemporaryFileOnExit(picFile);
return picFile;
} catch (ex) {}
}
return null;
throw new Error("Not implemented");
}
@ -423,81 +314,7 @@ function extractSignatures(gpgKeyList, ignoreUnknownUid) {
return listObj;
}
async function getGpgKeyData(armorKeyString) {
EnigmailLog.DEBUG("GnuPGCryptoAPI.jsm: getGpgKeyData()\n");
if (!EnigmailGpg.getGpgFeature("supports-show-only")) {
throw "unsupported";
}
let args = EnigmailGpg.getStandardArgs(false).concat(["--no-tty", "--batch", "--no-verbose", "--with-fingerprint", "--with-colons", "--import-options", "import-show", "--dry-run", "--import"]);
let res = await EnigmailExecution.execAsync(EnigmailGpg.agentPath, args, armorKeyString);
let lines = res.stdoutData.split(/\n/);
let key = {};
let keyId = "";
let keyList = [];
/*
pub:u:256:22:84F83BE88C892606:1525969855:1683649855::u:::scESC:::::ed25519:::0:
fpr:::::::::AFE1B65C5F39ACA7960B22CD84F83BE88C892606:
uid:u::::1525969914::22DB32406212400B52CDC74DA2B33418637430F1::Patrick (ECC) <patrick@enigmail.net>::::::::::0:
uid:u::::1525969855::F70B7A77F085AA7BA003D6AFAB6FF0DB1FC901B0::enigmail <patrick@enigmail.net>::::::::::0:
sub:u:256:18:329DAB3350400C40:1525969855:1683649855:::::e:::::cv25519::
fpr:::::::::3B154538D4DFAA19BDADAAD0329DAB3350400C40:
*/
for (let i = 0; i < lines.length; i++) {
const lineTokens = lines[i].split(/:/);
switch (lineTokens[ENTRY_ID]) {
case "pub":
case "sec":
key = {
id: lineTokens[KEY_ID],
fpr: null,
name: null,
isSecret: false,
created: EnigmailTime.getDateTime(lineTokens[CREATED_ID], true, false),
uids: []
};
if (!(key.id in keyList)) {
keyList[key.id] = key;
}
if (lineTokens[ENTRY_ID] === "sec") {
keyList[key.id].isSecret = true;
}
break;
case "fpr":
if (!key.fpr) {
key.fpr = lineTokens[USERID_ID];
}
break;
case "uid":
if (!key.name) {
key.name = lineTokens[USERID_ID];
}
else {
key.uids.push(lineTokens[USERID_ID]);
}
break;
case "rvs":
case "rvk":
keyId = lineTokens[KEY_ID];
if (keyId in keyList) {
keyList[keyId].revoke = true;
} else {
keyList[keyId] = {
revoke: true,
id: keyId
};
}
break;
}
}
return keyList;
throw new Error("Not implemented");
}

Просмотреть файл

@ -98,16 +98,6 @@ class CryptoAPI {
return [];
}
/**
* Get groups defined in gpg.conf in the same structure as KeyObject
* [synchronous]
*
* @return {Array of KeyObject} with type = "grp"
*/
getGroups() {
return [];
}
/**
* Extract a photo ID from a key, store it as file and return the file object.
*

Просмотреть файл

@ -21,7 +21,7 @@ function converter(charset) {
var EnigmailData = {
getUnicodeData: function(data) {
// convert output from subprocess to Unicode
// convert output to Unicode
var tmpStream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(Ci.nsIStringInputStream);
tmpStream.setData(data, data.length);
var inStream = Cc["@mozilla.org/scriptableinputstream;1"].createInstance(Ci.nsIScriptableInputStream);

Просмотреть файл

@ -16,13 +16,11 @@ const EnigmailLocale = ChromeUtils.import("chrome://openpgp/content/modules/loca
const EnigmailData = ChromeUtils.import("chrome://openpgp/content/modules/data.jsm").EnigmailData;
const EnigmailDialog = ChromeUtils.import("chrome://openpgp/content/modules/dialog.jsm").EnigmailDialog;
const EnigmailHttpProxy = ChromeUtils.import("chrome://openpgp/content/modules/httpProxy.jsm").EnigmailHttpProxy;
const EnigmailGpgAgent = ChromeUtils.import("chrome://openpgp/content/modules/gpgAgent.jsm").EnigmailGpgAgent;
const EnigmailFiles = ChromeUtils.import("chrome://openpgp/content/modules/files.jsm").EnigmailFiles;
const EnigmailGpg = ChromeUtils.import("chrome://openpgp/content/modules/gpg.jsm").EnigmailGpg;
const EnigmailErrorHandling = ChromeUtils.import("chrome://openpgp/content/modules/errorHandling.jsm").EnigmailErrorHandling;
const EnigmailKeyRing = ChromeUtils.import("chrome://openpgp/content/modules/keyRing.jsm").EnigmailKeyRing;
const EnigmailKey = ChromeUtils.import("chrome://openpgp/content/modules/key.jsm").EnigmailKey;
const EnigmailPassword = ChromeUtils.import("chrome://openpgp/content/modules/passwords.jsm").EnigmailPassword;
const EnigmailConstants = ChromeUtils.import("chrome://openpgp/content/modules/constants.jsm").EnigmailConstants;
const EnigmailFuncs = ChromeUtils.import("chrome://openpgp/content/modules/funcs.jsm").EnigmailFuncs;
const EnigmailCryptoAPI = ChromeUtils.import("chrome://openpgp/content/modules/cryptoAPI.jsm").EnigmailCryptoAPI;
@ -55,7 +53,8 @@ function newStatusObject() {
var EnigmailDecryption = {
isReady: function(win) {
return (EnigmailCore.getService(win)) && (!EnigmailKeyRing.isGeneratingKey());
// this used to return false while generating a key. still necessary?
return EnigmailCore.getService(win);
},
getFromAddr: function(win) {

Просмотреть файл

@ -1,345 +0,0 @@
/*
* 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 https://mozilla.org/MPL/2.0/.
*/
/**
* This module provides DNS query functionality via subprocesses.
* Supported record types: MX, SRV
*
* The following tools are currently supported:
* Windows: nslookup
* Unix/Linux: dig, kdig, host, nslookup
*/
"use strict";
var EXPORTED_SYMBOLS = ["EnigmailDns"];
const EnigmailCore = ChromeUtils.import("chrome://openpgp/content/modules/core.jsm").EnigmailCore;
const EnigmailLog = ChromeUtils.import("chrome://openpgp/content/modules/log.jsm").EnigmailLog;
const EnigmailOS = ChromeUtils.import("chrome://openpgp/content/modules/os.jsm").EnigmailOS;
const subprocess = ChromeUtils.import("chrome://openpgp/content/modules/subprocess.jsm").subprocess;
const EnigmailFiles = ChromeUtils.import("chrome://openpgp/content/modules/files.jsm").EnigmailFiles;
const RESTYPE_WIN_NLSOOKUP = 1;
const RESTYPE_UNIX_NLSOOKUP = 2;
const RESTYPE_DIG = 3;
const RESTYPE_HOST = 4;
const RESTYPE_NOT_AVAILABLE = 99;
var gHandler = null,
gResolverExecutable = null;
var EnigmailDns = {
/**
* Perform a DNS lookup
*
* @param {String} recordType: The resource record type to query. MX and SRV are currently supported.
* @param {String} queryName: The name to search for, e.g. "enigmail.net"
*
* @return {Promise<Array{String}>}: array of server(s) handling
*
*/
lookup: async function(recordType, queryName) {
EnigmailLog.DEBUG(`dns.jsm: lookup(${recordType}, ${queryName})\n`);
if (!determineResolver()) return null;
switch (recordType.toUpperCase()) {
case "MX":
case "SRV":
break;
default:
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
}
let dnsHandler = new gHandler(gResolverExecutable);
return dnsHandler.execute(recordType, queryName);
}
};
/**
* Determine the DNS resolver tool to use (e.g. dig, nslookup)
*
* @return {Boolean}: true: tool found / false: no tool found
*/
function determineResolver() {
if (!gHandler) {
gHandler = GenericHandler;
if (EnigmailOS.isWin32) {
gResolverExecutable = EnigmailFiles.resolvePathWithEnv("nslookup");
if (gResolverExecutable) {
EnigmailLog.DEBUG(`dns.jsm: determineResolver: executable = ${gResolverExecutable.path}\n`);
gHandler = NsLookupHandler_Windows;
}
}
else {
determineLinuxResolver();
}
if (!gResolverExecutable) EnigmailLog.DEBUG(`dns.jsm: determineResolver: no executable found\n`);
}
return gHandler !== GenericHandler;
}
function determineLinuxResolver() {
EnigmailLog.DEBUG(`dns.jsm: determineLinuxResolver()\n`);
const services = [{
exe: "dig",
class: DigHandler
}, {
exe: "kdig",
class: DigHandler
}, {
exe: "host",
class: HostHandler
}, {
exe: "nslookup",
class: NsLookupHandler
}];
for (let i of services) {
gResolverExecutable = EnigmailFiles.resolvePathWithEnv(i.exe);
if (gResolverExecutable) {
EnigmailLog.DEBUG(`dns.jsm: determineLinuxResolver: found ${i.class.handlerType}\n`);
gHandler = i.class;
return;
}
}
}
/**
* Base class for executing DNS requests
*/
class GenericHandler {
constructor(handlerFile) {
this._handlerFile = handlerFile;
this.recordType = "";
this.hostName = "";
}
getCmdArgs() {
return [];
}
execute(recordType, hostName) {
return new Promise((resolve, reject) => {
this.recordType = recordType.toUpperCase();
this.hostName = hostName;
let args = this.getCmdArgs();
if (args.length === 0) {
resolve([]);
return;
}
let stdoutData = "",
stderrData = "";
let self = this;
EnigmailLog.DEBUG(`dns.jsm: execute(): launching ${EnigmailFiles.formatCmdLine(this._handlerFile, args)}\n`);
subprocess.call({
command: this._handlerFile,
arguments: args,
environment: EnigmailCore.getEnvList(),
charset: null,
stdout: function(data) {
//EnigmailLog.DEBUG(`dns.jsm: execute.stdout: got data ${data}\n`);
stdoutData += data;
},
stderr: function(data) {
//EnigmailLog.DEBUG(`dns.jsm: execute.stderr: got data ${data}\n`);
stderrData += data;
},
done: function(result) {
EnigmailLog.DEBUG(`dns.jsm: execute.done(${result.exitCode})\n`);
try {
if (result.exitCode === 0) {
resolve(self.parseResult(stdoutData));
}
else {
resolve([]);
}
}
catch (ex) {
reject(ex);
}
},
mergeStderr: false
});
});
}
parseResult() {
return [];
}
}
/**
* Handler class for "dig" and "kdig"
*/
class DigHandler extends GenericHandler {
constructor(handlerFile) {
super(handlerFile);
this.handlerType = "dig";
}
getCmdArgs() {
return ["-t", this.recordType, "+short", this.hostName];
}
parseResult(stdoutData) {
let hosts = [];
let lines = stdoutData.split(/[\r\n]+/);
if (this.recordType === "MX") {
for (let i = 0; i < lines.length; i++) {
let m = lines[i].match(/^(\d+ )(.*)\./);
if (m && m.length >= 3) hosts.push(m[2]);
}
}
else if (this.recordType === "SRV") {
for (let i = 0; i < lines.length; i++) {
let m = lines[i].match(/^(\d+) (\d+) (\d+) ([^ ]+)\.$/);
if (m && m.length >= 5) hosts.push(m[4] + ":" + m[3]);
}
}
return hosts;
}
}
/**
* Handler class for "host"
*/
class HostHandler extends GenericHandler {
constructor(handlerFile) {
super(handlerFile);
this.handlerType = "host";
}
getCmdArgs() {
return ["-t", this.recordType, this.hostName];
}
parseResult(stdoutData) {
if (stdoutData.search(/3\(NXDOMAIN\)/) >= 0) return [];
let hosts = [];
let lines = stdoutData.split(/[\r\n]+/);
if (this.recordType === "MX") {
for (let i = 0; i < lines.length; i++) {
let m = lines[i].match(/^(.* )([^ ]+)\.$/);
if (m && m.length >= 3) hosts.push(m[2]);
}
}
else if (this.recordType === "SRV") {
for (let i = 0; i < lines.length; i++) {
let m = lines[i].match(/^(.*) (\d+) ([^ ]+)\.$/);
if (m && m.length >= 4) hosts.push(m[3] + ":" + m[2]);
}
}
return hosts;
}
}
/**
* Handler class for "nslookup" (on Linux/Unix)
*/
class NsLookupHandler extends GenericHandler {
constructor(handlerFile) {
super(handlerFile);
this.handlerType = "nslookup";
}
getCmdArgs() {
return ["-type=" + this.recordType, this.hostName];
}
parseResult(stdoutData) {
let hosts = [];
let lines = stdoutData.split(/[\r\n]+/);
if (lines.length > 3 && lines[3].search(/: NXDOMAIN/) > 0) return [];
if (this.recordType === "MX") {
let reg = new RegExp("^" + this.hostName.toLowerCase() + "(.* )([^ \t]+.*[^\.])\\.?$");
for (let i = 2; i < lines.length; i++) {
let m = lines[i].match(reg);
if (m && m.length >= 3) hosts.push(m[2]);
if (lines[i].length < 5) break;
}
}
else if (this.recordType === "SRV") {
for (let i = 2; i < lines.length; i++) {
let m = lines[i].match(/^(.*) (\d+) ([^ ]+)\.$/);
if (m && m.length >= 3) hosts.push(m[3] + ":" + m[2]);
if (lines[i].length < 5) break;
}
}
return hosts;
}
}
/**
* Handler class for "nslookup" on Windows
*/
class NsLookupHandler_Windows extends NsLookupHandler {
parseResult(stdoutData) {
let hosts = [];
let lines = stdoutData.split(/[\r\n]+/);
if (this.recordType === "MX") {
let reg = new RegExp("^" + this.hostName.toLowerCase() + "(.* )([^ \t]+.*[^\.])\\.?$");
for (let i = 2; i < lines.length; i++) {
let m = lines[i].match(reg);
if (m && m.length >= 3) hosts.push(m[2]);
if (lines[i].length < 5) break;
}
}
else if (this.recordType === "SRV") {
let svc = null;
for (let i = 2; i < lines.length; i++) {
if (lines[i].search(/SRV service location:$/) > 0) {
svc = null;
continue;
}
let m = lines[i].match(/^[\t ]+(port|svr hostname)([\t =]+)([^ \t]+)$/);
if (m && m.length >= 4) {
if (m[1] === "port" && svc === null) {
svc = m[3];
}
else if (m[1] === "svr hostname" && svc) {
hosts.push(m[3] + ":" + svc);
}
}
if (lines[i].length < 5) break;
}
}
return hosts;
}
}

Просмотреть файл

@ -16,12 +16,9 @@ const EnigmailPrefs = ChromeUtils.import("chrome://openpgp/content/modules/prefs
const EnigmailApp = ChromeUtils.import("chrome://openpgp/content/modules/app.jsm").EnigmailApp;
const EnigmailLocale = ChromeUtils.import("chrome://openpgp/content/modules/locale.jsm").EnigmailLocale;
const EnigmailDialog = ChromeUtils.import("chrome://openpgp/content/modules/dialog.jsm").EnigmailDialog;
const EnigmailGpgAgent = ChromeUtils.import("chrome://openpgp/content/modules/gpgAgent.jsm").EnigmailGpgAgent;
const EnigmailGpg = ChromeUtils.import("chrome://openpgp/content/modules/gpg.jsm").EnigmailGpg;
const EnigmailErrorHandling = ChromeUtils.import("chrome://openpgp/content/modules/errorHandling.jsm").EnigmailErrorHandling;
const EnigmailExecution = ChromeUtils.import("chrome://openpgp/content/modules/execution.jsm").EnigmailExecution;
const EnigmailFiles = ChromeUtils.import("chrome://openpgp/content/modules/files.jsm").EnigmailFiles;
const EnigmailPassword = ChromeUtils.import("chrome://openpgp/content/modules/passwords.jsm").EnigmailPassword;
const EnigmailFuncs = ChromeUtils.import("chrome://openpgp/content/modules/funcs.jsm").EnigmailFuncs;
const EnigmailKeyRing = ChromeUtils.import("chrome://openpgp/content/modules/keyRing.jsm").EnigmailKeyRing;
const EnigmailConstants = ChromeUtils.import("chrome://openpgp/content/modules/constants.jsm").EnigmailConstants;
@ -396,15 +393,6 @@ var EnigmailEncryption = {
}
};
// GnuPG
/*
var proc = EnigmailExecution.execStart(EnigmailGpgAgent.agentPath, encryptArgs, signMsg, win, listener, statusFlagsObj);
if (statusFlagsObj.value & EnigmailConstants.MISSING_PASSPHRASE) {
EnigmailLog.ERROR("encryption.jsm: encryptMessageStart: Error - no passphrase supplied\n");
errorMsgObj.value = "";
}
*/
let resultStatus = {};
const cApi = EnigmailCryptoAPI();
console.debug("listener: %o", listener);
@ -441,9 +429,9 @@ var EnigmailEncryption = {
return -1;
}
EnigmailErrorHandling.parseErrorOutput(stderrStr, retStatusObj);
//EnigmailErrorHandling.parseErrorOutput(stderrStr, retStatusObj);
exitCode = EnigmailExecution.fixExitCode(exitCode, retStatusObj);
//exitCode = EnigmailExecution.fixExitCode(exitCode, retStatusObj);
if ((exitCode === 0) && !outputLen) {
exitCode = -1;
}
@ -498,7 +486,9 @@ var EnigmailEncryption = {
encryptMessage: function(parent, uiFlags, plainText, fromMailAddr, toMailAddr, bccMailAddr, sendFlags,
exitCodeObj, statusFlagsObj, errorMsgObj) {
EnigmailLog.DEBUG("enigmail.js: Enigmail.encryptMessage: " + plainText.length + " bytes from " + fromMailAddr + " to " + toMailAddr + " (" + sendFlags + ")\n");
throw new Error("Not implemented");
/*
exitCodeObj.value = -1;
statusFlagsObj.value = 0;
errorMsgObj.value = "";
@ -567,56 +557,6 @@ var EnigmailEncryption = {
// Error processing
EnigmailLog.DEBUG("enigmail.js: Enigmail.encryptMessage: command execution exit code: " + exitCodeObj.value + "\n");
return "";
*/
},
encryptAttachment: function(parent, fromMailAddr, toMailAddr, bccMailAddr, sendFlags, inFile, outFile,
exitCodeObj, statusFlagsObj, errorMsgObj) {
EnigmailLog.DEBUG("encryption.jsm: EnigmailEncryption.encryptAttachment infileName=" + inFile.path + "\n");
statusFlagsObj.value = 0;
sendFlags |= EnigmailConstants.SEND_ATTACHMENT;
let asciiArmor = false;
try {
asciiArmor = EnigmailPrefs.getPrefBranch().getBoolPref("inlineAttachAsciiArmor");
}
catch (ex) {}
const asciiFlags = (asciiArmor ? ENC_TYPE_ATTACH_ASCII : ENC_TYPE_ATTACH_BINARY);
let args = EnigmailEncryption.getEncryptCommand(fromMailAddr, toMailAddr, bccMailAddr, "", sendFlags, asciiFlags, errorMsgObj);
if (!args) {
return null;
}
const signMessage = (sendFlags & EnigmailConstants.SEND_SIGNED);
if (signMessage) {
args = args.concat(EnigmailPassword.command());
}
//const inFilePath = EnigmailFiles.getEscapedFilename(EnigmailFiles.getFilePathReadonly(inFile.QueryInterface(Ci.nsIFile)));
const fileContents = EnigmailFiles.readBinaryFile(inFile.QueryInterface(Ci.nsIFile));
const inFileName = inFile.QueryInterface(Ci.nsIFile).leafName;
const outFilePath = EnigmailFiles.getEscapedFilename(EnigmailFiles.getFilePathReadonly(outFile.QueryInterface(Ci.nsIFile)));
args = args.concat(["--yes", "-o", outFilePath, "--set-filename", inFileName]);
let cmdErrorMsgObj = {};
const msg = EnigmailExecution.execCmd(EnigmailGpgAgent.agentPath, args, fileContents, exitCodeObj, statusFlagsObj, {}, cmdErrorMsgObj);
if (exitCodeObj.value !== 0) {
if (cmdErrorMsgObj.value) {
errorMsgObj.value = EnigmailFiles.formatCmdLine(EnigmailGpgAgent.agentPath, args);
errorMsgObj.value += "\n" + cmdErrorMsgObj.value;
}
else {
errorMsgObj.value = "An unknown error has occurred";
}
return "";
}
return msg;
}
};

Просмотреть файл

@ -1,752 +0,0 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
/* 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 https://mozilla.org/MPL/2.0/. */
"use strict";
/* exported BaseProcess, PromiseWorker */
/* global ChromeWorker: false, */
var {
results: Cr
} = Components;
const XPCOMUtils = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm").XPCOMUtils;
Components.utils.importGlobalProperties(["TextDecoder", "TextEncoder"]);
var _AsyncShutdown, _setTimeout;
var SubScriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader);
SubScriptLoader.loadSubScript("chrome://openpgp/content/modules/enigmailprocess_shared.js", this);
var EXPORTED_SYMBOLS = ["BaseProcess", "PromiseWorker", "SubprocessConstants"];
const BUFFER_SIZE = 32768;
let nextResponseId = 0;
function getAsyncShutdown() {
if (!_AsyncShutdown) {
_AsyncShutdown = ChromeUtils.import("resource://gre/modules/AsyncShutdown.jsm").AsyncShutdown;
}
return _AsyncShutdown;
}
function getSetTimeout() {
if (!_setTimeout) {
_setTimeout = ChromeUtils.import("resource://gre/modules/Timer.jsm").setTimeout;
}
return _setTimeout;
}
/* global SubprocessConstants: true */
/**
* Wraps a ChromeWorker so that messages sent to it return a promise which
* resolves when the message has been received and the operation it triggers is
* complete.
*/
class _PromiseWorker extends ChromeWorker {
constructor(url) {
super(url);
this.listeners = new Map();
this.pendingResponses = new Map();
this.addListener("close", this.onClose.bind(this));
this.addListener("failure", this.onFailure.bind(this));
this.addListener("success", this.onSuccess.bind(this));
this.addListener("debug", this.onDebug.bind(this));
this.addEventListener("message", this.onmessage);
this.shutdown = this.shutdown.bind(this);
getAsyncShutdown().webWorkersShutdown.addBlocker(
"Subprocess.jsm: Shut down IO worker",
this.shutdown);
}
onClose() {
getAsyncShutdown().webWorkersShutdown.removeBlocker(this.shutdown);
}
shutdown() {
return this.call("shutdown", []);
}
/**
* Adds a listener for the given message from the worker. Any message received
* from the worker with a `data.msg` property matching the given `msg`
* parameter are passed to the given listener.
*
* @param {string} msg
* The message to listen for.
* @param {function(Event)} listener
* The listener to call when matching messages are received.
*/
addListener(msg, listener) {
if (!this.listeners.has(msg)) {
this.listeners.set(msg, new Set());
}
this.listeners.get(msg).add(listener);
}
/**
* Removes the given message listener.
*
* @param {string} msg
* The message to stop listening for.
* @param {function(Event)} listener
* The listener to remove.
*/
removeListener(msg, listener) {
let listeners = this.listeners.get(msg);
if (listeners) {
listeners.delete(listener);
if (!listeners.size) {
this.listeners.delete(msg);
}
}
}
onmessage(event) {
let {
msg
} = event.data;
let listeners = this.listeners.get(msg) || new Set();
for (let listener of listeners) {
try {
listener(event.data);
}
catch (e) {
Cu.reportError(e);
}
}
}
/**
* Called when a message sent to the worker has failed, and rejects its
* corresponding promise.
*
* @private
*/
onFailure({
msgId, error
}) {
this.pendingResponses.get(msgId).reject(error);
this.pendingResponses.delete(msgId);
}
/**
* Called when a message sent to the worker has succeeded, and resolves its
* corresponding promise.
*
* @private
*/
onSuccess({
msgId, data
}) {
this.pendingResponses.get(msgId).resolve(data);
this.pendingResponses.delete(msgId);
}
onDebug({
message
}) {
//dump(`Worker debug: ${message}\n`);
}
/**
* Calls the given method in the worker, and returns a promise which resolves
* or rejects when the method has completed.
*
* @param {string} method
* The name of the method to call.
* @param {Array} args
* The arguments to pass to the method.
* @param {Array} [transferList]
* A list of objects to transfer to the worker, rather than cloning.
* @returns {Promise}
*/
call(method, args, transferList = []) {
let msgId = nextResponseId++;
return new Promise((resolve, reject) => {
this.pendingResponses.set(msgId, {
resolve, reject
});
let message = {
msg: method,
msgId,
args
};
this.postMessage(message, transferList);
});
}
}
var PromiseWorker = _PromiseWorker;
/**
* Represents an input or output pipe connected to a subprocess.
*
* @property {integer} fd
* The file descriptor number of the pipe on the child process's side.
* @readonly
*/
class Pipe {
/**
* @param {Process} process
* The child process that this pipe is connected to.
* @param {integer} fd
* The file descriptor number of the pipe on the child process's side.
* @param {integer} id
* The internal ID of the pipe, which ties it to the corresponding Pipe
* object on the Worker side.
*/
constructor(process, fd, id) {
this.id = id;
this.fd = fd;
this.processId = process.id;
this.worker = process.worker;
/**
* @property {boolean} closed
* True if the file descriptor has been closed, and can no longer
* be read from or written to. Pending IO operations may still
* complete, but new operations may not be initiated.
* @readonly
*/
this.closed = false;
}
/**
* Closes the end of the pipe which belongs to this process.
*
* @param {boolean} force
* If true, the pipe is closed immediately, regardless of any pending
* IO operations. If false, the pipe is closed after any existing
* pending IO operations have completed.
* @returns {Promise<object>}
* Resolves to an object with no properties once the pipe has been
* closed.
*/
close(force = false) {
this.closed = true;
return this.worker.call("close", [this.id, force]);
}
}
/**
* Represents an output-only pipe, to which data may be written.
*/
class OutputPipe extends Pipe {
constructor(...args) {
super(...args);
this.encoder = new TextEncoder();
}
/**
* Writes the given data to the stream.
*
* When given an array buffer or typed array, ownership of the buffer is
* transferred to the IO worker, and it may no longer be used from this
* thread.
*
* @param {ArrayBuffer|TypedArray|string} buffer
* Data to write to the stream.
* @returns {Promise<object>}
* Resolves to an object with a `bytesWritten` property, containing
* the number of bytes successfully written, once the operation has
* completed.
*
* @rejects {object}
* May be rejected with an Error object, or an object with similar
* properties. The object will include an `errorCode` property with
* one of the following values if it was rejected for the
* corresponding reason:
*
* - Subprocess.ERROR_END_OF_FILE: The pipe was closed before
* all of the data in `buffer` could be written to it.
*/
write(buffer) {
if (typeof buffer === "string") {
buffer = this.encoder.encode(buffer);
}
if (Cu.getClassName(buffer, true) !== "ArrayBuffer") {
if (buffer.byteLength === buffer.buffer.byteLength) {
buffer = buffer.buffer;
}
else {
buffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
}
}
let args = [this.id, buffer];
return this.worker.call("write", args, [buffer]);
}
}
/**
* Represents an input-only pipe, from which data may be read.
*/
class InputPipe extends Pipe {
constructor(...args) {
super(...args);
this.buffers = [];
/**
* @property {integer} dataAvailable
* The number of readable bytes currently stored in the input
* buffer.
* @readonly
*/
this.dataAvailable = 0;
this.decoder = new TextDecoder();
this.pendingReads = [];
this._pendingBufferRead = null;
this.fillBuffer();
}
/**
* @property {integer} bufferSize
* The current size of the input buffer. This varies depending on
* the size of pending read operations.
* @readonly
*/
get bufferSize() {
if (this.pendingReads.length) {
return Math.max(this.pendingReads[0].length, BUFFER_SIZE);
}
return BUFFER_SIZE;
}
/**
* Attempts to fill the input buffer.
*
* @private
*/
fillBuffer() {
let dataWanted = this.bufferSize - this.dataAvailable;
if (!this._pendingBufferRead && dataWanted > 0) {
this._pendingBufferRead = this._read(dataWanted);
this._pendingBufferRead.then((result) => {
this._pendingBufferRead = null;
if (result) {
this.onInput(result.buffer);
this.fillBuffer();
}
});
}
}
_read(size) {
let args = [this.id, size];
return this.worker.call("read", args).catch(e => {
this.closed = true;
for (let {
length, resolve, reject
}
of this.pendingReads.splice(0)) {
if (length === null && e.errorCode === SubprocessConstants.ERROR_END_OF_FILE) {
resolve(new ArrayBuffer(0));
}
else {
reject(e);
}
}
});
}
/**
* Adds the given data to the end of the input buffer.
*
* @param {ArrayBuffer} buffer
* An input buffer to append to the current buffered input.
* @private
*/
onInput(buffer) {
this.buffers.push(buffer);
this.dataAvailable += buffer.byteLength;
this.checkPendingReads();
}
/**
* Checks the topmost pending read operations and fulfills as many as can be
* filled from the current input buffer.
*
* @private
*/
checkPendingReads() {
this.fillBuffer();
let reads = this.pendingReads;
while (reads.length && this.dataAvailable &&
reads[0].length <= this.dataAvailable) {
let pending = this.pendingReads.shift();
let length = pending.length || this.dataAvailable;
let result;
let byteLength = this.buffers[0].byteLength;
if (byteLength == length) {
result = this.buffers.shift();
}
else if (byteLength > length) {
let buffer = this.buffers[0];
this.buffers[0] = buffer.slice(length);
result = ArrayBuffer.transfer(buffer, length);
}
else {
result = ArrayBuffer.transfer(this.buffers.shift(), length);
let u8result = new Uint8Array(result);
while (byteLength < length) {
let buffer = this.buffers[0];
let u8buffer = new Uint8Array(buffer);
let remaining = length - byteLength;
if (buffer.byteLength <= remaining) {
this.buffers.shift();
u8result.set(u8buffer, byteLength);
}
else {
this.buffers[0] = buffer.slice(remaining);
u8result.set(u8buffer.subarray(0, remaining), byteLength);
}
byteLength += Math.min(buffer.byteLength, remaining);
}
}
this.dataAvailable -= result.byteLength;
pending.resolve(result);
}
}
/**
* Reads exactly `length` bytes of binary data from the input stream, or, if
* length is not provided, reads the first chunk of data to become available.
* In the latter case, returns an empty array buffer on end of file.
*
* The read operation will not complete until enough data is available to
* fulfill the request. If the pipe closes without enough available data to
* fulfill the read, the operation fails, and any remaining buffered data is
* lost.
*
* @param {integer} [length]
* The number of bytes to read.
* @returns {Promise<ArrayBuffer>}
*
* @rejects {object}
* May be rejected with an Error object, or an object with similar
* properties. The object will include an `errorCode` property with
* one of the following values if it was rejected for the
* corresponding reason:
*
* - Subprocess.ERROR_END_OF_FILE: The pipe was closed before
* enough input could be read to satisfy the request.
*/
read(length = null) {
if (length !== null && !(Number.isInteger(length) && length >= 0)) {
throw new RangeError("Length must be a non-negative integer");
}
if (length == 0) {
return Promise.resolve(new ArrayBuffer(0));
}
return new Promise((resolve, reject) => {
this.pendingReads.push({
length, resolve, reject
});
this.checkPendingReads();
});
}
/**
* Reads exactly `length` bytes from the input stream, and parses them as
* UTF-8 JSON data.
*
* @param {integer} length
* The number of bytes to read.
* @returns {Promise<object>}
*
* @rejects {object}
* May be rejected with an Error object, or an object with similar
* properties. The object will include an `errorCode` property with
* one of the following values if it was rejected for the
* corresponding reason:
*
* - Subprocess.ERROR_END_OF_FILE: The pipe was closed before
* enough input could be read to satisfy the request.
* - Subprocess.ERROR_INVALID_JSON: The data read from the pipe
* could not be parsed as a valid JSON string.
*/
readJSON(length) {
if (!Number.isInteger(length) || length <= 0) {
throw new RangeError("Length must be a positive integer");
}
return this.readString(length).then(string => {
try {
return JSON.parse(string);
}
catch (e) {
e.errorCode = SubprocessConstants.ERROR_INVALID_JSON;
throw e;
}
});
}
/**
* Reads a chunk of UTF-8 data from the input stream, and converts it to a
* JavaScript string.
*
* If `length` is provided, reads exactly `length` bytes. Otherwise, reads the
* first chunk of data to become available, and returns an empty string on end
* of file. In the latter case, the chunk is decoded in streaming mode, and
* any incomplete UTF-8 sequences at the end of a chunk are returned at the
* start of a subsequent read operation.
*
* @param {integer} [length]
* The number of bytes to read.
* @param {object} [options]
* An options object as expected by TextDecoder.decode.
* @returns {Promise<string>}
*
* @rejects {object}
* May be rejected with an Error object, or an object with similar
* properties. The object will include an `errorCode` property with
* one of the following values if it was rejected for the
* corresponding reason:
*
* - Subprocess.ERROR_END_OF_FILE: The pipe was closed before
* enough input could be read to satisfy the request.
*/
readString(length = null, options = {
stream: length === null
}) {
if (length !== null && !(Number.isInteger(length) && length >= 0)) {
throw new RangeError("Length must be a non-negative integer");
}
return this.read(length).then(buffer => {
return this.decoder.decode(buffer, options);
});
}
/**
* Reads 4 bytes from the input stream, and parses them as an unsigned
* integer, in native byte order.
*
* @returns {Promise<integer>}
*
* @rejects {object}
* May be rejected with an Error object, or an object with similar
* properties. The object will include an `errorCode` property with
* one of the following values if it was rejected for the
* corresponding reason:
*
* - Subprocess.ERROR_END_OF_FILE: The pipe was closed before
* enough input could be read to satisfy the request.
*/
readUint32() {
return this.read(4).then(buffer => {
return new Uint32Array(buffer)[0];
});
}
}
/**
* @class Process
* @extends BaseProcess
*/
/**
* Represents a currently-running process, and allows interaction with it.
*/
class _BaseProcess {
/**
* @param {PromiseWorker} worker
* The worker instance which owns the process.
* @param {integer} processId
* The internal ID of the Process object, which ties it to the
* corresponding process on the Worker side.
* @param {integer[]} fds
* An array of internal Pipe IDs, one for each standard file descriptor
* in the child process.
* @param {integer} pid
* The operating system process ID of the process.
*/
constructor(worker, processId, fds, pid) {
this.id = processId;
this.worker = worker;
/**
* @property {integer} pid
* The process ID of the process, assigned by the operating system.
* @readonly
*/
this.pid = pid;
this.exitCode = null;
this.exitPromise = new Promise(resolve => {
this.worker.call("wait", [this.id]).then(({
exitCode
}) => {
resolve(Object.freeze({
exitCode
}));
this.exitCode = exitCode;
});
});
if (fds[0] !== undefined) {
/**
* @property {OutputPipe} stdin
* A Pipe object which allows writing to the process's standard
* input.
* @readonly
*/
this.stdin = new OutputPipe(this, 0, fds[0]);
}
if (fds[1] !== undefined) {
/**
* @property {InputPipe} stdout
* A Pipe object which allows reading from the process's standard
* output.
* @readonly
*/
this.stdout = new InputPipe(this, 1, fds[1]);
}
if (fds[2] !== undefined) {
/**
* @property {InputPipe} [stderr]
* An optional Pipe object which allows reading from the
* process's standard error output.
* @readonly
*/
this.stderr = new InputPipe(this, 2, fds[2]);
}
}
/**
* Spawns a process, and resolves to a BaseProcess instance on success.
*
* @param {object} options
* An options object as passed to `Subprocess.call`.
*
* @returns {Promise<BaseProcess>}
*/
static create(options) {
let worker = this.getWorker();
return worker.call("spawn", [options]).then(({
processId, fds, pid
}) => {
return new this(worker, processId, fds, pid);
});
}
static get WORKER_URL() {
throw new Error("Not implemented");
}
static get WorkerClass() {
return PromiseWorker;
}
/**
* Gets the current subprocess worker, or spawns a new one if it does not
* currently exist.
*
* @returns {PromiseWorker}
*/
static getWorker() {
if (!this._worker) {
this._worker = new this.WorkerClass(this.WORKER_URL);
}
return this._worker;
}
/**
* Kills the process.
*
* @param {integer} [timeout=300]
* A timeout, in milliseconds, after which the process will be forcibly
* killed. On platforms which support it, the process will be sent
* a `SIGTERM` signal immediately, so that it has a chance to terminate
* gracefully, and a `SIGKILL` signal if it hasn't exited within
* `timeout` milliseconds. On other platforms (namely Windows), the
* process will be forcibly terminated immediately.
*
* @returns {Promise<object>}
* Resolves to an object with an `exitCode` property when the process
* has exited.
*/
kill(timeout = 300) {
// If the process has already exited, don't bother sending a signal.
if (this.exitCode !== null) {
return this.wait();
}
let force = timeout <= 0;
this.worker.call("kill", [this.id, force]);
if (!force) {
getSetTimeout()(() => {
if (this.exitCode === null) {
this.worker.call("kill", [this.id, true]);
}
}, timeout);
}
return this.wait();
}
/**
* Returns a promise which resolves to the process's exit code, once it has
* exited.
*
* @returns {Promise<object>}
* Resolves to an object with an `exitCode` property, containing the
* process's exit code, once the process has exited.
*
* On Unix-like systems, a negative exit code indicates that the
* process was killed by a signal whose signal number is the absolute
* value of the error code. On Windows, an exit code of -9 indicates
* that the process was killed via the {@linkcode BaseProcess#kill kill()}
* method.
*/
wait() {
return this.exitPromise;
}
}
var BaseProcess = _BaseProcess;

Просмотреть файл

@ -1,178 +0,0 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
/* 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 https://mozilla.org/MPL/2.0/. */
/*
* These modules are loosely based on the subprocess.jsm module created
* by Jan Gerber and Patrick Brunschwig, though the implementation
* differs drastically.
*/
"use strict";
let EXPORTED_SYMBOLS = ["SubprocessMain"];
/* exported SubprocessMain */
var {
results: Cr
} = Components;
Components.utils.importGlobalProperties(["TextEncoder"]);
const AppConstants = ChromeUtils.import("resource://gre/modules/AppConstants.jsm").AppConstants;
const XPCOMUtils = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm").XPCOMUtils;
var SubprocessConstants = ChromeUtils.import("chrome://openpgp/content/modules/enigmailprocess_common.jsm").SubprocessConstants;
if (AppConstants.platform == "win") {
XPCOMUtils.defineLazyModuleGetter(this, "SubprocessImpl",
"chrome://openpgp/content/modules/enigmailprocess_win.jsm"); /* global SubprocessImpl: false */
}
else {
XPCOMUtils.defineLazyModuleGetter(this, "SubprocessImpl",
"chrome://openpgp/content/modules/enigmailprocess_unix.jsm");
}
function encodeEnvVar(name, value) {
if (typeof name === "string" && typeof value === "string") {
return `${name}=${value}`;
}
let encoder = new TextEncoder("utf-8");
function encode(val) {
return typeof val === "string" ? encoder.encode(val) : val;
}
return Uint8Array.of(...encode(name), ...encode("="), ...encode(value), 0);
}
/**
* Allows for creation of and communication with OS-level sub-processes.
* @namespace
*/
var SubprocessMain = {
/**
* Launches a process, and returns a handle to it.
*
* @param {object} options
* An object describing the process to launch.
*
* @param {string} options.command
* The full path of the execuable to launch. Relative paths are not
* accepted, and `$PATH` is not searched.
*
* If a path search is necessary, the {@link SubprocessMain.pathSearch} method may
* be used to map a bare executable name to a full path.
*
* @param {string[]} [options.arguments]
* A list of strings to pass as arguments to the process.
*
* @param {object} [options.environment]
* An object containing a key and value for each environment variable
* to pass to the process. Only the object's own, enumerable properties
* are added to the environment.
*
* @param {boolean} [options.environmentAppend]
* If true, append the environment variables passed in `environment` to
* the existing set of environment variables. Otherwise, the values in
* 'environment' constitute the entire set of environment variables
* passed to the new process.
*
* @param {string} [options.stderr]
* Defines how the process's stderr output is handled. One of:
*
* - `"ignore"`: (default) The process's standard error is not redirected.
* - `"stdout"`: The process's stderr is merged with its stdout.
* - `"pipe"`: The process's stderr is redirected to a pipe, which can be read
* from via its `stderr` property.
*
* @param {string} [options.workdir]
* The working directory in which to launch the new process.
*
* @returns {Promise<Process>}
*
* @rejects {Error}
* May be rejected with an Error object if the process can not be
* launched. The object will include an `errorCode` property with
* one of the following values if it was rejected for the
* corresponding reason:
*
* - SubprocessMain.ERROR_BAD_EXECUTABLE: The given command could not
* be found, or the file that it references is not executable.
*
* Note that if the process is successfully launched, but exits with
* a non-zero exit code, the promise will still resolve successfully.
*/
call(options) {
options = Object.assign({}, options);
options.stderr = options.stderr || "ignore";
options.workdir = options.workdir || null;
let environment = {};
if (!options.environment || options.environmentAppend) {
environment = this.getEnvironment();
}
if (options.environment) {
Object.assign(environment, options.environment);
}
options.environment = Object.entries(environment)
.map(([key, val]) => encodeEnvVar(key, val));
options.arguments = Array.from(options.arguments || []);
return Promise.resolve(SubprocessImpl.isExecutableFile(options.command)).then(isExecutable => {
if (!isExecutable) {
let error = new Error(`File at path "${options.command}" does not exist, or is not executable`);
error.errorCode = SubprocessConstants.ERROR_BAD_EXECUTABLE;
throw error;
}
options.arguments.unshift(options.command);
return SubprocessImpl.call(options);
});
},
/**
* Returns an object with a key-value pair for every variable in the process's
* current environment.
*
* @returns {object}
*/
getEnvironment() {
let environment = Object.create(null);
for (let [k, v] of SubprocessImpl.getEnvironment()) {
environment[k] = v;
}
return environment;
},
/**
* Searches for the given executable file in the system executable
* file paths as specified by the PATH environment variable.
*
* On Windows, if the unadorned filename cannot be found, the
* extensions in the semicolon-separated list in the PATHSEP
* environment variable are successively appended to the original
* name and searched for in turn.
*
* @param {string} command
* The name of the executable to find.
* @param {object} [environment]
* An object containing a key for each environment variable to be used
* in the search. If not provided, full the current process environment
* is used.
* @returns {Promise<string>}
*/
pathSearch(command, environment = this.getEnvironment()) {
let path = SubprocessImpl.pathSearch(command, environment);
return Promise.resolve(path);
}
};
Object.assign(SubprocessMain, SubprocessConstants);
Object.freeze(SubprocessMain);

Просмотреть файл

@ -1,102 +0,0 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
/* 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 https://mozilla.org/MPL/2.0/. */
"use strict";
/* exported Library, SubprocessConstants */
/* global ctypes: false */
if (!ArrayBuffer.transfer) {
/**
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer/transfer
*
* @param {ArrayBuffer} buffer
* @param {integer} [size = buffer.byteLength]
* @returns {ArrayBuffer}
*/
ArrayBuffer.transfer = function(buffer, size = buffer.byteLength) {
let u8out = new Uint8Array(size);
let u8buffer = new Uint8Array(buffer, 0, Math.min(size, buffer.byteLength));
u8out.set(u8buffer);
return u8out.buffer;
};
}
var libraries = {};
class Library {
constructor(name, names, definitions) {
if (name in libraries) {
return libraries[name];
}
for (let name of names) {
try {
if (!this.library) {
this.library = ctypes.open(name);
}
} catch (e) {
// Ignore errors until we've tried all the options.
}
}
if (!this.library) {
throw new Error("Could not load libc");
}
libraries[name] = this;
for (let symbol of Object.keys(definitions)) {
this.declare(symbol, ...definitions[symbol]);
}
return this;
}
declare(name, ...args) {
Object.defineProperty(this, name, {
configurable: true,
get() {
Object.defineProperty(this, name, {
configurable: true,
value: this.library.declare(name, ...args)
});
return this[name];
}
});
}
}
/**
* Holds constants which apply to various Subprocess operations.
* @namespace
* @lends Subprocess
*/
var SubprocessConstants = {
/**
* @property {integer} ERROR_END_OF_FILE
* The operation failed because the end of the file was reached.
* @constant
*/
ERROR_END_OF_FILE: 0xff7a0001,
/**
* @property {integer} ERROR_INVALID_JSON
* The operation failed because an invalid JSON was encountered.
* @constant
*/
ERROR_INVALID_JSON: 0xff7a0002,
/**
* @property {integer} ERROR_BAD_EXECUTABLE
* The operation failed because the given file did not exist, or
* could not be executed.
* @constant
*/
ERROR_BAD_EXECUTABLE: 0xff7a0003
};
Object.freeze(SubprocessConstants);

Просмотреть файл

@ -1,169 +0,0 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
/* 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 https://mozilla.org/MPL/2.0/. */
"use strict";
/* exported libc */
/* global ctypes: false, OS: false, Library: false */
const LIBC = OS.Constants.libc;
const LIBC_CHOICES = ["libc.so", "libSystem.B.dylib", "a.out"];
const unix = {
pid_t: ctypes.int32_t,
pollfd: new ctypes.StructType("pollfd", [{
"fd": ctypes.int
}, {
"events": ctypes.short
}, {
"revents": ctypes.short
}]),
posix_spawn_file_actions_t: ctypes.uint8_t.array(
LIBC.OSFILE_SIZEOF_POSIX_SPAWN_FILE_ACTIONS_T),
WEXITSTATUS(status) {
return (status >> 8) & 0xff;
},
WTERMSIG(status) {
return status & 0x7f;
}
};
var libc = new Library("libc", LIBC_CHOICES, {
environ: [ctypes.char.ptr.ptr],
// Darwin-only.
_NSGetEnviron: [
ctypes.default_abi,
ctypes.char.ptr.ptr.ptr
],
setenv: [
ctypes.default_abi,
ctypes.int,
ctypes.char.ptr,
ctypes.char.ptr,
ctypes.int
],
chdir: [
ctypes.default_abi,
ctypes.int,
ctypes.char.ptr /* path */
],
close: [
ctypes.default_abi,
ctypes.int,
ctypes.int /* fildes */
],
fcntl: [
ctypes.default_abi,
ctypes.int,
ctypes.int, /* fildes */
ctypes.int, /* cmd */
ctypes.int /* ... */
],
getcwd: [
ctypes.default_abi,
ctypes.char.ptr,
ctypes.char.ptr, /* buf */
ctypes.size_t /* size */
],
kill: [
ctypes.default_abi,
ctypes.int,
unix.pid_t, /* pid */
ctypes.int /* signal */
],
pipe: [
ctypes.default_abi,
ctypes.int,
ctypes.int.array(2) /* pipefd */
],
poll: [
ctypes.default_abi,
ctypes.int,
unix.pollfd.array(), /* fds */
ctypes.unsigned_int, /* nfds */
ctypes.int /* timeout */
],
posix_spawn: [
ctypes.default_abi,
ctypes.int,
unix.pid_t.ptr, /* pid */
ctypes.char.ptr, /* path */
unix.posix_spawn_file_actions_t.ptr, /* file_actions */
ctypes.voidptr_t, /* attrp */
ctypes.char.ptr.ptr, /* argv */
ctypes.char.ptr.ptr /* envp */
],
posix_spawn_file_actions_addclose: [
ctypes.default_abi,
ctypes.int,
unix.posix_spawn_file_actions_t.ptr, /* file_actions */
ctypes.int /* fildes */
],
posix_spawn_file_actions_adddup2: [
ctypes.default_abi,
ctypes.int,
unix.posix_spawn_file_actions_t.ptr, /* file_actions */
ctypes.int, /* fildes */
ctypes.int /* newfildes */
],
posix_spawn_file_actions_destroy: [
ctypes.default_abi,
ctypes.int,
unix.posix_spawn_file_actions_t.ptr /* file_actions */
],
posix_spawn_file_actions_init: [
ctypes.default_abi,
ctypes.int,
unix.posix_spawn_file_actions_t.ptr /* file_actions */
],
read: [
ctypes.default_abi,
ctypes.ssize_t,
ctypes.int, /* fildes */
ctypes.char.ptr, /* buf */
ctypes.size_t /* nbyte */
],
waitpid: [
ctypes.default_abi,
unix.pid_t,
unix.pid_t, /* pid */
ctypes.int.ptr, /* status */
ctypes.int /* options */
],
write: [
ctypes.default_abi,
ctypes.ssize_t,
ctypes.int, /* fildes */
ctypes.char.ptr, /* buf */
ctypes.size_t /* nbyte */
]
});
unix.Fd = function(fd) {
return ctypes.CDataFinalizer(ctypes.int(fd), libc.close);
};

Просмотреть файл

@ -1,581 +0,0 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
/* 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 https://mozilla.org/MPL/2.0/. */
"use strict";
/* exported LIBC, Win, createPipe, libc */
/* global ctypes: false, OS: false, Library: false */
/* eslint no-void: 0 */
const LIBC = OS.Constants.libc;
const Win = OS.Constants.Win;
const LIBC_CHOICES = ["kernel32.dll"];
var win32 = {
// On Windows 64, winapi_abi is an alias for default_abi.
WINAPI: ctypes.winapi_abi,
VOID: ctypes.void_t,
BYTE: ctypes.uint8_t,
WORD: ctypes.uint16_t,
DWORD: ctypes.uint32_t,
LONG: ctypes.long,
LARGE_INTEGER: ctypes.int64_t,
ULONGLONG: ctypes.uint64_t,
UINT: ctypes.unsigned_int,
UCHAR: ctypes.unsigned_char,
BOOL: ctypes.bool,
HANDLE: ctypes.voidptr_t,
PVOID: ctypes.voidptr_t,
LPVOID: ctypes.voidptr_t,
CHAR: ctypes.char,
WCHAR: ctypes.jschar,
ULONG_PTR: ctypes.uintptr_t,
SIZE_T: ctypes.size_t,
PSIZE_T: ctypes.size_t.ptr
};
Object.assign(win32, {
DWORD_PTR: win32.ULONG_PTR,
LPSTR: win32.CHAR.ptr,
LPWSTR: win32.WCHAR.ptr,
LPBYTE: win32.BYTE.ptr,
LPDWORD: win32.DWORD.ptr,
LPHANDLE: win32.HANDLE.ptr,
// This is an opaque type.
PROC_THREAD_ATTRIBUTE_LIST: ctypes.char.array(),
LPPROC_THREAD_ATTRIBUTE_LIST: ctypes.char.ptr
});
Object.assign(win32, {
LPCSTR: win32.LPSTR,
LPCWSTR: win32.LPWSTR,
LPCVOID: win32.LPVOID
});
Object.assign(win32, {
INVALID_HANDLE_VALUE: ctypes.cast(ctypes.int64_t(-1), win32.HANDLE),
NULL_HANDLE_VALUE: ctypes.cast(ctypes.uintptr_t(0), win32.HANDLE),
CREATE_SUSPENDED: 0x00000004,
CREATE_NEW_CONSOLE: 0x00000010,
CREATE_UNICODE_ENVIRONMENT: 0x00000400,
CREATE_NO_WINDOW: 0x08000000,
CREATE_BREAKAWAY_FROM_JOB: 0x01000000,
EXTENDED_STARTUPINFO_PRESENT: 0x00080000,
STARTF_USESTDHANDLES: 0x0100,
DUPLICATE_CLOSE_SOURCE: 0x01,
DUPLICATE_SAME_ACCESS: 0x02,
ERROR_HANDLE_EOF: 38,
ERROR_BROKEN_PIPE: 109,
ERROR_INSUFFICIENT_BUFFER: 122,
FILE_FLAG_OVERLAPPED: 0x40000000,
PIPE_TYPE_BYTE: 0x00,
PIPE_ACCESS_INBOUND: 0x01,
PIPE_ACCESS_OUTBOUND: 0x02,
PIPE_ACCESS_DUPLEX: 0x03,
PIPE_WAIT: 0x00,
PIPE_NOWAIT: 0x01,
STILL_ACTIVE: 259,
PROC_THREAD_ATTRIBUTE_HANDLE_LIST: 0x00020002,
JobObjectBasicLimitInformation: 2,
JobObjectExtendedLimitInformation: 9,
JOB_OBJECT_LIMIT_BREAKAWAY_OK: 0x00000800,
// These constants are 32-bit unsigned integers, but Windows defines
// them as negative integers cast to an unsigned type.
STD_INPUT_HANDLE: -10 + 0x100000000,
STD_OUTPUT_HANDLE: -11 + 0x100000000,
STD_ERROR_HANDLE: -12 + 0x100000000,
WAIT_TIMEOUT: 0x00000102,
WAIT_FAILED: 0xffffffff
});
Object.assign(win32, {
JOBOBJECT_BASIC_LIMIT_INFORMATION: new ctypes.StructType("JOBOBJECT_BASIC_LIMIT_INFORMATION", [{
"PerProcessUserTimeLimit": win32.LARGE_INTEGER
}, {
"PerJobUserTimeLimit": win32.LARGE_INTEGER
}, {
"LimitFlags": win32.DWORD
}, {
"MinimumWorkingSetSize": win32.SIZE_T
}, {
"MaximumWorkingSetSize": win32.SIZE_T
}, {
"ActiveProcessLimit": win32.DWORD
}, {
"Affinity": win32.ULONG_PTR
}, {
"PriorityClass": win32.DWORD
}, {
"SchedulingClass": win32.DWORD
}]),
IO_COUNTERS: new ctypes.StructType("IO_COUNTERS", [{
"ReadOperationCount": win32.ULONGLONG
}, {
"WriteOperationCount": win32.ULONGLONG
}, {
"OtherOperationCount": win32.ULONGLONG
}, {
"ReadTransferCount": win32.ULONGLONG
}, {
"WriteTransferCount": win32.ULONGLONG
}, {
"OtherTransferCount": win32.ULONGLONG
}])
});
Object.assign(win32, {
JOBOBJECT_EXTENDED_LIMIT_INFORMATION: new ctypes.StructType("JOBOBJECT_EXTENDED_LIMIT_INFORMATION", [{
"BasicLimitInformation": win32.JOBOBJECT_BASIC_LIMIT_INFORMATION
}, {
"IoInfo": win32.IO_COUNTERS
}, {
"ProcessMemoryLimit": win32.SIZE_T
}, {
"JobMemoryLimit": win32.SIZE_T
}, {
"PeakProcessMemoryUsed": win32.SIZE_T
}, {
"PeakJobMemoryUsed": win32.SIZE_T
}]),
OVERLAPPED: new ctypes.StructType("OVERLAPPED", [{
"Internal": win32.ULONG_PTR
}, {
"InternalHigh": win32.ULONG_PTR
}, {
"Offset": win32.DWORD
}, {
"OffsetHigh": win32.DWORD
}, {
"hEvent": win32.HANDLE
}]),
PROCESS_INFORMATION: new ctypes.StructType("PROCESS_INFORMATION", [{
"hProcess": win32.HANDLE
}, {
"hThread": win32.HANDLE
}, {
"dwProcessId": win32.DWORD
}, {
"dwThreadId": win32.DWORD
}]),
SECURITY_ATTRIBUTES: new ctypes.StructType("SECURITY_ATTRIBUTES", [{
"nLength": win32.DWORD
}, {
"lpSecurityDescriptor": win32.LPVOID
}, {
"bInheritHandle": win32.BOOL
}]),
STARTUPINFOW: new ctypes.StructType("STARTUPINFOW", [{
"cb": win32.DWORD
}, {
"lpReserved": win32.LPWSTR
}, {
"lpDesktop": win32.LPWSTR
}, {
"lpTitle": win32.LPWSTR
}, {
"dwX": win32.DWORD
}, {
"dwY": win32.DWORD
}, {
"dwXSize": win32.DWORD
}, {
"dwYSize": win32.DWORD
}, {
"dwXCountChars": win32.DWORD
}, {
"dwYCountChars": win32.DWORD
}, {
"dwFillAttribute": win32.DWORD
}, {
"dwFlags": win32.DWORD
}, {
"wShowWindow": win32.WORD
}, {
"cbReserved2": win32.WORD
}, {
"lpReserved2": win32.LPBYTE
}, {
"hStdInput": win32.HANDLE
}, {
"hStdOutput": win32.HANDLE
}, {
"hStdError": win32.HANDLE
}])
});
Object.assign(win32, {
STARTUPINFOEXW: new ctypes.StructType("STARTUPINFOEXW", [{
"StartupInfo": win32.STARTUPINFOW
}, {
"lpAttributeList": win32.LPPROC_THREAD_ATTRIBUTE_LIST
}])
});
var libc = new Library("libc", LIBC_CHOICES, {
AssignProcessToJobObject: [
win32.WINAPI,
win32.BOOL,
win32.HANDLE, /* hJob */
win32.HANDLE /* hProcess */
],
CloseHandle: [
win32.WINAPI,
win32.BOOL,
win32.HANDLE /* hObject */
],
CreateEventW: [
win32.WINAPI,
win32.HANDLE,
win32.SECURITY_ATTRIBUTES.ptr, /* opt lpEventAttributes */
win32.BOOL, /* bManualReset */
win32.BOOL, /* bInitialState */
win32.LPWSTR /* lpName */
],
CreateFileW: [
win32.WINAPI,
win32.HANDLE,
win32.LPWSTR, /* lpFileName */
win32.DWORD, /* dwDesiredAccess */
win32.DWORD, /* dwShareMode */
win32.SECURITY_ATTRIBUTES.ptr, /* opt lpSecurityAttributes */
win32.DWORD, /* dwCreationDisposition */
win32.DWORD, /* dwFlagsAndAttributes */
win32.HANDLE /* opt hTemplateFile */
],
CreateJobObjectW: [
win32.WINAPI,
win32.HANDLE,
win32.SECURITY_ATTRIBUTES.ptr, /* opt lpJobAttributes */
win32.LPWSTR /* lpName */
],
CreateNamedPipeW: [
win32.WINAPI,
win32.HANDLE,
win32.LPWSTR, /* lpName */
win32.DWORD, /* dwOpenMode */
win32.DWORD, /* dwPipeMode */
win32.DWORD, /* nMaxInstances */
win32.DWORD, /* nOutBufferSize */
win32.DWORD, /* nInBufferSize */
win32.DWORD, /* nDefaultTimeOut */
win32.SECURITY_ATTRIBUTES.ptr /* opt lpSecurityAttributes */
],
CreatePipe: [
win32.WINAPI,
win32.BOOL,
win32.LPHANDLE, /* out hReadPipe */
win32.LPHANDLE, /* out hWritePipe */
win32.SECURITY_ATTRIBUTES.ptr, /* opt lpPipeAttributes */
win32.DWORD /* nSize */
],
CreateProcessW: [
win32.WINAPI,
win32.BOOL,
win32.LPCWSTR, /* lpApplicationName */
win32.LPWSTR, /* lpCommandLine */
win32.SECURITY_ATTRIBUTES.ptr, /* lpProcessAttributes */
win32.SECURITY_ATTRIBUTES.ptr, /* lpThreadAttributes */
win32.BOOL, /* bInheritHandle */
win32.DWORD, /* dwCreationFlags */
win32.LPVOID, /* opt lpEnvironment */
win32.LPCWSTR, /* opt lpCurrentDirectory */
win32.STARTUPINFOW.ptr, /* lpStartupInfo */
win32.PROCESS_INFORMATION.ptr /* out lpProcessInformation */
],
CreateSemaphoreW: [
win32.WINAPI,
win32.HANDLE,
win32.SECURITY_ATTRIBUTES.ptr, /* opt lpSemaphoreAttributes */
win32.LONG, /* lInitialCount */
win32.LONG, /* lMaximumCount */
win32.LPCWSTR /* opt lpName */
],
DeleteProcThreadAttributeList: [
win32.WINAPI,
win32.VOID,
win32.LPPROC_THREAD_ATTRIBUTE_LIST /* in/out lpAttributeList */
],
DuplicateHandle: [
win32.WINAPI,
win32.BOOL,
win32.HANDLE, /* hSourceProcessHandle */
win32.HANDLE, /* hSourceHandle */
win32.HANDLE, /* hTargetProcessHandle */
win32.LPHANDLE, /* out lpTargetHandle */
win32.DWORD, /* dwDesiredAccess */
win32.BOOL, /* bInheritHandle */
win32.DWORD /* dwOptions */
],
FreeEnvironmentStringsW: [
win32.WINAPI,
win32.BOOL,
win32.LPCWSTR /* lpszEnvironmentBlock */
],
GetCurrentProcess: [
win32.WINAPI,
win32.HANDLE
],
GetCurrentProcessId: [
win32.WINAPI,
win32.DWORD
],
GetEnvironmentStringsW: [
win32.WINAPI,
win32.LPCWSTR
],
GetExitCodeProcess: [
win32.WINAPI,
win32.BOOL,
win32.HANDLE, /* hProcess */
win32.LPDWORD /* lpExitCode */
],
GetOverlappedResult: [
win32.WINAPI,
win32.BOOL,
win32.HANDLE, /* hFile */
win32.OVERLAPPED.ptr, /* lpOverlapped */
win32.LPDWORD, /* lpNumberOfBytesTransferred */
win32.BOOL /* bWait */
],
GetStdHandle: [
win32.WINAPI,
win32.HANDLE,
win32.DWORD /* nStdHandle */
],
InitializeProcThreadAttributeList: [
win32.WINAPI,
win32.BOOL,
win32.LPPROC_THREAD_ATTRIBUTE_LIST, /* out opt lpAttributeList */
win32.DWORD, /* dwAttributeCount */
win32.DWORD, /* dwFlags */
win32.PSIZE_T /* in/out lpSize */
],
ReadFile: [
win32.WINAPI,
win32.BOOL,
win32.HANDLE, /* hFile */
win32.LPVOID, /* out lpBuffer */
win32.DWORD, /* nNumberOfBytesToRead */
win32.LPDWORD, /* opt out lpNumberOfBytesRead */
win32.OVERLAPPED.ptr /* opt in/out lpOverlapped */
],
ReleaseSemaphore: [
win32.WINAPI,
win32.BOOL,
win32.HANDLE, /* hSemaphore */
win32.LONG, /* lReleaseCount */
win32.LONG.ptr /* opt out lpPreviousCount */
],
ResumeThread: [
win32.WINAPI,
win32.DWORD,
win32.HANDLE /* hThread */
],
SetInformationJobObject: [
win32.WINAPI,
win32.BOOL,
win32.HANDLE, /* hJob */
ctypes.int, /* JobObjectInfoClass */
win32.LPVOID, /* lpJobObjectInfo */
win32.DWORD /* cbJobObjectInfoLengt */
],
TerminateJobObject: [
win32.WINAPI,
win32.BOOL,
win32.HANDLE, /* hJob */
win32.UINT /* uExitCode */
],
TerminateProcess: [
win32.WINAPI,
win32.BOOL,
win32.HANDLE, /* hProcess */
win32.UINT /* uExitCode */
],
UpdateProcThreadAttribute: [
win32.WINAPI,
win32.BOOL,
win32.LPPROC_THREAD_ATTRIBUTE_LIST, /* in/out lpAttributeList */
win32.DWORD, /* dwFlags */
win32.DWORD_PTR, /* Attribute */
win32.PVOID, /* lpValue */
win32.SIZE_T, /* cbSize */
win32.PVOID, /* out opt lpPreviousValue */
win32.PSIZE_T /* opt lpReturnSize */
],
WaitForMultipleObjects: [
win32.WINAPI,
win32.DWORD,
win32.DWORD, /* nCount */
win32.HANDLE.ptr, /* hHandles */
win32.BOOL, /* bWaitAll */
win32.DWORD /* dwMilliseconds */
],
WaitForSingleObject: [
win32.WINAPI,
win32.DWORD,
win32.HANDLE, /* hHandle */
win32.BOOL, /* bWaitAll */
win32.DWORD /* dwMilliseconds */
],
WriteFile: [
win32.WINAPI,
win32.BOOL,
win32.HANDLE, /* hFile */
win32.LPCVOID, /* lpBuffer */
win32.DWORD, /* nNumberOfBytesToRead */
win32.LPDWORD, /* opt out lpNumberOfBytesWritten */
win32.OVERLAPPED.ptr /* opt in/out lpOverlapped */
]
});
var user32 = new Library("user32", ["user32.dll"], {
AllowSetForegroundWindow: [
win32.WINAPI,
win32.BOOL,
win32.DWORD /* dwProcessId */
]
});
let nextNamedPipeId = 0;
win32.Handle = function(handle) {
return ctypes.CDataFinalizer(win32.HANDLE(handle), libc.CloseHandle);
};
win32.createPipe = function(secAttr, readFlags = 0, writeFlags = 0, size = 0) {
readFlags |= win32.PIPE_ACCESS_INBOUND;
writeFlags |= Win.FILE_ATTRIBUTE_NORMAL;
if (size == 0) {
size = 4096;
}
let pid = libc.GetCurrentProcessId();
const pipePrefix = "\\\\.\\Pipe\\SubProcessPipe";
let pipeName = String.raw `${pipePrefix}.${pid}.${nextNamedPipeId++}`;
let readHandle = libc.CreateNamedPipeW(
pipeName, readFlags,
win32.PIPE_TYPE_BYTE | win32.PIPE_WAIT,
1, /* number of connections */
size, /* output buffer size */
size, /* input buffer size */
0, /* timeout */
secAttr.address());
let isInvalid = handle => String(handle) == String(win32.HANDLE(Win.INVALID_HANDLE_VALUE));
if (isInvalid(readHandle)) {
return [];
}
let writeHandle = libc.CreateFileW(
pipeName, Win.GENERIC_WRITE, 0, secAttr.address(),
Win.OPEN_EXISTING, writeFlags, null);
if (isInvalid(writeHandle)) {
libc.CloseHandle(readHandle);
return [];
}
return [win32.Handle(readHandle),
win32.Handle(writeHandle)
];
};
win32.createThreadAttributeList = function(handles) {
try {
void libc.InitializeProcThreadAttributeList;
void libc.DeleteProcThreadAttributeList;
void libc.UpdateProcThreadAttribute;
} catch (e) {
// This is only supported in Windows Vista and later.
return null;
}
let size = win32.SIZE_T();
if (!libc.InitializeProcThreadAttributeList(null, 1, 0, size.address()) &&
ctypes.winLastError != win32.ERROR_INSUFFICIENT_BUFFER) {
return null;
}
let attrList = win32.PROC_THREAD_ATTRIBUTE_LIST(size.value);
if (!libc.InitializeProcThreadAttributeList(attrList, 1, 0, size.address())) {
return null;
}
let ok = libc.UpdateProcThreadAttribute(
attrList, 0, win32.PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
handles, handles.constructor.size, null, null);
if (!ok) {
libc.DeleteProcThreadAttributeList(attrList);
return null;
}
return attrList;
};

Просмотреть файл

@ -1,209 +0,0 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
/* 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 https://mozilla.org/MPL/2.0/. */
"use strict";
/* exported SubprocessImpl */
/* globals BaseProcess, PromiseWorker */
/* global libc: false, LIBC: false */
var {
results: Cr
} = Components;
var EXPORTED_SYMBOLS = ["SubprocessImpl"];
Components.utils.importGlobalProperties(["TextDecoder"]);
const ctypes = ChromeUtils.import("resource://gre/modules/ctypes.jsm").ctypes;
const OS = ChromeUtils.import("resource://gre/modules/osfile.jsm").OS;
const Services = ChromeUtils.import("resource://gre/modules/Services.jsm").Services;
var {
SubprocessConstants,
BaseProcess,
PromiseWorker
} = ChromeUtils.import("chrome://openpgp/content/modules/enigmailprocess_common.jsm", this);
Services.scriptloader.loadSubScript("chrome://openpgp/content/modules/enigmailprocess_shared.js", this);
Services.scriptloader.loadSubScript("chrome://openpgp/content/modules/enigmailprocess_shared_unix.js", this);
class UnixPromiseWorker extends PromiseWorker {
constructor(...args) {
super(...args);
let fds = ctypes.int.array(2)();
let res = libc.pipe(fds);
if (res == -1) {
throw new Error("Unable to create pipe");
}
this.signalFd = fds[1];
libc.fcntl(fds[0], LIBC.F_SETFL, LIBC.O_NONBLOCK);
libc.fcntl(fds[0], LIBC.F_SETFD, LIBC.FD_CLOEXEC);
libc.fcntl(fds[1], LIBC.F_SETFD, LIBC.FD_CLOEXEC);
this.call("init", [{
signalFd: fds[0]
}]);
}
closePipe() {
if (this.signalFd) {
libc.close(this.signalFd);
this.signalFd = null;
}
}
onClose() {
this.closePipe();
super.onClose();
}
signalWorker() {
libc.write(this.signalFd, new ArrayBuffer(1), 1);
}
postMessage(...args) {
this.signalWorker();
return super.postMessage(...args);
}
}
class Process extends BaseProcess {
static get WORKER_URL() {
return "chrome://openpgp/content/modules/enigmailprocess_worker_unix.js";
}
static get WorkerClass() {
return UnixPromiseWorker;
}
}
// Convert a null-terminated char pointer into a sized char array, and then
// convert that into a JS typed array.
// The resulting array will not be null-terminated.
function ptrToUint8Array(input) {
let {
cast,
uint8_t
} = ctypes;
let len = 0;
for (let ptr = cast(input, uint8_t.ptr); ptr.contents; ptr = ptr.increment()) {
len++;
}
let aryPtr = cast(input, uint8_t.array(len).ptr);
return new Uint8Array(aryPtr.contents);
}
var SubprocessUnix = {
Process,
call(options) {
return Process.create(options);
},
* getEnvironment() {
let environ;
if (OS.Constants.Sys.Name == "Darwin") {
environ = libc._NSGetEnviron().contents;
} else {
environ = libc.environ;
}
const EQUAL = "=".charCodeAt(0);
let decoder = new TextDecoder("utf-8", {
fatal: true
});
function decode(array) {
try {
return decoder.decode(array);
} catch (e) {
return array;
}
}
for (let envp = environ; !envp.contents.isNull(); envp = envp.increment()) {
let buf = ptrToUint8Array(envp.contents);
for (let i = 0; i < buf.length; i++) {
if (buf[i] == EQUAL) {
yield [decode(buf.subarray(0, i)),
decode(buf.subarray(i + 1))
];
break;
}
}
}
},
async isExecutableFile(path) {
if (!OS.Path.split(path).absolute) {
return false;
}
try {
let info = await OS.File.stat(path);
// FIXME: We really want access(path, X_OK) here, but OS.File does not
// support it.
return !info.isDir && (info.unixMode & 0x49);
} catch (e) {
return false;
}
},
/**
* Searches for the given executable file in the system executable
* file paths as specified by the PATH environment variable.
*
* On Windows, if the unadorned filename cannot be found, the
* extensions in the semicolon-separated list in the PATHEXT
* environment variable are successively appended to the original
* name and searched for in turn.
*
* @param {string} bin
* The name of the executable to find.
* @param {object} environment
* An object containing a key for each environment variable to be used
* in the search.
* @returns {Promise<string>}
*/
async pathSearch(bin, environment) {
let split = OS.Path.split(bin);
if (split.absolute) {
if (await this.isExecutableFile(bin)) {
return bin;
}
let error = new Error(`File at path "${bin}" does not exist, or is not executable`);
error.errorCode = SubprocessConstants.ERROR_BAD_EXECUTABLE;
throw error;
}
let dirs = [];
if (typeof environment.PATH === "string") {
dirs = environment.PATH.split(":");
}
for (let dir of dirs) {
let path = OS.Path.join(dir, bin);
if (await this.isExecutableFile(path)) {
return path;
}
}
let error = new Error(`Executable not found: ${bin}`);
error.errorCode = SubprocessConstants.ERROR_BAD_EXECUTABLE;
throw error;
}
};
var SubprocessImpl = SubprocessUnix;

Просмотреть файл

@ -1,176 +0,0 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
/* 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 https://mozilla.org/MPL/2.0/. */
"use strict";
/* exported SubprocessImpl */
/* global libc: false */
/* globals BaseProcess, PromiseWorker */
var {
results: Cr
} = Components;
var EXPORTED_SYMBOLS = ["SubprocessImpl"];
const AppConstants = ChromeUtils.import("resource://gre/modules/AppConstants.jsm").AppConstants;
const ctypes = ChromeUtils.import("resource://gre/modules/ctypes.jsm").ctypes;
const OS = ChromeUtils.import("resource://gre/modules/osfile.jsm").OS;
const Services = ChromeUtils.import("resource://gre/modules/Services.jsm").Services;
const XPCOMUtils = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm").XPCOMUtils;
var {
SubprocessConstants,
BaseProcess,
PromiseWorker
} = ChromeUtils.import("chrome://openpgp/content/modules/enigmailprocess_common.jsm", this);
XPCOMUtils.defineLazyServiceGetter(this, "env", "@mozilla.org/process/environment;1",
"nsIEnvironment"); /* global env: false */
Services.scriptloader.loadSubScript("chrome://openpgp/content/modules/enigmailprocess_shared.js", this);
Services.scriptloader.loadSubScript("chrome://openpgp/content/modules/enigmailprocess_shared_win.js", this);
class WinPromiseWorker extends PromiseWorker {
constructor(...args) {
super(...args);
this.signalEvent = libc.CreateSemaphoreW(null, 0, 32, null);
this.call("init", [{
breakAwayFromJob: !AppConstants.isPlatformAndVersionAtLeast("win", "6.2"),
comspec: env.get("COMSPEC"),
signalEvent: String(ctypes.cast(this.signalEvent, ctypes.uintptr_t).value)
}]);
}
signalWorker() {
libc.ReleaseSemaphore(this.signalEvent, 1, null);
}
postMessage(...args) {
this.signalWorker();
return super.postMessage(...args);
}
}
class Process extends BaseProcess {
static get WORKER_URL() {
return "chrome://openpgp/content/modules/enigmailprocess_worker_win.js";
}
static get WorkerClass() {
return WinPromiseWorker;
}
}
var SubprocessWin = {
Process,
call(options) {
return Process.create(options);
},
* getEnvironment() {
let env = libc.GetEnvironmentStringsW();
try {
for (let p = env, q = env;; p = p.increment()) {
if (p.contents == "\0") {
if (String(p) == String(q)) {
break;
}
let str = q.readString();
q = p.increment();
let idx = str.indexOf("=");
if (idx == 0) {
idx = str.indexOf("=", 1);
}
if (idx >= 0) {
yield [str.slice(0, idx), str.slice(idx + 1)];
}
}
}
}
finally {
libc.FreeEnvironmentStringsW(env);
}
},
async isExecutableFile(path) {
if (!OS.Path.split(path).absolute) {
return false;
}
try {
let info = await OS.File.stat(path);
return !(info.isDir || info.isSymlink);
}
catch (e) {
return false;
}
},
/**
* Searches for the given executable file in the system executable
* file paths as specified by the PATH environment variable.
*
* On Windows, if the unadorned filename cannot be found, the
* extensions in the semicolon-separated list in the PATHEXT
* environment variable are successively appended to the original
* name and searched for in turn.
*
* @param {string} bin
* The name of the executable to find.
* @param {object} environment
* An object containing a key for each environment variable to be used
* in the search.
* @returns {Promise<string>}
*/
async pathSearch(bin, environment) {
let split = OS.Path.split(bin);
if (split.absolute) {
if (await this.isExecutableFile(bin)) {
return bin;
}
let error = new Error(`File at path "${bin}" does not exist, or is not a normal file`);
error.errorCode = SubprocessConstants.ERROR_BAD_EXECUTABLE;
throw error;
}
let dirs = [];
let exts = [];
if (environment.PATH) {
dirs = environment.PATH.split(";");
}
if (environment.PATHEXT) {
exts = environment.PATHEXT.split(";");
}
for (let dir of dirs) {
let path = OS.Path.join(dir, bin);
if (await this.isExecutableFile(path)) {
return path;
}
for (let ext of exts) {
let file = path + ext;
if (await this.isExecutableFile(file)) {
return file;
}
}
}
let error = new Error(`Executable not found: ${bin}`);
error.errorCode = SubprocessConstants.ERROR_BAD_EXECUTABLE;
throw error;
}
};
var SubprocessImpl = SubprocessWin;

Просмотреть файл

@ -1,269 +0,0 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
/* 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 https://mozilla.org/MPL/2.0/. */
"use strict";
/* exported BasePipe, BaseProcess, debug */
/* globals Process, io */
/* global ctypes: false */
/* eslint no-console: 0 */
function debug(message) {
self.postMessage({
msg: "debug",
message
});
}
class BasePipe {
constructor() {
this.closing = false;
this.closed = false;
this.closedPromise = new Promise(resolve => {
this.resolveClosed = resolve;
});
this.pending = [];
}
shiftPending() {
let result = this.pending.shift();
if (this.closing && this.pending.length == 0) {
this.close();
}
return result;
}
}
let nextProcessId = 0;
class BaseProcess {
constructor(options) {
this.id = nextProcessId++;
this.exitCode = null;
this.exitPromise = new Promise(resolve => {
this.resolveExit = resolve;
});
this.exitPromise.then(() => {
// The input file descriptors will be closed after poll
// reports that their input buffers are empty. If we close
// them now, we may lose output.
this.pipes[0].close(true);
});
this.pid = null;
this.pipes = [];
this.stringArrays = [];
this.spawn(options);
}
/**
* Waits for the process to exit and all of its pending IO operations to
* complete.
*
* @returns {Promise<void>}
*/
awaitFinished() {
return Promise.all([
this.exitPromise,
...this.pipes.map(pipe => pipe.closedPromise)
]);
}
/**
* Creates a null-terminated array of pointers to null-terminated C-strings,
* and returns it.
*
* @param {string[]} strings
* The strings to convert into a C string array.
*
* @returns {ctypes.char.ptr.array}
*/
stringArray(strings) {
let result = ctypes.char.ptr.array(strings.length + 1)();
let cstrings = strings.map(str => ctypes.char.array()(str));
for (let [i, cstring] of cstrings.entries()) {
result[i] = cstring;
}
// Char arrays used in char arg and environment vectors must be
// explicitly kept alive in a JS object, or they will be reaped
// by the GC if it runs before our process is started.
this.stringArrays.push(cstrings);
return result;
}
}
let requests = {
init(details) {
io.init(details);
return {
data: {}
};
},
shutdown() {
io.shutdown();
return {
data: {}
};
},
close(pipeId, force = false) {
let pipe = io.getPipe(pipeId);
return pipe.close(force).then(() => ({
data: {}
}));
},
spawn(options) {
let process = new Process(options);
let processId = process.id;
io.addProcess(process);
let fds = process.pipes.map(pipe => pipe.id);
return {
data: {
processId,
fds,
pid: process.pid
}
};
},
kill(processId, force = false) {
let process = io.getProcess(processId);
process.kill(force ? 9 : 15);
return {
data: {}
};
},
wait(processId) {
let process = io.getProcess(processId);
process.wait();
process.awaitFinished().then(() => {
io.cleanupProcess(process);
});
return process.exitPromise.then(exitCode => {
return {
data: {
exitCode
}
};
});
},
read(pipeId, count) {
let pipe = io.getPipe(pipeId);
return pipe.read(count).then(buffer => {
return {
data: {
buffer
}
};
});
},
write(pipeId, buffer) {
let pipe = io.getPipe(pipeId);
return pipe.write(buffer).then(bytesWritten => {
return {
data: {
bytesWritten
}
};
});
},
getOpenFiles() {
return {
data: new Set(io.pipes.keys())
};
},
getProcesses() {
let data = new Map(Array.from(io.processes.values())
.filter(proc => proc.exitCode === null)
.map(proc => [proc.id, proc.pid]));
return {
data
};
},
waitForNoProcesses() {
return Promise.all(Array.from(io.processes.values(),
proc => proc.awaitFinished()));
}
};
onmessage = event => {
io.messageCount--;
let {
msg,
msgId,
args
} = event.data;
new Promise(resolve => {
resolve(requests[msg](...args));
}).then(result => {
let response = {
msg: "success",
msgId,
data: result.data
};
self.postMessage(response, result.transfer || []);
}).catch(error => {
if (error instanceof Error) {
error = {
message: error.message,
fileName: error.fileName,
lineNumber: error.lineNumber,
column: error.column,
stack: error.stack,
errorCode: error.errorCode
};
}
self.postMessage({
msg: "failure",
msgId,
error
});
}).catch(error => {
console.error(error);
self.postMessage({
msg: "failure",
msgId,
error: {}
});
});
};

Просмотреть файл

@ -1,650 +0,0 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
/* 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 https://mozilla.org/MPL/2.0/. */
"use strict";
/* exported Process */
/* globals BaseProcess, BasePipe */
/* global importScripts: false */
importScripts("chrome://openpgp/content/modules/enigmailprocess_shared.js",
"chrome://openpgp/content/modules/enigmailprocess_shared_unix.js",
"chrome://openpgp/content/modules/enigmailprocess_worker_common.js");
/* global ctypes: false, LIBC: false, libc: false, unix: false, SubprocessConstants: false */
/* global debug: false */
/* eslint no-console: 0 */
const POLL_TIMEOUT = 5000;
let io;
let nextPipeId = 0;
class Pipe extends BasePipe {
constructor(process, fd) {
super();
this.process = process;
this.fd = fd;
this.id = nextPipeId++;
}
get pollEvents() {
throw new Error("Not implemented");
}
/**
* Closes the file descriptor.
*
* @param {boolean} [force=false]
* If true, the file descriptor is closed immediately. If false, the
* file descriptor is closed after all current pending IO operations
* have completed.
*
* @returns {Promise<void>}
* Resolves when the file descriptor has been closed.
*/
close(force = false) {
if (!force && this.pending.length) {
this.closing = true;
return this.closedPromise;
}
for (let {
reject
}
of this.pending) {
let error = new Error("File closed");
error.errorCode = SubprocessConstants.ERROR_END_OF_FILE;
reject(error);
}
this.pending.length = 0;
if (!this.closed) {
this.fd.dispose();
this.closed = true;
this.resolveClosed();
io.pipes.delete(this.id);
io.updatePollFds();
}
return this.closedPromise;
}
/**
* Called when an error occurred while polling our file descriptor.
*/
onError() {
this.close(true);
this.process.wait();
}
}
class InputPipe extends Pipe {
/**
* A bit mask of poll() events which we currently wish to be notified of on
* this file descriptor.
*/
get pollEvents() {
if (this.pending.length) {
return LIBC.POLLIN;
}
return 0;
}
/**
* Asynchronously reads at most `length` bytes of binary data from the file
* descriptor into an ArrayBuffer of the same size. Returns a promise which
* resolves when the operation is complete.
*
* @param {integer} length
* The number of bytes to read.
*
* @returns {Promise<ArrayBuffer>}
*/
read(length) {
if (this.closing || this.closed) {
throw new Error("Attempt to read from closed pipe");
}
return new Promise((resolve, reject) => {
this.pending.push({
resolve, reject, length
});
io.updatePollFds();
});
}
/**
* Synchronously reads at most `count` bytes of binary data into an
* ArrayBuffer, and returns that buffer. If no data can be read without
* blocking, returns null instead.
*
* @param {integer} count
* The number of bytes to read.
*
* @returns {ArrayBuffer|null}
*/
readBuffer(count) {
let buffer = new ArrayBuffer(count);
let read = Number(libc.read(this.fd, buffer, buffer.byteLength));
if (read < 0 && ctypes.errno != LIBC.EAGAIN) {
this.onError();
}
if (read <= 0) {
return null;
}
if (read < buffer.byteLength) {
return ArrayBuffer.transfer(buffer, read);
}
return buffer;
}
/**
* Called when one of the IO operations matching the `pollEvents` mask may be
* performed without blocking.
*
* @returns {boolean}
* True if any data was successfully read.
*/
onReady() {
let result = false;
let reads = this.pending;
while (reads.length) {
let {
resolve, length
} = reads[0];
let buffer = this.readBuffer(length);
if (buffer) {
result = true;
this.shiftPending();
resolve(buffer);
}
else {
break;
}
}
if (reads.length == 0) {
io.updatePollFds();
}
return result;
}
}
class OutputPipe extends Pipe {
/**
* A bit mask of poll() events which we currently wish to be notified of on
* this file discriptor.
*/
get pollEvents() {
if (this.pending.length) {
return LIBC.POLLOUT;
}
return 0;
}
/**
* Asynchronously writes the given buffer to our file descriptor, and returns
* a promise which resolves when the operation is complete.
*
* @param {ArrayBuffer} buffer
* The buffer to write.
*
* @returns {Promise<integer>}
* Resolves to the number of bytes written when the operation is
* complete.
*/
write(buffer) {
if (this.closing || this.closed) {
throw new Error("Attempt to write to closed pipe");
}
return new Promise((resolve, reject) => {
this.pending.push({
resolve, reject, buffer, length: buffer.byteLength
});
io.updatePollFds();
});
}
/**
* Attempts to synchronously write the given buffer to our file descriptor.
* Writes only as many bytes as can be written without blocking, and returns
* the number of byes successfully written.
*
* Closes the file descriptor if an IO error occurs.
*
* @param {ArrayBuffer} buffer
* The buffer to write.
*
* @returns {integer}
* The number of bytes successfully written.
*/
writeBuffer(buffer) {
let bytesWritten = libc.write(this.fd, buffer, buffer.byteLength);
if (bytesWritten < 0 && ctypes.errno != LIBC.EAGAIN) {
this.onError();
}
return bytesWritten;
}
/**
* Called when one of the IO operations matching the `pollEvents` mask may be
* performed without blocking.
*/
onReady() {
let writes = this.pending;
while (writes.length) {
let {
buffer, resolve, length
} = writes[0];
let written = this.writeBuffer(buffer);
if (written == buffer.byteLength) {
resolve(length);
this.shiftPending();
}
else if (written > 0) {
writes[0].buffer = buffer.slice(written);
}
else {
break;
}
}
if (writes.length == 0) {
io.updatePollFds();
}
}
}
class Signal {
constructor(fd) {
this.fd = fd;
}
cleanup() {
libc.close(this.fd);
this.fd = null;
}
get pollEvents() {
return LIBC.POLLIN;
}
/**
* Called when an error occurred while polling our file descriptor.
*/
onError() {
io.shutdown();
}
/**
* Called when one of the IO operations matching the `pollEvents` mask may be
* performed without blocking.
*/
onReady() {
let buffer = new ArrayBuffer(16);
let count = Number(libc.read(this.fd, buffer, buffer.byteLength));
if (count > 0) {
io.messageCount += count;
}
}
}
class Process extends BaseProcess {
/**
* Each Process object opens an additional pipe from the target object, which
* will be automatically closed when the process exits, but otherwise
* carries no data.
*
* This property contains a bit mask of poll() events which we wish to be
* notified of on this descriptor. We're not expecting any input from this
* pipe, but we need to poll for input until the process exits in order to be
* notified when the pipe closes.
*/
get pollEvents() {
if (this.exitCode === null) {
return LIBC.POLLIN;
}
return 0;
}
/**
* Kills the process with the given signal.
*
* @param {integer} signal
*/
kill(signal) {
libc.kill(this.pid, signal);
this.wait();
}
/**
* Initializes the IO pipes for use as standard input, output, and error
* descriptors in the spawned process.
*
* @param {object} options
* The Subprocess options object for this process.
* @returns {unix.Fd[]}
* The array of file descriptors belonging to the spawned process.
*/
initPipes(options) {
let stderr = options.stderr;
let our_pipes = [];
let their_pipes = new Map();
let pipe = (input, id) => {
let fds = ctypes.int.array(2)();
let res = libc.pipe(fds);
if (res == -1) {
throw new Error("Unable to create pipe");
}
fds = Array.from(fds, unix.Fd);
if (input) {
fds.reverse();
}
if (input) {
our_pipes[id] = new InputPipe(this, fds[1]);
}
else {
our_pipes[id] = new OutputPipe(this, fds[1]);
}
libc.fcntl(fds[0], LIBC.F_SETFD, LIBC.FD_CLOEXEC);
libc.fcntl(fds[1], LIBC.F_SETFD, LIBC.FD_CLOEXEC);
libc.fcntl(fds[1], LIBC.F_SETFL, LIBC.O_NONBLOCK);
return fds[0];
};
their_pipes.set(0, pipe(false, 0));
their_pipes.set(1, pipe(true, 1));
if (stderr == "pipe") {
their_pipes.set(2, pipe(true, 2));
}
else if (stderr == "stdout") {
their_pipes.set(2, their_pipes.get(1));
}
// Create an additional pipe that we can use to monitor for process exit.
their_pipes.set(3, pipe(true, 3));
this.fd = our_pipes[3].fd;
delete our_pipes[3];
this.pipes = our_pipes;
return their_pipes;
}
spawn(options) {
let {
command, arguments: args
} = options;
let argv = this.stringArray(args);
let envp = this.stringArray(options.environment);
let actions = unix.posix_spawn_file_actions_t();
let actionsp = actions.address();
let fds = this.initPipes(options);
let cwd;
try {
if (options.workdir) {
cwd = ctypes.char.array(LIBC.PATH_MAX)();
libc.getcwd(cwd, cwd.length);
if (libc.chdir(options.workdir) < 0) {
throw new Error(`Unable to change working directory to ${options.workdir}`);
}
}
libc.posix_spawn_file_actions_init(actionsp);
for (let [i, fd] of fds.entries()) {
libc.posix_spawn_file_actions_adddup2(actionsp, fd, i);
}
let pid = unix.pid_t();
let rv = libc.posix_spawn(pid.address(), command, actionsp, null, argv, envp);
if (rv != 0) {
for (let pipe of this.pipes) {
pipe.close();
}
throw new Error(`Failed to execute command "${command}"`);
}
this.pid = pid.value;
}
finally {
libc.posix_spawn_file_actions_destroy(actionsp);
this.stringArrays.length = 0;
if (cwd) {
libc.chdir(cwd);
}
for (let fd of new Set(fds.values())) {
fd.dispose();
}
}
}
/**
* Called when input is available on our sentinel file descriptor.
*
* @see pollEvents
*/
onReady() {
// We're not actually expecting any input on this pipe. If we get any, we
// can't poll the pipe any further without reading it.
if (this.wait() == undefined) {
this.kill(9);
}
}
/**
* Called when an error occurred while polling our sentinel file descriptor.
*
* @see pollEvents
*/
onError() {
this.wait();
}
/**
* Attempts to wait for the process's exit status, without blocking. If
* successful, resolves the `exitPromise` to the process's exit value.
*
* @returns {integer|null}
* The process's exit status, if it has already exited.
*/
wait() {
if (this.exitCode !== null) {
return this.exitCode;
}
let status = ctypes.int();
let res = libc.waitpid(this.pid, status.address(), LIBC.WNOHANG);
const EINTR = 4; // TODO: change to LIBC.EINTR with TB 59
// If there's a failure here and we get any errno other than EINTR, it
// means that the process has been reaped by another thread (most likely
// the nspr process wait thread), and its actual exit status is not
// available to us. In that case, we have to assume success.
if (res == 0 || (res == -1 && ctypes.errno == EINTR)) {
return null;
}
let sig = unix.WTERMSIG(status.value);
if (sig) {
this.exitCode = -sig;
}
else {
this.exitCode = unix.WEXITSTATUS(status.value);
}
this.fd.dispose();
io.updatePollFds();
this.resolveExit(this.exitCode);
return this.exitCode;
}
}
io = {
pollFds: null,
pollHandlers: null,
pipes: new Map(),
processes: new Map(),
messageCount: 0,
running: true,
init(details) {
this.signal = new Signal(details.signalFd);
this.updatePollFds();
setTimeout(this.loop.bind(this), 0);
},
shutdown() {
if (this.running) {
this.running = false;
this.signal.cleanup();
this.signal = null;
self.postMessage({
msg: "close"
});
self.close();
}
},
getPipe(pipeId) {
let pipe = this.pipes.get(pipeId);
if (!pipe) {
let error = new Error("File closed");
error.errorCode = SubprocessConstants.ERROR_END_OF_FILE;
throw error;
}
return pipe;
},
getProcess(processId) {
let process = this.processes.get(processId);
if (!process) {
throw new Error(`Invalid process ID: ${processId}`);
}
return process;
},
updatePollFds() {
let handlers = [this.signal,
...this.pipes.values(),
...this.processes.values()
];
handlers = handlers.filter(handler => handler.pollEvents);
let pollfds = unix.pollfd.array(handlers.length)();
for (let [i, handler] of handlers.entries()) {
let pollfd = pollfds[i];
pollfd.fd = handler.fd;
pollfd.events = handler.pollEvents;
pollfd.revents = 0;
}
this.pollFds = pollfds;
this.pollHandlers = handlers;
},
loop() {
this.poll();
if (this.running) {
setTimeout(this.loop.bind(this), 0);
}
},
poll() {
let handlers = this.pollHandlers;
let pollfds = this.pollFds;
let timeout = this.messageCount > 0 ? 0 : POLL_TIMEOUT;
let count = libc.poll(pollfds, pollfds.length, timeout);
for (let i = 0; count && i < pollfds.length; i++) {
let pollfd = pollfds[i];
if (pollfd.revents) {
count--;
let handler = handlers[i];
try {
let success = false;
if (pollfd.revents & handler.pollEvents) {
success = handler.onReady();
}
// Only call the error handler in this iteration if we didn't also
// have a success. This is necessary because Linux systems set POLLHUP
// on a pipe when it's closed but there's still buffered data to be
// read, and Darwin sets POLLIN and POLLHUP on a closed pipe, even
// when there's no data to be read.
if (!success && (pollfd.revents & (LIBC.POLLERR | LIBC.POLLHUP | LIBC.POLLNVAL))) {
handler.onError();
}
}
catch (e) {
console.error(e);
debug(`Worker error: ${e} :: ${e.stack}`);
handler.onError();
}
pollfd.revents = 0;
}
}
},
addProcess(process) {
this.processes.set(process.id, process);
for (let pipe of process.pipes) {
if (pipe !== undefined)
this.pipes.set(pipe.id, pipe);
}
},
cleanupProcess(process) {
this.processes.delete(process.id);
}
};

Просмотреть файл

@ -1,753 +0,0 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
/* 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 https://mozilla.org/MPL/2.0/. */
"use strict";
/* exported Process */
/* globals BaseProcess, BasePipe, win32 */
/* global importScripts: false */
importScripts("chrome://openpgp/content/modules/enigmailprocess_shared.js",
"chrome://openpgp/content/modules/enigmailprocess_shared_win.js",
"chrome://openpgp/content/modules/enigmailprocess_worker_common.js");
/* global ctypes: false, libc: false, unix: false, SubprocessConstants: false, user32: false */
/* global debug: false */
/* eslint no-console: 0 */
const POLL_TIMEOUT = 5000;
// The exit code that we send when we forcibly terminate a process.
const TERMINATE_EXIT_CODE = 0x7f;
let io;
let nextPipeId = 0;
class Pipe extends BasePipe {
constructor(process, origHandle) {
super();
let handle = win32.HANDLE();
let curProc = libc.GetCurrentProcess();
libc.DuplicateHandle(curProc, origHandle, curProc, handle.address(),
0, false /* inheritable */ , win32.DUPLICATE_SAME_ACCESS);
origHandle.dispose();
this.id = nextPipeId++;
this.process = process;
this.handle = win32.Handle(handle);
let event = libc.CreateEventW(null, false, false, null);
this.overlapped = win32.OVERLAPPED();
this.overlapped.hEvent = event;
this._event = win32.Handle(event);
this.buffer = null;
}
get event() {
if (this.pending.length) {
return this._event;
}
return null;
}
maybeClose() {}
/**
* Closes the file handle.
*
* @param {boolean} [force=false]
* If true, the file handle is closed immediately. If false, the
* file handle is closed after all current pending IO operations
* have completed.
*
* @returns {Promise<void>}
* Resolves when the file handle has been closed.
*/
close(force = false) {
if (!force && this.pending.length) {
this.closing = true;
return this.closedPromise;
}
for (let {reject} of this.pending) {
let error = new Error("File closed");
error.errorCode = SubprocessConstants.ERROR_END_OF_FILE;
reject(error);
}
this.pending.length = 0;
this.buffer = null;
if (!this.closed) {
this.handle.dispose();
this._event.dispose();
io.pipes.delete(this.id);
this.handle = null;
this.closed = true;
this.resolveClosed();
io.updatePollEvents();
}
return this.closedPromise;
}
/**
* Called when an error occurred while attempting an IO operation on our file
* handle.
*/
onError() {
this.close(true);
}
}
class InputPipe extends Pipe {
/**
* Queues the next chunk of data to be read from the pipe if, and only if,
* there is no IO operation currently pending.
*/
readNext() {
if (this.buffer === null) {
this.readBuffer(this.pending[0].length);
}
}
/**
* Closes the pipe if there is a pending read operation with no more
* buffered data to be read.
*/
maybeClose() {
if (this.buffer) {
let read = win32.DWORD();
let ok = libc.GetOverlappedResult(
this.handle, this.overlapped.address(),
read.address(), false);
if (!ok) {
this.onError();
}
}
}
/**
* Asynchronously reads at most `length` bytes of binary data from the file
* descriptor into an ArrayBuffer of the same size. Returns a promise which
* resolves when the operation is complete.
*
* @param {integer} length
* The number of bytes to read.
*
* @returns {Promise<ArrayBuffer>}
*/
read(length) {
if (this.closing || this.closed) {
throw new Error("Attempt to read from closed pipe");
}
return new Promise((resolve, reject) => {
this.pending.push({
resolve,
reject,
length
});
this.readNext();
});
}
/**
* Initializes an overlapped IO read operation to read exactly `count` bytes
* into a new ArrayBuffer, which is stored in the `buffer` property until the
* operation completes.
*
* @param {integer} count
* The number of bytes to read.
*/
readBuffer(count) {
this.buffer = new ArrayBuffer(count);
let ok = libc.ReadFile(this.handle, this.buffer, count,
null, this.overlapped.address());
if (!ok && (!this.process.handle || libc.winLastError)) {
this.onError();
}
else {
io.updatePollEvents();
}
}
/**
* Called when our pending overlapped IO operation has completed, whether
* successfully or in failure.
*/
onReady() {
let read = win32.DWORD();
let ok = libc.GetOverlappedResult(
this.handle, this.overlapped.address(),
read.address(), false);
read = read.value;
if (!ok) {
this.onError();
}
else if (read > 0) {
let buffer = this.buffer;
this.buffer = null;
let {resolve} = this.shiftPending();
if (read == buffer.byteLength) {
resolve(buffer);
}
else {
resolve(ArrayBuffer.transfer(buffer, read));
}
if (this.pending.length) {
this.readNext();
}
else {
io.updatePollEvents();
}
}
}
}
class OutputPipe extends Pipe {
/**
* Queues the next chunk of data to be written to the pipe if, and only if,
* there is no IO operation currently pending.
*/
writeNext() {
if (this.buffer === null) {
this.writeBuffer(this.pending[0].buffer);
}
}
/**
* Asynchronously writes the given buffer to our file descriptor, and returns
* a promise which resolves when the operation is complete.
*
* @param {ArrayBuffer} buffer
* The buffer to write.
*
* @returns {Promise<integer>}
* Resolves to the number of bytes written when the operation is
* complete.
*/
write(buffer) {
if (this.closing || this.closed) {
throw new Error("Attempt to write to closed pipe");
}
return new Promise((resolve, reject) => {
this.pending.push({
resolve,
reject,
buffer
});
this.writeNext();
});
}
/**
* Initializes an overapped IO read operation to write the data in `buffer` to
* our file descriptor.
*
* @param {ArrayBuffer} buffer
* The buffer to write.
*/
writeBuffer(buffer) {
this.buffer = buffer;
let ok = libc.WriteFile(this.handle, buffer, buffer.byteLength,
null, this.overlapped.address());
if (!ok && libc.winLastError) {
this.onError();
}
else {
io.updatePollEvents();
}
}
/**
* Called when our pending overlapped IO operation has completed, whether
* successfully or in failure.
*/
onReady() {
let written = win32.DWORD();
let ok = libc.GetOverlappedResult(
this.handle, this.overlapped.address(),
written.address(), false);
written = written.value;
if (!ok || written != this.buffer.byteLength) {
this.onError();
}
else if (written > 0) {
let {resolve} = this.shiftPending();
this.buffer = null;
resolve(written);
if (this.pending.length) {
this.writeNext();
}
else {
io.updatePollEvents();
}
}
}
}
class Signal {
constructor(event) {
this.event = event;
}
cleanup() {
libc.CloseHandle(this.event);
this.event = null;
}
onError() {
io.shutdown();
}
onReady() {
io.messageCount += 1;
}
}
class Process extends BaseProcess {
constructor(...args) {
super(...args);
this.killed = false;
}
/**
* Returns our process handle for use as an event in a WaitForMultipleObjects
* call.
*/
get event() {
return this.handle;
}
/**
* Forcibly terminates the process and all children.
*/
kill() {
this.killed = true;
libc.TerminateJobObject(this.jobHandle, TERMINATE_EXIT_CODE);
}
/**
* Initializes the IO pipes for use as standard input, output, and error
* descriptors in the spawned process.
*
* @returns {win32.Handle[]}
* The array of file handles belonging to the spawned process.
*/
initPipes({stderr}) {
let our_pipes = [];
let their_pipes = [];
let secAttr = new win32.SECURITY_ATTRIBUTES();
secAttr.nLength = win32.SECURITY_ATTRIBUTES.size;
secAttr.bInheritHandle = true;
let pipe = input => {
if (input) {
let handles = win32.createPipe(secAttr, win32.FILE_FLAG_OVERLAPPED);
our_pipes.push(new InputPipe(this, handles[0]));
return handles[1];
}
let handles = win32.createPipe(secAttr, 0, win32.FILE_FLAG_OVERLAPPED);
our_pipes.push(new OutputPipe(this, handles[1]));
return handles[0];
};
their_pipes[0] = pipe(false);
their_pipes[1] = pipe(true);
if (stderr == "pipe") {
their_pipes[2] = pipe(true);
}
else {
let srcHandle;
if (stderr == "stdout") {
srcHandle = their_pipes[1];
}
else {
srcHandle = libc.GetStdHandle(win32.STD_ERROR_HANDLE);
}
// If we don't have a valid stderr handle, just pass it along without duplicating.
if (String(srcHandle) == win32.INVALID_HANDLE_VALUE ||
String(srcHandle) == win32.NULL_HANDLE_VALUE) {
their_pipes[2] = srcHandle;
}
else {
let handle = win32.HANDLE();
let curProc = libc.GetCurrentProcess();
let ok = libc.DuplicateHandle(curProc, srcHandle, curProc, handle.address(),
0, true /* inheritable */ ,
win32.DUPLICATE_SAME_ACCESS);
their_pipes[2] = ok && win32.Handle(handle);
}
}
if (!their_pipes.every(handle => handle)) {
throw new Error("Failed to create pipe");
}
this.pipes = our_pipes;
return their_pipes;
}
/**
* Creates a null-separated, null-terminated string list.
*
* @param {Array<string>} strings
* @returns {win32.WCHAR.array}
*/
stringList(strings) {
// Remove empty strings, which would terminate the list early.
strings = strings.filter(string => string);
let string = strings.join("\0") + "\0\0";
return win32.WCHAR.array()(string);
}
/**
* Quotes a string for use as a single command argument, using Windows quoting
* conventions.
*
* @see https://msdn.microsoft.com/en-us/library/17w5ykft(v=vs.85).aspx
*
* @param {string} str
* The argument string to quote.
* @returns {string}
*/
quoteString(str) {
if (!/[\s"]/.test(str)) {
return str;
}
let escaped = str.replace(/(\\*)("|$)/g, (m0, m1, m2) => {
if (m2) {
m2 = `${m2}`;
}
return `${m1}${m1}${m2}`;
});
return `"${escaped}"`;
}
spawn(options) {
let {command, arguments: args} = options;
if (/\\cmd\.exe$/i.test(command) && args.length == 3 && /^(\/S)?\/C$/i.test(args[1])) {
// cmd.exe is insane and requires special treatment.
args = [this.quoteString(args[0]), "/S/C", `"${args[2]}"`];
}
else {
args = args.map(arg => this.quoteString(arg));
}
if (/\.(bat|cmd)$/i.test(command)) {
command = io.comspec;
args = ["cmd.exe", "/s/c", `"${args.join(" ")}"`];
}
let envp = this.stringList(options.environment);
let handles = this.initPipes(options);
let processFlags = win32.CREATE_NO_WINDOW | win32.CREATE_SUSPENDED | win32.CREATE_UNICODE_ENVIRONMENT;
if (io.breakAwayFromJob) {
processFlags |= win32.CREATE_BREAKAWAY_FROM_JOB;
}
let startupInfoEx = new win32.STARTUPINFOEXW();
let startupInfo = startupInfoEx.StartupInfo;
startupInfo.cb = win32.STARTUPINFOW.size;
startupInfo.dwFlags = win32.STARTF_USESTDHANDLES;
startupInfo.hStdInput = handles[0];
startupInfo.hStdOutput = handles[1];
startupInfo.hStdError = handles[2];
// Note: This needs to be kept alive until we destroy the attribute list.
let handleArray = win32.HANDLE.array()(handles);
let threadAttrs = win32.createThreadAttributeList(handleArray);
if (threadAttrs) {
// If have thread attributes to pass, pass the size of the full extended
// startup info struct.
processFlags |= win32.EXTENDED_STARTUPINFO_PRESENT;
startupInfo.cb = win32.STARTUPINFOEXW.size;
startupInfoEx.lpAttributeList = threadAttrs;
}
let procInfo = new win32.PROCESS_INFORMATION();
let errorMessage = "Failed to create process";
let ok = libc.CreateProcessW(
command, args.join(" "),
null, /* Security attributes */
null, /* Thread security attributes */
true, /* Inherits handles */
processFlags, envp, options.workdir,
startupInfo.address(),
procInfo.address());
for (let handle of new Set(handles)) {
// If any of our handles are invalid, they don't have finalizers.
if (handle && handle.dispose) {
handle.dispose();
}
}
if (threadAttrs) {
libc.DeleteProcThreadAttributeList(threadAttrs);
}
if (ok) {
this.jobHandle = win32.Handle(libc.CreateJobObjectW(null, null));
let info = win32.JOBOBJECT_EXTENDED_LIMIT_INFORMATION();
info.BasicLimitInformation.LimitFlags = win32.JOB_OBJECT_LIMIT_BREAKAWAY_OK;
ok = libc.SetInformationJobObject(this.jobHandle, win32.JobObjectExtendedLimitInformation,
ctypes.cast(info.address(), ctypes.voidptr_t),
info.constructor.size);
errorMessage = `Failed to set job limits: 0x${(ctypes.winLastError || 0).toString(16)}`;
}
if (ok) {
ok = libc.AssignProcessToJobObject(this.jobHandle, procInfo.hProcess);
if (!ok) {
errorMessage = `Failed to attach process to job object: 0x${(ctypes.winLastError || 0).toString(16)}`;
libc.TerminateProcess(procInfo.hProcess, TERMINATE_EXIT_CODE);
}
}
if (!ok) {
for (let pipe of this.pipes) {
pipe.close();
}
throw new Error(errorMessage);
}
// Allow child processes to obtain focus via Alt-Tab
try {
user32.AllowSetForegroundWindow(procInfo.dwProcessId);
} catch (x) {}
this.handle = win32.Handle(procInfo.hProcess);
this.pid = procInfo.dwProcessId;
libc.ResumeThread(procInfo.hThread);
libc.CloseHandle(procInfo.hThread);
}
/**
* Called when our process handle is signaled as active, meaning the process
* has exited.
*/
onReady() {
this.wait();
}
/**
* Attempts to wait for the process's exit status, without blocking. If
* successful, resolves the `exitPromise` to the process's exit value.
*
* @returns {integer|null}
* The process's exit status, if it has already exited.
*/
wait() {
if (this.exitCode !== null) {
return this.exitCode;
}
let status = win32.DWORD();
let ok = libc.GetExitCodeProcess(this.handle, status.address());
if (ok && status.value != win32.STILL_ACTIVE) {
let exitCode = status.value;
if (this.killed && exitCode == TERMINATE_EXIT_CODE) {
// If we forcibly terminated the process, return the force kill exit
// code that we return on other platforms.
exitCode = -9;
}
this.resolveExit(exitCode);
this.exitCode = exitCode;
this.handle.dispose();
this.handle = null;
//libc.TerminateJobObject(this.jobHandle, TERMINATE_EXIT_CODE);
this.jobHandle.dispose();
this.jobHandle = null;
for (let pipe of this.pipes) {
pipe.maybeClose();
}
io.updatePollEvents();
return exitCode;
}
else {
return null;
}
}
}
io = {
events: null,
eventHandlers: null,
pipes: new Map(),
processes: new Map(),
messageCount: 0,
running: true,
init(details) {
this.comspec = details.comspec;
let signalEvent = ctypes.cast(ctypes.uintptr_t(details.signalEvent),
win32.HANDLE);
this.signal = new Signal(signalEvent);
this.updatePollEvents();
this.breakAwayFromJob = details.breakAwayFromJob;
setTimeout(this.loop.bind(this), 0);
},
shutdown() {
if (this.running) {
this.running = false;
this.signal.cleanup();
this.signal = null;
self.postMessage({
msg: "close"
});
self.close();
}
},
getPipe(pipeId) {
let pipe = this.pipes.get(pipeId);
if (!pipe) {
let error = new Error("File closed");
error.errorCode = SubprocessConstants.ERROR_END_OF_FILE;
throw error;
}
return pipe;
},
getProcess(processId) {
let process = this.processes.get(processId);
if (!process) {
throw new Error(`Invalid process ID: ${processId}`);
}
return process;
},
updatePollEvents() {
let handlers = [this.signal,
...this.pipes.values(),
...this.processes.values()
];
handlers = handlers.filter(handler => handler.event);
this.eventHandlers = handlers;
let handles = handlers.map(handler => handler.event);
this.events = win32.HANDLE.array()(handles);
},
loop() {
this.poll();
if (this.running) {
setTimeout(this.loop.bind(this), 0);
}
},
poll() {
let timeout = this.messageCount > 0 ? 0 : POLL_TIMEOUT;
for (;; timeout = 0) {
let events = this.events;
let handlers = this.eventHandlers;
let result = libc.WaitForMultipleObjects(events.length, events,
false, timeout);
if (result < handlers.length) {
try {
handlers[result].onReady();
} catch (e) {
console.error(e);
debug(`Worker error: ${e} :: ${e.stack}`);
handlers[result].onError();
}
}
else {
break;
}
}
},
addProcess(process) {
this.processes.set(process.id, process);
for (let pipe of process.pipes) {
this.pipes.set(pipe.id, pipe);
}
},
cleanupProcess(process) {
this.processes.delete(process.id);
}
};

Просмотреть файл

@ -12,7 +12,6 @@ const EnigmailLog = ChromeUtils.import("chrome://openpgp/content/modules/log.jsm
const EnigmailLocale = ChromeUtils.import("chrome://openpgp/content/modules/locale.jsm").EnigmailLocale;
const EnigmailData = ChromeUtils.import("chrome://openpgp/content/modules/data.jsm").EnigmailData;
const EnigmailCore = ChromeUtils.import("chrome://openpgp/content/modules/core.jsm").EnigmailCore;
const EnigmailSystem = ChromeUtils.import("chrome://openpgp/content/modules/system.jsm").EnigmailSystem;
const EnigmailConstants = ChromeUtils.import("chrome://openpgp/content/modules/constants.jsm").EnigmailConstants;
const EnigmailLazy = ChromeUtils.import("chrome://openpgp/content/modules/lazy.jsm").EnigmailLazy;
@ -129,7 +128,7 @@ function handleErrorCode(c, errorNumber) {
case 93: // dirmngr error
c.statusFlags |= EnigmailConstants.DISPLAY_MESSAGE;
c.retStatusObj.extendedStatus += "disp:get_passphrase ";
c.retStatusObj.statusMsg = EnigmailLocale.getString("errorHandling.dirmngrError") + "\n\n" + EnigmailLocale.getString("errorHandling.readFaq");
c.retStatusObj.statusMsg = "";
c.isError = true;
break;
case 2:
@ -484,62 +483,7 @@ function buildErrorMessageForCardCtrl(c, errCode, detectedCard) {
return errorMsg;
}
function parseErrorOutputWith(c) {
EnigmailLog.DEBUG("errorHandling.jsm: parseErrorOutputWith: status message: \n" + c.errOutput + "\n");
c.errLines = splitErrorOutput(c.errOutput);
c.isError = false; // set to true if a hard error was found
// parse all error lines
c.inDecryptionFailed = false; // to save details of encryption failed messages
for (var j = 0; j < c.errLines.length; j++) {
var errLine = c.errLines[j];
parseErrorLine(errLine, c);
if (c.isError) break;
}
detectForgedInsets(c);
c.retStatusObj.blockSeparation = c.retStatusObj.blockSeparation.replace(/ $/, "");
c.retStatusObj.statusFlags = c.statusFlags;
if (c.retStatusObj.statusMsg.length === 0) c.retStatusObj.statusMsg = c.statusArray.join("\n");
if (c.errorMsg.length === 0) {
c.errorMsg = c.errArray.map(function f(str, idx) {
return EnigmailSystem.convertNativeToUnicode(str);
}, EnigmailSystem).join("\n");
} else {
c.errorMsg = EnigmailSystem.convertNativeToUnicode(c.errorMsg);
}
if ((c.statusFlags & EnigmailConstants.CARDCTRL) && c.errCode > 0) {
c.errorMsg = buildErrorMessageForCardCtrl(c, c.errCode, c.detectedCard);
c.statusFlags |= EnigmailConstants.DISPLAY_MESSAGE;
}
let inDecryption = 0;
let m;
for (let i in c.statusArray) {
if (c.statusArray[i].search(/^BEGIN_DECRYPTION( .*)?$/) === 0) {
inDecryption = 1;
} else if (c.statusArray[i].search(/^END_DECRYPTION( .*)?$/) === 0) {
inDecryption = 0;
} else if (inDecryption >0) {
m = c.statusArray[i].match(/^(PLAINTEXT [0-9]+ [0-9]+ )(.*)$/);
if (m && m.length >= 3) c.retStatusObj.encryptedFileName = m[2];
}
}
EnigmailLog.DEBUG("errorHandling.jsm: parseErrorOutputWith: statusFlags = " + EnigmailData.bytesToHex(EnigmailData.pack(c.statusFlags, 4)) + "\n");
EnigmailLog.DEBUG("errorHandling.jsm: parseErrorOutputWith: return with c.errorMsg = " + c.errorMsg + "\n");
return c.errorMsg;
}
var EnigmailErrorHandling = {
parseErrorOutput: function(errOutput, retStatusObj) {
var context = newContext(errOutput, retStatusObj);
return parseErrorOutputWith(context);
},
/**
* Determin why a given key or userID cannot be used for signing
*
@ -639,4 +583,4 @@ var EnigmailErrorHandling = {
} catch (ex) {}
}
}
};
};

Просмотреть файл

@ -1,525 +0,0 @@
/*
* 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 https://mozilla.org/MPL/2.0/.
*/
"use strict";
const EXPORTED_SYMBOLS = ["EnigmailExecution"];
const EnigmailData = ChromeUtils.import("chrome://openpgp/content/modules/data.jsm").EnigmailData;
const EnigmailFiles = ChromeUtils.import("chrome://openpgp/content/modules/files.jsm").EnigmailFiles;
const EnigmailLog = ChromeUtils.import("chrome://openpgp/content/modules/log.jsm").EnigmailLog;
const subprocess = ChromeUtils.import("chrome://openpgp/content/modules/subprocess.jsm").subprocess;
const EnigmailErrorHandling = ChromeUtils.import("chrome://openpgp/content/modules/errorHandling.jsm").EnigmailErrorHandling;
const EnigmailCore = ChromeUtils.import("chrome://openpgp/content/modules/core.jsm").EnigmailCore;
const EnigmailLazy = ChromeUtils.import("chrome://openpgp/content/modules/lazy.jsm").EnigmailLazy;
const EnigmailConstants = ChromeUtils.import("chrome://openpgp/content/modules/constants.jsm").EnigmailConstants;
const loadOS = EnigmailLazy.loader("enigmail/os.jsm", "EnigmailOS");
var EnigmailExecution = {
agentType: "",
/**
* execStart Listener Object
*
* The listener object must implement at least the following methods:
*
* stdin(pipe) - OPTIONAL - write data to subprocess stdin via |pipe| hanlde
* stdout(data) - receive |data| from subprocess stdout
* stderr(data) - receive |data| from subprocess stderr
* done(exitCode) - receive signal when subprocess has terminated
*/
/**
* start a subprocess (usually gpg) that gets and/or receives data via stdin/stdout/stderr.
*
* @param {String/nsIFile} command: either full path to executable
* or: object referencing executable
* @param {Array of Strings} args: command line parameters for executable
* @param {Boolean} needPassphrase: is a passphrase required for the action?
* (this is currently a no-op)
* @param {nsIWindow} domWindow: window on top of which password dialog is shown
* @param {Object} listener: Listener to interact with subprocess; see spec. above
* @param {Object} statusflagsObj: .value will hold status Flags
*
* @return {Object}: handle to subprocess
*/
execStart: function(command, args, needPassphrase, domWindow, listener, statusFlagsObj) {
EnigmailLog.WRITE("execution.jsm: execStart: " +
"command = " + EnigmailFiles.formatCmdLine(command, args) +
", needPassphrase=" + needPassphrase +
", domWindow=" + domWindow +
", listener=" + listener + "\n");
listener = listener || {};
statusFlagsObj.value = 0;
let proc = null;
listener.command = command;
EnigmailLog.CONSOLE("enigmail> " + EnigmailFiles.formatCmdLine(command, args) + "\n");
try {
proc = subprocess.call({
command: command,
arguments: args,
environment: EnigmailCore.getEnvList(),
charset: null,
bufferedOutput: true,
stdin: function(pipe) {
if (listener.stdin) listener.stdin(pipe);
},
stdout: function(data) {
listener.stdout(data);
},
stderr: function(data) {
listener.stderr(data);
},
done: function(result) {
try {
listener.done(result.exitCode);
}
catch (ex) {
EnigmailLog.writeException("execution.jsm", ex);
}
},
mergeStderr: false
});
}
catch (ex) {
EnigmailLog.ERROR("execution.jsm: execStart: subprocess.call failed with '" + ex.toString() + "'\n");
EnigmailLog.DEBUG(" enigmail> DONE with FAILURE\n");
return null;
}
EnigmailLog.DEBUG(" enigmail> DONE\n");
return proc;
},
/*
requirements for listener object:
exitCode
stderrData
*/
execEnd: function(listener, statusFlagsObj, statusMsgObj, cmdLineObj, errorMsgObj, blockSeparationObj) {
EnigmailLog.DEBUG("execution.jsm: execEnd:\n");
cmdLineObj.value = listener.command;
let exitCode = listener.exitCode;
const errOutput = listener.stderrData;
EnigmailLog.DEBUG("execution.jsm: execEnd: exitCode = " + exitCode + "\n");
EnigmailLog.DEBUG("execution.jsm: execEnd: errOutput = " + errOutput.substr(0, 500) + "\n");
const retObj = {};
errorMsgObj.value = EnigmailErrorHandling.parseErrorOutput(errOutput, retObj);
statusFlagsObj.value = retObj.statusFlags;
statusMsgObj.value = retObj.statusMsg;
statusFlagsObj.encryptedFileName = retObj.encryptedFileName;
if (!blockSeparationObj) blockSeparationObj = {};
blockSeparationObj.value = retObj.blockSeparation;
if (errOutput.search(/jpeg image of size \d+/) > -1) {
statusFlagsObj.value |= EnigmailConstants.PHOTO_AVAILABLE;
}
if (blockSeparationObj && blockSeparationObj.value.indexOf(" ") > 0) {
exitCode = 2;
}
EnigmailLog.CONSOLE(EnigmailData.convertFromUnicode(errorMsgObj.value) + "\n");
return exitCode;
},
/**
* Resolve the path to the command and execute it if available
* Returns output from simpleExecCmd
*/
resolveAndSimpleExec: function(command, args, exitCodeObj, errorMsgObj) {
const resolvedCommand = EnigmailFiles.resolvePathWithEnv(command);
if (resolvedCommand === null) {
return null;
}
return EnigmailExecution.simpleExecCmd(resolvedCommand, args, exitCodeObj, errorMsgObj);
},
/**
* Execute a command and return the output from stdout
* No input and no statusFlags are returned.
*/
simpleExecCmd: function(command, args, exitCodeObj, errorMsgObj) {
EnigmailLog.WRITE("execution.jsm: EnigmailExecution.simpleExecCmd: command = " + command + " " + args.join(" ") + "\n");
let outputData = "";
let errOutput = "";
errorMsgObj.value = "";
exitCodeObj.value = -1;
EnigmailLog.CONSOLE("enigmail> " + EnigmailFiles.formatCmdLine(command, args) + "\n");
try {
subprocess.call({
command: command,
arguments: args,
charset: null,
environment: EnigmailCore.getEnvList(),
done: function(result) {
exitCodeObj.value = result.exitCode;
outputData = result.stdout;
errOutput = result.stderr;
},
mergeStderr: false
}).wait();
}
catch (ex) {
EnigmailLog.ERROR("execution.jsm: EnigmailExecution.simpleExecCmd: " + command.path + " failed\n");
EnigmailLog.DEBUG(" enigmail> DONE with FAILURE\n");
exitCodeObj.value = -1;
}
EnigmailLog.DEBUG(" enigmail> DONE\n");
if (errOutput) {
errorMsgObj.value = errOutput;
}
EnigmailLog.DEBUG("execution.jsm: EnigmailExecution.simpleExecCmd: exitCode = " + exitCodeObj.value + "\n");
EnigmailLog.DEBUG("execution.jsm: EnigmailExecution.simpleExecCmd: errOutput = " + errOutput.substr(0, 500) + "\n");
return outputData;
},
/**
* Execute a command and return the output from stdout.
* Accepts input and returns error message and statusFlags.
*/
execCmd: function(command, args, input, exitCodeObj, statusFlagsObj, statusMsgObj,
errorMsgObj, retStatusObj) {
EnigmailLog.WRITE("execution.jsm: EnigmailExecution.execCmd: subprocess = '" + command.path + "'\n");
if ((typeof input) != "string") input = "";
let preInput = "";
let outputData = "";
let errOutput = "";
EnigmailLog.CONSOLE("enigmail> " + EnigmailFiles.formatCmdLine(command, args) + "\n");
const procBuilder = new EnigmailExecution.processBuilder();
procBuilder.setCommand(command);
procBuilder.setArguments(args);
procBuilder.setEnvironment(EnigmailCore.getEnvList());
procBuilder.setStdin(
function(pipe) {
if (input.length > 0 || preInput.length > 0) {
pipe.write(preInput + input);
}
pipe.close();
}
);
procBuilder.setStdout(
function(data) {
outputData += data;
}
);
procBuilder.setStderr(
function(data) {
errOutput += data;
}
);
procBuilder.setDone(
function(result) {
exitCodeObj.value = result.exitCode;
}
);
const proc = procBuilder.build();
try {
subprocess.call(proc).wait();
}
catch (ex) {
EnigmailLog.ERROR("execution.jsm: EnigmailExecution.execCmd: subprocess.call failed with '" + ex.toString() + "'\n");
EnigmailLog.DEBUG(" enigmail> DONE with FAILURE\n");
exitCodeObj.value = -1;
}
EnigmailLog.DEBUG(" enigmail> DONE\n");
if (proc.resultData) outputData = proc.resultData;
if (proc.errorData) errOutput = proc.errorData;
EnigmailLog.DEBUG("execution.jsm: EnigmailExecution.execCmd: exitCode = " + exitCodeObj.value + "\n");
EnigmailLog.DEBUG("execution.jsm: EnigmailExecution.execCmd: errOutput = " + errOutput.substr(0, 500) + "\n");
if (!retStatusObj) {
retStatusObj = {};
}
errorMsgObj.value = EnigmailErrorHandling.parseErrorOutput(errOutput, retStatusObj);
statusFlagsObj.value = retStatusObj.statusFlags;
statusMsgObj.value = retStatusObj.statusMsg;
const blockSeparation = retStatusObj.blockSeparation;
exitCodeObj.value = EnigmailExecution.fixExitCode(exitCodeObj.value, statusFlagsObj);
if (blockSeparation.indexOf(" ") > 0) {
exitCodeObj.value = 2;
}
EnigmailLog.CONSOLE(errorMsgObj.value + "\n");
return outputData;
},
/**
* Execute a command and asynchronously, and return a Promise
* Accepts input and returns error message and statusFlags.
*
* @param {String/nsIFile} command: either full path to executable
* or: object referencing executable
* @param {Array of Strings} args: command line parameters for executable
* @param {String} input: data to pass to subprocess via stdin
* @param {Object} subprocessHandle: handle to subprocess. The subprocess may be
* killed via subprocessHandle.value.killProcess();
*
* @return {Promise<Object>}: Object with:
* - {Number} exitCode
* - {String} stdoutData - unmodified data from stdout
* - {String} stderrData - unmodified data from stderr
* - {String} errorMsg - error message from parseErrorOutput()
* - {Number} statusFlags
* - {String} statusMsg - pre-processed status messages (without [GNUPG:])
* - blockSeparation
* - isKilled: 0
*/
execAsync: function(command, args, input, subprocessHandle = null) {
EnigmailLog.WRITE("execution.jsm: execAsync: command = '" + command.path + "'\n");
return new Promise((resolve, reject) => {
if ((typeof input) != "string") input = "";
let outputData = "";
let errOutput = "";
let returnObj = {
exitCode: -1,
stdoutData: "",
stderrData: "",
errorMsg: "",
statusFlags: 0,
statusMsg: "",
blockSeparation: "",
isKilled: 0
};
EnigmailLog.CONSOLE("enigmail> " + EnigmailFiles.formatCmdLine(command, args) + "\n");
EnigmailLog.CONSOLE(input + "\n");
const procBuilder = new EnigmailExecution.processBuilder();
procBuilder.setCommand(command);
procBuilder.setArguments(args);
procBuilder.setEnvironment(EnigmailCore.getEnvList());
procBuilder.setStdin(
function(pipe) {
if (input.length > 0) {
pipe.write(input);
}
pipe.close();
}
);
procBuilder.setStdout(
function(data) {
outputData += data;
}
);
procBuilder.setStderr(
function(data) {
errOutput += data;
}
);
procBuilder.setDone(
function(result) {
let exitCode = result.exitCode;
EnigmailLog.DEBUG(" enigmail> DONE\n");
EnigmailLog.DEBUG("execution.jsm: execAsync: exitCode = " + exitCode + "\n");
EnigmailLog.DEBUG("execution.jsm: execAsync: errOutput = " + errOutput.substr(0, 500) + "\n");
let retStatusObj = {};
let errorMsg = EnigmailErrorHandling.parseErrorOutput(errOutput, retStatusObj);
let statusFlagsObj = {
value: retStatusObj.statusFlags
};
exitCode = EnigmailExecution.fixExitCode(exitCode, statusFlagsObj);
if (retStatusObj.blockSeparation.indexOf(" ") > 0) {
exitCode = 2;
}
EnigmailLog.CONSOLE(errorMsg + "\n");
returnObj.exitCode = exitCode;
returnObj.stdoutData = outputData;
returnObj.stderrData = errOutput;
returnObj.errorMsg = errorMsg;
returnObj.statusFlags = statusFlagsObj.value;
returnObj.statusMsg = retStatusObj.statusMsg;
returnObj.blockSeparation = retStatusObj.blockSeparation;
resolve(returnObj);
}
);
const proc = procBuilder.build();
try {
let p = subprocess.call(proc);
if (subprocessHandle) {
p.killProcess = function(hardKill) {
returnObj.isKilled = 1;
this.kill(hardKill);
};
subprocessHandle.value = p;
}
}
catch (ex) {
EnigmailLog.ERROR("execution.jsm: execAsync: subprocess.call failed with '" + ex.toString() + "'\n");
EnigmailLog.DEBUG(" enigmail> DONE with FAILURE\n");
reject(returnObj);
}
});
},
/**
* Fix the exit code of GnuPG (which may be wrong in some circumstances)
*
* @exitCode: Number - the exitCode obtained from GnuPG
* @statusFlagsObj: Object - the statusFlagsObj as received from parseErrorOutput()
*
* @return: Number - fixed exit code
*/
fixExitCode: function(exitCode, statusFlagsObj) {
EnigmailLog.DEBUG("execution.jsm: EnigmailExecution.fixExitCode: agentType: " + EnigmailExecution.agentType + " exitCode: " + exitCode + " statusFlags " + statusFlagsObj.statusFlags + "\n");
const statusFlags = statusFlagsObj.statusFlags;
if (exitCode !== 0) {
if ((statusFlags & (EnigmailConstants.BAD_PASSPHRASE | EnigmailConstants.UNVERIFIED_SIGNATURE)) &&
(statusFlags & EnigmailConstants.DECRYPTION_OKAY)) {
EnigmailLog.DEBUG("enigmailCommon.jsm: Enigmail.fixExitCode: Changing exitCode for decrypted msg " + exitCode + "->0\n");
exitCode = 0;
}
if ((EnigmailExecution.agentType === "gpg") && (exitCode == 256) && (loadOS().getOS() == "WINNT")) {
EnigmailLog.WARNING("enigmailCommon.jsm: Enigmail.fixExitCode: Using gpg and exit code is 256. You seem to use cygwin-gpg, activating countermeasures.\n");
if (statusFlags & (EnigmailConstants.BAD_PASSPHRASE | EnigmailConstants.UNVERIFIED_SIGNATURE)) {
EnigmailLog.WARNING("enigmailCommon.jsm: Enigmail.fixExitCode: Changing exitCode 256->2\n");
exitCode = 2;
}
else {
EnigmailLog.WARNING("enigmailCommon.jsm: Enigmail.fixExitCode: Changing exitCode 256->0\n");
exitCode = 0;
}
}
}
else {
if (statusFlags & (EnigmailConstants.INVALID_RECIPIENT | EnigmailConstants.DECRYPTION_FAILED | EnigmailConstants.BAD_ARMOR |
EnigmailConstants.MISSING_PASSPHRASE | EnigmailConstants.BAD_PASSPHRASE)) {
exitCode = 1;
}
else if (typeof(statusFlagsObj.extendedStatus) === "string" && statusFlagsObj.extendedStatus.search(/\bdisp:/) >= 0) {
exitCode = 1;
}
}
return exitCode;
},
processBuilder: function() {
this.process = {};
this.setCommand = function(command) {
this.process.command = command;
};
this.setArguments = function(args) {
this.process.arguments = args;
};
this.setEnvironment = function(envList) {
this.process.environment = envList;
};
this.setStdin = function(stdin) {
this.process.stdin = stdin;
};
this.setStdout = function(stdout) {
this.process.stdout = stdout;
};
this.setStderr = function(stderr) {
this.process.stderr = stderr;
};
this.setDone = function(done) {
this.process.done = done;
};
this.build = function() {
this.process.charset = null;
this.process.mergeStderr = false;
this.process.resultData = "";
this.process.errorData = "";
this.process.exitCode = -1;
return this.process;
};
return this;
},
execCmd2: function(command, args, stdinFunc, stdoutFunc, doneFunc) {
EnigmailLog.CONSOLE("enigmail> " + EnigmailFiles.formatCmdLine(command, args) + "\n");
const procBuilder = new EnigmailExecution.processBuilder();
procBuilder.setCommand(command);
procBuilder.setArguments(args);
procBuilder.setEnvironment(EnigmailCore.getEnvList());
procBuilder.setStdin(stdinFunc);
procBuilder.setStdout(stdoutFunc);
procBuilder.setDone(doneFunc);
const proc = procBuilder.build();
subprocess.call(proc).wait();
},
/**
* simple listener for using with execStart
*
* stdinFunc: optional function to write to stdin
* doneFunc : optional function that is called when the process is terminated
*/
newSimpleListener: function(stdinFunc, doneFunc) {
const simpleListener = {
stdoutData: "",
stderrData: "",
exitCode: -1,
stdin: function(pipe) {
if (stdinFunc) {
stdinFunc(pipe);
}
else {
pipe.close();
}
},
stdout: function(data) {
simpleListener.stdoutData += data;
},
stderr: function(data) {
simpleListener.stderrData += data;
},
done: function(exitCode) {
simpleListener.exitCode = exitCode;
if (doneFunc) {
doneFunc(exitCode);
}
}
};
return simpleListener;
}
};

Просмотреть файл

@ -17,13 +17,10 @@ const EnigmailFiles = ChromeUtils.import("chrome://openpgp/content/modules/files
const EnigmailLog = ChromeUtils.import("chrome://openpgp/content/modules/log.jsm").EnigmailLog;
const EnigmailLocale = ChromeUtils.import("chrome://openpgp/content/modules/locale.jsm").EnigmailLocale;
const EnigmailPrefs = ChromeUtils.import("chrome://openpgp/content/modules/prefs.jsm").EnigmailPrefs;
const EnigmailExecution = ChromeUtils.import("chrome://openpgp/content/modules/execution.jsm").EnigmailExecution;
const subprocess = ChromeUtils.import("chrome://openpgp/content/modules/subprocess.jsm").subprocess;
const EnigmailCore = ChromeUtils.import("chrome://openpgp/content/modules/core.jsm").EnigmailCore;
const EnigmailOS = ChromeUtils.import("chrome://openpgp/content/modules/os.jsm").EnigmailOS;
const EnigmailVersioning = ChromeUtils.import("chrome://openpgp/content/modules/versioning.jsm").EnigmailVersioning;
const EnigmailLazy = ChromeUtils.import("chrome://openpgp/content/modules/lazy.jsm").EnigmailLazy;
const getGpgAgent = EnigmailLazy.loader("enigmail/gpgAgent.jsm", "EnigmailGpgAgent");
const getDialog = EnigmailLazy.loader("enigmail/dialog.jsm", "EnigmailDialog");
const MINIMUM_GPG_VERSION = "2.0.14";
@ -46,54 +43,6 @@ function pushTrimmedStr(arr, str, splitStr) {
return (str.length > 0);
}
function getDirmngrTorStatus(exitCodeObj) {
const command = getGpgAgent().resolveToolPath("gpg-connect-agent");
if (command === null) {
return null;
}
const args = ["--dirmngr"];
EnigmailLog.CONSOLE("enigmail> " + EnigmailFiles.formatCmdLine(command, args) + "\n");
let stdout = "";
try {
exitCodeObj.value = subprocess.call({
command: command,
arguments: args,
environment: EnigmailCore.getEnvList(),
stdin: function(stdin) {
stdin.write("GETINFO tor\r\n");
stdin.write("bye\r\n");
stdin.write("\r\n");
stdin.close();
},
stdout: function(data) {
stdout += data;
}
}).wait();
} catch (ex) {
exitCodeObj.value = -1;
EnigmailLog.DEBUG("enigmail> DONE with FAILURE\n");
}
return stdout;
}
function dirmngrConfiguredWithTor() {
if (!EnigmailGpg.getGpgFeature("supports-dirmngr")) return false;
const exitCodeObj = {
value: null
};
const output = getDirmngrTorStatus(exitCodeObj);
if (output === null || exitCodeObj.value < 0) {
return false;
}
return output.match(/Tor mode is enabled/) !== null;
}
var EnigmailGpg = {
agentVersion: "",
_agentPath: null,
@ -124,7 +73,6 @@ var EnigmailGpg = {
genkey-no-protection - is "%no-protection" supported for generting keys (true for gpg >= 2.1)
search-keys-cmd - what command to use to terminate the --search-key operation. ("save" for gpg > 2.1; "quit" otherwise)
socks-on-windows - is SOCKS proxy supported on Windows (true for gpg >= 2.0.20)
supports-dirmngr - is dirmngr supported (true for gpg >= 2.1)
supports-ecc-keys - are ECC (elliptic curve) keys supported (true for gpg >= 2.1)
supports-sender - does gnupg understand the --sender argument (true for gpg >= 2.1.15)
supports-wkd - does gpg support wkd (web key directory) (true for gpg >= 2.1.19)
@ -162,8 +110,6 @@ var EnigmailGpg = {
return EnigmailVersioning.greaterThan(gpgVersion, "2.1");
case "windows-photoid-bug":
return EnigmailVersioning.lessThan(gpgVersion, "2.0.16");
case "supports-dirmngr":
return EnigmailVersioning.greaterThan(gpgVersion, "2.1");
case "supports-ecc-keys":
return EnigmailVersioning.greaterThan(gpgVersion, "2.1");
case "socks-on-windows":
@ -193,149 +139,6 @@ var EnigmailGpg = {
return undefined;
},
/**
* get the standard arguments to pass to every GnuPG subprocess
*
* @withBatchOpts: Boolean - true: use --batch and some more options
* false: don't use --batch and co.
*
* @return: Array of String - the list of arguments
*/
getStandardArgs: function(withBatchOpts) {
// return the arguments to pass to every GnuPG subprocess
let r = ["--charset", "utf-8", "--display-charset", "utf-8", "--no-auto-check-trustdb"]; // mandatory parameters to add in all cases
try {
let p = EnigmailPrefs.getPref("agentAdditionalParam").replace(/\\\\/g, "\\");
let i = 0;
let last = 0;
let foundSign = "";
let startQuote = -1;
while ((i = p.substr(last).search(/['"]/)) >= 0) {
if (startQuote == -1) {
startQuote = i;
foundSign = p.substr(last).charAt(i);
last = i + 1;
} else if (p.substr(last).charAt(i) == foundSign) {
// found enquoted part
if (startQuote > 1) pushTrimmedStr(r, p.substr(0, startQuote), true);
pushTrimmedStr(r, p.substr(startQuote + 1, last + i - startQuote - 1), false);
p = p.substr(last + i + 1);
last = 0;
startQuote = -1;
foundSign = "";
} else {
last = last + i + 1;
}
}
pushTrimmedStr(r, p, true);
} catch (ex) {}
if (withBatchOpts) {
r = r.concat(GPG_BATCH_OPT_LIST);
}
return r;
},
// returns the output of --with-colons --list-config
getGnupgConfig: function(exitCodeObj, errorMsgObj) {
if (!EnigmailGpg.agentPath) {
exitCodeObj.value = 0;
return "";
}
const args = EnigmailGpg.getStandardArgs(true).
concat(["--fixed-list-mode", "--with-colons", "--list-config"]);
const statusMsgObj = {};
const cmdErrorMsgObj = {};
const statusFlagsObj = {};
const listText = EnigmailExecution.execCmd(EnigmailGpg.agentPath, args, "", exitCodeObj, statusFlagsObj, statusMsgObj, cmdErrorMsgObj);
if (exitCodeObj.value !== 0) {
errorMsgObj.value = EnigmailLocale.getString("badCommand");
if (cmdErrorMsgObj.value) {
errorMsgObj.value += "\n" + EnigmailFiles.formatCmdLine(EnigmailGpg.agentPath, args);
errorMsgObj.value += "\n" + cmdErrorMsgObj.value;
}
return "";
}
return listText.replace(/(\r\n|\r)/g, "\n");
},
/**
* return an array containing the aliases and the email addresses
* of groups defined in gpg.conf
*
* @return: array of objects with the following properties:
* - alias: group name as used by GnuPG
* - keylist: list of keys (any form that GnuPG accepts), separated by ";"
*
* (see docu for gnupg parameter --group)
*/
getGpgGroups: function() {
const exitCodeObj = {};
const errorMsgObj = {};
const cfgStr = EnigmailGpg.getGnupgConfig(exitCodeObj, errorMsgObj);
if (exitCodeObj.value !== 0) {
getDialog().alert(errorMsgObj.value);
return null;
}
const groups = [];
const cfg = cfgStr.split(/\n/);
for (let i = 0; i < cfg.length; i++) {
if (cfg[i].indexOf("cfg:group") === 0) {
const groupArr = cfg[i].split(/:/);
groups.push({
alias: groupArr[2],
keylist: groupArr[3]
});
}
}
return groups;
},
/**
* Force GnuPG to recalculate the trust db. This is sometimes required after importing keys.
*
* no return value
*/
recalcTrustDb: function() {
EnigmailLog.DEBUG("enigmailCommon.jsm: recalcTrustDb:\n");
const command = EnigmailGpg.agentPath;
const args = EnigmailGpg.getStandardArgs(false).
concat(["--check-trustdb"]);
try {
const proc = subprocess.call({
command: EnigmailGpg.agentPath,
arguments: args,
environment: EnigmailCore.getEnvList(),
charset: null,
mergeStderr: false
});
proc.wait();
} catch (ex) {
EnigmailLog.ERROR("enigmailCommon.jsm: recalcTrustDb: subprocess.call failed with '" + ex.toString() + "'\n");
throw ex;
}
},
signingAlgIdToString: function(id) {
// RFC 4880 Sec. 9.1, RFC 6637 Sec. 5 and draft-koch-eddsa-for-openpgp-03 Sec. 8
switch (parseInt(id, 10)) {
@ -381,11 +184,4 @@ var EnigmailGpg = {
return EnigmailLocale.getString("unknownHashAlg", [parseInt(id, 10)]);
}
},
/**
* For versions of GPG 2.1 and higher, checks to see if the dirmngr is configured to use Tor
*
* @return Boolean - True if dirmngr is configured with Tor. False otherwise
*/
dirmngrConfiguredWithTor: dirmngrConfiguredWithTor
};
};

Просмотреть файл

@ -1,803 +0,0 @@
/*
* 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 https://mozilla.org/MPL/2.0/.
*/
"use strict";
var EXPORTED_SYMBOLS = ["EnigmailGpgAgent"];
const ctypes = ChromeUtils.import("resource://gre/modules/ctypes.jsm").ctypes;
const subprocess = ChromeUtils.import("chrome://openpgp/content/modules/subprocess.jsm").subprocess;
const EnigmailCore = ChromeUtils.import("chrome://openpgp/content/modules/core.jsm").EnigmailCore;
const EnigmailFiles = ChromeUtils.import("chrome://openpgp/content/modules/files.jsm").EnigmailFiles;
const EnigmailLog = ChromeUtils.import("chrome://openpgp/content/modules/log.jsm").EnigmailLog;
const EnigmailPrefs = ChromeUtils.import("chrome://openpgp/content/modules/prefs.jsm").EnigmailPrefs;
const EnigmailOS = ChromeUtils.import("chrome://openpgp/content/modules/os.jsm").EnigmailOS;
const EnigmailLocale = ChromeUtils.import("chrome://openpgp/content/modules/locale.jsm").EnigmailLocale;
const EnigmailWindows = ChromeUtils.import("chrome://openpgp/content/modules/windows.jsm").EnigmailWindows;
const EnigmailApp = ChromeUtils.import("chrome://openpgp/content/modules/app.jsm").EnigmailApp;
const EnigmailExecution = ChromeUtils.import("chrome://openpgp/content/modules/execution.jsm").EnigmailExecution;
const EnigmailPassword = ChromeUtils.import("chrome://openpgp/content/modules/passwords.jsm").EnigmailPassword;
const EnigmailSystem = ChromeUtils.import("chrome://openpgp/content/modules/system.jsm").EnigmailSystem;
const EnigmailData = ChromeUtils.import("chrome://openpgp/content/modules/data.jsm").EnigmailData;
const EnigmailLazy = ChromeUtils.import("chrome://openpgp/content/modules/lazy.jsm").EnigmailLazy;
const getEnigmailGpg = EnigmailLazy.loader("enigmail/gpg.jsm", "EnigmailGpg");
const getDialog = EnigmailLazy.loader("enigmail/dialog.jsm", "EnigmailDialog");
const NS_LOCAL_FILE_CONTRACTID = "@mozilla.org/file/local;1";
const DIR_SERV_CONTRACTID = "@mozilla.org/file/directory_service;1";
const NS_LOCALFILEOUTPUTSTREAM_CONTRACTID = "@mozilla.org/network/file-output-stream;1";
const DEFAULT_FILE_PERMS = 0o600;
// Making this a var makes it possible to test windows things on linux
var nsIWindowsRegKey = Ci.nsIWindowsRegKey;
var gIsGpgAgent = -1;
const DUMMY_AGENT_INFO = "none";
function cloneOrNull(v) {
if (v && typeof v.clone === "function") {
return v.clone();
}
else {
return v;
}
}
function extractAgentInfo(fullStr) {
if (fullStr) {
return fullStr.
replace(/[\r\n]/g, "").
replace(/^.*=/, "").
replace(/;.*$/, "");
}
else {
return "";
}
}
function getHomedirFromParam(param) {
let i = param.search(/--homedir/);
if (i >= 0) {
param = param.substr(i + 9);
let m = param.match(/^(\s*)([^\\]".+[^\\]")/);
if (m && m.length > 2) {
param = m[2].substr(1);
let j = param.search(/[^\\]"/);
return param.substr(1, j);
}
m = param.match(/^(\s*)([^\\]'.+[^\\]')/);
if (m && m.length > 2) {
param = m[2].substr(1);
let j = param.search(/[^\\]'/);
return param.substr(1, j);
}
m = param.match(/^(\s*)(\S+)/);
if (m && m.length > 2) {
return m[2];
}
}
return null;
}
var EnigmailGpgAgent = {
agentType: "",
agentPath: null,
connGpgAgentPath: null,
gpgconfPath: null,
gpgAgentInfo: {
preStarted: false,
envStr: ""
},
gpgAgentProcess: null,
gpgAgentIsOptional: true,
isDummy: function() {
return EnigmailGpgAgent.gpgAgentInfo.envStr === DUMMY_AGENT_INFO;
},
resetGpgAgent: function() {
EnigmailLog.DEBUG("gpgAgent.jsm: resetGpgAgent\n");
gIsGpgAgent = -1;
},
isCmdGpgAgent: function(pid) {
console.debug("reaching disabled EnigmailGpgAgent.isCmdGpgAgent()");
return;
EnigmailLog.DEBUG("gpgAgent.jsm: isCmdGpgAgent:\n");
const environment = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
let ret = false;
let path = environment.get("PATH");
if (!path || path.length === 0) {
path = "/bin:/usr/bin:/usr/local/bin";
}
const psCmd = EnigmailFiles.resolvePath("ps", path, false);
let outStr = "";
const proc = {
command: psCmd,
arguments: ["-o", "comm", "-p", pid],
environment: EnigmailCore.getEnvList(),
charset: null,
stdout: function(data) {
outStr += data;
}
};
try {
subprocess.call(proc).wait();
EnigmailLog.DEBUG("gpgAgent.jsm: isCmdGpgAgent: got data: '" + outStr + "'\n");
var data = outStr.replace(/[\r\n]/g, " ");
if (data.search(/gpg-agent/) >= 0) {
ret = true;
}
}
catch (ex) {}
return ret;
},
isAgentTypeGpgAgent: function() {
console.debug("reaching disabled EnigmailGpgAgent.isAgentTypeGpgAgent()");
return;
// determine if the used agent is a gpg-agent
EnigmailLog.DEBUG("gpgAgent.jsm: isAgentTypeGpgAgent:\n");
// to my knowledge there is no other agent than gpg-agent on Windows
if (EnigmailOS.getOS() == "WINNT") return true;
if (gIsGpgAgent >= 0) {
return gIsGpgAgent == 1;
}
let pid = -1;
let exitCode = -1;
let outStr = "";
if (!EnigmailCore.getService()) return false;
const proc = {
command: EnigmailGpgAgent.connGpgAgentPath,
arguments: [],
charset: null,
environment: EnigmailCore.getEnvList(),
stdin: function(pipe) {
pipe.write("/subst\n");
pipe.write("/serverpid\n");
pipe.write("/echo pid: ${get serverpid}\n");
pipe.write("/bye\n");
pipe.close();
},
stdout: function(data) {
outStr += data;
}
};
try {
exitCode = subprocess.call(proc).wait();
if (exitCode) pid = -2;
const data = outStr.replace(/[\r\n]/g, "");
if (data.search(/^pid: [0-9]+$/) === 0) {
pid = data.replace(/^pid: /, "");
}
}
catch (ex) {}
EnigmailLog.DEBUG("gpgAgent.jsm: isAgentTypeGpgAgent: pid=" + pid + "\n");
EnigmailGpgAgent.isCmdGpgAgent(pid);
let isAgent = false;
try {
isAgent = EnigmailGpgAgent.isCmdGpgAgent(pid);
gIsGpgAgent = isAgent ? 1 : 0;
}
catch (ex) {}
return isAgent;
},
getAgentMaxIdle: function() {
console.debug("reaching disabled EnigmailGpgAgent.getAgentMaxIdle()");
return;
EnigmailLog.DEBUG("gpgAgent.jsm: getAgentMaxIdle:\n");
let maxIdle = -1;
if (!EnigmailCore.getService()) return maxIdle;
const DEFAULT = 7;
const CFGVALUE = 9;
let outStr = "";
const proc = {
command: EnigmailGpgAgent.gpgconfPath,
arguments: ["--list-options", "gpg-agent"],
charset: null,
environment: EnigmailCore.getEnvList(),
stdout: function(data) {
outStr += data;
}
};
subprocess.call(proc).wait();
const lines = outStr.split(/[\r\n]/);
for (let i = 0; i < lines.length; i++) {
EnigmailLog.DEBUG("gpgAgent.jsm: getAgentMaxIdle: line: " + lines[i] + "\n");
if (lines[i].search(/^default-cache-ttl:/) === 0) {
const m = lines[i].split(/:/);
if (m[CFGVALUE].length === 0) {
maxIdle = Math.round(m[DEFAULT] / 60);
}
else {
maxIdle = Math.round(m[CFGVALUE] / 60);
}
break;
}
}
return maxIdle;
},
setAgentMaxIdle: function(idleMinutes) {
console.debug("reaching disabled EnigmailGpgAgent.setAgentMaxIdle()");
return;
EnigmailLog.DEBUG("gpgAgent.jsm: setAgentMaxIdle:\n");
if (!EnigmailCore.getService()) return;
const RUNTIME = 8;
const proc = {
command: EnigmailGpgAgent.gpgconfPath,
arguments: ["--runtime", "--change-options", "gpg-agent"],
environment: EnigmailCore.getEnvList(),
charset: null,
mergeStderr: true,
stdin: function(pipe) {
pipe.write("default-cache-ttl:" + RUNTIME + ":" + (idleMinutes * 60) + "\n");
pipe.write("max-cache-ttl:" + RUNTIME + ":" + (idleMinutes * 600) + "\n");
pipe.close();
},
stdout: function(data) {
EnigmailLog.DEBUG("gpgAgent.jsm: setAgentMaxIdle.stdout: " + data + "\n");
}
};
try {
let exitCode = subprocess.call(proc);
EnigmailLog.DEBUG("gpgAgent.jsm: setAgentMaxIdle.stdout: gpgconf exitCode=" + exitCode + "\n");
}
catch (ex) {
EnigmailLog.DEBUG("gpgAgent.jsm: setAgentMaxIdle: exception: " + ex.toString() + "\n");
}
},
getMaxIdlePref: function(win) {
let maxIdle = EnigmailPrefs.getPref("maxIdleMinutes");
try {
if (EnigmailCore.getService(win)) {
if (EnigmailGpgAgent.gpgconfPath &&
EnigmailGpgAgent.connGpgAgentPath) {
if (EnigmailGpgAgent.isAgentTypeGpgAgent()) {
const m = EnigmailGpgAgent.getAgentMaxIdle();
if (m > -1) maxIdle = m;
}
}
}
}
catch (ex) {}
return maxIdle;
},
setMaxIdlePref: function(minutes) {
EnigmailPrefs.setPref("maxIdleMinutes", minutes);
if (EnigmailGpgAgent.isAgentTypeGpgAgent()) {
try {
EnigmailGpgAgent.setAgentMaxIdle(minutes);
}
catch (ex) {}
}
},
/**
* Determine the "gpg home dir", i.e. the directory where gpg.conf and the keyring are
* stored using the "additional parameter" and gpgconf.
*
* @return String - directory name, or NULL (in case the command did not succeed)
*/
getGpgHomeDir: function() {
console.debug("reaching disabled EnigmailGpgAgent.getGpgHomeDir()");
return;
let param = EnigmailPrefs.getPref("agentAdditionalParam");
if (param) {
let hd = getHomedirFromParam(param);
if (hd) return hd;
}
if (EnigmailGpgAgent.gpgconfPath === null) return null;
const command = EnigmailGpgAgent.gpgconfPath;
let args = ["--list-dirs"];
let exitCode = -1;
let outStr = "";
EnigmailLog.DEBUG("gpgAgent.jsm: getGpgHomeDir: calling subprocess with '" + command.path + "'\n");
EnigmailLog.CONSOLE("enigmail> " + EnigmailFiles.formatCmdLine(command, args) + "\n");
const proc = {
command: command,
arguments: args,
environment: EnigmailCore.getEnvList(),
charset: null,
stdout: function(data) {
outStr += data;
},
mergeStderr: false
};
try {
exitCode = subprocess.call(proc).wait();
}
catch (ex) {
EnigmailLog.ERROR("gpgAgent.jsm: getGpgHomeDir: subprocess.call failed with '" + ex.toString() + "'\n");
EnigmailLog.DEBUG(" enigmail> DONE with FAILURE\n");
throw ex;
}
let m = outStr.match(/^(homedir:)(.*)$/mi);
if (m && m.length > 2) {
return EnigmailData.convertGpgToUnicode(unescape(m[2]));
}
return null;
},
/**
* @param domWindow: Object - parent window, may be NULL
* @param esvc: Object - Enigmail service object
* @param preferredPath: String - try to use specific path to locate gpg
*/
setAgentPath: function(domWindow, esvc, preferredPath) {
console.debug("reaching disabled EnigmailGpgAgent.setAgentPath()");
return;
EnigmailLog.DEBUG("gpgAgent.jsm: setAgentPath()\n");
let agentPath = "";
try {
if (preferredPath) {
agentPath = preferredPath;
}
else {
agentPath = EnigmailPrefs.getPrefBranch().getCharPref("agentPath");
}
}
catch (ex) {}
var agentType = "gpg";
var agentName = "";
EnigmailGpgAgent.resetGpgAgent();
if (agentPath) {
// Locate GnuPG executable
// Append default .exe extension for DOS-Like systems, if needed
if (EnigmailOS.isDosLike && (agentPath.search(/\.\w+$/) < 0)) {
agentPath += ".exe";
}
try {
let pathDir = Cc[NS_LOCAL_FILE_CONTRACTID].createInstance(Ci.nsIFile);
if (!EnigmailFiles.isAbsolutePath(agentPath, EnigmailOS.isDosLike)) {
// path relative to Mozilla installation dir
const ds = Cc[DIR_SERV_CONTRACTID].getService();
const dsprops = ds.QueryInterface(Ci.nsIProperties);
pathDir = dsprops.get("CurProcD", Ci.nsIFile);
const dirs = agentPath.split(new RegExp(EnigmailOS.isDosLike ? "\\\\" : "/"));
for (let i = 0; i < dirs.length; i++) {
if (dirs[i] != ".") {
pathDir.append(dirs[i]);
}
}
if (pathDir.exists()) {
pathDir.normalize();
}
}
else {
// absolute path
EnigmailFiles.initPath(pathDir, agentPath);
}
if (!(pathDir.isFile() /* && pathDir.isExecutable()*/ )) {
throw Components.results.NS_ERROR_FAILURE;
}
agentPath = pathDir.QueryInterface(Ci.nsIFile);
}
catch (ex) {
esvc.initializationError = EnigmailLocale.getString("gpgNotFound", [agentPath]);
EnigmailLog.ERROR("gpgAgent.jsm: initialize: Error - " + esvc.initializationError + "\n");
throw Components.results.NS_ERROR_FAILURE;
}
}
else {
agentPath = this.resolveGpgPath(esvc.environment);
if (!agentPath) {
esvc.initializationError = EnigmailLocale.getString("gpgNotInPath");
EnigmailLog.ERROR("gpgAgent.jsm: Error - " + esvc.initializationError + "\n");
throw Components.results.NS_ERROR_FAILURE;
}
}
agentPath.normalize(); // replace a/../b with b
EnigmailLog.CONSOLE("EnigmailAgentPath=" + EnigmailFiles.getFilePathDesc(agentPath) + "\n\n");
EnigmailGpgAgent.agentType = agentType;
EnigmailGpgAgent.agentPath = agentPath;
getEnigmailGpg().setAgentPath(agentPath);
EnigmailExecution.agentType = agentType;
const command = agentPath;
let args = [];
if (agentType == "gpg") {
args = ["--batch", "--no-tty", "--charset", "utf-8", "--display-charset", "utf-8", "--version", "--version"];
}
let exitCode = -1;
let outStr = "";
let errStr = "";
EnigmailLog.DEBUG("gpgAgent.jsm: setAgentPath: calling subprocess with '" + command.path + "'\n");
EnigmailLog.CONSOLE("enigmail> " + EnigmailFiles.formatCmdLine(command, args) + "\n");
const proc = {
command: command,
arguments: args,
environment: EnigmailCore.getEnvList(),
charset: null,
stdout: function(data) {
outStr += data;
},
stderr: function(data) {
errStr += data;
},
mergeStderr: false
};
try {
exitCode = subprocess.call(proc).wait();
}
catch (ex) {
EnigmailLog.ERROR("gpgAgent.jsm: setAgentPath: subprocess.call failed with '" + ex.toString() + "'\n");
EnigmailLog.DEBUG(" enigmail> DONE with FAILURE\n");
throw ex;
}
EnigmailLog.DEBUG(" enigmail> DONE\n");
outStr = EnigmailSystem.convertNativeToUnicode(outStr);
if (exitCode !== 0) {
EnigmailLog.ERROR("gpgAgent.jsm: setAgentPath: gpg failed with exitCode " + exitCode + " msg='" + outStr + " " + errStr + "'\n");
throw Components.results.NS_ERROR_FAILURE;
}
EnigmailLog.CONSOLE(outStr + "\n");
// detection for Gpg4Win wrapper
if (outStr.search(/^gpgwrap.*;/) === 0) {
const outLines = outStr.split(/[\n\r]+/);
const firstLine = outLines[0];
outLines.splice(0, 1);
outStr = outLines.join("\n");
agentPath = firstLine.replace(/^.*;[ \t]*/, "");
EnigmailLog.CONSOLE("gpg4win-gpgwrapper detected; EnigmailAgentPath=" + agentPath + "\n\n");
}
const versionParts = outStr.replace(/[\r\n].*/g, "").replace(/ *\(gpg4win.*\)/i, "").split(/ /);
const gpgVersion = versionParts[versionParts.length - 1];
EnigmailLog.DEBUG("gpgAgent.jsm: detected GnuPG version '" + gpgVersion + "'\n");
getEnigmailGpg().agentVersion = gpgVersion;
if (!getEnigmailGpg().getGpgFeature("version-supported")) {
if (!domWindow) {
domWindow = EnigmailWindows.getBestParentWin();
}
getDialog().alert(domWindow, EnigmailLocale.getString("oldGpgVersion20", [gpgVersion, getEnigmailGpg().getMinimumGpgVersion()]));
throw Components.results.NS_ERROR_FAILURE;
}
EnigmailGpgAgent.gpgconfPath = EnigmailGpgAgent.resolveToolPath("gpgconf");
EnigmailGpgAgent.connGpgAgentPath = EnigmailGpgAgent.resolveToolPath("gpg-connect-agent");
EnigmailGpgAgent.checkGpgHomeDir(domWindow, esvc);
EnigmailLog.DEBUG("gpgAgent.jsm: setAgentPath: gpgconf found: " + (EnigmailGpgAgent.gpgconfPath ? "yes" : "no") + "\n");
},
/**
* Determine the location of the GnuPG executable
*
* @param env: Object: nsIEnvironment to use
*
* @return Object: nsIFile pointing to gpg, or NULL
*/
resolveGpgPath: function(env) {
console.debug("reaching disabled EnigmailGpgAgent.resolveGpgPath()");
return;
EnigmailLog.DEBUG("gpgAgent.jsm: resolveGpgPath()\n");
let agentName = "";
if (EnigmailOS.isDosLike) {
agentName = "gpg2.exe;gpg.exe";
}
else {
agentName = "gpg2;gpg";
}
// Resolve relative path using PATH environment variable
const envPath = env.get("PATH");
let agentPath = EnigmailFiles.resolvePath(agentName, envPath, EnigmailOS.isDosLike);
if (!agentPath && EnigmailOS.isDosLike) {
// DOS-like systems: search for GPG in c:\gnupg, c:\gnupg\bin, d:\gnupg, d:\gnupg\bin
let gpgPath = "c:\\gnupg;c:\\gnupg\\bin;d:\\gnupg;d:\\gnupg\\bin";
agentPath = EnigmailFiles.resolvePath(agentName, gpgPath, EnigmailOS.isDosLike);
}
if ((!agentPath) && EnigmailOS.isWin32) {
// Look up in Windows Registry
const installDir = ["Software\\GNU\\GNUPG", "Software\\GNUPG"];
try {
for (let i = 0; i < installDir.length && !agentPath; i++) {
let gpgPath = EnigmailOS.getWinRegistryString(installDir[i], "Install Directory", nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE);
agentPath = EnigmailFiles.resolvePath(agentName, gpgPath, EnigmailOS.isDosLike());
if (!agentPath) {
gpgPath += "\\bin";
agentPath = EnigmailFiles.resolvePath(agentName, gpgPath, EnigmailOS.isDosLike());
}
}
}
catch (ex) {}
if (!agentPath) {
// try to determine the default PATH from the registry after the installation
// if we could not get any information from the registry
try {
let winPath = EnigmailOS.getWinRegistryString("SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", "Path", nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE);
agentPath = EnigmailFiles.resolvePath(agentName, winPath, EnigmailOS.isDosLike);
}
catch (ex) {}
}
if (!agentPath) {
// default for gpg4win 3.0
let gpgPath = "C:\\Program Files\\GnuPG\\bin;C:\\Program Files (x86)\\GnuPG\\bin";
agentPath = EnigmailFiles.resolvePath(agentName, gpgPath, EnigmailOS.isDosLike);
}
}
if (!agentPath && !EnigmailOS.isDosLike) {
// Unix-like systems: check /usr/bin and /usr/local/bin
let gpgPath = "/usr/bin:/usr/local/bin";
agentPath = EnigmailFiles.resolvePath(agentName, gpgPath, EnigmailOS.isDosLike);
}
if (!agentPath) {
return null;
}
return agentPath.QueryInterface(Ci.nsIFile);
},
// resolve the path for GnuPG helper tools
resolveToolPath: function(fileName) {
let filePath = cloneOrNull(EnigmailGpgAgent.agentPath);
if (filePath) {
// try to get the install directory of gpg/gpg2 executable
filePath.normalize();
filePath = filePath.parent;
}
if (filePath) {
filePath.append(EnigmailFiles.potentialWindowsExecutable(fileName));
if (filePath.exists()) {
filePath.normalize();
return filePath;
}
}
return EnigmailFiles.resolvePathWithEnv(fileName);
},
detectGpgAgent: function(domWindow, esvc) {
EnigmailLog.DEBUG("gpgAgent.jsm: detectGpgAgent\n");
var gpgAgentInfo = esvc.environment.get("GPG_AGENT_INFO");
if (gpgAgentInfo && gpgAgentInfo.length > 0) {
EnigmailLog.DEBUG("gpgAgent.jsm: detectGpgAgent: GPG_AGENT_INFO variable available\n");
// env. variable suggests running gpg-agent
EnigmailGpgAgent.gpgAgentInfo.preStarted = true;
EnigmailGpgAgent.gpgAgentInfo.envStr = gpgAgentInfo;
EnigmailGpgAgent.gpgAgentIsOptional = false;
}
else {
EnigmailLog.DEBUG("gpgAgent.jsm: detectGpgAgent: no GPG_AGENT_INFO variable set\n");
EnigmailGpgAgent.gpgAgentInfo.preStarted = false;
if (!getEnigmailGpg().getGpgFeature("supports-gpg-agent")) {
esvc.initializationError = EnigmailLocale.getString("gpgAgent.noAutostart", getEnigmailGpg().agentVersion);
EnigmailLog.ERROR("gpgAgent.jsm: Error - " + esvc.initializationError + "\n");
throw Components.results.NS_ERROR_FAILURE;
}
var command = null;
var outStr = "";
var errorStr = "";
var exitCode = -1;
EnigmailGpgAgent.gpgAgentIsOptional = false;
EnigmailGpgAgent.gpgAgentInfo.envStr = DUMMY_AGENT_INFO;
var envFile = Components.classes[NS_LOCAL_FILE_CONTRACTID].createInstance(Ci.nsIFile);
EnigmailFiles.initPath(envFile, EnigmailGpgAgent.determineGpgHomeDir(esvc));
envFile.append("gpg-agent.conf");
if (!envFile.exists()) {
EnigmailLog.DEBUG("gpgAgent.jsm: detectGpgAgent: writing gpg-agent.conf file\n");
let data = "default-cache-ttl " + (EnigmailPassword.getMaxIdleMinutes() * 60) + "\n";
data += "max-cache-ttl 999999\n";
try {
var flags = 0x02 | 0x08 | 0x20;
var fileOutStream = Cc[NS_LOCALFILEOUTPUTSTREAM_CONTRACTID].createInstance(Ci.nsIFileOutputStream);
fileOutStream.init(envFile, flags, 384, 0); // 0600
fileOutStream.write(data, data.length);
fileOutStream.flush();
fileOutStream.close();
}
catch (ex) {} // ignore file write errors
}
}
EnigmailLog.DEBUG("gpgAgent.jsm: detectGpgAgent: GPG_AGENT_INFO='" + EnigmailGpgAgent.gpgAgentInfo.envStr + "'\n");
},
/**
* Determine the GnuPG home directory based on the same logic as GnuPG, but without involving
* any external tool.
*
* @return String - the path to the gpg home directory
*/
determineGpgHomeDir: function(esvc) {
let param = EnigmailPrefs.getPref("agentAdditionalParam");
if (param) {
let hd = getHomedirFromParam(param);
if (hd) return hd;
}
let homeDir = esvc.environment.get("GNUPGHOME");
if (!homeDir && EnigmailOS.isWin32) {
homeDir = EnigmailOS.getWinRegistryString("Software\\GNU\\GNUPG", "HomeDir", nsIWindowsRegKey.ROOT_KEY_CURRENT_USER);
if (!homeDir) {
homeDir = esvc.environment.get("USERPROFILE") || esvc.environment.get("SystemRoot");
if (homeDir) homeDir += "\\Application Data\\GnuPG";
}
if (!homeDir) homeDir = "C:\\gnupg";
}
if (!homeDir) homeDir = esvc.environment.get("HOME") + "/.gnupg";
return homeDir;
},
/**
* Check if the users directory for GnuPG exists and is writeable.
* Throw exception if directory cannot be created or adjusted.
*/
checkGpgHomeDir: function(domWindow, esvc) {
EnigmailLog.DEBUG("gpgAgent.jsm: checkGpgHomeDir:\n");
let homeDir = EnigmailGpgAgent.getGpgHomeDir();
if (!homeDir) homeDir = EnigmailGpgAgent.determineGpgHomeDir(esvc);
EnigmailLog.DEBUG("gpgAgent.jsm: checkGpgHomeDir: got homedir = '" + homeDir + "'\n");
let homeDirObj = Components.classes[NS_LOCAL_FILE_CONTRACTID].createInstance(Ci.nsIFile);
EnigmailFiles.initPath(homeDirObj, homeDir);
if (homeDirObj.exists()) {
homeDirObj.normalize(); // resolve symlinks etc.
}
let dirType = EnigmailFiles.ensureWritableDirectory(homeDirObj, 0x1C0); // 0700
let errMsg = "";
switch (dirType) {
case 1:
errMsg = "gpghomedir.notexists";
break;
case 2:
errMsg = "gpghomedir.notwritable";
break;
case 3:
errMsg = "gpghomedir.notdirectory";
break;
}
if (errMsg.length > 0) {
if (!domWindow) {
domWindow = EnigmailWindows.getBestParentWin();
}
getDialog().alert(domWindow, EnigmailLocale.getString(errMsg, homeDir) + "\n\n" + EnigmailLocale.getString("gpghomedir.notusable"));
throw Components.results.NS_ERROR_FAILURE;
}
},
finalize: function() {
console.debug("reaching disabled EnigmailGpgAgent.finalize()");
return;
if (EnigmailGpgAgent.gpgAgentProcess) {
EnigmailLog.DEBUG("gpgAgent.jsm: EnigmailGpgAgent.finalize: stopping gpg-agent\n");
try {
const proc = {
command: EnigmailGpgAgent.connGpgAgentPath,
arguments: ['killagent', '/bye'],
environment: EnigmailCore.getEnvList()
};
subprocess.call(proc).wait();
}
catch (ex) {
EnigmailLog.ERROR("gpgAgent.jsm: EnigmailGpgAgent.finalize ERROR: " + ex + "\n");
}
}
}
};

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -10,7 +10,6 @@ var EXPORTED_SYMBOLS = ["EnigmailKeyRing"];
const EnigmailCore = ChromeUtils.import("chrome://openpgp/content/modules/core.jsm").EnigmailCore;
const EnigmailLog = ChromeUtils.import("chrome://openpgp/content/modules/log.jsm").EnigmailLog;
const EnigmailExecution = ChromeUtils.import("chrome://openpgp/content/modules/execution.jsm").EnigmailExecution;
const EnigmailLocale = ChromeUtils.import("chrome://openpgp/content/modules/locale.jsm").EnigmailLocale;
const EnigmailFiles = ChromeUtils.import("chrome://openpgp/content/modules/files.jsm").EnigmailFiles;
const EnigmailGpg = ChromeUtils.import("chrome://openpgp/content/modules/gpg.jsm").EnigmailGpg;
@ -18,7 +17,6 @@ const EnigmailTrust = ChromeUtils.import("chrome://openpgp/content/modules/trust
const EnigmailArmor = ChromeUtils.import("chrome://openpgp/content/modules/armor.jsm").EnigmailArmor;
const EnigmailTime = ChromeUtils.import("chrome://openpgp/content/modules/time.jsm").EnigmailTime;
const EnigmailData = ChromeUtils.import("chrome://openpgp/content/modules/data.jsm").EnigmailData;
//const subprocess = ChromeUtils.import("chrome://openpgp/content/modules/subprocess.jsm").subprocess;
const EnigmailLazy = ChromeUtils.import("chrome://openpgp/content/modules/lazy.jsm").EnigmailLazy;
const newEnigmailKeyObj = ChromeUtils.import("chrome://openpgp/content/modules/keyObj.jsm").newEnigmailKeyObj;
const EnigmailTimer = ChromeUtils.import("chrome://openpgp/content/modules/timer.jsm").EnigmailTimer;
@ -33,7 +31,6 @@ const getKeyUsability = EnigmailLazy.loader("enigmail/keyUsability.jsm", "Enigma
const DEFAULT_FILE_PERMS = 0o600;
let gKeygenProcess = null;
let gKeyListObj = null;
let gKeyIndex = [];
let gSubkeyIndex = [];
@ -421,61 +418,6 @@ var EnigmailKeyRing = {
return "";
}
/*
let args = EnigmailGpg.getStandardArgs(true).concat(["-a", "--export"]);
if (userId) {
args = args.concat(userId.split(/[ ,\t]+/));
}
const cmdErrorMsgObj = {};
let keyBlock = EnigmailExecution.execCmd(EnigmailGpg.agentPath, args, "", exitCodeObj, {}, {}, cmdErrorMsgObj);
if ((exitCodeObj.value === 0) && !keyBlock) {
exitCodeObj.value = -1;
}
if (exitCodeObj.value !== 0) {
errorMsgObj.value = EnigmailLocale.getString("failKeyExtract");
if (cmdErrorMsgObj.value) {
errorMsgObj.value += "\n" + EnigmailFiles.formatCmdLine(EnigmailGpg.agentPath, args);
errorMsgObj.value += "\n" + cmdErrorMsgObj.value;
}
return "";
}
if (includeSecretKey) {
let keyList;
if (userId) {
keyList = userId.split(/[ ,\t]+/);
}
else {
keyList = this.getAllSecretKeys().map(keyObj => {
return keyObj.keyId;
});
}
let secKeyBlock = keyList.map(uid => {
let keyObj = EnigmailKeyRing.getKeyById(uid);
if (keyObj) return keyObj.getSecretKey(false).keyData;
let k = EnigmailKeyRing.getKeysByUserId(uid);
if (k && k.length > 0) {
return k.map(keyObj => {
return keyObj.getSecretKey(false).keyData;
}).join("\n");
}
else {
return "";
}
}).join("\n");
keyBlock += "\n" + secKeyBlock;
}
*/
exitCodeObj.value = 0;
if (outputFile) {
if (!EnigmailFiles.writeFileContents(outputFile, keyBlock, DEFAULT_FILE_PERMS)) {
@ -487,50 +429,6 @@ var EnigmailKeyRing = {
return keyBlock;
},
/**
* Export the ownertrust database from GnuPG
* @param outputFile String or nsIFile - output file name or Object - or NULL
* @param exitCodeObj Object - o.value will contain exit code
* @param errorMsgObj Object - o.value will contain error message from GnuPG
*
* @return String - if outputFile is NULL, the key block data; "" if a file is written
*/
extractOwnerTrust: function(outputFile, exitCodeObj, errorMsgObj) {
let args = EnigmailGpg.getStandardArgs(true).concat(["--export-ownertrust"]);
let trustData = EnigmailExecution.execCmd(EnigmailGpg.agentPath, args, "", exitCodeObj, {}, {}, errorMsgObj);
if (outputFile) {
if (!EnigmailFiles.writeFileContents(outputFile, trustData, DEFAULT_FILE_PERMS)) {
exitCodeObj.value = -1;
errorMsgObj.value = EnigmailLocale.getString("fileWriteFailed", [outputFile]);
}
return "";
}
return trustData;
},
/**
* Import the ownertrust database into GnuPG
* @param inputFile String or nsIFile - input file name or Object - or NULL
* @param errorMsgObj Object - o.value will contain error message from GnuPG
*
* @return exit code
*/
importOwnerTrust: function(inputFile, errorMsgObj) {
let args = EnigmailGpg.getStandardArgs(true).concat(["--import-ownertrust"]);
let exitCodeObj = {};
try {
let trustData = EnigmailFiles.readFile(inputFile);
EnigmailExecution.execCmd(EnigmailGpg.agentPath, args, trustData, exitCodeObj, {}, {}, errorMsgObj);
}
catch (ex) {}
return exitCodeObj.value;
},
/**
* import key from provided key data (synchronous)
*
@ -604,27 +502,6 @@ var EnigmailKeyRing = {
throw "importKeyAsync with minimizeKey: not implemented";
}
/*
let args = EnigmailGpg.getStandardArgs(false).concat(["--no-verbose", "--status-fd", "2"]);
if (minimizeKey) {
args = args.concat(["--import-options", "import-minimal"]);
}
if (limitedUids.length > 0 && EnigmailGpg.getGpgFeature("export-specific-uid")) {
let filter = limitedUids.map(i => {
return `mbox =~ ${i}`;
}).join(" || ");
args.push("--import-filter");
args.push(`keep-uid=${filter}`);
}
args = args.concat(["--no-auto-check-trustdb", "--import"]);
const res = await EnigmailExecution.execAsync(EnigmailGpg.agentPath, args, pgpBlock);
const statusMsg = res.statusMsg;
*/
const cApi = EnigmailCryptoAPI();
let result = cApi.sync(cApi.importKeyBlock(pgpBlock));
@ -637,49 +514,9 @@ var EnigmailKeyRing = {
EnigmailKeyRing.clearCache();
/*
let exitCode = 1;
if (statusMsg && (statusMsg.search(/^IMPORT_RES /m) > -1)) {
let importRes = statusMsg.match(/^IMPORT_RES ([0-9]+) ([0-9]+) ([0-9]+) 0 ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)/m);
if (importRes !== null) {
let secCount = parseInt(importRes[9], 10); // number of secret keys found
let secImported = parseInt(importRes[10], 10); // number of secret keys imported
let secDups = parseInt(importRes[11], 10); // number of secret keys already on the keyring
if (secCount !== secImported + secDups) {
EnigmailKeyRing.clearCache();
errorMsgObj.value = EnigmailLocale.getString("import.secretKeyImportError");
return 1;
}
}
exitCode = 0;
// Normal return
if (statusMsg.search(/^IMPORT_OK /m) > -1) {
let l = statusMsg.split(/\r|\n/);
for (let i = 0; i < l.length; i++) {
const matches = l[i].match(/^(IMPORT_OK [0-9]+ )(([0-9a-fA-F]{8}){2,5})/);
if (matches && (matches.length > 2)) {
EnigmailLog.DEBUG("enigmail.js: Enigmail.importKey: IMPORTED 0x" + matches[2] + "\n");
importedKeysObj.value.push(matches[2]);
}
}
if (importedKeysObj.value.length > 0) {
EnigmailKeyRing.updateKeys(importedKeysObj.value);
}
}
}
*/
return exitCode;
},
isGeneratingKey: function() {
return gKeygenProcess !== null;
},
/**
* Generate a new key pair with GnuPG
*
@ -700,102 +537,7 @@ var EnigmailKeyRing = {
generateKey: function(name, comment, email, expiryDate, keyLength, keyType,
passphrase, listener) {
EnigmailLog.WRITE("keyRing.jsm: generateKey:\n");
if (EnigmailKeyRing.isGeneratingKey()) {
// key generation already ongoing
throw Components.results.NS_ERROR_FAILURE;
}
const args = EnigmailGpg.getStandardArgs(true).concat(["--gen-key"]);
EnigmailLog.CONSOLE(EnigmailFiles.formatCmdLine(EnigmailGpg.agentPath, args));
let inputData = "%echo Generating key\nKey-Type: ";
switch (keyType) {
case "RSA":
inputData += "RSA\nKey-Usage: sign,auth\nKey-Length: " + keyLength;
inputData += "\nSubkey-Type: RSA\nSubkey-Usage: encrypt\nSubkey-Length: " + keyLength + "\n";
break;
case "ECC":
inputData += "EDDSA\nKey-Curve: Ed25519\nKey-Usage: sign\n";
inputData += "Subkey-Type: ECDH\nSubkey-Curve: Curve25519\nSubkey-Usage: encrypt\n";
break;
default:
return null;
}
if (name.replace(/ /g, "").length) {
inputData += "Name-Real: " + name + "\n";
}
if (comment && comment.replace(/ /g, "").length) {
inputData += "Name-Comment: " + comment + "\n";
}
inputData += "Name-Email: " + email + "\n";
inputData += "Expire-Date: " + String(expiryDate) + "\n";
EnigmailLog.CONSOLE(inputData + " \n");
if (passphrase.length) {
inputData += "Passphrase: " + passphrase + "\n";
}
else {
if (EnigmailGpg.getGpgFeature("genkey-no-protection")) {
inputData += "%echo no-protection\n";
inputData += "%no-protection\n";
}
}
inputData += "%commit\n%echo done\n";
let proc = null;
console.debug("reaching disabled keyRing.generateKey");
/*
try {
proc = subprocess.call({
command: EnigmailGpg.agentPath,
arguments: args,
environment: EnigmailCore.getEnvList(),
charset: null,
stdin: function(pipe) {
pipe.write(inputData);
pipe.close();
},
stderr: function(data) {
// extract key ID
if (data.search(/^\[GNUPG:\] KEY_CREATED/m)) {
let m = data.match(/^(\[GNUPG:\] KEY_CREATED [BPS] )([^ \r\n\t]+)$/m);
if (m && m.length > 2) {
listener.keyId = "0x" + m[2];
}
}
listener.onDataAvailable(data);
},
done: function(result) {
gKeygenProcess = null;
try {
if (result.exitCode === 0) {
EnigmailKeyRing.clearCache();
}
listener.onStopRequest(result.exitCode);
}
catch (ex) {}
},
mergeStderr: false
});
}
catch (ex) {
EnigmailLog.ERROR("keyRing.jsm: generateKey: subprocess.call failed with '" + ex.toString() + "'\n");
throw ex;
}
*/
gKeygenProcess = proc;
EnigmailLog.DEBUG("keyRing.jsm: generateKey: subprocess = " + proc + "\n");
return proc;
throw new Error("Not implemented");
},
/**

Просмотреть файл

@ -16,7 +16,6 @@ const EnigmailKeyRing = ChromeUtils.import("chrome://openpgp/content/modules/key
const EnigmailKeyserverURIs = ChromeUtils.import("chrome://openpgp/content/modules/keyserverUris.jsm").EnigmailKeyserverURIs;
const EnigmailData = ChromeUtils.import("chrome://openpgp/content/modules/data.jsm").EnigmailData;
const EnigmailConstants = ChromeUtils.import("chrome://openpgp/content/modules/constants.jsm").EnigmailConstants;
const EnigmailExecution = ChromeUtils.import("chrome://openpgp/content/modules/execution.jsm").EnigmailExecution;
const EnigmailGpg = ChromeUtils.import("chrome://openpgp/content/modules/gpg.jsm").EnigmailGpg;
const EnigmailHttpProxy = ChromeUtils.import("chrome://openpgp/content/modules/httpProxy.jsm").EnigmailHttpProxy;
const EnigmailOS = ChromeUtils.import("chrome://openpgp/content/modules/os.jsm").EnigmailOS;
@ -836,58 +835,24 @@ const accessGnuPG = {
*/
accessKeyServer: function(actionFlag, keyserver, keyId, listener) {
EnigmailLog.DEBUG(`keyserver.jsm: accessGnuPG: accessKeyServer(${keyserver})\n`);
throw new Error("Not implemented");
let processHandle = {
value: null
};
if (listener) {
listener._isCancelled = 0;
listener.onCancel = function() {
EnigmailLog.DEBUG(`keyserver.jsm: accessGnuPG: accessKeyServer: onCancel\n`);
if (processHandle.value) {
processHandle.value.killProcess();
}
this._isCancelled = 1;
};
}
/*
if (keyserver === null) {
keyserver = EnigmailKeyserverURIs.getDefaultKeyServer();
}
let args = EnigmailGpg.getStandardArgs(true);
args.push("--log-file");
args.push(EnigmailOS.isWin32 ? "NUL" : "/dev/null");
args.push("--with-colons");
let cmd = "";
let proxyHost = EnigmailHttpProxy.getHttpProxy();
if (proxyHost) {
args = args.concat(["--keyserver-options", "http-proxy=" + proxyHost]);
}
args.push("--keyserver");
args.push(keyserver);
switch (actionFlag) {
case EnigmailConstants.SEARCH_KEY:
cmd = "--search-keys";
break;
case EnigmailConstants.DOWNLOAD_KEY:
cmd = "--recv-keys";
break;
case EnigmailConstants.UPLOAD_KEY:
cmd = "--send-keys";
break;
}
args.push(cmd);
args = args.concat(keyId.split(/ +/));
return EnigmailExecution.execAsync(EnigmailGpg.agentPath, args, "", processHandle);
*/
},
parseStatusMsg: function(execResult) {

Просмотреть файл

@ -12,7 +12,6 @@ const EnigmailLocale = ChromeUtils.import("chrome://openpgp/content/modules/loca
const EnigmailBuildDate = ChromeUtils.import("chrome://openpgp/content/modules/buildDate.jsm").EnigmailBuildDate;
const EnigmailApp = ChromeUtils.import("chrome://openpgp/content/modules/app.jsm").EnigmailApp;
const EnigmailCore = ChromeUtils.import("chrome://openpgp/content/modules/core.jsm").EnigmailCore;
const EnigmailGpgAgent = ChromeUtils.import("chrome://openpgp/content/modules/gpgAgent.jsm").EnigmailGpgAgent;
const Services = ChromeUtils.import("resource://gre/modules/Services.jsm").Services;
function getEnigmailVersion() {
@ -20,22 +19,6 @@ function getEnigmailVersion() {
return EnigmailLocale.getString("usingVersion", versionStr);
}
function getGpgWorking() {
var enigmailSvc = EnigmailCore.getService();
var agentStr;
if (enigmailSvc) {
agentStr = EnigmailLocale.getString("usingAgent", [EnigmailGpgAgent.agentType, EnigmailGpgAgent.agentPath.path]);
} else {
agentStr = EnigmailLocale.getString("agentError");
if (enigmailSvc && enigmailSvc.initializationError)
agentStr += "\n" + enigmailSvc.initializationError;
}
return agentStr;
}
var EnigmailLocalizeHtml = {
getAllElementsWithAttribute: function(doc, attribute) {
let matchingElements = [];
@ -48,7 +31,6 @@ var EnigmailLocalizeHtml = {
return matchingElements;
},
onPageLoad: function(doc) {
let elem = this.getAllElementsWithAttribute(doc, "txtId");
@ -59,10 +41,10 @@ var EnigmailLocalizeHtml = {
switch (txtId) {
case "FNC_enigmailVersion":
node.innerHTML = getEnigmailVersion();
node.innerHTML = "no version";
break;
case "FNC_isGpgWorking":
node.innerHTML = getGpgWorking();
node.innerHTML = "no agent";
break;
default:
node.innerHTML = EnigmailLocale.getString(txtId, param);

Просмотреть файл

@ -301,7 +301,7 @@ MimeDecryptHandler.prototype = {
return ret;
},
// cache encrypted data for writing to subprocess
// cache encrypted data
cacheData: function(str) {
if (gDebugLogLevel > 4)
LOCAL_DEBUG("mimeDecrypt.jsm: cacheData: " + str.length + "\n");

Просмотреть файл

@ -700,7 +700,6 @@ PgpMimeEncrypt.prototype = {
if (this.exitCode !== 0)
EnigmailDialog.alert(this.win, retStatusObj.errorMsg);
},
};

Просмотреть файл

@ -14,7 +14,6 @@ var EXPORTED_SYMBOLS = ["EnigmailMsgRead"];
const EnigmailPrefs = ChromeUtils.import("chrome://openpgp/content/modules/prefs.jsm").EnigmailPrefs;
const EnigmailApp = ChromeUtils.import("chrome://openpgp/content/modules/app.jsm").EnigmailApp;
const EnigmailVersioning = ChromeUtils.import("chrome://openpgp/content/modules/versioning.jsm").EnigmailVersioning;
const EnigmailKeyRing = ChromeUtils.import("chrome://openpgp/content/modules/keyRing.jsm").EnigmailKeyRing;
const EnigmailFuncs = ChromeUtils.import("chrome://openpgp/content/modules/funcs.jsm").EnigmailFuncs;
const EnigmailAutocrypt = ChromeUtils.import("chrome://openpgp/content/modules/autocrypt.jsm").EnigmailAutocrypt;

Просмотреть файл

@ -10,8 +10,6 @@ const EXPORTED_SYMBOLS = ["EnigmailOS"];
const XPCOM_APPINFO = "@mozilla.org/xre/app-info;1";
// const getExecution = EnigmailLazy.loader("enigmail/execution.jsm", "EnigmailExecution");
let operatingSystem = null;
function getOS() {

Просмотреть файл

@ -1,79 +0,0 @@
/*
* 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 https://mozilla.org/MPL/2.0/.
*/
"use strict";
var EXPORTED_SYMBOLS = ["EnigmailPassword"];
const EnigmailLazy = ChromeUtils.import("chrome://openpgp/content/modules/lazy.jsm").EnigmailLazy;
const EnigmailPrefs = ChromeUtils.import("chrome://openpgp/content/modules/prefs.jsm").EnigmailPrefs;
const EnigmailCore = ChromeUtils.import("chrome://openpgp/content/modules/core.jsm").EnigmailCore;
const subprocess = ChromeUtils.import("chrome://openpgp/content/modules/subprocess.jsm").subprocess;
const gpgAgent = EnigmailLazy.loader("enigmail/gpgAgent.jsm", "EnigmailGpgAgent");
const getDialog = EnigmailLazy.loader("enigmail/dialog.jsm", "EnigmailDialog");
const getLocale = EnigmailLazy.loader("enigmail/locale.jsm", "EnigmailLocale");
var EnigmailPassword = {
/*
* Get GnuPG command line options for receiving the password depending
* on the various user and system settings (gpg-agent/no passphrase)
*
* @return: Array the GnuPG command line options
*/
command: function() {
return ["--use-agent"];
},
getMaxIdleMinutes: function() {
try {
return EnigmailPrefs.getPref("maxIdleMinutes");
}
catch (ex) {}
return 5;
},
clearPassphrase: function(win) {
// clear all passphrases from gpg-agent by reloading the config
if (!EnigmailCore.getService()) return;
let exitCode = -1;
let isError = 0;
const proc = {
command: gpgAgent().connGpgAgentPath,
arguments: [],
charset: null,
environment: EnigmailCore.getEnvList(),
stdin: function(pipe) {
pipe.write("RELOADAGENT\n");
pipe.write("/bye\n");
pipe.close();
},
stdout: function(data) {
if (data.search(/^ERR/m) >= 0) {
++isError;
}
}
};
try {
exitCode = subprocess.call(proc).wait();
}
catch (ex) {}
if (isError === 0) {
getDialog().alert(win, getLocale().getString("passphraseCleared"));
}
else {
getDialog().alert(win, getLocale().getString("cannotClearPassphrase"));
}
}
};

Просмотреть файл

@ -13,7 +13,6 @@ const EnigmailLazy = ChromeUtils.import("chrome://openpgp/content/modules/lazy.j
const EnigmailLog = ChromeUtils.import("chrome://openpgp/content/modules/log.jsm").EnigmailLog;
const EnigmailArmor = ChromeUtils.import("chrome://openpgp/content/modules/armor.jsm").EnigmailArmor;
const EnigmailLocale = ChromeUtils.import("chrome://openpgp/content/modules/locale.jsm").EnigmailLocale;
const EnigmailExecution = ChromeUtils.import("chrome://openpgp/content/modules/execution.jsm").EnigmailExecution;
const GlodaUtils = ChromeUtils.import("chrome://openpgp/content/modules/glodaUtils.jsm").GlodaUtils;
const EnigmailCompat = ChromeUtils.import("chrome://openpgp/content/modules/compat.jsm").EnigmailCompat;
const EnigmailCore = ChromeUtils.import("chrome://openpgp/content/modules/core.jsm").EnigmailCore;
@ -30,7 +29,6 @@ const EnigmailEncryption = ChromeUtils.import("chrome://openpgp/content/modules/
const getFixExchangeMsg = EnigmailLazy.loader("enigmail/fixExchangeMsg.jsm", "EnigmailFixExchangeMsg");
const getDecryption = EnigmailLazy.loader("enigmail/decryption.jsm", "EnigmailDecryption");
const getDialog = EnigmailLazy.loader("enigmail/dialog.jsm", "EnigmailDialog");
const getGpgAgent = EnigmailLazy.loader("enigmail/gpgAgent.jsm", "EnigmailGpgAgent");
const STATUS_OK = 0;
const STATUS_FAILURE = 1;
@ -433,8 +431,10 @@ CryptMessageIntoFolder.prototype = {
},
decryptAttachment: function(mimePart) {
EnigmailLog.DEBUG("persistentCrypto.jsm: decryptAttachment()\n");
throw new Error("Not implemented");
/*
let attachmentHead = mimePart.body.substr(0, 30);
if (attachmentHead.search(/-----BEGIN PGP \w{5,10} KEY BLOCK-----/) >= 0) {
// attachment appears to be a PGP key file, we just go-a-head
@ -444,62 +444,6 @@ CryptMessageIntoFolder.prototype = {
let attachmentName = getAttachmentName(mimePart);
attachmentName = attachmentName ? attachmentName.replace(/\.(pgp|asc|gpg)$/, "") : "";
let args = EnigmailGpg.getStandardArgs(true);
args.push("-d");
let statusMsgObj = {};
let cmdLineObj = {};
let exitCode = -1;
let statusFlagsObj = {
value: 0
};
let errorMsgObj = {};
let listener = EnigmailExecution.newSimpleListener((pipe) => {
pipe.write(mimePart.body);
pipe.close();
});
do {
// loop to allow for multiple tries of the passphrase
let proc = EnigmailExecution.execStart(getGpgAgent().agentPath, args, false, null, listener, statusFlagsObj);
if (!proc) {
return;
}
// Wait for child STDOUT to close
proc.wait();
EnigmailExecution.execEnd(listener, statusFlagsObj, statusMsgObj, cmdLineObj, errorMsgObj);
if ((listener.stdoutData && listener.stdoutData.length > 0) ||
(statusFlagsObj.value & EnigmailConstants.DECRYPTION_OKAY)) {
EnigmailLog.DEBUG("persistentCrypto.jsm: decryptAttachment: decryption OK\n");
exitCode = 0;
} else if (statusFlagsObj.value & (EnigmailConstants.DECRYPTION_FAILED | EnigmailConstants.MISSING_MDC)) {
EnigmailLog.DEBUG("persistentCrypto.jsm: decryptAttachment: decryption without MDC protection\n");
exitCode = 0;
} else if (statusFlagsObj.value & EnigmailConstants.DECRYPTION_FAILED) {
EnigmailLog.DEBUG("persistentCrypto.jsm: decryptAttachment: decryption failed\n");
// since we cannot find out if the user wants to cancel
// we should ask
let msg = EnigmailLocale.getString("converter.decryptAtt.failed", [attachmentName, this.subject]);
if (!getDialog().confirmDlg(null, msg,
EnigmailLocale.getString("dlg.button.retry"), EnigmailLocale.getString("dlg.button.skip"))) {
return;
}
} else if (statusFlagsObj.value & EnigmailConstants.DECRYPTION_INCOMPLETE) {
// failure; message not complete
EnigmailLog.DEBUG("persistentCrypto.jsm: decryptAttachment: decryption incomplete\n");
return;
} else {
// there is nothing to be decrypted
EnigmailLog.DEBUG("persistentCrypto.jsm: decryptAttachment: no decryption required\n");
return;
}
} while (exitCode !== 0);
EnigmailLog.DEBUG("persistentCrypto.jsm: decryptAttachment: decrypted to " + listener.stdoutData.length + " bytes\n");
if (statusFlagsObj.encryptedFileName && statusFlagsObj.encryptedFileName.length > 0) {
attachmentName = statusFlagsObj.encryptedFileName;
@ -521,6 +465,7 @@ CryptMessageIntoFolder.prototype = {
}
mimePart.headers._rawHeaders.set("content-type", [ct]);
*/
},

Просмотреть файл

@ -1,390 +0,0 @@
/* 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 https://mozilla.org/MPL/2.0/. */
/*
* Import into a JS component using
* 'ChromeUtils.import("chrome://openpgp/content/modules/subprocess.jsm");'
*
* This object allows to start a process, and read/write data to/from it
* using stdin/stdout/stderr streams.
* Usage example:
*
* var p = subprocess.call({
* command: '/bin/foo',
* arguments: ['-v', 'foo'],
* environment: [ "XYZ=abc", "MYVAR=def" ],
* //stdin: "some value to write to stdin\nfoobar",
* stdin: function(stdin) {
* stdin.write("some value to write to stdin\nfoobar");
* stdin.close();
* },
* stdout: function(data) {
* dump("got data on stdout:" + data + "\n");
* },
* stderr: function(data) {
* dump("got data on stderr:" + data + "\n");
* },
* done: function(result) {
* dump("process terminated with " + result.exitCode + "\n");
* },
* mergeStderr: false
* });
* p.wait(); // wait for the subprocess to terminate
* // this will block the main thread,
* // only do if you can wait that long
*
*
* Description of parameters:
* --------------------------
* Apart from <command>, all arguments are optional.
*
* command: either a |nsIFile| object pointing to an executable file or a
* String containing the platform-dependent path to an executable
* file.
*
* arguments: optional string array containing the arguments to the command.
*
* environment: optional string array containing environment variables to pass
* to the command. The array elements must have the form
* "VAR=data". Please note that if environment is defined, it
* replaces any existing environment variables for the subprocess.
*
* stdin: optional input data for the process to be passed on standard
* input. stdin can either be a string or a function.
* A |string| gets written to stdin and stdin gets closed;
* A |function| gets passed an object with write and close function.
* Please note that the write() function will return almost immediately;
* data is always written asynchronously on a separate thread.
*
* stdout: an optional function that can receive output data from the
* process. The stdout-function is called asynchronously; it can be
* called mutliple times during the execution of a process.
* At a minimum at each occurance of \n or \r.
* Please note that null-characters might need to be escaped
* with something like 'data.replace(/\0/g, "\\0");'.
*
* stderr: an optional function that can receive stderr data from the
* process. The stderr-function is called asynchronously; it can be
* called mutliple times during the execution of a process. Please
* note that null-characters might need to be escaped with
* something like 'data.replace(/\0/g, "\\0");'.
* (on windows it only gets called once right now)
*
*
* done: optional function that is called when the process has terminated.
* The exit code from the process available via result.exitCode. If
* stdout is not defined, then the output from stdout is available
* via result.stdout. stderr data is in result.stderr
* done() is guaranteed to be called before .wait() finishes
*
* mergeStderr: optional boolean value. If true, stderr is merged with stdout;
* no data will be provided to stderr. Default is false.
*
*
* Description of object returned by subprocess.call(...)
* ------------------------------------------------------
* The object returned by subprocess.call offers a few methods that can be
* executed:
*
* wait(): waits for the subprocess to terminate. It is not required to use
* wait; done will be called in any case when the subprocess terminated.
*
* kill(hardKill): kill the subprocess. Any open pipes will be closed and
* done will be called.
* hardKill [ignored on Windows]:
* - false: signal the process terminate (SIGTERM)
* - true: kill the process (SIGKILL)
*
*
* Other methods in subprocess
* ---------------------------
*
* registerDebugHandler(functionRef): register a handler that is called to get
* debugging information
* registerLogHandler(functionRef): register a handler that is called to get error
* messages
*
* example:
* subprocess.registerLogHandler( function(s) { dump(s); } );
*/
'use strict';
const SubprocessMain = ChromeUtils.import("chrome://openpgp/content/modules/enigmailprocess_main.jsm").SubprocessMain;
const Services = ChromeUtils.import("resource://gre/modules/Services.jsm").Services;
var EXPORTED_SYMBOLS = ["subprocess"];
const DEFAULT_ENVIRONMENT = [];
var gDebugFunction = null;
var gErrorFunction = null;
var gRunningProcesses = []; // Array with all running subprocesses
function write(pipe, data) {
let buffer = new Uint8Array(Array.from(data, c => c.charCodeAt(0)));
return pipe.write(buffer);
}
function arrayBufferToString(buffer) {
const MAXLEN = 102400;
let uArr = new Uint8Array(buffer);
let ret = "";
let len = buffer.byteLength;
for (let j = 0; j < Math.floor(len / MAXLEN) + 1; j++) {
ret += String.fromCharCode.apply(null, uArr.subarray(j * MAXLEN, ((j + 1) * MAXLEN)));
}
return ret;
}
async function read(pipe) {
let buffer = await pipe.read();
try {
if (buffer.byteLength > 0) {
return arrayBufferToString(buffer);
}
} catch (ex) {
DEBUG_LOG("err: " + ex.toString());
}
return "";
}
var readAllData = async function(pipe, read, callback) {
/* eslint no-cond-assign: 0 */
let string;
while (string = await read(pipe)) {
callback(string);
}
};
function removeProcRef(proc) {
if (proc) {
let i = gRunningProcesses.indexOf(proc);
if (i >= 0) {
gRunningProcesses.splice(i, 1);
}
}
}
var subprocess = {
registerLogHandler: function(func) {
gErrorFunction = func;
},
registerDebugHandler: function(func) {
gDebugFunction = func;
},
call: function(options) {
let resolved = null;
let promises = [];
let inputPromises = [];
let stdinClosed = false;
let stdoutData = "";
let stderrData = "";
let formattedStack = Components.stack.formattedStack;
function writePipe(pipe, value) {
let p = write(pipe, value);
promises.push(p);
inputPromises.push(p);
}
function subProcessThen(proc) {
gRunningProcesses.push(proc);
if (typeof options.stdin === "function") {
// Some callers (e.g. child_process.js) depend on this
// being called synchronously.
options.stdin({
write(val) {
writePipe(proc.stdin, val);
},
close() {
Promise.all(inputPromises).then(() => {
if (!stdinClosed) {
stdinClosed = true;
proc.stdin.close();
}
});
}
});
} else {
if (typeof options.stdin === "string") {
DEBUG_LOG("write Stdin");
writePipe(proc.stdin, options.stdin);
}
Promise.all(inputPromises).then(() => {
proc.stdin.close();
});
}
promises.push(
readAllData(proc.stdout, read, data => {
DEBUG_LOG("Got Stdout: " + data.length + "\n");
if (typeof options.stdout === "function") {
try {
options.stdout(data);
} catch (ex) {}
} else
stdoutData += data;
}));
if (!options.mergeStderr) {
promises.push(
readAllData(proc.stderr, read, data => {
DEBUG_LOG("Got Stderr: " + data.length + "\n");
if (typeof options.stderr === "function") {
try {
options.stderr(data);
} catch (ex) {}
} else
stderrData += data;
}));
}
Promise.all(promises)
.then(() => proc.wait())
.then(result => {
DEBUG_LOG("Complete: " + result.exitCode + "\n");
removeProcRef(proc);
if (gRunningProcesses.indexOf(proc) >= 0) {
}
if (result.exitCode === null) result.exitCode = -1;
resolved = result.exitCode;
if (typeof options.done === "function") {
try {
options.done({
exitCode: result.exitCode,
stdout: stdoutData,
stderr: stderrData
});
} catch (ex) {}
}
})
.catch(error => {
resolved = -1;
let errStr = "";
if (typeof error === "string") {
errStr = error;
} else if (error) {
for (let i in error) {
errStr += "\n" + i + ": " + error[i];
}
}
ERROR_LOG(errStr);
throw ("subprocess.jsm: caught error: " + errStr);
});
}
let opts = {};
if (options.mergeStderr) {
opts.stderr = "stdout";
} else {
opts.stderr = "pipe";
}
if (options.command instanceof Ci.nsIFile) {
opts.command = options.command.path;
} else {
opts.command = options.command;
}
if (options.workdir) {
opts.workdir = options.workdir;
}
opts.arguments = options.arguments || [];
// Set up environment
let envVars = options.environment || DEFAULT_ENVIRONMENT;
if (envVars.length) {
let environment = {};
for (let val of envVars) {
let idx = val.indexOf("=");
if (idx >= 0)
environment[val.slice(0, idx)] = val.slice(idx + 1);
}
opts.environment = environment;
}
let subproc = SubprocessMain.call(opts).then(subProcessThen).catch(
error => {
resolved = -1;
let errStr = formattedStack;
throw ("subprocess.jsm: launch error: " + errStr + 'error: ' +
error + "\n" + JSON.stringify(error));
}
);
return {
wait: function() {
let mainThread = Services.tm.mainThread;
try {
while (resolved === null) {
mainThread.processNextEvent(true);
}
} catch(ex) {
console.log(ex);
}
return resolved;
},
kill: function(hard = false) {
subproc.then(proc => {
proc.kill(hard ? 0 : undefined);
removeProcRef();
});
}
};
},
/**
* on shutdown kill all still running child processes
*/
onShutdown: function() {
// create a copy of the array because gRunningProcesses will
// get altered during kill()
let procs = gRunningProcesses.map(x => x);
for (let i = 0; i < procs.length; i++) {
if (procs[i] && ("kill" in procs[i])) {
procs[i].kill(true);
}
}
}
};
function DEBUG_LOG(str) {
if (gDebugFunction) {
gDebugFunction("subprocess.jsm: " + str + "\n");
}
}
function ERROR_LOG(str) {
if (gErrorFunction) {
gErrorFunction("subprocess.jsm: " + str + "\n");
}
}

Просмотреть файл

@ -1,288 +0,0 @@
/* 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 https://mozilla.org/MPL/2.0/. */
"use strict";
var EXPORTED_SYMBOLS = ["EnigmailSystem"];
const ctypes = ChromeUtils.import("resource://gre/modules/ctypes.jsm").ctypes;
const EnigmailOS = ChromeUtils.import("chrome://openpgp/content/modules/os.jsm").EnigmailOS;
const EnigmailData = ChromeUtils.import("chrome://openpgp/content/modules/data.jsm").EnigmailData;
const subprocess = ChromeUtils.import("chrome://openpgp/content/modules/subprocess.jsm").subprocess;
const EnigmailLog = ChromeUtils.import("chrome://openpgp/content/modules/log.jsm").EnigmailLog;
const EnigmailPrefs = ChromeUtils.import("chrome://openpgp/content/modules/prefs.jsm").EnigmailPrefs;
var gKernel32Dll = null;
var gSystemCharset = null;
const CODEPAGE_MAPPING = {
"437": "ISO-8859-1",
"855": "IBM855",
"866": "IBM866",
"874": "ISO-8859-11",
"932": "Shift_JIS",
"936": "GB2312",
"950": "BIG5",
"1200": "UTF-16LE",
"1201": "UTF-16BE",
"1250": "windows-1250",
"1251": "windows-1251",
"1252": "windows-1252",
"1253": "windows-1253",
"1254": "windows-1254",
"1255": "windows-1255",
"1256": "windows-1256",
"1257": "windows-1257",
"1258": "windows-1258",
"20866": "KOI8-R",
"20932": "EUC-JP",
"28591": "ISO-8859-1",
"28592": "ISO-8859-2",
"28593": "ISO-8859-3",
"28594": "ISO-8859-4",
"28595": "ISO-8859-5",
"28596": "ISO-8859-6",
"28597": "ISO-8859-7",
"28598": "ISO-8859-8",
"28599": "ISO-8859-9",
"28603": "ISO-8859-13",
"28605": "ISO-8859-15",
"38598": "ISO-8859-8",
"50220": "ISO-2022-JP",
"50221": "ISO-2022-JP",
"50222": "ISO-2022-JP",
"50225": "ISO-2022-KR",
"50227": "ISO-2022-CN",
"50229": "ISO-2022-CN",
"51932": "EUC-JP",
"51949": "EUC-KR",
"52936": "HZ-GB2312",
"65000": "UTF-7",
"65001": "UTF-8"
};
/**
* Get the default codepage that is set on Windows (which equals to the chatset of the console output of gpg)
*/
function getWindowsCopdepage() {
EnigmailLog.DEBUG("system.jsm: getWindowsCopdepage\n");
if (EnigmailPrefs.getPref("gpgLocaleEn")) {
return "437";
}
let output = "";
let env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
let sysRoot = env.get("SystemRoot");
if (!sysRoot || sysRoot.length === 0) {
sysRoot = "C:\\windows";
}
try {
let p = subprocess.call({
command: sysRoot + "\\system32\\chcp.com",
arguments: [],
environment: [],
charset: null,
mergeStderr: false,
stdout: function(data) {
output += data;
}
});
p.wait();
output = output.replace(/[\r\n]/g, "");
output = output.replace(/^(.*[: ])([0-9]+)([^0-9].*)?$/, "$2");
}
catch (ex) {
output = "437";
}
return output;
}
/**
* Get the charset defined with LC_ALL or locale. That's the charset used by gpg console output
*/
function getUnixCharset() {
EnigmailLog.DEBUG("system.jsm: getUnixCharset\n");
let env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
let lc = env.get("LC_ALL");
if (lc.length === 0) {
let places = [
"/usr/bin/locale",
"/usr/local/bin/locale",
"/opt/bin/locale"
];
var localeFile = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
for (let i = 0; i < places.length; i++) {
localeFile.initWithPath(places[i]);
if (localeFile.exists()) break;
}
if (!localeFile.exists()) return "utf-8";
let output = "";
let p = subprocess.call({
command: localeFile,
arguments: [],
environment: [],
charset: null,
mergeStderr: false,
stdout: function(data) {
output += data;
}
});
p.wait();
let m = output.match(/^(LC_ALL=)(.*)$/m);
if (m && m.length > 2) {
lc = m[2].replace(/"/g, "");
}
else return "utf-8";
}
let i = lc.search(/[.@]/);
if (i < 0) return "utf-8";
lc = lc.substr(i + 1);
return lc;
}
function getKernel32Dll() {
if (!gKernel32Dll) {
if (EnigmailOS.isWin32) {
gKernel32Dll = ctypes.open("kernel32.dll");
}
else {
return null;
}
}
return gKernel32Dll;
}
var EnigmailSystem = {
determineSystemCharset: function() {
EnigmailLog.DEBUG("system.jsm: determineSystemCharset\n");
if (!gSystemCharset) {
if (EnigmailOS.isWin32) {
gSystemCharset = getWindowsCopdepage();
}
else {
gSystemCharset = getUnixCharset();
}
}
EnigmailLog.DEBUG("system.jsm: determineSystemCharset: charset='" + gSystemCharset + "'\n");
return gSystemCharset;
},
/**
* Convert system output coming in a native charset into Unicode (Gecko-platfrom)
* applying an appropriate charset conversion
*
* @param str String - input string in native charset
* @param cs String - [Optional] character set (Unix), or codepage (Windows).
* If not specified, determine the system default.
*
* @param String - output in Unicode format. If something failed, the unmodified
* input isreturned.
*/
convertNativeToUnicode: function(str, cs) {
try {
if (!cs) cs = this.determineSystemCharset();
if (EnigmailOS.isWin32) {
if (cs in CODEPAGE_MAPPING) {
return EnigmailData.convertToUnicode(str, CODEPAGE_MAPPING[cs]);
}
else {
let charSetNum = Number(cs);
if (Number.isNaN(charSetNum)) {
return EnigmailData.convertToUnicode(str, cs);
}
else
return EnigmailData.convertToUnicode(this.winConvertNativeToUnichar(str, Number(cs)), "UTF-8");
}
}
else {
return EnigmailData.convertToUnicode(str, cs);
}
}
catch (ex) {
EnigmailLog.DEBUG("system.jsm: convertNativeToUnicode: exception +" + ex.toString() + "\n");
return str;
}
},
/**
* Convert from native Windows output (often Codepage 437) to a Mozilla Unichar string
*
* @param byteStr: String - the data to convert in the current Windows codepage
*
* @return String: the Unicode string directly display-able
*/
winConvertNativeToUnichar: function(byteStr, codePage) {
/*
int MultiByteToWideChar(
_In_ UINT CodePage,
_In_ DWORD dwFlags,
_In_ LPCSTR lpMultiByteStr,
_In_ int cbMultiByte,
_Out_opt_ LPWSTR lpWideCharStr,
_In_ int cchWideChar
);
*/
if (!getKernel32Dll()) {
return byteStr;
}
var multiByteToWideChar = gKernel32Dll.declare("MultiByteToWideChar",
ctypes.winapi_abi,
ctypes.int, // return value
ctypes.unsigned_int, // Codepage
ctypes.uint32_t, // dwFlags
ctypes.char.ptr, // input string
ctypes.int, // cbMultiByte
ctypes.jschar.ptr, // widechar string
ctypes.int // ccWideChar
);
let n = multiByteToWideChar(codePage, 0, byteStr, byteStr.length, null, 0);
if (n > 0) {
let OutStrType = ctypes.jschar.array(n + 1);
let outStr = new OutStrType();
multiByteToWideChar(codePage, 0, byteStr, byteStr.length, outStr.addressOfElement(0), n);
let r = new RegExp(String.fromCharCode(9516), "g");
return outStr.readString().replace(r, "");
}
else
return byteStr;
}
};

Просмотреть файл

@ -1,280 +0,0 @@
/*
* 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 https://mozilla.org/MPL/2.0/.
*/
"use strict";
const EnigmailLog = ChromeUtils.import("chrome://openpgp/content/modules/log.jsm").EnigmailLog;
const EnigmailPrefs = ChromeUtils.import("chrome://openpgp/content/modules/prefs.jsm").EnigmailPrefs;
const EnigmailRNG = ChromeUtils.import("chrome://openpgp/content/modules/rng.jsm").EnigmailRNG;
const EnigmailVersioning = ChromeUtils.import("chrome://openpgp/content/modules/versioning.jsm").EnigmailVersioning;
const EnigmailOS = ChromeUtils.import("chrome://openpgp/content/modules/os.jsm").EnigmailOS;
const EnigmailSocks5Proxy = ChromeUtils.import("chrome://openpgp/content/modules/socks5Proxy.jsm").EnigmailSocks5Proxy;
const EnigmailGpg = ChromeUtils.import("chrome://openpgp/content/modules/gpg.jsm").EnigmailGpg;
const EnigmailFiles = ChromeUtils.import("chrome://openpgp/content/modules/files.jsm").EnigmailFiles;
const EnigmailConstants = ChromeUtils.import("chrome://openpgp/content/modules/constants.jsm").EnigmailConstants;
const EXPORTED_SYMBOLS = ["EnigmailTor"];
// Minimum for using socks5h:// prefix
const MINIMUM_CURL_SOCKS5H_VERSION = "7.21.7";
// Minimum for using socks5 proxies with curl
const MINIMUM_CURL_SOCKS5_PROXY_VERSION = "7.18.0";
const TORSOCKS_VERSION_2 = "2.0.0";
const TOR_SERVICE_PORT_PREF = "torServicePort";
const TOR_BROWSER_BUNDLE_PORT_PREF = "torBrowserBundlePort";
const NEW_CURL_PROTOCOL = "socks5h://";
const OLD_CURL_PROTOCOL = "socks5-hostname://";
const TOR_USER_PREFERENCES = {
DOWNLOAD: {
requires: "downloadKeyRequireTor",
uses: "downloadKeyWithTor",
constant: EnigmailConstants.DOWNLOAD_KEY
},
SEARCH: {
requires: "searchKeyRequireTor",
uses: "searchKeyWithTor",
constant: EnigmailConstants.SEARCH_KEY
},
UPLOAD: {
requires: "uploadKeyRequireTor",
uses: "uploadKeyWithTor",
constant: EnigmailConstants.UPLOAD_KEY
},
REFRESH: {
requires: "refreshAllKeysRequireTor",
uses: "refreshAllKeysWithTor",
constant: EnigmailConstants.REFRESH_KEY
}
};
function getAction(actionFlags) {
for (let key in TOR_USER_PREFERENCES) {
if (TOR_USER_PREFERENCES[key].constant & actionFlags) {
return TOR_USER_PREFERENCES[key];
}
}
return null;
}
/**
* Sets user preference about requiring requests only to be made over Tor
*
* @param actionFlags - long: A Keyserver action flag
*
* @return true if user has requested gpg requests to be attempted over Tor, false otherwise
*/
function isPreferred(actionFlags) {
const action = getAction(actionFlags);
return EnigmailPrefs.getPref(action.requires) || EnigmailPrefs.getPref(action.uses);
}
/**
* Sets user preference about requiring requests only to be made over Tor
*
* @param actionFlags - long: A Keyserver action flag
*
* @return true if user has requested gpg requests ONLY to be attempted over Tor, false otherwise
*/
function isRequired(actionFlags) {
return EnigmailPrefs.getPref(getAction(actionFlags).requires);
}
function combineIntoProxyhostURI(protocol, tor) {
EnigmailLog.DEBUG("tor.jsm: combineIntoProxyhostURI()\n");
return protocol + createRandomCredential() + ":" + createRandomCredential() + "@" + tor.ip + ":" + tor.port;
}
function gpgProxyArgs(tor, versioning) {
EnigmailLog.DEBUG("tor.jsm: gpgProxyArgs()\n");
if (EnigmailOS.isDosLike || !versioning.versionFoundMeetsMinimumVersionRequired("curl", MINIMUM_CURL_SOCKS5H_VERSION)) {
return combineIntoProxyhostURI(OLD_CURL_PROTOCOL, tor);
}
else {
return combineIntoProxyhostURI(NEW_CURL_PROTOCOL, tor);
}
}
function createHelperArgs(helper, addAuth) {
EnigmailLog.DEBUG("tor.jsm: createHelperArgs()\n");
let args = [];
if (addAuth) {
args = ["--user", createRandomCredential(), "--pass", createRandomCredential()];
}
args.push(EnigmailGpg.agentPath);
return args;
}
function buildEnvVars() {
return [
"TORSOCKS_USERNAME=" + createRandomCredential(),
"TORSOCKS_PASSWORD=" + createRandomCredential()
];
}
function createRandomCredential() {
return EnigmailRNG.generateRandomUint32().toString();
}
function torOn(portPref) {
EnigmailLog.DEBUG("tor.jsm: torOn()\n");
if (EnigmailSocks5Proxy.checkTorExists(portPref)) {
const port = EnigmailPrefs.getPref(portPref);
EnigmailLog.CONSOLE("Tor found on IP: " + EnigmailSocks5Proxy.torIpAddr() + ", port: " + port + "\n\n");
return {
ip: EnigmailSocks5Proxy.torIpAddr(),
port: port
};
}
return null;
}
function meetsOSConstraints() {
if (EnigmailOS.isDosLike) {
return EnigmailGpg.getGpgFeature("socks-on-windows");
}
else {
return EnigmailVersioning.versionFoundMeetsMinimumVersionRequired("curl", MINIMUM_CURL_SOCKS5_PROXY_VERSION);
}
}
function useAuthOverArgs(helper, versioning) {
if (helper === "torsocks2") {
return versioning.versionFoundMeetsMinimumVersionRequired("torsocks2", TORSOCKS_VERSION_2);
}
return versioning.versionFoundMeetsMinimumVersionRequired("torsocks", TORSOCKS_VERSION_2);
}
function usesDirmngr() {
return EnigmailGpg.getGpgFeature("supports-dirmngr");
}
function findTorExecutableHelper(versioning) {
EnigmailLog.DEBUG("tor.jsm: findTorExecutableHelper()\n");
const helper = EnigmailFiles.resolvePathWithEnv("torsocks2") || EnigmailFiles.resolvePathWithEnv("torsocks");
if (helper !== null) {
const authOverArgs = useAuthOverArgs(helper, versioning);
return {
envVars: (authOverArgs ? [] : buildEnvVars()),
command: helper,
args: createHelperArgs(helper, authOverArgs)
};
}
else {
return null;
}
}
/**
* Checks if Tor is running on specified ports in preferences for Tor browser bundle and Tor service
*
* @return true if Tor is running on either port, false if Tor is not running on either
*/
function findTor() {
EnigmailLog.DEBUG("tor.jsm: findTor()\n");
const torOnBrowser = torOn(TOR_BROWSER_BUNDLE_PORT_PREF);
if (torOnBrowser !== null) {
return torOnBrowser;
}
return torOn(TOR_SERVICE_PORT_PREF);
}
const systemCaller = {
findTor: findTor,
findTorExecutableHelper: findTorExecutableHelper
};
function buildSocksProperties(tor) {
EnigmailLog.DEBUG("tor.jsm: buildSocksProperties()\n");
return {
command: "gpg",
args: gpgProxyArgs(tor, EnigmailVersioning),
envVars: []
};
}
function torNotAvailableProperties() {
EnigmailLog.DEBUG("tor.jsm: torNotAvailableProperties()\n");
return {
isAvailable: false,
useTorMode: false,
socks: null,
helper: null
};
}
/**
* Constructs object with properites about how we will use tor for key refreshes
*
* @param system - object with functions to locate Tor and Tor helpers
*
* @return object with
* isAvailable - boolean, true if Tor is available, false otherwise
* useTorMode - boolean, true if dirManager is available and configured to use Tor, false otherwise
* socks - object with
* command - the name of the gpg executable
* args - proxy host URI
* envVars - an empty array
null if Tor is not available
* helper - object with
* envVars - environment variables, if we need them for the helper
* command - the path to the helper executable
* args - flags used with the helper, if we do not use environment variables
If no helper is found, return null
*/
function torProperties(system) {
EnigmailLog.DEBUG("tor.jsm: torProperties()\n");
const tor = system.findTor();
if (!meetsOSConstraints()) {
EnigmailLog.DEBUG("tor.jsm: this version of curl does not support socks5 proxies \n");
return torNotAvailableProperties();
}
if (tor === null) {
return torNotAvailableProperties();
}
const helper = system.findTorExecutableHelper(EnigmailVersioning);
let socks = null;
let useTorMode = false;
if (usesDirmngr()) {
useTorMode = EnigmailGpg.dirmngrConfiguredWithTor();
}
else {
socks = buildSocksProperties(tor);
}
return {
isAvailable: true,
useTorMode: useTorMode,
socks: socks,
helper: helper
};
}
var EnigmailTor = {
torProperties: function() {
return torProperties(systemCaller);
},
getTorNotAvailableProperties: torNotAvailableProperties,
isPreferred: isPreferred,
isRequired: isRequired
};

Просмотреть файл

@ -9,12 +9,7 @@
const EXPORTED_SYMBOLS = ["EnigmailVersioning"];
const EnigmailLog = ChromeUtils.import("chrome://openpgp/content/modules/log.jsm").EnigmailLog;
const EnigmailExecution = ChromeUtils.import("chrome://openpgp/content/modules/execution.jsm").EnigmailExecution;
let vc = null;
@ -47,29 +42,6 @@ function getVersion(output, executable) {
}
}
/**
* Test the version number of any application (not gpg)
*/
function versionFoundMeetsMinimumVersionRequired(executable, minimumVersion) {
const args = ["--version"];
const exitCodeObj = {
value: null
};
const output = EnigmailExecution.resolveAndSimpleExec(executable, args, exitCodeObj, {});
if (!output || exitCodeObj.value < 0) {
EnigmailLog.DEBUG("executable not found: " + executable + "\n");
return false;
}
const version = getVersion(output, executable);
if (!version) {
EnigmailLog.DEBUG("couldn't find a version in the output from " + executable + " - total output: " + output + "\n");
return false;
}
return greaterThanOrEqual(version, minimumVersion);
}
function greaterThanOrEqual(versionWeHave, versionWeAreComparingWith) {
return getVersionComparator().compare(versionWeHave, versionWeAreComparingWith) >= 0;
}
@ -113,16 +85,4 @@ var EnigmailVersioning = {
* @return Boolean - The result of versionWeHave < versionWeAreComparingWith
*/
lessThan: lessThan,
/**
* Uses Mozilla's Version Comparator Component to identify whether an executable version
* meets the required version specified
*
* @param String executable - version of the executable
* @param String minimumVersion - version we want to compare with
*
* @return Boolean - True if the executable version meets the minimum version required,
* false if it does not or it does not exist, or if a version was not
* parseable from its output
*/
versionFoundMeetsMinimumVersionRequired: versionFoundMeetsMinimumVersionRequired
};

Просмотреть файл

@ -15,16 +15,10 @@
var EXPORTED_SYMBOLS = ["EnigmailWks"];
const EnigmailFiles = ChromeUtils.import("chrome://openpgp/content/modules/files.jsm").EnigmailFiles;
const EnigmailLog = ChromeUtils.import("chrome://openpgp/content/modules/log.jsm").EnigmailLog;
const EnigmailCore = ChromeUtils.import("chrome://openpgp/content/modules/core.jsm").EnigmailCore;
const EnigmailExecution = ChromeUtils.import("chrome://openpgp/content/modules/execution.jsm").EnigmailExecution;
const EnigmailGpgAgent = ChromeUtils.import("chrome://openpgp/content/modules/gpgAgent.jsm").EnigmailGpgAgent;
const EnigmailStdlib = ChromeUtils.import("chrome://openpgp/content/modules/stdlib.jsm").EnigmailStdlib;
const EnigmailSend = ChromeUtils.import("chrome://openpgp/content/modules/send.jsm").EnigmailSend;
const EnigmailMimeEncrypt = ChromeUtils.import("chrome://openpgp/content/modules/mimeEncrypt.jsm").EnigmailMimeEncrypt;
const EnigmailConstants = ChromeUtils.import("chrome://openpgp/content/modules/constants.jsm").EnigmailConstants;
const EnigmailFuncs = ChromeUtils.import("chrome://openpgp/content/modules/funcs.jsm").EnigmailFuncs;
@ -45,59 +39,7 @@ var EnigmailWks = {
*/
getWksClientPathAsync: function(window, cb) {
EnigmailLog.DEBUG("webKey.jsm: getWksClientPathAsync\n");
if (EnigmailWks.wksClientPath === null) {
let listener = EnigmailExecution.newSimpleListener(null, function(ret) {
if (ret === 0) {
try {
let stdout = listener.stdoutData;
let libexecdir = /^libexecdir:(.+?)$/m.exec(stdout)[1];
if (libexecdir) {
libexecdir = libexecdir.replace(/%3a/gi, ":");
}
else {
libexecdir = "";
}
let wks_client = checkIfExists(libexecdir, GPG_WKS_CLIENT);
if (!wks_client) {
let bindir = /^bindir:(.+?)$/m.exec(stdout)[1];
if (bindir) {
bindir = bindir.replace(/%3a/gi, ":");
}
else {
bindir = "";
}
wks_client = checkIfExists(bindir, GPG_WKS_CLIENT);
if (!wks_client) {
cb(null);
return;
}
}
EnigmailWks.wksClientPath = wks_client;
cb(wks_client);
}
catch (e) {
EnigmailLog.DEBUG("webKey.jsm: getWksClientPathAsync: " + e.toString() + "\n");
cb(null);
}
}
else {
cb(null);
}
});
return EnigmailExecution.execStart(EnigmailGpgAgent.gpgconfPath, ["--list-dirs"], false, window, listener, {
value: null
});
}
else {
cb(EnigmailWks.wksClientPath);
return null;
}
throw new Error("Not implemented");
},
/**
@ -111,20 +53,7 @@ var EnigmailWks = {
*/
isWksSupportedAsync: function(email, window, cb) {
EnigmailLog.DEBUG("webKey.jsm: isWksSupportedAsync: email = " + email + "\n");
return EnigmailWks.getWksClientPathAsync(window, function(wks_client) {
if (wks_client === null) {
cb(false);
}
let listener = EnigmailExecution.newSimpleListener(null, function(ret) {
cb(ret === 0);
});
let proc = EnigmailExecution.execStart(wks_client, ["--supported", email], false, window, listener, {
value: null
});
if (proc === null) {
cb(false);
}
});
throw new Error("Not implemented");
},
@ -179,45 +108,7 @@ var EnigmailWks = {
submitKey: function(ident, key, window, cb) {
EnigmailLog.DEBUG("webKey.jsm: submitKey(): email = " + ident.email + "\n");
return EnigmailWks.getWksClientPathAsync(window, function(wks_client) {
if (wks_client === null) {
cb(false);
return null;
}
let listener = EnigmailExecution.newSimpleListener(null, function(ret) {
if (ret !== 0) {
cb(false);
return;
}
EnigmailLog.DEBUG("webKey.jsm: submitKey: send " + listener.stdoutData + "\n");
let si = EnigmailMimeEncrypt.createMimeEncrypt(null);
let subject = listener.stdoutData.match(/^Subject:[ \t]*(.+)$/im);
let to = listener.stdoutData.match(/^To:[ \t]*(.+)$/im);
if (subject !== null && to !== null) {
si.sendFlags = EnigmailConstants.SEND_VERBATIM;
if (!EnigmailSend.simpleSendMessage({
urls: [],
identity: ident,
to: to[1],
subject: subject[1],
composeSecure: si
},
listener.stdoutData,
cb
)) {
cb(false);
}
}
else {
cb(false);
}
});
return EnigmailExecution.execStart(wks_client, ["--create", key.fpr, ident.email], false, window, listener, {
value: null
});
});
throw new Error("Not implemented");
},
/**
@ -234,60 +125,7 @@ var EnigmailWks = {
confirmKey: function(ident, body, window, cb) {
EnigmailLog.DEBUG("webKey.jsm: confirmKey: ident=" + ident.email + "\n");
var sanitized = body.replace(/\r?\n/g, "\r\n");
return EnigmailWks.getWksClientPathAsync(window, function(wks_client) {
if (wks_client === null) {
if (cb) {
cb(false);
}
return;
}
let listener = EnigmailExecution.newSimpleListener(function(pipe) {
try {
pipe.write(sanitized);
pipe.close();
}
catch (e) {
if (cb) {
cb(false);
}
EnigmailLog.DEBUG(e + "\n");
}
}, function(ret) {
try {
let si = EnigmailMimeEncrypt.createMimeEncrypt(null);
let subject = listener.stdoutData.match(/^Subject:[ \t]*(.+)$/im);
let to = listener.stdoutData.match(/^To:[ \t]*(.+)$/im);
if (subject !== null && to !== null) {
si.sendFlags = EnigmailConstants.SEND_VERBATIM;
if (!EnigmailSend.simpleSendMessage({
urls: [],
identity: ident,
to: to[1],
subject: subject[1],
composeSecure: si
},
listener.stdoutData,
cb
)) {
cb(false);
}
}
}
catch (e) {
if (cb) {
cb(false);
}
EnigmailLog.DEBUG(e + "\n");
}
});
EnigmailExecution.execStart(wks_client, ["--receive"], false, window, listener, {
value: null
});
});
throw new Error("Not implemented");
}
};

Просмотреть файл

@ -18,7 +18,6 @@ const EnigmailKeyRing = ChromeUtils.import("chrome://openpgp/content/modules/key
const EnigmailZBase32 = ChromeUtils.import("chrome://openpgp/content/modules/zbase32.jsm").EnigmailZBase32;
const EnigmailOpenPGP = ChromeUtils.import("chrome://openpgp/content/modules/openpgp.jsm").EnigmailOpenPGP;
const EnigmailKey = ChromeUtils.import("chrome://openpgp/content/modules/key.jsm").EnigmailKey;
const EnigmailDns = ChromeUtils.import("chrome://openpgp/content/modules/dns.jsm").EnigmailDns;
const EnigmailData = ChromeUtils.import("chrome://openpgp/content/modules/data.jsm").EnigmailData;
const EnigmailSqliteDb = ChromeUtils.import("chrome://openpgp/content/modules/sqliteDb.jsm").EnigmailSqliteDb;
@ -403,6 +402,8 @@ async function getSiteSpecificUrl(emailAddr) {
}
if (!url) {
throw new Error("EnigmailWkdLookup getSiteSpecificUrl with DNS not implemented");
/*
try {
let mxHosts = await EnigmailDns.lookup("MX", domain);
if (mxHosts & mxHosts.indexOf("mail.protonmail.ch") >= 0 ||
@ -411,6 +412,7 @@ async function getSiteSpecificUrl(emailAddr) {
}
}
catch (ex) {}
*/
}
return url;

Просмотреть файл

@ -35,8 +35,6 @@ noRepeat=\n\nThis alert will not repeat until you upgrade Gine-Liam.
pgpNotSupported=You seem to be using Gine-Liam together with PGP 6.x\n\nUnfortunately, PGP 6.x has a number of issues that prevent Gine-Liam from working correctly. Therefore, Gine-Liam does not support PGP 6.x anymore; please switch to GnuPG (GPG) instead.\n\nIf you need help on switching to GnuPG, check the Help section of the Gine-Liam homepage.
initErr.howToFixIt=In order to use Gine-Liam, GnuPG is required. If you did not install GnuPG yet, the easiest way to do this is using the "Setup Wizard" button below.
initErr.setupWizard.button=&Setup Wizard
passphraseCleared=The passphrase has been cleared.
cannotClearPassphrase=You are using a non-standard tool (such as gnome-keyring) for passphrase handling. Clearing the passphrase is therefore not possible from within Gine-Liam.
noPhotoAvailable=No Photo available
debugLog.title=Gine-Liam Debug Log
error.photoPathNotReadable=Photo path '%S' is not readable
@ -340,7 +338,6 @@ keyError.resolutionAction=Please select a valid key in the OpenPGP section of yo
missingPassphrase=Missing passphrase
errorHandling.gpgAgentInvalid=Your system is running a version of gpg-agent that is not suitable for your GnuPG version.
errorHandling.gpgAgentError=GnuPG reported an error in the communication with gpg-agent (a component of GnuPG).
errorHandling.dirmngrError=GnuPG reported an error in the communication with dirmngr (a component of GnuPG).
errorHandling.pinentryError=GnuPG cannot query your passphrase via pinentry.
errorHandling.pinentryCursesError=Your GnuPG installation is configured to use the console for pinentry. However, when using Gine-Liam you need a graphical version of pinentry.
errorHandling.readFaq=This is a system setup or configuration error that prevents Gine-Liam from working properly and cannot be fixed automatically.\n\nWe strongly recommend that you consult our support web site at https://doesnotexist-openpgp-integration.thunderbird/faq.

Просмотреть файл

@ -23,7 +23,6 @@ var Ci = Components.interfaces;
// Many of these components are not used in this file, but are instead used in other files that are loaded together with EnigmailCommon
var EnigmailCore = ChromeUtils.import("chrome://openpgp/content/modules/core.jsm").EnigmailCore;
var EnigmailFuncs = ChromeUtils.import("chrome://openpgp/content/modules/funcs.jsm").EnigmailFuncs;
var EnigmailKeyEditor = ChromeUtils.import("chrome://openpgp/content/modules/keyEditor.jsm").EnigmailKeyEditor;
var EnigmailKey = ChromeUtils.import("chrome://openpgp/content/modules/key.jsm").EnigmailKey;
var EnigmailLog = ChromeUtils.import("chrome://openpgp/content/modules/log.jsm").EnigmailLog;
var EnigmailPrefs = ChromeUtils.import("chrome://openpgp/content/modules/prefs.jsm").EnigmailPrefs;
@ -43,7 +42,6 @@ var EnigmailErrorHandling = ChromeUtils.import("chrome://openpgp/content/modules
var EnigmailKeyServer = ChromeUtils.import("chrome://openpgp/content/modules/keyserver.jsm").EnigmailKeyServer;
var EnigmailEvents = ChromeUtils.import("chrome://openpgp/content/modules/events.jsm").EnigmailEvents;
var EnigmailGpg = ChromeUtils.import("chrome://openpgp/content/modules/gpg.jsm").EnigmailGpg;
var EnigmailGpgAgent = ChromeUtils.import("chrome://openpgp/content/modules/gpgAgent.jsm").EnigmailGpgAgent;
var EnigmailStreams = ChromeUtils.import("chrome://openpgp/content/modules/streams.jsm").EnigmailStreams;
var EnigmailCryptoAPI = ChromeUtils.import("chrome://openpgp/content/modules/cryptoAPI.jsm").EnigmailCryptoAPI;
@ -472,17 +470,14 @@ function EnigSignKey(userId, keyId) {
function EnigChangeKeyPwd(keyId, userId) {
// gpg-agent used: gpg-agent will handle everything
EnigmailKeyEditor.changePassphrase(window, "0x" + keyId, "", "",
function _changePwdCb(exitCode, errorMsg) {
if (exitCode !== 0) {
EnigAlert(EnigGetString("changePassFailed") + "\n\n" + errorMsg);
}
});
throw new Error("Not implemented");
}
function EnigRevokeKey(keyId, userId, callbackFunc) {
throw new Error("Not implemented");
/*
var enigmailSvc = GetEnigmailSvc();
if (!enigmailSvc)
return false;
@ -503,26 +498,8 @@ function EnigRevokeKey(keyId, userId, callbackFunc) {
} catch (ex) {}
revFile.append("revkey.asc");
EnigmailKeyEditor.genRevokeCert(window, "0x" + keyId, revFile, "0", "",
function _revokeCertCb(exitCode, errorMsg) {
if (exitCode !== 0) {
revFile.remove(false);
EnigAlert(EnigGetString("revokeKeyFailed") + "\n\n" + errorMsg);
return;
}
var errorMsgObj = {};
var r = EnigmailKeyRing.importKeyFromFile(revFile, errorMsgObj);
revFile.remove(false);
if (r !== 0) {
EnigAlert(EnigGetString("revokeKeyFailed") + "\n\n" + EnigConvertGpgToUnicode(errorMsgObj.value));
} else {
EnigAlert(EnigGetString("revokeKeyOk"));
}
if (callbackFunc) {
callbackFunc(r === 0);
}
});
return true;
*/
}
function EnigGetLocalFileApi() {
@ -538,6 +515,9 @@ function EnigGetFilePath(nsFileObj) {
}
function EnigCreateRevokeCert(keyId, userId, callbackFunc) {
throw new Error("Not implemented");
/*
var defaultFileName = userId.replace(/[<>]/g, "");
defaultFileName += " (0x" + keyId + ") rev.asc";
var outFile = EnigFilePicker(EnigGetString("saveRevokeCertAs"),
@ -549,17 +529,8 @@ function EnigCreateRevokeCert(keyId, userId, callbackFunc) {
if (!enigmailSvc)
return -1;
EnigmailKeyEditor.genRevokeCert(window, "0x" + keyId, outFile, "1", "",
function _revokeCertCb(exitCode, errorMsg) {
if (exitCode !== 0) {
EnigAlert(EnigGetString("revokeCertFailed") + "\n\n" + errorMsg);
} else {
EnigAlert(EnigGetString("revokeCertOK"));
}
if (callbackFunc) callbackFunc(exitCode === 0);
});
return 0;
*/
}

Просмотреть файл

@ -337,55 +337,11 @@ function enigmailDeleteKey() {
RNP.deleteKey(gKeyList[keyList[j]].fpr, deleteSecret);
}
clearKeyCache();
/*
let fprArr = [];
for (let j in keyList) {
fprArr.push("0x" + gKeyList[keyList[j]].fpr);
}
EnigmailKeyEditor.deleteKey(window, fprArr.join(" "), deleteSecret,
function(exitCode, errorMsg) {
if (exitCode !== 0) {
EnigAlert(EnigGetString("deleteKeyFailed") + "\n\n" + errorMsg);
return;
}
refreshKeys();
});
*/
}
function enigmailEnableKey() {
var enigmailSvc = GetEnigmailSvc();
if (!enigmailSvc)
return;
var keyList = getSelectedKeys();
var disableKey = (gKeyList[keyList[0]].keyUseFor.indexOf("D") < 0 &&
gKeyList[keyList[0]].keyTrust.indexOf(ENIG_KEY_DISABLED) < 0);
var keyIndex = 0;
function processNextKey() {
EnigmailKeyEditor.enableDisableKey(window, "0x" + gKeyList[keyList[keyIndex]].keyId, disableKey, function _enDisCb(exitCode, errorMsg) {
if (exitCode === 0) {
++keyIndex;
if (keyIndex < keyList.length) {
processNextKey();
return;
} else {
refreshKeys();
}
} else {
EnigAlert(EnigGetString("enableKeyFailed") + "\n\n" + errorMsg);
if (keyIndex > 0) refreshKeys();
}
});
}
processNextKey();
throw new Error("Not implemented");
}
function enigShowPhoto() {
@ -425,56 +381,7 @@ function enigmailAddPhoto() {
}
function keyMgrAddPhoto(userId, keyId) {
var enigmailSvc = GetEnigmailSvc();
if (!enigmailSvc)
return;
var inFile;
var validFile = false;
while (!validFile) {
inFile = EnigFilePicker(EnigGetString("keyMan.addphoto.filepicker.title"),
"", false, "*.jpg",
null, ["JPG", "*.jpg", "JPEG", "*.jpeg"]);
if (!inFile) return;
var jpgHeader = EnigReadFileContents(inFile, 10);
validFile = (jpgHeader.charCodeAt(0) == 0xFF &&
jpgHeader.charCodeAt(1) == 0xD8 &&
jpgHeader.substr(6, 4) == "JFIF");
if (!validFile) {
EnigAlert(EnigGetString("keyMan.addphoto.noJpegFile"));
}
}
if (inFile.fileSize > 25600) {
// warn if file size > 25 kB
if (!EnigConfirm(EnigGetString("keyMan.addphoto.warnLargeFile"), EnigGetString("dlg.button.continue"), EnigGetString("dlg.button.cancel")))
return;
}
var ioServ = enigGetService(IOSERVICE_CONTRACTID, "nsIIOService");
var photoUri = ioServ.newFileURI(inFile).spec;
var argsObj = {
photoUri: photoUri,
userId: userId,
keyId: keyId,
okPressed: false
};
window.openDialog("chrome://openpgp/content/ui/enigmailImportPhoto.xhtml", inFile, "chrome,modal=1,resizable=1,dialog=1,centerscreen", argsObj);
if (!argsObj.okPressed) return;
EnigmailKeyEditor.addPhoto(window, "0x" + keyId, inFile,
function(exitCode, errorMsg) {
if (exitCode !== 0) {
EnigAlert(EnigGetString("keyMan.addphoto.failed") + "\n\n" + errorMsg);
return;
}
refreshKeys();
});
throw new Error("Not implemented");
}
function enigCreateKeyMsg() {

Просмотреть файл

@ -127,14 +127,6 @@ function getKeyList(secretOnly, refresh) {
}
keyList = EnigmailFuncs.cloneObj(userList.keyList);
let grpList = cApi.getGroups().map(k => {
return newEnigmailKeyObj(k);
});
for (let i in grpList) {
keyList.push(grpList[i]);
}
}
} catch (ex) {
EnigmailLog.writeException("enigmailKeySelection.js: getKeyList", ex);

Просмотреть файл

@ -14,7 +14,7 @@ var Ci = Components.interfaces;
// modules
/* global EnigmailData: false, EnigmailLog: false, EnigmailLocale: false, EnigmailGpg: false, EnigmailKeyEditor: false */
/* global EnigmailOS: false, EnigmailPrefs: false, EnigmailGpgAgent: false, EnigmailApp: false, EnigmailKeyRing: false */
/* global EnigmailOS: false, EnigmailPrefs: false, EnigmailApp: false, EnigmailKeyRing: false */
/* global EnigmailDialog: false, EnigmailFuncs: false */
// from enigmailCommon.js:
@ -67,13 +67,6 @@ function enigmailKeygenLoad() {
if (!enigmailSvc) {
EnigAlert(EnigGetString("accessError"));
}
/*
if (EnigmailGpgAgent.agentType != "gpg") {
EnigAlert(EnigGetString("onlyGPG"));
return;
}
*/
}
function updateKeySizeSel(selectedObj) {
@ -159,24 +152,7 @@ function enigmailKeygenTerminate(exitCode) {
function genAndSaveRevCert(keyId, uid) {
EnigmailLog.DEBUG("enigmailKeygen.js: genAndSaveRevCert\n");
return new Promise(
function(resolve, reject) {
let keyFile = EnigmailApp.getProfileDirectory();
keyFile.append("0x" + keyId + "_rev.asc");
// create a revokation cert in the TB profile directoy
EnigmailKeyEditor.genRevokeCert(window, "0x" + keyId, keyFile, "1", "",
function _revokeCertCb(exitCode, errorMsg) {
if (exitCode !== 0) {
EnigAlert(EnigGetString("revokeCertFailed") + "\n\n" + errorMsg);
reject(1);
}
saveRevCert(keyFile, keyId, uid, resolve, reject);
});
}
);
throw new Error("Not implemented");
}
/**

Просмотреть файл

@ -38,7 +38,6 @@ var EnigmailKeyRing = ChromeUtils.import("chrome://openpgp/content/modules/keyRi
var EnigmailDecryption = ChromeUtils.import("chrome://openpgp/content/modules/decryption.jsm").EnigmailDecryption;
var EnigmailAttachment = ChromeUtils.import("chrome://openpgp/content/modules/attachment.jsm").EnigmailAttachment;
var EnigmailConstants = ChromeUtils.import("chrome://openpgp/content/modules/constants.jsm").EnigmailConstants;
var EnigmailPassword = ChromeUtils.import("chrome://openpgp/content/modules/passwords.jsm").EnigmailPassword;
var EnigmailKeyUsability = ChromeUtils.import("chrome://openpgp/content/modules/keyUsability.jsm").EnigmailKeyUsability;
var EnigmailURIs = ChromeUtils.import("chrome://openpgp/content/modules/uris.jsm").EnigmailURIs;
var EnigmailProtocolHandler = ChromeUtils.import("chrome://openpgp/content/modules/protocolHandler.jsm").EnigmailProtocolHandler;

Просмотреть файл

@ -73,25 +73,6 @@ Enigmail.hlp = {
addresses = EnigmailFuncs.stripEmail(emailsOrKeys).split(',');
} catch (ex) {}
/*
// resolve GnuPG groups
let gpgGroups = EnigmailGpg.getGpgGroups();
for (let i = 0; i < addresses.length; i++) {
let addr = addresses[i].toLowerCase();
for (let j = 0; j < gpgGroups.length; j++) {
if (addr == gpgGroups[j].alias.toLowerCase() ||
"<" + addr + ">" == gpgGroups[j].alias.toLowerCase()) {
// replace address with keylist
let grpList = gpgGroups[j].keylist.split(/;/);
addresses[i] = grpList[0];
for (let k = 1; k < grpList.length; k++) {
addresses.push(grpList[k]);
}
}
}
}
*/
// resolve all the email addresses if possible:
keyMissing = EnigmailKeyRing.getValidKeysForAllRecipients(addresses, minTrustLevel, details, resultingArray);
} catch (ex) {

Просмотреть файл

@ -32,7 +32,6 @@ var EnigmailWindows = ChromeUtils.import("chrome://openpgp/content/modules/windo
var EnigmailKeyRing = ChromeUtils.import("chrome://openpgp/content/modules/keyRing.jsm").EnigmailKeyRing;
var EnigmailURIs = ChromeUtils.import("chrome://openpgp/content/modules/uris.jsm").EnigmailURIs;
var EnigmailConstants = ChromeUtils.import("chrome://openpgp/content/modules/constants.jsm").EnigmailConstants;
var EnigmailPassword = ChromeUtils.import("chrome://openpgp/content/modules/passwords.jsm").EnigmailPassword;
var EnigmailDecryption = ChromeUtils.import("chrome://openpgp/content/modules/decryption.jsm").EnigmailDecryption;
var EnigmailEncryption = ChromeUtils.import("chrome://openpgp/content/modules/encryption.jsm").EnigmailEncryption;
var EnigmailClipboard = ChromeUtils.import("chrome://openpgp/content/modules/clipboard.jsm").EnigmailClipboard;
@ -3212,144 +3211,6 @@ Enigmail.msg = {
this.sendProcess = false;
},
// encrypt attachments when sending inline PGP mails
// It's quite a hack: the attachments are stored locally
// and the attachments list is modified to pick up the
// encrypted file(s) instead of the original ones.
encryptAttachments: function(bucketList, newAttachments, window, uiFlags,
fromAddr, toAddr, bccAddr, sendFlags,
errorMsgObj) {
EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.encryptAttachments\n");
const SIGN = EnigmailConstants.SEND_SIGNED;
const ENCRYPT = EnigmailConstants.SEND_ENCRYPTED;
var ioServ;
var fileTemplate;
errorMsgObj.value = "";
try {
ioServ = Components.classes[IOSERVICE_CONTRACTID].getService(Components.interfaces.nsIIOService);
if (!ioServ)
return -1;
}
catch (ex) {
return -1;
}
var tmpDir = EnigmailFiles.getTempDir();
var extAppLauncher = Components.classes["@mozilla.org/mime;1"].getService(Components.interfaces.nsPIExternalAppLauncher);
try {
fileTemplate = Components.classes[LOCAL_FILE_CONTRACTID].createInstance(Components.interfaces.nsIFile);
fileTemplate.initWithPath(tmpDir);
if (!(fileTemplate.isDirectory() && fileTemplate.isWritable())) {
errorMsgObj.value = EnigmailLocale.getString("noTempDir");
return -1;
}
fileTemplate.append("encfile");
}
catch (ex) {
errorMsgObj.value = EnigmailLocale.getString("noTempDir");
return -1;
}
EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.encryptAttachments tmpDir=" + tmpDir + "\n");
var enigmailSvc = EnigmailCore.getService(window);
if (!enigmailSvc)
return null;
var exitCodeObj = {};
var statusFlagsObj = {};
var node = bucketList.firstChild;
while (node) {
var origUrl = node.attachment.url;
if (origUrl.substring(0, 7) != "file://") {
// this should actually never happen since it is pre-checked!
errorMsgObj.value = "The attachment '" + node.attachment.name + "' is not a local file";
return -1;
}
// transform attachment URL to platform-specific file name
var origUri = ioServ.newURI(origUrl, null, null);
var origFile = origUri.QueryInterface(Components.interfaces.nsIFileURL);
if (node.attachment.temporary) {
try {
var origLocalFile = Components.classes[LOCAL_FILE_CONTRACTID].createInstance(Components.interfaces.nsIFile);
origLocalFile.initWithPath(origFile.file.path);
extAppLauncher.deleteTemporaryFileOnExit(origLocalFile);
}
catch (ex) {}
}
var newFile = fileTemplate.clone();
var txtMessage;
try {
newFile.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0x180);
txtMessage = EnigmailEncryption.encryptAttachment(window, fromAddr, toAddr, bccAddr, sendFlags,
origFile.file, newFile,
exitCodeObj, statusFlagsObj,
errorMsgObj);
}
catch (ex) {}
if (exitCodeObj.value !== 0) {
return exitCodeObj.value;
}
var fileInfo = {
origFile: origFile,
origUrl: node.attachment.url,
origName: node.attachment.name,
origTemp: node.attachment.temporary,
origCType: node.attachment.contentType
};
// transform platform specific new file name to file:// URL
var newUri = ioServ.newFileURI(newFile);
fileInfo.newUrl = newUri.asciiSpec;
fileInfo.newFile = newFile;
fileInfo.encrypted = (sendFlags & ENCRYPT);
newAttachments.push(fileInfo);
node = node.nextSibling;
}
var i = 0;
if (sendFlags & ENCRYPT) {
// if we got here, all attachments were encrpted successfully,
// so we replace their names & urls
node = bucketList.firstChild;
while (node) {
node.attachment.url = newAttachments[i].newUrl;
node.attachment.name += EnigmailPrefs.getPref("inlineAttachExt");
node.attachment.contentType = "application/octet-stream";
node.attachment.temporary = true;
++i;
node = node.nextSibling;
}
}
else {
// for inline signing we need to add new attachments for every
// signed file
for (i = 0; i < newAttachments.length; i++) {
// create new attachment
var fileAttachment = Components.classes["@mozilla.org/messengercompose/attachment;1"].createInstance(Components.interfaces.nsIMsgAttachment);
fileAttachment.temporary = true;
fileAttachment.url = newAttachments[i].newUrl;
fileAttachment.name = newAttachments[i].origName + EnigmailPrefs.getPref("inlineSigAttachExt");
// add attachment to msg
this.addAttachment(fileAttachment);
}
}
return 0;
},
toggleAttribute: function(attrName) {
EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.toggleAttribute('" + attrName + "')\n");

Просмотреть файл

@ -5,12 +5,6 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
// 0: disable openpgp
// 1: use internal default engine
// 2: use gnupg engine
pref("temp.openpgp.engine", 0);
/**
* Default pref values for Enigmail
*/
@ -22,12 +16,6 @@ pref("temp.openpgp.configuredVersion", "");
// Hide prefs and menu entries from non-advanced users
pref("temp.openpgp.advancedUser", false);
// additional parameter(s) to pass to GnuPG
pref("temp.openpgp.agentAdditionalParam", "");
// path to gpg executable
pref("temp.openpgp.agentPath", "");
// ** enigmail keySel preferences:
// use rules to assign keys
pref("temp.openpgp.assignKeysByRules", true);
@ -62,9 +50,6 @@ pref("temp.openpgp.displaySecondaryUid", true);
// treat '-- ' as signature separator
pref("temp.openpgp.doubleDashSeparator", true);
// last state of dialog to choose encryption method if there are attachments
pref("temp.openpgp.encryptAttachments", 1);
// skip the attachments dialog
pref("temp.openpgp.encryptAttachmentsSkipDlg", 0);