37
37
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
38
38
Components.utils.import("resource://gre/modules/AddonManager.jsm");
39
39
Components.utils.import("resource://enigmail/subprocess.jsm");
40
Components.utils.import("resource://enigmail/pipeConsole.jsm");
41
Components.utils.import("resource://gre/modules/ctypes.jsm");
41
43
// Maximum size of message directly processed by Enigmail
42
44
const MSG_BUFFER_SIZE = 98304; // 96 kB
74
76
const ENIGMAIL_EXTENSION_ID = "{847b3a00-7ab1-11d4-8f02-006008948af5}";
76
78
// Contract IDs and CIDs used by this module
77
const NS_IPCSERVICE_CONTRACTID = "@mozilla.org/process/ipc-service;1";
78
79
const NS_IPCBUFFER_CONTRACTID = "@mozilla.org/ipc/ipc-buffer;1";
79
const NS_PIPECONSOLE_CONTRACTID = "@mozilla.org/process/pipe-console;1";
80
80
const NS_PIPETRANSPORT_CONTRACTID="@mozilla.org/ipc/pipe-transport;1";
81
81
const NS_PROCESS_UTIL_CONTRACTID = "@mozilla.org/process/util;1"
82
82
const NS_MSGCOMPOSESECURE_CONTRACTID = "@mozilla.org/messengercompose/composesecure;1";
112
112
const nsILocalFile = Ci.nsILocalFile;
113
113
const nsILocalFileWin = Ci.nsILocalFileWin;
114
114
const nsIProtocolHandler = Ci.nsIProtocolHandler;
115
const nsIIPCService = Ci.nsIIPCService;
116
const nsIPipeConsole = Ci.nsIPipeConsole;
117
115
const nsIEnvironment = Ci.nsIEnvironment;
118
116
const nsIEnigmail = Ci.nsIEnigmail;
119
117
const nsIEnigStrBundle = Ci.nsIStringBundleService;
484
479
contentData = "Enigmail error: invalid URI "+aURI.spec;
487
var channel = gEnigmailSvc.ipcService.newStringChannel(aURI,
482
var channel =Ec.newStringChannel(aURI, contentType, "UTF-8", contentData);
495
487
if (aURI.spec == aURI.scheme+":dummy") {
496
488
// Dummy PKCS7 content (to access mimeEncryptedClass)
497
channel = gEnigmailSvc.ipcService.newStringChannel(aURI,
489
channel = Ec.newStringChannel(aURI, "message/rfc822", "", gDummyPKCS7);
557
546
// Enigmail encryption/decryption service
558
547
///////////////////////////////////////////////////////////////////////////////
560
function GetPassphrase(domWindow, passwdObj, useAgentObj, rememberXTimes) {
561
Ec.DEBUG_LOG("enigmail.js: GetPassphrase:\n");
563
useAgentObj.value = false;
565
var noPassphrase = gEnigmailSvc.prefBranch.getBoolPref("noPassphrase");
566
useAgentObj.value = gEnigmailSvc.useGpgAgent();
568
if (noPassphrase || useAgentObj.value) {
569
passwdObj.value = "";
576
var maxIdleMinutes = gEnigmailSvc.getMaxIdleMinutes();
578
if (gEnigmailSvc.haveCachedPassphrase()) {
579
passwdObj.value = gCachedPassphrase;
581
if (gEnigmailSvc._passwdAccessTimes > 0) {
582
--gEnigmailSvc._passwdAccessTimes;
584
if (gEnigmailSvc._passwdAccessTimes <= 0 && maxIdleMinutes <= 0) {
585
gEnigmailSvc.clearCachedPassphrase();
591
// Obtain password interactively
592
var checkObj = new Object();
594
var promptMsg = Ec.getString("enterPassOrPin");
595
passwdObj.value = "";
596
checkObj.value = true;
598
var checkMsg = (maxIdleMinutes>0) ? Ec.getString("rememberPass", [ maxIdleMinutes ]) : "";
602
var promptService = Cc[NS_PROMPTSERVICE_CONTRACTID].getService(Ci.nsIPromptService);
603
success = promptService.promptPassword(domWindow,
604
Ec.getString("enigPrompt"),
613
Ec.DEBUG_LOG("enigmail.js: GetPassphrase: got passphrase\n");
615
// remember the passphrase for accessing serveral times in a sequence
617
if (rememberXTimes) gEnigmailSvc._passwdAccessTimes = rememberXTimes;
619
// Remember passphrase only if necessary
620
if ((checkObj.value && (maxIdleMinutes > 0)) || rememberXTimes)
621
gEnigmailSvc.setCachedPassphrase(passwdObj.value, rememberXTimes);
626
549
// Remove all quoted strings (and angle brackets) from a list of email
627
550
// addresses, returning a list of pure email address
628
551
function EnigStripEmail(mailAddrs) {
723
642
observe: function (aSubject, aTopic, aData) {
724
643
Ec.DEBUG_LOG("enigmail.js: Enigmail.observe: topic='"+aTopic+"' \n");
726
if (aTopic == "timer-callback") {
727
// Cause cached password to expire, if need be
728
if (!this.haveCachedPassphrase()) {
729
// No cached password; cancel repeating timer
731
gCacheTimer.cancel();
734
} else if (aTopic == NS_XPCOM_SHUTDOWN_OBSERVER_ID) {
645
if (aTopic == NS_XPCOM_SHUTDOWN_OBSERVER_ID) {
735
646
// XPCOM shutdown
817
718
return dirPrefix;
820
getExtensionBaseDir: function () {
822
var isComplete = false;
823
AddonManager.getAddonByID(ENIGMAIL_EXTENSION_ID, function (addon) {
825
file = addon.getResourceURI(".").QueryInterface(Ci.nsIFileURL).file;
828
Ec.ERROR_LOG("enigmail.js: getExtensionBaseDir: "+ex+"\n");
832
var thread = Cc["@mozilla.org/thread-manager;1"]
833
.getService(Ci.nsIThreadManager)
836
thread.processNextEvent(true);
841
stillActive: function () {
842
Ec.DEBUG_LOG("enigmail.js: Enigmail.stillActive: \n");
844
// Update last active time
845
var curDate = new Date();
846
this._lastActiveTime = curDate.getTime();
847
// Ec.DEBUG_LOG("enigmail.js: Enigmail.stillActive: _lastActiveTime="+this._lastActiveTime+"\n");
851
clearCachedPassphrase: function () {
852
Ec.DEBUG_LOG("enigmail.js: Enigmail.clearCachedPassphrase: \n");
854
gCachedPassphrase = null;
857
setCachedPassphrase: function (passphrase) {
858
Ec.DEBUG_LOG("enigmail.js: Enigmail.setCachedPassphrase: \n");
860
gCachedPassphrase = passphrase;
863
var maxIdleMinutes = this.getMaxIdleMinutes();
865
var createTimerType = null;
866
const nsITimer = Ci.nsITimer;
868
if (this.haveCachedPassphrase() && (this._passwdAccessTimes > 0) && (maxIdleMinutes <= 0)) {
869
// we remember the passphrase for at most 1 minute
870
createTimerType = nsITimer.TYPE_ONE_SHOT;
873
else if (this.haveCachedPassphrase() && (maxIdleMinutes > 0)) {
874
createTimerType = nsITimer.TYPE_REPEATING_SLACK;
877
if (createTimerType != null) {
880
gCacheTimer.cancel();
882
var delayMillisec = maxIdleMinutes*60*1000;
884
gCacheTimer = Cc[NS_TIMER_CONTRACTID].createInstance(nsITimer);
887
Ec.ERROR_LOG("enigmail.js: Enigmail.setCachedPassphrase: Error - failed to create timer\n");
888
throw Components.results.NS_ERROR_FAILURE;
891
gCacheTimer.init(this, delayMillisec,
894
Ec.DEBUG_LOG("enigmail.js: Enigmail.setCachedPassphrase: gCacheTimer="+gCacheTimer+"\n");
898
haveCachedPassphrase: function () {
899
Ec.DEBUG_LOG("enigmail.js: Enigmail.haveCachedPassphrase: \n");
901
var havePassphrase = ((typeof gCachedPassphrase) == "string");
906
var curDate = new Date();
907
var currentTime = curDate.getTime();
909
var maxIdleMinutes = this.getMaxIdleMinutes();
910
var delayMillisec = maxIdleMinutes*60*1000;
912
var expired = ((currentTime - this._lastActiveTime) >= delayMillisec);
914
// Ec.DEBUG_LOG("enigmail.js: Enigmail.haveCachedPassphrase: ")
915
// Ec.DEBUG_LOG("currentTime="+currentTime+", _lastActiveTime="+this._lastActiveTime+", expired="+expired+"\n");
917
if (expired && (this._passwdAccessTimes <= 0)) {
918
// Too much idle time; forget cached password
919
gCachedPassphrase = null;
920
havePassphrase = false;
922
Ec.WRITE_LOG("enigmail.js: Enigmail.haveCachedPassphrase: CACHE IDLED OUT\n");
925
return havePassphrase;
929
722
finalize: function () {
930
723
Ec.DEBUG_LOG("enigmail.js: Enigmail.finalize:\n");
933
726
if (this.gpgAgentProcess != null) {
934
727
Ec.DEBUG_LOG("enigmail.js: Enigmail.finalize: stopping gpg-agent PID="+this.gpgAgentProcess+"\n");
936
var extensionLoc = this.getExtensionBaseDir();
937
extensionLoc.append("wrappers");
938
extensionLoc.append("gpg-agent-wrapper.sh");
940
extensionLoc.permissions=0755;
944
agentProcess = Cc[NS_PROCESS_UTIL_CONTRACTID].createInstance(Ci.nsIProcess);
945
agentProcess.init(extensionLoc);
946
agentProcess.run(true, [ "stop", this.gpgAgentProcess ], 2);
729
var libName=subprocess.getPlatformValue(0);
730
var libc = ctypes.open(libName);
732
//int kill(pid_t pid, int sig);
733
var kill = libc.declare("kill",
739
kill(parseInt(this.gpgAgentProcess), 15);
742
Ec.ERROR_LOG("enigmail.js: Enigmail.finalize ERROR: "+ex+"\n");
1051
835
Ec.WARNING_LOG("enigmail.js: Enigmail: gLogLevel="+gLogLevel+"\n");
838
subprocess.registerLogHandler(function(txt) { Ec.ERROR_LOG("subprocess.jsm: "+txt) });
840
matches = nspr_log_modules.match(/subprocess:(\d+)/);
841
if (matches && (matches.length > 1)) {
842
if (matches[1] > 2) subprocess.registerDebugHandler(function(txt) { Ec.DEBUG_LOG("subprocess.jsm: "+txt) });
1054
846
// Initialize global environment variables list
1055
847
var passEnv = [ "GNUPGHOME", "GPGDIR", "ETC",
1056
848
"ALLUSERSPROFILE", "APPDATA", "BEGINLIBPATH",
1083
875
Ec.DEBUG_LOG("enigmail.js: Enigmail.initialize: Ec.envList = "+Ec.envList+"\n");
1086
// Access IPC Service
1088
var ipcService = Cc[NS_IPCSERVICE_CONTRACTID].getService();
1089
ipcService = ipcService.QueryInterface(nsIIPCService);
1091
this.ipcService = ipcService;
1093
// Create a non-joinable console
1094
var pipeConsole = Cc[NS_PIPECONSOLE_CONTRACTID].createInstance(nsIPipeConsole);
1096
pipeConsole.open(499, 0, false);
1098
this.console = pipeConsole;
1100
pipeConsole.write("Initializing Enigmail service ...\n");
878
EnigmailConsole.write("Initializing Enigmail service ...\n");
1103
881
this.initializationError = Ec.getString("enigmimeNotAvail");
1454
1232
if ((! this.isDosLike) && (this.agentVersion < "2.1" )) {
1455
1233
args = [ "--sh", "--no-use-standard-socket",
1457
"--default-cache-ttl", (this.getMaxIdleMinutes()*60).toString(),
1235
"--default-cache-ttl", (Ec.getMaxIdleMinutes()*60).toString(),
1458
1236
"--max-cache-ttl", "999999" ]; // ca. 11 days
1520
passwdCommand: function () {
1522
var commandArgs = [];
1525
var gpgVersion = this.agentVersion.match(/^\d+\.\d+/);
1526
if (this.useGpgAgent()) {
1527
commandArgs.push("--use-agent");
1530
if (! gEnigmailSvc.prefBranch.getBoolPref("noPassphrase")) {
1531
commandArgs.push("--passphrase-fd");
1532
commandArgs.push("0");
1533
if (gpgVersion && gpgVersion[0] >= "1.1") {
1534
commandArgs.push("--no-use-agent");
1544
* determine if a password is required to be sent to GnuPG
1546
requirePassword: function () {
1547
if (this.useGpgAgent()) {
1551
return (! gEnigmailSvc.prefBranch.getBoolPref("noPassphrase"));
1554
1298
simpleExecCmd: function (command, args, exitCodeObj, errorMsgObj) {
1555
1299
Ec.WRITE_LOG("enigmail.js: Enigmail.simpleExecCmd: command = "+command+" "+args.join(" ")+"\n");
1715
1459
var useAgentObj = {value: false};
1717
1461
if (needPassphrase) {
1718
args = args.concat(this.passwdCommand());
1462
args = args.concat(Ec.passwdCommand());
1720
1464
var passwdObj = new Object();
1722
if (!GetPassphrase(domWindow, passwdObj, useAgentObj, 0)) {
1466
if (!Ec.getPassphrase(domWindow, passwdObj, useAgentObj, 0)) {
1723
1467
Ec.ERROR_LOG("enigmail.js: Enigmail.execStart: Error - no passphrase supplied\n");
1725
1469
statusFlagsObj.value |= nsIEnigmail.MISSING_PASSPHRASE;
2168
1912
var passwdObj = new Object();
2169
1913
var useAgentObj = new Object();
2170
1914
// Get the passphrase and remember it for the next 2 subsequent calls to gpg
2171
if (!GetPassphrase(null, passwdObj, useAgentObj, 2)) {
1915
if (!Ec.getPassphrase(null, passwdObj, useAgentObj, 2)) {
2172
1916
Ec.ERROR_LOG("enigmail.js: Enigmail.determineHashAlgorithm: Error - no passphrase supplied\n");
2491
2235
RegExp.multiline = false;
2238
// HACK to better support messages from Outlook: if there are empty lines, drop them
2239
if (pgpBlock.search(/MESSAGE-----\r?\n\r?\nVersion/) >=0) {
2240
Ec.DEBUG_LOG("enigmail.js: Enigmail.decryptMessage: apply Outlook empty line workaround\n");
2241
pgpBlock = pgpBlock.replace( /\r?\n\r?\n/g, "\n" );
2494
2244
var head = cipherText.substr(0, beginIndexObj.value);
2495
2245
var tail = cipherText.substr(endIndexObj.value+1,
2496
2246
cipherText.length - endIndexObj.value - 1);
3420
3170
var signMessage = (sendFlags & nsIEnigmail.SEND_SIGNED);
3422
3172
if (signMessage ) {
3423
args = args.concat(this.passwdCommand());
3173
args = args.concat(Ec.passwdCommand());
3425
3175
var passwdObj = new Object();
3426
3176
var useAgentObj = new Object();
3428
if (!GetPassphrase(parent, passwdObj, useAgentObj, 0)) {
3178
if (!Ec.getPassphrase(parent, passwdObj, useAgentObj, 0)) {
3429
3179
Ec.ERROR_LOG("enigmail.js: Enigmail.encryptAttachment: Error - no passphrase supplied\n");
3431
3181
statusFlagsObj.value |= nsIEnigmail.MISSING_PASSPHRASE;
3467
3217
Ec.DEBUG_LOG("enigmail.js: Enigmail.getAttachmentFileName\n");
3469
3219
var args = Ec.getAgentArgs(true);
3470
args = args.concat(this.passwdCommand());
3220
args = args.concat(Ec.passwdCommand());
3471
3221
args.push("--list-packets");
3473
3223
var passphrase = null;
3474
3224
var passwdObj = new Object();
3475
3225
var useAgentObj = new Object();
3477
if (!GetPassphrase(parent, passwdObj, useAgentObj, 0)) {
3227
if (!Ec.getPassphrase(parent, passwdObj, useAgentObj, 0)) {
3478
3228
Ec.ERROR_LOG("enigmail.js: Enigmail.getAttachmentFileName: Error - no passphrase supplied\n");
3615
3365
var passwdObj = new Object();
3616
3366
var useAgentObj = new Object();
3618
if (!GetPassphrase(parent, passwdObj, useAgentObj, 0)) {
3368
if (!Ec.getPassphrase(parent, passwdObj, useAgentObj, 0)) {
3619
3369
Ec.ERROR_LOG("enigmail.js: Enigmail.decryptAttachment: Error - no passphrase supplied\n");
3621
3371
statusFlagsObj.value |= nsIEnigmail.MISSING_PASSPHRASE;
3858
3608
this.rulesList = null;
3861
signKey: function (parent, userId, keyId, signLocally, trustLevel, errorMsgObj) {
3862
Ec.DEBUG_LOG("enigmail.js: Enigmail.signKey: trustLevel="+trustLevel+", userId="+userId+", keyId="+keyId+"\n");
3863
var r = this.editKey(parent, true, userId, keyId,
3864
(signLocally ? "lsign" : "sign"),
3865
{ trustLevel: trustLevel},
3874
setKeyTrust: function (parent, keyId, trustLevel, errorMsgObj) {
3875
Ec.DEBUG_LOG("enigmail.js: Enigmail.setKeyTrust: trustLevel="+trustLevel+", keyId="+keyId+"\n");
3877
return this.editKey(parent, false, null, keyId, "trust",
3878
{ trustLevel: trustLevel},
3884
3611
genRevokeCert: function (parent, keyId, outFile, reasonCode, reasonText, errorMsgObj) {
3885
3612
Ec.DEBUG_LOG("enigmail.js: Enigmail.genRevokeCert: keyId="+keyId+"\n");
4104
3831
var useAgentObj = new Object();
4106
3833
if (needPassphrase) {
4107
args=args.concat(this.passwdCommand());
3834
args=args.concat(Ec.passwdCommand());
4109
3836
var passwdObj = new Object();
4111
if (!GetPassphrase(parent, passwdObj, useAgentObj, 0)) {
3838
if (!Ec.getPassphrase(parent, passwdObj, useAgentObj, 0)) {
4112
3839
Ec.ERROR_LOG("enigmail.js: Enigmail.editKey: Error - no passphrase supplied\n");
4114
3841
errorMsgObj.value = Ec.getString("noPassphrase");
4151
3878
var pipeTrans = this.execStart(this.agentPath, args, false, parent, null, null,
4153
3880
if (! pipeTrans) return -1;
4155
if (needPassphrase && this.requirePassword()) {
3882
if (needPassphrase && Ec.requirePassword()) {
4157
3884
pipeTrans.writeSync(passphrase, passphrase.length);
4158
3885
pipeTrans.writeSync("\n", 1);
4329
function signKeyCallback(inputData, keyEdit, ret) {
4334
if (keyEdit.doCheck(GET_BOOL, "sign_uid.okay" )) {
4338
else if (keyEdit.doCheck(GET_BOOL, "keyedit.sign_all.okay" )) {
4342
else if (keyEdit.doCheck(GET_LINE, "sign_uid.expire" )) {
4346
else if (keyEdit.doCheck(GET_LINE, "trustsig_prompt.trust_value" )) {
4350
else if (keyEdit.doCheck(GET_LINE, "trustsig_prompt.trust_depth" )) {
4353
else if (keyEdit.doCheck(GET_LINE, "trustsig_prompt.trust_regexp" )) {
4355
ret.writeTxt = "0";}
4356
else if (keyEdit.doCheck(GET_LINE, "siggen.valid" )) {
4360
else if (keyEdit.doCheck(GET_BOOL, "sign_uid.local_promote_okay" )) {
4364
else if (keyEdit.doCheck(GET_LINE, "sign_uid.class" )) {
4366
ret.writeTxt = new String(inputData.trustLevel);
4368
else if (keyEdit.doCheck(GET_HIDDEN, "passphrase.adminpin.ask")) {
4369
GetPin(inputData.parent, Ec.getString("enterAdminPin"), ret);
4371
else if (keyEdit.doCheck(GET_HIDDEN, "passphrase.pin.ask")) {
4372
GetPin(inputData.parent, Ec.getString("enterCardPin"), ret);
4374
else if (keyEdit.doCheck(GET_LINE, "keyedit.prompt")) {
4380
Ec.ERROR_LOG("Unknown command prompt: "+keyEdit.getText()+"\n");
4385
function keyTrustCallback(inputData, keyEdit, ret) {
4389
if (keyEdit.doCheck(GET_LINE, "edit_ownertrust.value" )) {
4391
ret.writeTxt = new String(inputData.trustLevel);
4393
else if (keyEdit.doCheck(GET_BOOL, "edit_ownertrust.set_ultimate.okay")) {
4397
else if (keyEdit.doCheck(GET_LINE, "keyedit.prompt")) {
4401
else if (keyEdit.doCheck(GET_HIDDEN, "passphrase.adminpin.ask")) {
4402
GetPin(inputData.parent, Ec.getString("enterAdminPin"), ret);
4404
else if (keyEdit.doCheck(GET_HIDDEN, "passphrase.pin.ask")) {
4405
GetPin(inputData.parent, Ec.getString("enterCardPin"), ret);
4409
Ec.ERROR_LOG("Unknown command prompt: "+keyEdit.getText()+"\n");
4415
4057
function addUidCallback(inputData, keyEdit, ret) {
4416
4058
ret.writeTxt = "";