diff --git a/package/encryptPermanently.jsm b/package/encryptPermanently.jsm index fb1cd773..3e5efbf9 100644 --- a/package/encryptPermanently.jsm +++ b/package/encryptPermanently.jsm @@ -1,1119 +1,1194 @@ /*global Components: false*/ /*jshint -W097 */ /* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; var EXPORTED_SYMBOLS = ["EnigmailEncryptPermanently"]; const Cu = Components.utils; Cu.import("resource://enigmail/lazy.jsm"); /*global EnigmailLazy: false */ Cu.import("resource://gre/modules/AddonManager.jsm"); /*global AddonManager: false */ Cu.import("resource://gre/modules/XPCOMUtils.jsm"); /*global XPCOMUtils: false */ +Cu.import("resource://gre/modules/NetUtil.jsm"); /*global NetUtil: false */ Cu.import("resource://enigmail/log.jsm"); /*global EnigmailLog: false */ Cu.import("resource://enigmail/armor.jsm"); /*global EnigmailArmor: false */ Cu.import("resource://enigmail/locale.jsm"); /*global EnigmailLocale: false */ Cu.import("resource://enigmail/execution.jsm"); /*global EnigmailExecution: false */ Cu.import("resource://enigmail/dialog.jsm"); /*global EnigmailDialog: false */ Cu.import("resource://enigmail/glodaUtils.jsm"); /*global GlodaUtils: false */ Cu.import("resource:///modules/MailUtils.js"); /*global MailUtils: false */ Cu.import("resource://enigmail/core.jsm"); /*global EnigmailCore: false */ Cu.import("resource://enigmail/gpg.jsm"); /*global EnigmailGpg: false */ Cu.import("resource://enigmail/streams.jsm"); /*global EnigmailStreams: false */ Cu.import("resource://enigmail/passwords.jsm"); /*global EnigmailPassword: false */ Cu.import("resource://enigmail/mime.jsm"); /*global EnigmailMime: false */ Cu.import("resource://enigmail/data.jsm"); /*global EnigmailData: false */ Cu.import("resource://enigmail/attachment.jsm"); /*global EnigmailAttachment: false */ Cu.import("resource://enigmail/timer.jsm"); /*global EnigmailTimer: false */ Cu.import("resource://enigmail/encryption.jsm"); /*global EnigmailEncryption: false */ +Cu.import("resource://enigmail/stdlib.jsm"); /*global EnigmailStdlib: false */ Cu.import("resource:///modules/jsmime.jsm"); /*global jsmime: false*/ /*global MimeBody: false, MimeUnknown: false, MimeMessageAttachment: false */ /*global msgHdrToMimeMessage: false, MimeMessage: false, MimeContainer: false */ Cu.import("resource://enigmail/glodaMime.jsm"); const getGpgAgent = EnigmailLazy.loader("enigmail/gpgAgent.jsm", "EnigmailGpgAgent"); var EC = EnigmailCore; const Cc = Components.classes; const Ci = Components.interfaces; const nsIEnigmail = Components.interfaces.nsIEnigmail; const STATUS_OK = 0; const STATUS_FAILURE = 1; const STATUS_NOT_REQUIRED = 2; const IOSERVICE_CONTRACTID = "@mozilla.org/network/io-service;1"; /* * Decrypt a message and copy it to a folder * * @param nsIMsgDBHdr hdr Header of the message * @param String destFolder Folder URI * @param Boolean move If true the original message will be deleted * * @return a Promise that we do that */ const EnigmailEncryptPermanently = { /*** * dispatchMessages * * Because Thunderbird throws all messages at once at us thus we have to rate limit the dispatching * of the message processing. Because there is only a negligible performance gain when dispatching * several message at once we serialize to not overwhelm low power devices. * * The function is implemented asynchronously. * * Parameters * aMsgHdrs: Array of nsIMsgDBHdr * targetFolder: String; target folder URI * copyListener: listener for async request (nsIMsgCopyServiceListener) * move: Boolean: type of action; true = "move" / false = "copy" * **/ dispatchMessages: function(aMsgHdrs, targetFolder, copyListener, move) { EnigmailLog.DEBUG("encryptPermanently.jsm: dispatchMessages()\n"); if (copyListener) { copyListener.OnStartCopy(); } let promise = EnigmailEncryptPermanently.encryptMessage(aMsgHdrs[0], targetFolder, move); var processNext = function(data) { aMsgHdrs.splice(0, 1); if (aMsgHdrs.length > 0) { EnigmailEncryptPermanently.dispatchMessages(aMsgHdrs, targetFolder, move); } else { // last message was finished processing if (copyListener) { copyListener.OnStopCopy(0); } EnigmailLog.DEBUG("encryptPermanently.jsm: dispatchMessages - DONE\n"); } }; promise.then(processNext); promise.catch(function(err) { processNext(null); }); }, encryptMessage: function(msgHdr, encryptTo) { return new Promise( function(resolve, reject) { let msgUriSpec = msgHdr.folder.getUriForMsg(msgHdr); const msgSvc = Cc["@mozilla.org/messenger;1"].createInstance(Ci.nsIMessenger).messageServiceFromURI(msgUriSpec); const encrypt = new EncryptMessageIntoFolder(resolve,encryptTo); try { msgHdrToMimeMessage(msgHdr, encrypt, encrypt.messageParseCallback, true, { examineEncryptedParts: false, partsOnDemand: false }); } catch (ex) { reject("msgHdrToMimeMessage failed"); } return; } ); } }; function EncryptMessageIntoFolder(resolve,encryptTo) { this.resolve = resolve; this.foundPGP = 0; this.mime = null; this.hdr = null; this.encryptionTasks = []; this.subject = ""; this.fromEmail = ""; this.encryptTo = encryptTo; } +function getMessage(aMessageHeader) { + let messenger = Cc["@mozilla.org/messenger;1"].createInstance(Ci.nsIMessenger); + let bodyListener = Cc["@mozilla.org/network/sync-stream-listener;1"].createInstance(Ci.nsISyncStreamListener); + let hdrListener = Cc["@mozilla.org/network/sync-stream-listener;1"].createInstance(Ci.nsISyncStreamListener); + let uri = aMessageHeader.folder.getUriForMsg(aMessageHeader); + + messenger.messageServiceFromURI(uri).streamMessage(uri, bodyListener, null, null, false, ""); + messenger.messageServiceFromURI(uri).streamHeaders(uri, hdrListener, null, false); + + let folder = aMessageHeader.folder; + let body = folder.getMsgTextFromStream(bodyListener.inputStream, aMessageHeader.Charset, 65536, 32768, false, true, { }); + let hdr = folder.getMsgTextFromStream(hdrListener.inputStream, aMessageHeader.Charset, 65536, 32768, false, true, { }); + + return {'hdr': hdr, 'body':body }; +} + EncryptMessageIntoFolder.prototype = { messageParseCallback: function(hdr, mime) { this.hdr = hdr; this.mime = mime; var self = this; try { if (!mime) { this.resolve(true); return; } if (!("content-type" in mime.headers)) { mime.headers["content-type"] = ["text/plain"]; } var ct = getContentType(getHeaderValue(mime, 'content-type')); var pt = getProtocol(getHeaderValue(mime, 'content-type')); this.subject = GlodaUtils.deMime(getHeaderValue(mime, 'subject')); this.fromEmail = GlodaUtils.deMime(getHeaderValue(mime, 'from')); if (!ct) { this.resolve(true); return; } - this.walkMimeTree(this.mime, this.mime); + //this.walkMimeTree(this.mime, this.mime); EnigmailLog.DEBUG("encrypt to: " + this.encryptTo + ", from: " + this.fromEmail + "\n"); - let exitCode = {}; - let errorMsg = {}; - - let msg = EnigmailEncryption.encryptMessage( - null, - nsIEnigmail.UI_UNVERIFIED_ENC_OK, - this.mimeToString(mime), - this.encryptTo, - this.fromEmail, - "", - nsIEnigmail.SAVE_MESSAGE | nsIEnigmail.SEND_ENCRYPTED | - nsIEnigmail.SEND_ALWAYS_TRUST | nsIEnigmail.SEND_PGP_MIME, - exitCode, {}, errorMsg); - let outHandler = { - 'deliverData': function(s) { EnigmailLog.DEBUG("got: " + s + "\n"); }, - 'deliverEOF': function() { EnigmailLog.DEBUG("got EOF\n"); } - }; - let enc = Cc["@enigmail.net/enigmail/composesecure;1"].createInstance(Ci.nsIMsgComposeSecure); - enc.beginCryptoEncapsulation({},"",null,null,null,false); - EnigmailLog.DEBUG("emit: " + jsmime.headeremitter.emitStructuredHeader("content-type", "lala", {})); + + let msgCompFields = Cc["@mozilla.org/messengercompose/composefields;1"].createInstance(Ci.nsIMsgCompFields); + let ident = EnigmailStdlib.getIdentityForEmail(this.encryptTo).identity; + let enc = Cc["@mozilla.org/messengercompose/composesecure;1"].createInstance(Ci.nsIMsgComposeSecure); + let pipe = Cc["@mozilla.org/pipe;1"].createInstance(Ci.nsIPipe); + let securityInfo = Cc["@mozdev.org/enigmail/composefields;1"].createInstance(Ci.nsIEnigMsgCompFields); + //let body = this.mimeToString(mime); + let message = getMessage(hdr); + let boundary = "lala"; + + pipe.init(false,false,0,0xffffffff,null); + + msgCompFields.subject = this.subject; + msgCompFields.from = this.fromEmail; + msgCompFields.body = message.body; + + let hh = + //message.hdr + '\r\n' + + 'Content-Type: multipart/mixed; boundary="' + boundary + '"\r\n'; + + let body = + '--' + boundary + '\r\n' + + 'Content-Type: ' + ct + '\r\n\r\n' + + message.body + '\r\n\r\n' + + '--' + boundary + '\r\n'; + + for(let a in mime.allAttachments) { + let att = mime.allAttachments[a]; + EnigmailLog.DEBUG("attachement " + JSON.stringify(att) + "\n"); + let strm = EnigmailStreams.createChannel(att.url).open(); + let val = EnigmailData.bytesToHex(NetUtil.readInputStreamToString(strm,strm.available())); + + body += 'Content-Type: ' + att.contentType + '\r\n' + + 'Content-Disposition: attachment; filename="' + att.name + '"\r\n\r\n' + + val + '\r\n' + + '--' + boundary + '--\r\n'; + } + + // XXX + // enable base64 encoding if non-ASCII character(s) found + let j = decryptedMessage.search(/[^\x01-\x7F]/); // eslint-disable-line no-control-regex + if (j >= 0) { + mime.headers['content-transfer-encoding'] = ['base64']; + mime.body = EnigmailData.encodeBase64(decryptedMessage); + } + else { + mime.body = decryptedMessage; + mime.headers['content-transfer-encoding'] = ['8bit']; + } + + let origCharset = getCharset(getHeaderValue(mime, 'content-type')); + if (origCharset) { + mime.headers['content-type'] = getHeaderValue(mime, 'content-type').replace(origCharset, charset); + } + else { + mime.headers['content-type'] = getHeaderValue(mime, 'content-type') + "; charset=" + charset; + } + + + securityInfo.sendFlags = Ci.nsIEnigmail.SEND_PGP_MIME | Ci.nsIEnigmail.SEND_ATTACHMENT | Ci.nsIEnigmail.SEND_ENCRYPTED; + securityInfo.senderEmailAddr = this.encryptTo; + securityInfo.originalSubject = this.subject; + securityInfo.recipients = this.encryptTo; + securityInfo.bccRecipients = ""; + msgCompFields.securityInfo = securityInfo; + + EnigmailLog.DEBUG("########## body ###########\n"); + EnigmailLog.DEBUG(body + "\n"); + EnigmailLog.DEBUG("########## end ############\n"); + + enc.requiresCryptoEncapsulation(ident,msgCompFields); + enc.beginCryptoEncapsulation(pipe.outputStream,this.encryptTo,msgCompFields,ident,null,false); + enc.mimeCryptoWriteBlock(hh,hh.length); + enc.mimeCryptoWriteBlock("\r\n",2); + enc.mimeCryptoWriteBlock(body,body.length); + enc.finishCryptoEncapsulation(false,null); + + //pipe.outputStream.close(); EnigmailLog.DEBUG("########## msg ###########\n"); - EnigmailLog.DEBUG(msg); - EnigmailLog.DEBUG("########## end ###########\n"); - EnigmailLog.DEBUG("exit: " + JSON.stringify(exitCode) + "\n"); - EnigmailLog.DEBUG("error: " + JSON.stringify(errorMsg) + "\n"); + EnigmailLog.DEBUG(NetUtil.readInputStreamToString(pipe.inputStream,pipe.inputStream.available())); + EnigmailLog.DEBUG("\n########## end ###########\n"); + } catch(ex) { EnigmailLog.DEBUG("caught: " + ex + "\n"); } }, readAttachment: function(attachment, strippedName) { return new Promise( function(resolve, reject) { - EnigmailLog.DEBUG("decryptPermanently.jsm: readAttachment\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: readAttachment\n"); let o; var f = function _cb(data) { - EnigmailLog.DEBUG("decryptPermanently.jsm: readAttachment - got data (" + data.length + ")\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: readAttachment - got data (" + data.length + ")\n"); o = { type: "attachment", data: data, name: strippedName ? strippedName : attachment.name, partName: attachment.partName, origName: attachment.name, status: STATUS_NOT_REQUIRED }; resolve(o); }; try { var bufferListener = EnigmailStreams.newStringStreamListener(f); var ioServ = Cc[IOSERVICE_CONTRACTID].getService(Components.interfaces.nsIIOService); var msgUri = ioServ.newURI(attachment.url, null, null); var channel = EnigmailStreams.createChannelFromURI(msgUri); channel.asyncOpen(bufferListener, msgUri); } catch (ex) { reject(o); } } ); }, decryptAttachment: function(attachment, strippedName) { var self = this; return new Promise( function(resolve, reject) { - EnigmailLog.DEBUG("decryptPermanently.jsm: decryptAttachment\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: decryptAttachment\n"); self.readAttachment(attachment, strippedName).then( function(o) { var attachmentHead = o.data.substr(0, 30); if (attachmentHead.match(/-----BEGIN PGP \w+ KEY BLOCK-----/)) { // attachment appears to be a PGP key file, we just go-a-head resolve(o); return; } var enigmailSvc = EnigmailCore.getService(); var args = EnigmailGpg.getStandardArgs(true); args = args.concat(EnigmailPassword.command()); args.push("-d"); var statusMsgObj = {}; var cmdLineObj = {}; var exitCode = -1; var statusFlagsObj = {}; var errorMsgObj = {}; statusFlagsObj.value = 0; var listener = EnigmailExecution.newSimpleListener( function _stdin(pipe) { // try to get original file name if file does not contain suffix if (strippedName.indexOf(".") < 0) { let s = EnigmailAttachment.getFileName(null, o.data); if (s) o.name = s; } pipe.write(o.data); pipe.close(); } ); do { var proc = EnigmailExecution.execStart(getGpgAgent().agentPath, args, false, null, listener, statusFlagsObj); if (!proc) { resolve(o); 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 & nsIEnigmail.DECRYPTION_OKAY)) { - EnigmailLog.DEBUG("decryptPermanently.jsm: decryptAttachment: decryption OK\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: decryptAttachment: decryption OK\n"); exitCode = 0; } else if (statusFlagsObj.value & nsIEnigmail.DECRYPTION_FAILED) { - EnigmailLog.DEBUG("decryptPermanently.jsm: decryptAttachment: decryption failed\n"); + EnigmailLog.DEBUG("encryptPermanently.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", [attachment.name, self.subject]); if (!EnigmailDialog.confirmDlg(null, msg, EnigmailLocale.getString("dlg.button.retry"), EnigmailLocale.getString("dlg.button.skip"))) { o.status = STATUS_FAILURE; resolve(o); return; } } else if (statusFlagsObj.value & nsIEnigmail.DECRYPTION_INCOMPLETE) { // failure; message not complete - EnigmailLog.DEBUG("decryptPermanently.jsm: decryptAttachment: decryption incomplete\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: decryptAttachment: decryption incomplete\n"); o.status = STATUS_FAILURE; resolve(o); return; } else { // there is nothing to be decrypted - EnigmailLog.DEBUG("decryptPermanently.jsm: decryptAttachment: no decryption required\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: decryptAttachment: no decryption required\n"); o.status = STATUS_NOT_REQUIRED; resolve(o); return; } } while (exitCode !== 0); - EnigmailLog.DEBUG("decryptPermanently.jsm: decryptAttachment: decrypted to " + listener.stdoutData.length + " bytes\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: decryptAttachment: decrypted to " + listener.stdoutData.length + " bytes\n"); o.data = listener.stdoutData; o.status = STATUS_OK; resolve(o); } ); } ); }, /* * The following functions walk the MIME message structure and decrypt if they find something to decrypt */ // the sunny world of PGP/MIME walkMimeTree: function(mime, parent) { - EnigmailLog.DEBUG("decryptPermanently.jsm: walkMimeTree:\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: walkMimeTree:\n"); let ct = getContentType(getHeaderValue(mime, 'content-type')); - EnigmailLog.DEBUG("decryptPermanently.jsm: walkMimeTree: part=" + mime.partName + " - " + ct + "\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: walkMimeTree: part=" + mime.partName + " - " + ct + "\n"); // assign part name on lowest possible level -> that's where the attachment // really belongs to for (let i in mime.allAttachments) { mime.allAttachments[i].partName = mime.partName; } if (this.isPgpMime(mime) || this.isSMime(mime)) { EnigmailLog.DEBUG("pgp mime part " + mime.partName + "\n"); - let p = this.decryptPGPMIME(parent, mime.partName); - this.decryptionTasks.push(p); + //let p = this.decryptPGPMIME(parent, mime.partName); + //this.decryptionTasks.push(p); } else if (this.isBrokenByExchange(mime)) { - let p = this.decryptAttachment(mime.parts[0].parts[2], "decrypted.txt"); + //let p = this.decryptAttachment(mime.parts[0].parts[2], "decrypted.txt"); mime.isBrokenByExchange = true; mime.parts[0].parts[2].name = "ignore.txt"; - this.decryptionTasks.push(p); + //this.decryptionTasks.push(p); } else if (typeof(mime.body) == "string") { EnigmailLog.DEBUG(" body size: " + mime.body.length + "\n"); } for (var i in mime.parts) { this.walkMimeTree(mime.parts[i], mime); } }, /*** * * Detect if mime part is PGP/MIME message that got modified by MS-Exchange: * * - multipart/mixed Container with * - application/pgp-encrypted Attachment with name "PGPMIME Version Identification" * - application/octet-stream Attachment with name "encrypted.asc" having the encrypted content in base64 * - see: * - http://www.mozilla-enigmail.org/forum/viewtopic.php?f=4&t=425 * - http://sourceforge.net/p/enigmail/forum/support/thread/4add2b69/ */ isBrokenByExchange: function(mime) { - EnigmailLog.DEBUG("decryptPermanently.jsm: isBrokenByExchange:\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: isBrokenByExchange:\n"); try { if (mime.parts && mime.parts.length && mime.parts.length == 1 && mime.parts[0].parts && mime.parts[0].parts.length && mime.parts[0].parts.length == 3 && mime.parts[0].headers["content-type"][0].indexOf("multipart/mixed") >= 0 && mime.parts[0].parts[0].size === 0 && mime.parts[0].parts[0].headers["content-type"][0].search(/multipart\/encrypted/i) < 0 && mime.parts[0].parts[0].headers["content-type"][0].indexOf("text/plain") >= 0 && mime.parts[0].parts[1].headers["content-type"][0].indexOf("application/pgp-encrypted") >= 0 && mime.parts[0].parts[1].headers["content-type"][0].search(/multipart\/encrypted/i) < 0 && mime.parts[0].parts[1].headers["content-type"][0].search(/PGPMIME Versions? Identification/i) >= 0 && mime.parts[0].parts[2].headers["content-type"][0].indexOf("application/octet-stream") >= 0 && mime.parts[0].parts[2].headers["content-type"][0].indexOf("encrypted.asc") >= 0) { - EnigmailLog.DEBUG("decryptPermanently.jsm: isBrokenByExchange: found message broken by MS-Exchange\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: isBrokenByExchange: found message broken by MS-Exchange\n"); return true; } } catch (ex) {} return false; }, isPgpMime: function(mime) { - EnigmailLog.DEBUG("decryptPermanently.jsm: isPgpMime:\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: isPgpMime:\n"); try { var ct = mime.contentType; if (!ct) return false; if (!('content-type' in mime.headers)) return false; var pt = getProtocol(getHeaderValue(mime, 'content-type')); if (!pt) return false; if (ct.toLowerCase() == "multipart/encrypted" && pt == "application/pgp-encrypted") { return true; } } catch (ex) { - //EnigmailLog.DEBUG("decryptPermanently.jsm: isPgpMime:"+ex+"\n"); + //EnigmailLog.DEBUG("encryptPermanently.jsm: isPgpMime:"+ex+"\n"); } return false; }, // smime-type=enveloped-data isSMime: function(mime) { - EnigmailLog.DEBUG("decryptPermanently.jsm: isSMime:\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: isSMime:\n"); try { var ct = mime.contentType; if (!ct) return false; if (!('content-type' in mime.headers)) return false; var pt = getSMimeProtocol(getHeaderValue(mime, 'content-type')); if (!pt) return false; if (ct.toLowerCase() == "application/pkcs7-mime" && pt == "enveloped-data") { return true; } } catch (ex) { - EnigmailLog.DEBUG("decryptPermanently.jsm: isSMime:" + ex + "\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: isSMime:" + ex + "\n"); } return false; }, decryptPGPMIME: function(mime, part) { - EnigmailLog.DEBUG("decryptPermanently.jsm: decryptPGPMIME: part=" + part + "\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: decryptPGPMIME: part=" + part + "\n"); var self = this; return new Promise( function(resolve, reject) { var m = Cc["@mozilla.org/messenger/mimeheaders;1"].createInstance(Ci.nsIMimeHeaders); var messenger = Cc["@mozilla.org/messenger;1"].getService(Ci.nsIMessenger); let msgSvc = messenger.messageServiceFromURI(self.hdr.folder.getUriForMsg(self.hdr)); let u = {}; msgSvc.GetUrlForUri(self.hdr.folder.getUriForMsg(self.hdr), u, null); let op = (u.value.spec.indexOf("?") > 0 ? "&" : "?"); let url = u.value.spec + op + 'part=' + part + "&header=enigmailConvert"; - EnigmailLog.DEBUG("decryptPermanently.jsm: getting data from URL " + url + "\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: getting data from URL " + url + "\n"); let s = EnigmailStreams.newStringStreamListener( function analyzeDecryptedData(data) { - EnigmailLog.DEBUG("decryptPermanently.jsm: analyzeDecryptedData: got " + data.length + " bytes\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: analyzeDecryptedData: got " + data.length + " bytes\n"); if (EnigmailLog.getLogLevel() > 5) { EnigmailLog.DEBUG("*** start data ***\n'" + data + "'\n***end data***\n"); } let subpart = mime.parts[0]; let o = { type: "mime", name: "", origName: "", data: "", partName: part, status: STATUS_OK }; if (data.length === 0) { // fail if no data found o.status = STATUS_FAILURE; resolve(o); return; } let bodyIndex = data.search(/\n\s*\r?\n/); if (bodyIndex < 0) { bodyIndex = 0; } else { ++bodyIndex; } if (data.substr(bodyIndex).search(/\r?\n$/) === 0) { o.status = STATUS_FAILURE; resolve(o); return; } m.initialize(data.substr(0, bodyIndex)); let ct = m.extractHeader("content-type", false) || ""; if (part.length > 0 && part.search(/[^01.]/) < 0) { if (ct.search(/protected-headers/i) >= 0) { if (m.hasHeader("subject")) { let subject = m.extractHeader("subject", false) || ""; self.mime.headers.subject = [subject]; } } else if (self.mime.headers.subject.join("") === "pEp") { let subject = getPepSubject(data); if (subject) { self.mime.headers.subject = [subject]; } } } let boundary = getBoundary(getHeaderValue(mime, 'content-type')); if (!boundary) boundary = EnigmailMime.createBoundary(); // append relevant headers mime.headers['content-type'] = "multipart/mixed; boundary=\"" + boundary + "\""; o.data = "--" + boundary + "\n"; o.data += "Content-Type: " + ct + "\n"; let h = m.headerNames; while (h.hasMore()) { let hdr = h.getNext(); if (hdr.search(/^content-type$/i) < 0) { try { - EnigmailLog.DEBUG("decryptPermanently.jsm: getUnstructuredHeader: hdr=" + hdr + "\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: getUnstructuredHeader: hdr=" + hdr + "\n"); let hdrVal = m.getUnstructuredHeader(hdr.toLowerCase()); o.data += hdr + ": " + hdrVal + "\n"; } catch (ex) { - EnigmailLog.DEBUG("decryptPermanently.jsm: getUnstructuredHeader: exception " + ex.toString() + "\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: getUnstructuredHeader: exception " + ex.toString() + "\n"); } } } - EnigmailLog.DEBUG("decryptPermanently.jsm: getUnstructuredHeader: done\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: getUnstructuredHeader: done\n"); o.data += data.substr(bodyIndex); if (subpart) { subpart.body = undefined; subpart.headers['content-type'] = ct; } resolve(o); } ); try { var channel = EnigmailStreams.createChannel(url); channel.asyncOpen(s, null); } catch (e) { - EnigmailLog.DEBUG("decryptPermanently.jsm: decryptPGPMIME: exception " + e.toString() + "\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: decryptPGPMIME: exception " + e.toString() + "\n"); } } ); }, //inline wonderland decryptINLINE: function(mime) { - EnigmailLog.DEBUG("decryptPermanently.jsm: decryptINLINE:\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: decryptINLINE:\n"); if (typeof mime.body !== 'undefined') { let ct = getContentType(getHeaderValue(mime, 'content-type')); if (ct == "text/html") { mime.body = this.stripHTMLFromArmoredBlocks(mime.body); } var enigmailSvc = EnigmailCore.getService(); var exitCodeObj = {}; var statusFlagsObj = {}; var userIdObj = {}; var sigDetailsObj = {}; var errorMsgObj = {}; var keyIdObj = {}; var blockSeparationObj = { value: "" }; var encToDetailsObj = {}; var signatureObj = {}; signatureObj.value = ""; var uiFlags = nsIEnigmail.UI_INTERACTIVE | nsIEnigmail.UI_UNVERIFIED_ENC_OK; var plaintexts = []; var blocks = EnigmailArmor.locateArmoredBlocks(mime.body); var tmp = []; for (let i = 0; i < blocks.length; i++) { if (blocks[i].blocktype == "MESSAGE") { tmp.push(blocks[i]); } } blocks = tmp; if (blocks.length < 1) { return 0; } let charset = "utf-8"; for (let i = 0; i < blocks.length; i++) { let plaintext = null; do { let ciphertext = mime.body.substring(blocks[i].begin, blocks[i].end + 1); if (ciphertext.length === 0) { break; } let hdr = ciphertext.search(/(\r\r|\n\n|\r\n\r\n)/); if (hdr > 0) { let chset = ciphertext.substr(0, hdr).match(/^(charset:)(.*)$/mi); if (chset && chset.length == 3) { charset = chset[2].trim(); } } plaintext = enigmailSvc.decryptMessage(null, uiFlags, ciphertext, signatureObj, exitCodeObj, statusFlagsObj, keyIdObj, userIdObj, sigDetailsObj, errorMsgObj, blockSeparationObj, encToDetailsObj); if (!plaintext || plaintext.length === 0) { if (statusFlagsObj.value & nsIEnigmail.DISPLAY_MESSAGE) { EnigmailDialog.alert(null, errorMsgObj.value); this.foundPGP = -1; return -1; } if (statusFlagsObj.value & nsIEnigmail.DECRYPTION_FAILED) { // since we cannot find out if the user wants to cancel // we should ask let msg = EnigmailLocale.getString("converter.decryptBody.failed", this.subject); if (!EnigmailDialog.confirmDlg(null, msg, EnigmailLocale.getString("dlg.button.retry"), EnigmailLocale.getString("dlg.button.skip"))) { this.foundPGP = -1; return -1; } } else if (statusFlagsObj.value & nsIEnigmail.DECRYPTION_INCOMPLETE) { this.foundPGP = -1; return -1; } } if (ct == "text/html") { plaintext = plaintext.replace(/\n/ig, "
\n"); } if (i == 0 && this.mime.headers.subject && this.mime.headers.subject[0] === "pEp" && mime.partName.length > 0 && mime.partName.search(/[^01.]/) < 0) { let m = EnigmailMime.extractSubjectFromBody(plaintext); if (m) { plaintext = m.messageBody; this.mime.headers.subject = [m.subject]; } } if (plaintext) { plaintexts.push(plaintext); } } while (!plaintext || plaintext === ""); } var decryptedMessage = mime.body.substring(0, blocks[0].begin) + plaintexts[0]; for (let i = 1; i < blocks.length; i++) { decryptedMessage += mime.body.substring(blocks[i - 1].end + 1, blocks[i].begin + 1) + plaintexts[i]; } decryptedMessage += mime.body.substring(blocks[(blocks.length - 1)].end + 1); // enable base64 encoding if non-ASCII character(s) found let j = decryptedMessage.search(/[^\x01-\x7F]/); // eslint-disable-line no-control-regex if (j >= 0) { mime.headers['content-transfer-encoding'] = ['base64']; mime.body = EnigmailData.encodeBase64(decryptedMessage); } else { mime.body = decryptedMessage; mime.headers['content-transfer-encoding'] = ['8bit']; } let origCharset = getCharset(getHeaderValue(mime, 'content-type')); if (origCharset) { mime.headers['content-type'] = getHeaderValue(mime, 'content-type').replace(origCharset, charset); } else { mime.headers['content-type'] = getHeaderValue(mime, 'content-type') + "; charset=" + charset; } this.foundPGP = 1; return 1; } if (typeof mime.parts !== 'undefined' && mime.parts.length > 0) { var ret = 0; for (let part in mime.parts) { ret += this.decryptINLINE(mime.parts[part]); } return ret; } let ct = getContentType(getHeaderValue(mime, 'content-type')); - EnigmailLog.DEBUG("decryptPermanently.jsm: Decryption skipped: " + ct + "\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: Decryption skipped: " + ct + "\n"); return 0; }, stripHTMLFromArmoredBlocks: function(text) { var index = 0; var begin = text.indexOf("-----BEGIN PGP"); var end = text.indexOf("-----END PGP"); while (begin > -1 && end > -1) { let sub = text.substring(begin, end); sub = sub.replace(/(<([^>]+)>)/ig, ""); sub = sub.replace(/&[A-z]+;/ig, ""); text = text.substring(0, begin) + sub + text.substring(end); index = end + 10; begin = text.indexOf("-----BEGIN PGP", index); end = text.indexOf("-----END PGP", index); } return text; }, /****** * * We have the technology we can rebuild. * * Function to reassemble the message from the MIME Tree * into a String. * ******/ mimeToString: function(mime, topLevel) { - EnigmailLog.DEBUG("decryptPermanently.jsm: mimeToString: part: '" + mime.partName + "', is of type '" + typeof(mime) + "'\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: mimeToString: part: '" + mime.partName + "', is of type '" + typeof(mime) + "'\n"); let ct = getContentType(getHeaderValue(mime, 'content-type')); if (!ct) { return ""; } let boundary = getBoundary(getHeaderValue(mime, 'content-type')); let msg = ""; if (mime.isBrokenByExchange) { - EnigmailLog.DEBUG("decryptPermanently.jsm: mimeToString: MS-Exchange fix\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: mimeToString: MS-Exchange fix\n"); for (let j in this.allTasks) { if (this.allTasks[j].partName == mime.parts[0].partName) { boundary = EnigmailMime.createBoundary(); msg += getRfc822Headers(mime.headers, ct, "content-type"); msg += 'Content-Type: multipart/mixed; boundary="' + boundary + '"\r\n\r\n'; msg += "This is a multi-part message in MIME format."; msg += "\r\n--" + boundary + "\r\n"; msg += this.allTasks[j].data; msg += "\r\n--" + boundary + "--\r\n"; return msg; } } } else if (mime instanceof MimeMessageAttachment) { for (let j in this.allTasks) { if (this.allTasks[j].partName == mime.partName && this.allTasks[j].origName == mime.name) { let a = this.allTasks[j]; - EnigmailLog.DEBUG("decryptPermanently.jsm: mimeToString: attaching " + j + " as '" + a.name + "'\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: mimeToString: attaching " + j + " as '" + a.name + "'\n"); for (let header in mime.headers) { if (!(a.status == STATUS_OK && header == "content-type")) { msg += prettyPrintHeader(header, mime.headers[header]) + "\r\n"; } } if (a.type == "attachment") { if (a.status == STATUS_OK) { msg += "Content-Type: application/octet-stream; name=\"" + a.name + "\"\r\n"; msg += "Content-Disposition: attachment; filename\"" + a.name + "\"\r\n"; } msg += "Content-Transfer-Encoding: base64\r\n\r\n"; msg += EnigmailData.encodeBase64(a.data) + "\r\n"; } } } } else if (mime instanceof MimeContainer || mime instanceof MimeUnknown) { for (let j in this.allTasks) { if (this.allTasks[j].partName == mime.partName && this.allTasks[j].type == "mime") { let a = this.allTasks[j]; msg += a.data; mime.noBottomBoundary = true; } } } else if (mime instanceof MimeMessage && ct.substr(0, 5) == "text/") { let subct = mime.parts[0].headers['content-type']; if (subct) { mime.headers['content-type'] = subct; } subct = mime.parts[0].headers['content-transfer-encoding']; if (subct) { mime.headers['content-transfer-encoding'] = subct; } msg += getRfc822Headers(mime.headers, ct); msg += "\r\n" + mime.parts[0].body + "\r\n"; return msg; } else { if (!topLevel && (mime instanceof MimeMessage)) { let mimeName = mime.name; if (!mimeName || mimeName === "") { mimeName = getHeaderValue(mime, 'subject') + ".eml"; } msg += 'Content-Type: message/rfc822; name="' + EnigmailMime.encodeHeaderValue(mimeName) + '\r\n'; msg += 'Content-Transfer-Encoding: 7bit\r\n'; msg += 'Content-Disposition: attachment; filename="' + EnigmailMime.encodeHeaderValue(mimeName) + '"\r\n\r\n'; } msg += getRfc822Headers(mime.headers, ct); msg += "\r\n"; if (mime.body) { msg += mime.body + "\r\n"; } else if ((mime instanceof MimeMessage) && ct.substr(0, 5) != "text/") { msg += "This is a multi-part message in MIME format.\r\n"; } } for (let i in mime.parts) { let subPart = this.mimeToString(mime.parts[i], false); if (subPart.length > 0) { if (boundary && !(mime instanceof MimeMessage)) { msg += "--" + boundary + "\r\n"; } msg += subPart + "\r\n"; } } if (ct.indexOf("multipart/") === 0 && !(mime instanceof MimeContainer)) { if (!mime.noBottomBoundary) { msg += "--" + boundary + "--\r\n"; } } return msg; } }; /** * Format a mime header * * e.g. content-type -> Content-Type */ function formatHeader(headerLabel) { return headerLabel.replace(/^.|(-.)/g, function(match) { return match.toUpperCase(); }); } function formatMimeHeader(headerLabel, headerValue) { if (headerLabel.search(/^(sender|from|reply-to|to|cc|bcc)$/i) === 0) { return formatHeader(headerLabel) + ": " + EnigmailMime.formatHeaderData(EnigmailMime.formatEmailAddress(headerValue)); } else { return formatHeader(headerLabel) + ": " + EnigmailMime.formatHeaderData(EnigmailMime.encodeHeaderValue(headerValue)); } } function prettyPrintHeader(headerLabel, headerData) { let hdrData = ""; if (Array.isArray(headerData)) { let h = []; for (let i in headerData) { h.push(formatMimeHeader(headerLabel, GlodaUtils.deMime(headerData[i]))); } return h.join("\r\n"); } else { return formatMimeHeader(headerLabel, GlodaUtils.deMime(String(headerData))); } } function getHeaderValue(mimeStruct, header) { - EnigmailLog.DEBUG("decryptPermanently.jsm: getHeaderValue: '" + header + "'\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: getHeaderValue: '" + header + "'\n"); try { if (header in mimeStruct.headers) { if (typeof mimeStruct.headers[header] == "string") { return mimeStruct.headers[header]; } else { return mimeStruct.headers[header].join(" "); } } else { return ""; } } catch (ex) { - EnigmailLog.DEBUG("decryptPermanently.jsm: getHeaderValue: header not present\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: getHeaderValue: header not present\n"); return ""; } } /*** * get the formatted headers for MimeMessage objects * * @headerArr: Array of headers (key/value pairs), such as mime.headers * @ignoreHeadersArr: Array of headers to exclude from returning * * @return: String containing formatted header list */ function getRfc822Headers(headerArr, contentType, ignoreHeadersArr) { let hdrs = ""; let ignore = []; if (contentType.indexOf("multipart/") >= 0) { ignore = ['content-transfer-encoding', 'content-disposition', 'content-description' ]; } if (ignoreHeadersArr) { ignore = ignore.concat(ignoreHeadersArr); } for (let i in headerArr) { if (ignore.indexOf(i) < 0) { hdrs += prettyPrintHeader(i, headerArr[i]) + "\r\n"; } } return hdrs; } function getContentType(shdr) { try { shdr = String(shdr); return shdr.match(/([A-z-]+\/[A-z-]+)/)[1].toLowerCase(); } catch (e) { - EnigmailLog.DEBUG("decryptPermanently.jsm: getContentType: " + e + "\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: getContentType: " + e + "\n"); return null; } } // return the content of the boundary parameter function getBoundary(shdr) { try { shdr = String(shdr); return EnigmailMime.getBoundary(shdr); } catch (e) { - EnigmailLog.DEBUG("decryptPermanently.jsm: getBoundary: " + e + "\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: getBoundary: " + e + "\n"); return null; } } function getCharset(shdr) { try { shdr = String(shdr); return EnigmailMime.getParameter(shdr, 'charset').toLowerCase(); } catch (e) { - EnigmailLog.DEBUG("decryptPermanently.jsm: getCharset: " + e + "\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: getCharset: " + e + "\n"); return null; } } function getProtocol(shdr) { try { shdr = String(shdr); return EnigmailMime.getProtocol(shdr).toLowerCase(); } catch (e) { - EnigmailLog.DEBUG("decryptPermanently.jsm: getProtocol: " + e + "\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: getProtocol: " + e + "\n"); return ""; } } function getSMimeProtocol(shdr) { try { shdr = String(shdr); return shdr.match(/smime-type="?([A-z0-9'()+_,-./:=?]+)"?/)[1].toLowerCase(); } catch (e) { - EnigmailLog.DEBUG("decryptPermanently.jsm: getSMimeProtocol: " + e + "\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: getSMimeProtocol: " + e + "\n"); return ""; } } function getPepSubject(mimeString) { - EnigmailLog.DEBUG("decryptPermanently.jsm: getPepSubject()\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: getPepSubject()\n"); let subject = null; let emitter = { ct: "", firstPlainText: false, startPart: function(partNum, headers) { - EnigmailLog.DEBUG("decryptPermanently.jsm: getPepSubject.startPart: partNum=" + partNum + "\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: getPepSubject.startPart: partNum=" + partNum + "\n"); try { this.ct = String(headers.getRawHeader("content-type")).toLowerCase(); if (!subject && !this.firstPlainText) { let s = headers.getRawHeader("subject"); if (s) { subject = String(s); this.firstPlainText = true; } } } catch (ex) { this.ct = ""; } }, endPart: function(partNum) {}, deliverPartData: function(partNum, data) { - EnigmailLog.DEBUG("decryptPermanently.jsm: getPepSubject.deliverPartData: partNum=" + partNum + " ct=" + this.ct + "\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: getPepSubject.deliverPartData: partNum=" + partNum + " ct=" + this.ct + "\n"); if (!this.firstPlainText && this.ct.search(/^text\/plain/) === 0) { // check data this.firstPlainText = true; let o = EnigmailMime.extractSubjectFromBody(data); if (o) { subject = o.subject; } } } }; let opt = { strformat: "unicode", bodyformat: "decode" }; try { let p = new jsmime.MimeParser(emitter, opt); p.deliverData(mimeString); } catch (ex) {} return subject; } /** * Lazy deletion of original messages */ function deleteOriginalMail(msgHdr) { - EnigmailLog.DEBUG("decryptPermanently.jsm: deleteOriginalMail(" + msgHdr.messageKey + ")\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: deleteOriginalMail(" + msgHdr.messageKey + ")\n"); let delMsg = function() { try { - EnigmailLog.DEBUG("decryptPermanently.jsm: deleting original message " + msgHdr.messageKey + "\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: deleting original message " + msgHdr.messageKey + "\n"); let folderInfoObj = {}; msgHdr.folder.getDBFolderInfoAndDB(folderInfoObj).DeleteMessage(msgHdr.messageKey, null, true); } catch (e) { - EnigmailLog.DEBUG("decryptPermanently.jsm: deletion failed. Error: " + e.toString() + "\n"); + EnigmailLog.DEBUG("encryptPermanently.jsm: deletion failed. Error: " + e.toString() + "\n"); } }; EnigmailTimer.setTimeout(delMsg, 500); } diff --git a/package/encryption.jsm b/package/encryption.jsm index 6e995d98..72c7fc68 100644 --- a/package/encryption.jsm +++ b/package/encryption.jsm @@ -1,493 +1,494 @@ /*global Components: false, EnigmailCore: false, EnigmailLog: false, EnigmailPrefs: false, EnigmailApp: false, EnigmailLocale: false, EnigmailDialog: false */ /*jshint -W097 */ /* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; var EXPORTED_SYMBOLS = ["EnigmailEncryption"]; const Cc = Components.classes; const Ci = Components.interfaces; const Cu = Components.utils; Cu.import("resource://enigmail/lazy.jsm"); /*global EnigmailLazy: false */ Cu.import("resource://enigmail/core.jsm"); Cu.import("resource://enigmail/data.jsm"); /*global EnigmailData: false */ Cu.import("resource://enigmail/log.jsm"); Cu.import("resource://enigmail/prefs.jsm"); Cu.import("resource://enigmail/app.jsm"); Cu.import("resource://enigmail/locale.jsm"); Cu.import("resource://enigmail/dialog.jsm"); Cu.import("resource://enigmail/gpgAgent.jsm"); /*global EnigmailGpgAgent: false */ Cu.import("resource://enigmail/gpg.jsm"); /*global EnigmailGpg: false */ Cu.import("resource://enigmail/errorHandling.jsm"); /*global EnigmailErrorHandling: false */ Cu.import("resource://enigmail/execution.jsm"); /*global EnigmailExecution: false */ Cu.import("resource://enigmail/files.jsm"); /*global EnigmailFiles: false */ Cu.import("resource://enigmail/passwords.jsm"); /*global EnigmailPassword: false */ Cu.import("resource://enigmail/funcs.jsm"); /*global EnigmailFuncs: false */ Cu.import("resource://enigmail/keyRing.jsm"); /*global EnigmailKeyRing: false */ const getGpgAgent = EnigmailLazy.loader("enigmail/gpgAgent.jsm", "EnigmailGpgAgent"); const nsIEnigmail = Ci.nsIEnigmail; var EC = EnigmailCore; const gMimeHashAlgorithms = [null, "sha1", "ripemd160", "sha256", "sha384", "sha512", "sha224", "md5"]; const ENC_TYPE_MSG = 0; const ENC_TYPE_ATTACH_BINARY = 1; const ENC_TYPE_ATTACH_ASCII = 2; const GPG_COMMENT_OPT = "Using GnuPG with %s - http://www.enigmail.net/"; const EnigmailEncryption = { getEncryptCommand: function(fromMailAddr, toMailAddr, bccMailAddr, hashAlgorithm, sendFlags, isAscii, errorMsgObj) { EnigmailLog.DEBUG("encryption.jsm: getEncryptCommand: hashAlgorithm=" + hashAlgorithm + "\n"); try { fromMailAddr = EnigmailFuncs.stripEmail(fromMailAddr); toMailAddr = EnigmailFuncs.stripEmail(toMailAddr); bccMailAddr = EnigmailFuncs.stripEmail(bccMailAddr); } catch (ex) { errorMsgObj.value = EnigmailLocale.getString("invalidEmail"); return null; } var defaultSend = sendFlags & nsIEnigmail.SEND_DEFAULT; var signMsg = sendFlags & nsIEnigmail.SEND_SIGNED; var encryptMsg = sendFlags & nsIEnigmail.SEND_ENCRYPTED; var usePgpMime = sendFlags & nsIEnigmail.SEND_PGP_MIME; var useDefaultComment = false; try { useDefaultComment = EnigmailPrefs.getPref("useDefaultComment"); } catch (ex) {} var hushMailSupport = false; try { hushMailSupport = EnigmailPrefs.getPref("hushMailSupport"); } catch (ex) {} var detachedSig = (usePgpMime || (sendFlags & nsIEnigmail.SEND_ATTACHMENT)) && signMsg && !encryptMsg; var toAddrList = toMailAddr.split(/\s*,\s*/); var bccAddrList = bccMailAddr.split(/\s*,\s*/); var k; var encryptArgs = EnigmailGpg.getStandardArgs(true); if (!useDefaultComment) encryptArgs = encryptArgs.concat(["--comment", GPG_COMMENT_OPT.replace(/%s/, EnigmailApp.getName())]); var angledFromMailAddr = ((fromMailAddr.search(/^0x/) === 0) || hushMailSupport) ? fromMailAddr : "<" + fromMailAddr + ">"; angledFromMailAddr = angledFromMailAddr.replace(/(["'`])/g, "\\$1"); if (signMsg && hashAlgorithm) { encryptArgs = encryptArgs.concat(["--digest-algo", hashAlgorithm]); } if (encryptMsg) { switch (isAscii) { case ENC_TYPE_MSG: encryptArgs.push("-a"); encryptArgs.push("-t"); break; case ENC_TYPE_ATTACH_ASCII: encryptArgs.push("-a"); } encryptArgs.push("--encrypt"); if (signMsg) encryptArgs.push("--sign"); if (sendFlags & nsIEnigmail.SEND_ALWAYS_TRUST) { encryptArgs.push("--trust-model"); encryptArgs.push("always"); } if ((sendFlags & nsIEnigmail.SEND_ENCRYPT_TO_SELF) && fromMailAddr) encryptArgs = encryptArgs.concat(["--encrypt-to", angledFromMailAddr]); for (k = 0; k < toAddrList.length; k++) { toAddrList[k] = toAddrList[k].replace(/'/g, "\\'"); if (toAddrList[k].length > 0) { encryptArgs.push("-r"); if (toAddrList[k].search(/^GROUP:/) === 0) { // groups from gpg.conf file encryptArgs.push(toAddrList[k].substr(6)); } else { encryptArgs.push((hushMailSupport || (toAddrList[k].search(/^0x/) === 0)) ? toAddrList[k] : "<" + toAddrList[k] + ">"); } } } for (k = 0; k < bccAddrList.length; k++) { bccAddrList[k] = bccAddrList[k].replace(/'/g, "\\'"); if (bccAddrList[k].length > 0) { encryptArgs.push("--hidden-recipient"); encryptArgs.push((hushMailSupport || (bccAddrList[k].search(/^0x/) === 0)) ? bccAddrList[k] : "<" + bccAddrList[k] + ">"); } } } else if (detachedSig) { encryptArgs = encryptArgs.concat(["-s", "-b"]); switch (isAscii) { case ENC_TYPE_MSG: encryptArgs = encryptArgs.concat(["-a", "-t"]); break; case ENC_TYPE_ATTACH_ASCII: encryptArgs.push("-a"); } } else if (signMsg) { encryptArgs = encryptArgs.concat(["-t", "--clearsign"]); } if (fromMailAddr) { encryptArgs = encryptArgs.concat(["-u", angledFromMailAddr]); } return encryptArgs; }, /** * Determine if the sender key ID or user ID can be used for signing and/or encryption * * @param sendFlags: Number - the send Flags; need to contain SEND_SIGNED and/or SEND_ENCRYPTED * @param fromMailAddr: String - the sender email address or key ID * * @return Object: * - keyId: String - the found key ID, or null if fromMailAddr is not valid * - errorMsg: String - the erorr message if key not valid, or null if key is valid */ determineOwnKeyUsability: function(sendFlags, fromMailAddr) { EnigmailLog.DEBUG("encryption.jsm: determineOwnKeyUsability: sendFlags=" + sendFlags + ", sender=" + fromMailAddr + "\n"); let keyList = []; let ret = { keyId: null, errorMsg: null }; let sign = (sendFlags & nsIEnigmail.SEND_SIGNED ? true : false); let encrypt = (sendFlags & nsIEnigmail.SEND_ENCRYPTED ? true : false); if (fromMailAddr.search(/^(0x)?[A-Z0-9]+$/) === 0) { // key ID specified let key = EnigmailKeyRing.getKeyById(fromMailAddr); keyList.push(key); } else { // email address specified keyList = EnigmailKeyRing.getKeysByUserId(fromMailAddr); } if (keyList.length === 0) { + EnigmailLog.DEBUG("encryption.jsm: determineOwnKeyUsability: no key for " + fromMailAddr + "\n"); ret.errorMsg = EnigmailLocale.getString("errorOwnKeyUnusable", fromMailAddr); return ret; } if (sign) { keyList = keyList.reduce(function _f(p, keyObj) { if (keyObj.getSigningValidity().keyValid) p.push(keyObj); return p; }, []); } if (encrypt) { keyList = keyList.reduce(function _f(p, keyObj) { if (keyObj && keyObj.getEncryptionValidity().keyValid) p.push(keyObj); return p; }, []); } if (keyList.length === 0) { if (sign) { ret.errorMsg = EnigmailErrorHandling.determineInvSignReason(fromMailAddr); } else { ret.errorMsg = EnigmailErrorHandling.determineInvRcptReason(fromMailAddr); } } else { ret.keyId = keyList[0].fpr; } return ret; }, encryptMessageStart: function(win, uiFlags, fromMailAddr, toMailAddr, bccMailAddr, hashAlgorithm, sendFlags, listener, statusFlagsObj, errorMsgObj) { EnigmailLog.DEBUG("encryption.jsm: encryptMessageStart: uiFlags=" + uiFlags + ", from " + fromMailAddr + " to " + toMailAddr + ", hashAlgorithm=" + hashAlgorithm + " (" + EnigmailData.bytesToHex( EnigmailData.pack(sendFlags, 4)) + ")\n"); let keyUseability = this.determineOwnKeyUsability(sendFlags, fromMailAddr); if (!keyUseability.keyId) { EnigmailLog.DEBUG("encryption.jsm: encryptMessageStart: own key invalid\n"); errorMsgObj.value = keyUseability.errorMsg; statusFlagsObj.value = nsIEnigmail.INVALID_RECIPIENT | nsIEnigmail.NO_SECKEY | nsIEnigmail.DISPLAY_MESSAGE; return null; } var pgpMime = uiFlags & nsIEnigmail.UI_PGP_MIME; var hashAlgo = gMimeHashAlgorithms[EnigmailPrefs.getPref("mimeHashAlgorithm")]; if (hashAlgorithm) { hashAlgo = hashAlgorithm; } errorMsgObj.value = ""; if (!sendFlags) { EnigmailLog.DEBUG("encryption.jsm: encryptMessageStart: NO ENCRYPTION!\n"); errorMsgObj.value = EnigmailLocale.getString("notRequired"); return null; } if (!EnigmailCore.getService(win)) { EnigmailLog.ERROR("encryption.jsm: encryptMessageStart: not yet initialized\n"); errorMsgObj.value = EnigmailLocale.getString("notInit"); return null; } var encryptArgs = EnigmailEncryption.getEncryptCommand(fromMailAddr, toMailAddr, bccMailAddr, hashAlgo, sendFlags, ENC_TYPE_MSG, errorMsgObj); if (!encryptArgs) return null; var signMsg = sendFlags & nsIEnigmail.SEND_SIGNED; var proc = EnigmailExecution.execStart(getGpgAgent().agentPath, encryptArgs, signMsg, win, listener, statusFlagsObj); if (statusFlagsObj.value & nsIEnigmail.MISSING_PASSPHRASE) { EnigmailLog.ERROR("encryption.jsm: encryptMessageStart: Error - no passphrase supplied\n"); errorMsgObj.value = ""; } if (pgpMime && errorMsgObj.value) { EnigmailDialog.alert(win, errorMsgObj.value); } return proc; }, encryptMessageEnd: function(fromMailAddr, stderrStr, exitCode, uiFlags, sendFlags, outputLen, retStatusObj) { EnigmailLog.DEBUG("encryption.jsm: encryptMessageEnd: uiFlags=" + uiFlags + ", sendFlags=" + EnigmailData.bytesToHex(EnigmailData.pack(sendFlags, 4)) + ", outputLen=" + outputLen + "\n"); var pgpMime = uiFlags & nsIEnigmail.UI_PGP_MIME; var defaultSend = sendFlags & nsIEnigmail.SEND_DEFAULT; var signMsg = sendFlags & nsIEnigmail.SEND_SIGNED; var encryptMsg = sendFlags & nsIEnigmail.SEND_ENCRYPTED; retStatusObj.statusFlags = 0; retStatusObj.errorMsg = ""; retStatusObj.blockSeparation = ""; if (!EnigmailCore.getService().initialized) { EnigmailLog.ERROR("encryption.jsm: encryptMessageEnd: not yet initialized\n"); retStatusObj.errorMsg = EnigmailLocale.getString("notInit"); return -1; } EnigmailErrorHandling.parseErrorOutput(stderrStr, retStatusObj); exitCode = EnigmailExecution.fixExitCode(exitCode, retStatusObj); if ((exitCode === 0) && !outputLen) { exitCode = -1; } if (exitCode !== 0 && (signMsg || encryptMsg)) { // GnuPG might return a non-zero exit code, even though the message was correctly // signed or encryped -> try to fix the exit code var correctedExitCode = 0; if (signMsg) { if (!(retStatusObj.statusFlags & nsIEnigmail.SIG_CREATED)) correctedExitCode = exitCode; } if (encryptMsg) { if (!(retStatusObj.statusFlags & nsIEnigmail.END_ENCRYPTION)) correctedExitCode = exitCode; } exitCode = correctedExitCode; } EnigmailLog.DEBUG("encryption.jsm: encryptMessageEnd: command execution exit code: " + exitCode + "\n"); if (retStatusObj.statusFlags & nsIEnigmail.DISPLAY_MESSAGE) { if (retStatusObj.extendedStatus.search(/\bdisp:/) >= 0) { retStatusObj.errorMsg = retStatusObj.statusMsg; } else { if (fromMailAddr.search(/^0x/) === 0) { fromMailAddr = fromMailAddr.substr(2); } if (fromMailAddr.search(/^[A-F0-9]{8,40}$/i) === 0) { fromMailAddr = "[A-F0-9]+" + fromMailAddr; } let s = new RegExp("^(\\[GNUPG:\\] )?INV_(RECP|SGNR) [0-9]+ (\\<|0x)?" + fromMailAddr + "\\>?", "m"); if (retStatusObj.statusMsg.search(s) >= 0) { retStatusObj.errorMsg += "\n\n" + EnigmailLocale.getString("keyError.resolutionAction"); } else if (retStatusObj.statusMsg.length > 0) { retStatusObj.errorMsg = retStatusObj.statusMsg; } } } else if (retStatusObj.statusFlags & nsIEnigmail.INVALID_RECIPIENT) { retStatusObj.errorMsg = retStatusObj.statusMsg; } else if (exitCode !== 0) { retStatusObj.errorMsg = EnigmailLocale.getString("badCommand"); } return exitCode; }, 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"); exitCodeObj.value = -1; statusFlagsObj.value = 0; errorMsgObj.value = ""; if (!plainText) { EnigmailLog.DEBUG("enigmail.js: Enigmail.encryptMessage: NO ENCRYPTION!\n"); exitCodeObj.value = 0; - EnigmailLog.DEBUG(" <=== encryptMessage()\n"); + EnigmailLog.DEBUG(" <=== encryptMessage(1)\n"); return plainText; } var defaultSend = sendFlags & nsIEnigmail.SEND_DEFAULT; var signMsg = sendFlags & nsIEnigmail.SEND_SIGNED; var encryptMsg = sendFlags & nsIEnigmail.SEND_ENCRYPTED; if (encryptMsg) { // First convert all linebreaks to newlines plainText = plainText.replace(/\r\n/g, "\n"); plainText = plainText.replace(/\r/g, "\n"); // we need all data in CRLF according to RFC 4880 plainText = plainText.replace(/\n/g, "\r\n"); } var listener = EnigmailExecution.newSimpleListener( function _stdin(pipe) { pipe.write(plainText); pipe.close(); }, function _done(exitCode) {}); var proc = EnigmailEncryption.encryptMessageStart(parent, uiFlags, fromMailAddr, toMailAddr, bccMailAddr, null, sendFlags, listener, statusFlagsObj, errorMsgObj); if (!proc) { exitCodeObj.value = -1; - EnigmailLog.DEBUG(" <=== encryptMessage()\n"); + EnigmailLog.DEBUG(" <=== encryptMessage(2)\n"); return ""; } // Wait for child pipes to close proc.wait(); var retStatusObj = {}; exitCodeObj.value = EnigmailEncryption.encryptMessageEnd(fromMailAddr, EnigmailData.getUnicodeData(listener.stderrData), listener.exitCode, uiFlags, sendFlags, listener.stdoutData.length, retStatusObj); statusFlagsObj.value = retStatusObj.statusFlags; statusFlagsObj.statusMsg = retStatusObj.statusMsg; errorMsgObj.value = retStatusObj.errorMsg; if ((exitCodeObj.value === 0) && listener.stdoutData.length === 0) exitCodeObj.value = -1; if (exitCodeObj.value === 0) { // Normal return - EnigmailLog.DEBUG(" <=== encryptMessage()\n"); + EnigmailLog.DEBUG(" <=== encryptMessage(3)\n"); return EnigmailData.getUnicodeData(listener.stdoutData); } // 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 |= nsIEnigmail.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 & nsIEnigmail.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; }, registerOn: function(target) { target.encryptMessage = EnigmailEncryption.encryptMessage; target.encryptAttachment = EnigmailEncryption.encryptAttachment; } }; diff --git a/package/mimeEncrypt.js b/package/mimeEncrypt.js index 10b855ab..b1c2b018 100644 --- a/package/mimeEncrypt.js +++ b/package/mimeEncrypt.js @@ -1,793 +1,794 @@ /*global Components: false, EnigmailLog: false, EnigmailDialog: false, EnigmailFuncs: false */ /*jshint -W097 */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; /** * Module for creating PGP/MIME signed and/or encrypted messages * implemented as XPCOM component */ const Cc = Components.classes; const Ci = Components.interfaces; const Cr = Components.results; const Cu = Components.utils; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); /*global XPCOMUtils: false */ Cu.import("resource:///modules/jsmime.jsm"); /*global jsmime: false*/ Cu.import("resource://enigmail/funcs.jsm"); Cu.import("resource://enigmail/dialog.jsm"); Cu.import("resource://enigmail/log.jsm"); Cu.import("resource://enigmail/encryption.jsm"); /*global EnigmailEncryption: false */ Cu.import("resource://enigmail/mime.jsm"); /*global EnigmailMime: false */ Cu.import("resource://enigmail/hash.jsm"); /*global EnigmailHash: false */ Cu.import("resource://enigmail/data.jsm"); /*global EnigmailData: false */ Cu.import("resource://enigmail/pEpAdapter.jsm"); /*global EnigmailPEPAdapter: false */ const PGPMIME_JS_ENCRYPT_CONTRACTID = "@enigmail.net/enigmail/composesecure;1"; const PGPMIME_JS_ENCRYPT_CID = Components.ID("{1b040e64-e704-42b9-b05a-942e569afffc}"); const APPSHELL_MEDIATOR_CONTRACTID = "@mozilla.org/appshell/window-mediator;1"; const kMsgComposeSecureCID = "{dd753201-9a23-4e08-957f-b3616bf7e012}"; const maxBufferLen = 102400; const MIME_SIGNED = 1; const MIME_ENCRYPTED = 2; -var gDebugLogLevel = 0; +var gDebugLogLevel = 9999; function PgpMimeEncrypt() {} PgpMimeEncrypt.prototype = { classDescription: "Enigmail JS Encryption Handler", classID: PGPMIME_JS_ENCRYPT_CID, contractID: PGPMIME_JS_ENCRYPT_CONTRACTID, QueryInterface: XPCOMUtils.generateQI([Ci.nsIMsgComposeSecure, Ci.nsIStreamListener, Ci.nsIEnigScriptableMsgCompose]), // private variables inStream: Cc["@mozilla.org/scriptableinputstream;1"].createInstance(Ci.nsIScriptableInputStream), msgCompFields: null, smimeCompose: null, useSmime: false, outStringStream: null, // 0: processing headers // 1: processing body // 2: skipping header inputMode: 0, dataLength: 0, headerData: "", encapsulate: null, encHeader: null, cryptoBoundary: null, win: null, pipe: null, proc: null, statusStr: "", encryptedData: "", hashAlgorithm: null, pipeQueue: "", outQueue: "", closePipe: false, cryptoMode: 0, exitCode: -1, inspector: null, checkSMime: true, // nsIStreamListener interface onStartRequest: function(request) { EnigmailLog.DEBUG("mimeEncrypt.js: onStartRequest\n"); this.encHeader = null; }, onDataAvailable: function(req, sup, stream, offset, count) { LOCAL_DEBUG("mimeEncrypt.js: onDataAvailable\n"); this.inStream.init(stream); var data = this.inStream.read(count); //LOCAL_DEBUG("mimeEncrypt.js: >"+data+"<\n"); }, onStopRequest: function(request, win, status) { EnigmailLog.DEBUG("mimeEncrypt.js: onStopRequest\n"); }, disableSMimeCheck: function() { this.useSmime = false; this.checkSMime = false; }, // nsIMsgComposeSecure interface requiresCryptoEncapsulation: function(msgIdentity, msgCompFields) { EnigmailLog.DEBUG("mimeEncrypt.js: requiresCryptoEncapsulation\n"); try { if (EnigmailPEPAdapter.usingPep()) { try { return msgIdentity.getBoolAttribute("enablePEP"); } catch (ex) { return false; } } else { if (this.checkSMime) { // Remember to use original CID, not CONTRACTID, to avoid infinite looping! this.smimeCompose = Components.classesByID[kMsgComposeSecureCID].createInstance(Ci.nsIMsgComposeSecure); this.useSmime = this.smimeCompose.requiresCryptoEncapsulation(msgIdentity, msgCompFields); } if (this.useSmime) return true; var securityInfo = msgCompFields.securityInfo; if (!securityInfo) return false; try { var enigSecurityInfo = securityInfo.QueryInterface(Ci.nsIEnigMsgCompFields); return (enigSecurityInfo.sendFlags & (Ci.nsIEnigmail.SEND_SIGNED | Ci.nsIEnigmail.SEND_ENCRYPTED | Ci.nsIEnigmail.SEND_VERBATIM)) !== 0; } catch (ex) { return false; } } } catch (ex) { EnigmailLog.writeException("mimeEncrypt.js", ex); throw (ex); } }, beginCryptoEncapsulation: function(outStream, recipientList, msgCompFields, msgIdentity, sendReport, isDraft) { EnigmailLog.DEBUG("mimeEncrypt.js: beginCryptoEncapsulation\n"); if (EnigmailPEPAdapter.usingPep()) { this.recipientList = recipientList; this.msgIdentity = msgIdentity; this.outStream = outStream; this.msgCompFields = msgCompFields; this.isDraft = isDraft; this.inspector = Cc["@mozilla.org/jsinspector;1"].createInstance(Ci.nsIJSInspector); this.outStringStream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(Ci.nsIStringInputStream); return null; } if (this.checkSMime && (!this.smimeCompose)) { LOCAL_DEBUG("mimeEncrypt.js: beginCryptoEncapsulation: ERROR MsgComposeSecure not instantiated\n"); throw Cr.NS_ERROR_FAILURE; } if (this.useSmime) return this.smimeCompose.beginCryptoEncapsulation(outStream, recipientList, msgCompFields, msgIdentity, sendReport, isDraft); if (!outStream) throw Cr.NS_ERROR_NULL_POINTER; try { this.outStream = outStream; this.isDraft = isDraft; this.msgCompFields = msgCompFields; var securityInfo = msgCompFields.securityInfo; if (!securityInfo) throw Cr.NS_ERROR_FAILURE; - this.enigSecurityInfo = securityInfo.QueryInterface(Ci.nsIEnigMsgCompFields); //might throw an error this.outStringStream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(Ci.nsIStringInputStream); var windowManager = Cc[APPSHELL_MEDIATOR_CONTRACTID].getService(Ci.nsIWindowMediator); this.win = windowManager.getMostRecentWindow(null); if (this.enigSecurityInfo.sendFlags & Ci.nsIEnigmail.SEND_VERBATIM) { this.recipientList = recipientList; this.msgIdentity = msgIdentity; this.msgCompFields = msgCompFields; this.inputMode = 2; return null; } if (securityInfo.sendFlags & Ci.nsIEnigmail.SEND_PGP_MIME) { if (securityInfo.sendFlags & Ci.nsIEnigmail.SEND_ENCRYPTED) { // applies to encrypted and signed & encrypted this.cryptoMode = MIME_ENCRYPTED; } else if (securityInfo.sendFlags & Ci.nsIEnigmail.SEND_SIGNED) { this.cryptoMode = MIME_SIGNED; let hashAlgoObj = {}; if (EnigmailHash.determineAlgorithm(this.win, this.enigSecurityInfo.UIFlags, this.enigSecurityInfo.senderEmailAddr, hashAlgoObj) === 0) { this.hashAlgorithm = hashAlgoObj.value; } else throw Cr.NS_ERROR_FAILURE; } } else throw Cr.NS_ERROR_NOT_IMPLEMENTED; var statusFlagsObj = {}; var errorMsgObj = {}; + EnigmailLog.DEBUG("mimeEncrypt.js: beginCryptoEncapsulation ++++++++++++++++++++++++++++++++++++++++++++++\n"); this.proc = EnigmailEncryption.encryptMessageStart(this.win, this.enigSecurityInfo.UIFlags, this.enigSecurityInfo.senderEmailAddr, this.enigSecurityInfo.recipients, this.enigSecurityInfo.bccRecipients, this.hashAlgorithm, this.enigSecurityInfo.sendFlags, this, statusFlagsObj, errorMsgObj); if (!this.proc) throw Cr.NS_ERROR_FAILURE; + EnigmailLog.DEBUG("mimeEncrypt.js: beginCryptoEncapsulation ##############################################\n"); this.cryptoBoundary = EnigmailMime.createBoundary(); this.startCryptoHeaders(); } catch (ex) { EnigmailLog.writeException("mimeEncrypt.js", ex); throw (ex); } return null; }, startCryptoHeaders: function() { EnigmailLog.DEBUG("mimeEncrypt.js: startCryptoHeaders\n"); if (this.cryptoMode == MIME_SIGNED) this.signedHeaders1(false); if (this.cryptoMode == MIME_ENCRYPTED) this.encryptedHeaders(); this.writeSecureHeaders(); }, writeSecureHeaders: function() { this.encHeader = EnigmailMime.createBoundary(); let allHdr = ""; let visibleHdr = ""; let addrParser = jsmime.headerparser.parseAddressingHeader; let noParser = function(s) { return s; }; let h = { from: { field: "From", parser: addrParser }, replyTo: { field: "Reply-To", parser: addrParser }, to: { field: "To", parser: addrParser }, cc: { field: "Cc", parser: addrParser }, newsgroups: { field: "Newsgroups", parser: noParser }, followupTo: { field: "Followup-To", parser: addrParser }, messageId: { field: "Message-Id", parser: noParser } }; // visible headers list let vH = [ 'from', 'to', 'subject', 'cc' ]; for (let i in h) { if (this.msgCompFields[i] && this.msgCompFields[i].length > 0) { allHdr += jsmime.headeremitter.emitStructuredHeader(h[i].field, h[i].parser(this.msgCompFields[i]), {}); } if (vH.indexOf(i) >= 0 && this.msgCompFields[i].length > 0) { visibleHdr += jsmime.headeremitter.emitStructuredHeader(h[i].field, h[i].parser(this.msgCompFields[i]), {}); } } if (this.enigSecurityInfo.originalSubject && this.enigSecurityInfo.originalSubject.length > 0) { allHdr += jsmime.headeremitter.emitStructuredHeader("subject", this.enigSecurityInfo.originalSubject, {}); visibleHdr += jsmime.headeremitter.emitStructuredHeader("subject", this.enigSecurityInfo.originalSubject, {}); } // special handling for references and in-reply-to if (this.enigSecurityInfo.originalReferences && this.enigSecurityInfo.originalReferences.length > 0) { allHdr += jsmime.headeremitter.emitStructuredHeader("references", this.enigSecurityInfo.originalReferences, {}); let bracket = this.enigSecurityInfo.originalReferences.lastIndexOf("<"); if (bracket >= 0) { allHdr += jsmime.headeremitter.emitStructuredHeader("in-reply-to", this.enigSecurityInfo.originalReferences.substr(bracket), {}); } } let w = 'Content-Type: multipart/mixed; boundary="' + this.encHeader + '";\r\n' + ' protected-headers="v1"\r\n' + allHdr + '\r\n' + "--" + this.encHeader + "\r\n"; if (this.cryptoMode == MIME_ENCRYPTED && this.enigSecurityInfo.sendFlags & Ci.nsIEnigmail.ENCRYPT_HEADERS) { w += 'Content-Type: text/rfc822-headers; protected-headers="v1"\r\n' + 'Content-Disposition: inline\r\n' + visibleHdr + "\r\n--" + this.encHeader + "\r\n"; } this.writeToPipe(w); if (this.cryptoMode == MIME_SIGNED) this.writeOut(w); }, encryptedHeaders: function(isEightBit) { EnigmailLog.DEBUG("mimeEncrypt.js: encryptedHeaders\n"); let subj = ""; if (this.enigSecurityInfo.sendFlags & Ci.nsIEnigmail.ENCRYPT_HEADERS) { subj = jsmime.headeremitter.emitStructuredHeader("subject", EnigmailFuncs.getProtectedSubjectText(), {}); } this.writeOut(subj + "Content-Type: multipart/encrypted;\r\n" + " protocol=\"application/pgp-encrypted\";\r\n" + " boundary=\"" + this.cryptoBoundary + "\"\r\n" + "\r\n" + "This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156)\r\n" + "--" + this.cryptoBoundary + "\r\n" + "Content-Type: application/pgp-encrypted\r\n" + "Content-Description: PGP/MIME version identification\r\n" + "\r\n" + "Version: 1\r\n" + "\r\n" + "--" + this.cryptoBoundary + "\r\n" + "Content-Type: application/octet-stream; name=\"encrypted.asc\"\r\n" + "Content-Description: OpenPGP encrypted message\r\n" + "Content-Disposition: inline; filename=\"encrypted.asc\"\r\n" + "\r\n"); }, signedHeaders1: function(isEightBit) { LOCAL_DEBUG("mimeEncrypt.js: signedHeaders1\n"); this.writeOut("Content-Type: multipart/signed; micalg=pgp-" + this.hashAlgorithm.toLowerCase() + ";\r\n" + " protocol=\"application/pgp-signature\";\r\n" + " boundary=\"" + this.cryptoBoundary + "\"\r\n" + (isEightBit ? "Content-Transfer-Encoding: 8bit\r\n\r\n" : "\r\n") + "This is an OpenPGP/MIME signed message (RFC 4880 and 3156)\r\n" + "--" + this.cryptoBoundary + "\r\n"); }, signedHeaders2: function() { LOCAL_DEBUG("mimeEncrypt.js: signedHeaders2\n"); this.writeOut("\r\n--" + this.cryptoBoundary + "\r\n" + "Content-Type: application/pgp-signature; name=\"signature.asc\"\r\n" + "Content-Description: OpenPGP digital signature\r\n" + "Content-Disposition: attachment; filename=\"signature.asc\"\r\n\r\n"); }, finishCryptoHeaders: function() { EnigmailLog.DEBUG("mimeEncrypt.js: finishCryptoHeaders\n"); this.writeOut("\r\n--" + this.cryptoBoundary + "--\r\n"); }, finishCryptoEncapsulation: function(abort, sendReport) { EnigmailLog.DEBUG("mimeEncrypt.js: finishCryptoEncapsulation\n"); if (EnigmailPEPAdapter.usingPep()) { this.processPepEncryption(); return; } else if ((this.enigSecurityInfo.sendFlags & Ci.nsIEnigmail.SEND_VERBATIM) !== 0) { this.flushOutput(); return; } if (this.checkSMime && (!this.smimeCompose)) throw Cr.NS_ERROR_NOT_INITIALIZED; if (this.useSmime) { this.smimeCompose.finishCryptoEncapsulation(abort, sendReport); return; } try { if (this.encapsulate) this.writeToPipe("--" + this.encapsulate + "--\r\n"); if (this.encHeader) { this.writeToPipe("\r\n--" + this.encHeader + "--\r\n"); if (this.cryptoMode == MIME_SIGNED) this.writeOut("\r\n--" + this.encHeader + "--\r\n"); } if (!this.proc) return; this.flushInput(); if (!this.pipe) { this.closePipe = true; } else this.pipe.close(); // wait here for this.proc to terminate this.proc.wait(); LOCAL_DEBUG("mimeEncrypt.js: finishCryptoEncapsulation: exitCode = " + this.exitCode + "\n"); if (this.exitCode !== 0) throw Cr.NS_ERROR_FAILURE; if (this.cryptoMode == MIME_SIGNED) this.signedHeaders2(); this.encryptedData = this.encryptedData.replace(/\r/g, "").replace(/\n/g, "\r\n"); // force CRLF this.writeOut(this.encryptedData); this.finishCryptoHeaders(); this.flushOutput(); } catch (ex) { EnigmailLog.writeException("mimeEncrypt.js", ex); throw (ex); } }, mimeCryptoWriteBlock: function(buffer, length) { if (gDebugLogLevel > 4) LOCAL_DEBUG("mimeEncrypt.js: mimeCryptoWriteBlock: " + length + "\n"); if (EnigmailPEPAdapter.usingPep()) { this.pipeQueue += buffer.substr(0, length); return null; } if (this.checkSMime && (!this.smimeCompose)) throw Cr.NS_ERROR_NOT_INITIALIZED; if (this.useSmime) return this.smimeCompose.mimeCryptoWriteBlock(buffer, length); try { let line = buffer.substr(0, length); if (this.inputMode === 0) { if ((this.enigSecurityInfo.sendFlags & Ci.nsIEnigmail.SEND_VERBATIM) !== 0) { line = EnigmailData.decodeQuotedPrintable(line.replace("=\r\n", "")); } if ((this.enigSecurityInfo.sendFlags & Ci.nsIEnigmail.SEND_VERBATIM) === 0 || line.match(/^(From|To|Subject|Message-ID|Date|User-Agent|MIME-Version):/i) === null) { this.headerData += line; } if (line.replace(/[\r\n]/g, "").length === 0) { this.inputMode = 1; if (this.cryptoMode == MIME_ENCRYPTED) { if (!this.encHeader) { let ct = this.getHeader("content-type", false); if ((ct.search(/text\/plain/i) === 0) || (ct.search(/text\/html/i) === 0)) { this.encapsulate = EnigmailMime.createBoundary(); this.writeToPipe('Content-Type: multipart/mixed; boundary="' + this.encapsulate + '"\r\n\r\n'); this.writeToPipe("--" + this.encapsulate + "\r\n"); } } } else if (this.cryptoMode == MIME_SIGNED) { let ct = this.getHeader("content-type", true); let hdr = EnigmailFuncs.getHeaderData(ct); hdr.boundary = hdr.boundary || ""; hdr.boundary = hdr.boundary.replace(/['"]/g, ""); } this.writeToPipe(this.headerData); if (this.cryptoMode == MIME_SIGNED || (this.enigSecurityInfo.sendFlags & Ci.nsIEnigmail.SEND_VERBATIM) !== 0) { this.writeOut(this.headerData); } } } else if (this.inputMode == 1) { if (this.cryptoMode == MIME_SIGNED) { // special treatments for various special cases with PGP/MIME signed messages if (line.substr(0, 5) == "From ") { LOCAL_DEBUG("mimeEncrypt.js: added >From\n"); this.writeToPipe(">"); } } this.writeToPipe(line); if (this.cryptoMode == MIME_SIGNED) { this.writeOut(line); } else if ((this.enigSecurityInfo.sendFlags & Ci.nsIEnigmail.SEND_VERBATIM) !== 0) { this.writeOut(EnigmailData.decodeQuotedPrintable(line.replace("=\r\n", ""))); } } else if (this.inputMode == 2) { if (line.replace(/[\r\n]/g, "").length === 0) { this.inputMode = 0; } } } catch (ex) { EnigmailLog.writeException("mimeEncrypt.js", ex); throw (ex); } return null; }, writeOut: function(str) { if (gDebugLogLevel > 4) LOCAL_DEBUG("mimeEncrypt.js: writeOut: " + str.length + "\n"); this.outQueue += str; if (this.outQueue.length > maxBufferLen) this.flushOutput(); }, flushOutput: function() { LOCAL_DEBUG("mimeEncrypt.js: flushOutput: " + this.outQueue.length + "\n"); this.outStringStream.setData(this.outQueue, this.outQueue.length); var writeCount = this.outStream.writeFrom(this.outStringStream, this.outQueue.length); if (writeCount < this.outQueue.length) { LOCAL_DEBUG("mimeEncrypt.js: flushOutput: wrote " + writeCount + " instead of " + this.outQueue.length + " bytes\n"); } this.outQueue = ""; }, writeToPipe: function(str) { if (gDebugLogLevel > 4) LOCAL_DEBUG("mimeEncrypt.js: writeToPipe: " + str.length + "\n"); if (this.pipe) { this.pipeQueue += str; if (this.pipeQueue.length > maxBufferLen) this.flushInput(); } else this.pipeQueue += str; }, flushInput: function() { LOCAL_DEBUG("mimeEncrypt.js: flushInput\n"); if (!this.pipe) return; this.pipe.write(this.pipeQueue); this.pipeQueue = ""; }, getHeader: function(hdrStr, fullHeader) { var foundIndex = 0; var res = ""; var hdrLines = this.headerData.split(/[\r\n]+/); var i; for (i = 0; i < hdrLines.length; i++) { if (hdrLines[i].length > 0) { if (fullHeader && res !== "") { if (hdrLines[i].search(/^\s+/) === 0) { res += hdrLines[i].replace(/\s*[\r\n]*$/, ""); } else return res; } else { let j = hdrLines[i].indexOf(":"); if (j > 0) { let h = hdrLines[i].substr(0, j).replace(/\s*$/, ""); let re = new RegExp("^" + hdrStr + "$", "i"); if (h.search(re) === 0) { foundIndex = 1; res = hdrLines[i].substr(j + 1).replace(/^\s*/, ""); if (!fullHeader) return res; } } } } } return res; }, // API for decryptMessage Listener stdin: function(pipe) { LOCAL_DEBUG("mimeEncrypt.js: stdin\n"); if (this.pipeQueue.length > 0) { pipe.write(this.pipeQueue); this.pipeQueue = ""; } if (this.closePipe) { pipe.close(); } else { this.pipe = pipe; } }, stdout: function(s) { LOCAL_DEBUG("mimeEncrypt.js: stdout:" + s.length + "\n"); this.encryptedData += s; this.dataLength += s.length; }, stderr: function(s) { LOCAL_DEBUG("mimeEncrypt.js: stderr\n"); this.statusStr += s; }, done: function(exitCode) { EnigmailLog.DEBUG("mimeEncrypt.js: done: " + exitCode + "\n"); let retStatusObj = {}; this.exitCode = EnigmailEncryption.encryptMessageEnd(this.enigSecurityInfo.senderEmailAddr, this.statusStr, exitCode, this.enigSecurityInfo.UIFlags, this.enigSecurityInfo.sendFlags, this.dataLength, retStatusObj); if (this.exitCode !== 0) EnigmailDialog.alert(this.win, retStatusObj.errorMsg); }, processPepEncryption: function() { EnigmailLog.DEBUG("mimeEncrypt.js: processPepEncryption:\n"); let self = this; let resultObj = null; let originalSubject = null; let sendFlags = 0; this.outQueue = ""; if ((!this.isDraft) || self.msgIdentity.getBoolAttribute("autoEncryptDrafts")) { let securityInfo = this.msgCompFields.securityInfo; if (!securityInfo) throw Cr.NS_ERROR_FAILURE; let enigSecurityInfo; try { enigSecurityInfo = securityInfo.QueryInterface(Ci.nsIEnigMsgCompFields); originalSubject = enigSecurityInfo.originalSubject; sendFlags = enigSecurityInfo.sendFlags; } catch (ex) {} let toAddrList = EnigmailFuncs.stripEmail(this.recipientList).split(/,/); let fromAddr = jsmime.headerparser.parseAddressingHeader(self.msgIdentity.email); let toAddr; let encryptFlags = 0; if (!this.isDraft) { toAddr = jsmime.headerparser.parseAddressingHeader(this.recipientList); if (!self.msgIdentity.getBoolAttribute("attachPgpKey")) { encryptFlags = 0x4; // do not attach own key } } else { toAddr = fromAddr; sendFlags = Ci.nsIEnigmail.SEND_ENCRYPTED; encryptFlags = 0x2 + 0x4; // unsigned message; do not attach own key } if (sendFlags & Ci.nsIEnigmail.SEND_ENCRYPTED) { let s = jsmime.headeremitter.emitStructuredHeader("from", fromAddr, {}); s += jsmime.headeremitter.emitStructuredHeader("to", toAddr, {}); if (originalSubject !== null) { s += jsmime.headeremitter.emitStructuredHeader("subject", originalSubject, {}); } EnigmailPEPAdapter.pep.encryptMimeString(s + this.pipeQueue, null, encryptFlags).then(function _f(res) { EnigmailLog.DEBUG("mimeEncrypt.js: processPepEncryption: SUCCESS\n"); if ((typeof(res) === "object") && ("result" in res)) { resultObj = res.result.outParams; } else EnigmailLog.DEBUG("mimeEncrypt.js: processPepEncryption: typeof res=" + typeof(res) + "\n"); if (self.inspector && self.inspector.eventLoopNestLevel > 0) { // unblock the waiting lock in finishCryptoEncapsulation self.inspector.exitNestedEventLoop(); } }).catch(function _error(err) { EnigmailLog.DEBUG("mimeEncrypt.js: processPepEncryption: ERROR\n"); try { EnigmailLog.DEBUG(err.code + ": " + ("exception" in err ? err.exception.toString() : err.message) + "\n"); } catch (x) { EnigmailLog.DEBUG(JSON.stringify(err) + "\n"); } if (self.inspector && self.inspector.eventLoopNestLevel > 0) { // unblock the waiting lock in finishCryptoEncapsulation self.inspector.exitNestedEventLoop(); } }); // wait here for PEP to terminate this.inspector.enterNestedEventLoop(0); } } if (resultObj !== null) { this.outQueue = EnigmailPEPAdapter.stripMsgHeadersFromEncryption(resultObj); } if (this.outQueue === "") { if (originalSubject !== null) { this.outQueue = jsmime.headeremitter.emitStructuredHeader("subject", originalSubject, {}) + this.pipeQueue; } else { this.outQueue = this.pipeQueue; } } this.outStringStream.setData(this.outQueue, this.outQueue.length); let writeCount = this.outStream.writeFrom(this.outStringStream, this.outQueue.length); if (writeCount < this.outQueue.length) { EnigmailLog.DEBUG("mimeEncrypt.js: flushOutput: wrote " + writeCount + " instead of " + this.outQueue.length + " bytes\n"); } this.outQueue = ""; } }; //////////////////////////////////////////////////////////////////// // General-purpose functions, not exported function LOCAL_DEBUG(str) { if (gDebugLogLevel) EnigmailLog.DEBUG(str); } function initModule() { var env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment); var nspr_log_modules = env.get("NSPR_LOG_MODULES"); var matches = nspr_log_modules.match(/mimeEncrypt:(\d+)/); if (matches && (matches.length > 1)) { gDebugLogLevel = matches[1]; LOCAL_DEBUG("mimeEncrypt.js: enabled debug logging\n"); } } var NSGetFactory = XPCOMUtils.generateNSGetFactory([PgpMimeEncrypt]); initModule(); diff --git a/ui/content/enigmailMsgComposeOverlay.js b/ui/content/enigmailMsgComposeOverlay.js index dedbcbaa..ca4bdc2d 100644 --- a/ui/content/enigmailMsgComposeOverlay.js +++ b/ui/content/enigmailMsgComposeOverlay.js @@ -1,5283 +1,5283 @@ /*global Components: false, EnigmailLocale: false, EnigmailApp: false, Dialog: false, EnigmailTimer: false */ /* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; /*globally available Thunderbird variables/object/functions: */ /*global MimeBody: false, MimeUnknown: false, MimeMessageAttachment: false, gMsgCompose: false, getCurrentIdentity: false */ /*global msgHdrToMimeMessage: false, MimeMessage: false, MimeContainer: false, UpdateAttachmentBucket: false, gContentChanged: true */ /*global AddAttachments: false, AddAttachment: false, ChangeAttachmentBucketVisibility: false, GetResourceFromUri: false */ /*global Recipients2CompFields: false, Attachments2CompFields: false, DetermineConvertibility: false, gWindowLocked: false */ /*global CommandUpdate_MsgCompose: false, gSMFields: false, setSecuritySettings: false */ /*global Sendlater3Composing: false */ Components.utils.import("resource://enigmail/glodaMime.jsm"); Components.utils.import("resource://enigmail/core.jsm"); /*global EnigmailCore: false */ Components.utils.import("resource://enigmail/funcs.jsm"); /*global EnigmailFuncs: false */ Components.utils.import("resource://enigmail/log.jsm"); /*global EnigmailLog: false */ Components.utils.import("resource://enigmail/prefs.jsm"); /*global EnigmailPrefs: false */ Components.utils.import("resource://enigmail/os.jsm"); /*global EnigmailOS: false */ Components.utils.import("resource://enigmail/armor.jsm"); /*global EnigmailArmor: false */ Components.utils.import("resource://enigmail/locale.jsm"); /*global EnigmailLocale: false */ Components.utils.import("resource://enigmail/files.jsm"); /*global EnigmailFiles: false */ Components.utils.import("resource://enigmail/data.jsm"); /*global EnigmailData: false */ Components.utils.import("resource://enigmail/app.jsm"); /*global EnigmailApp: false */ Components.utils.import("resource://enigmail/dialog.jsm"); /*global EnigmailDialog: false */ Components.utils.import("resource://enigmail/timer.jsm"); /*global EnigmailTimer: false */ Components.utils.import("resource://enigmail/windows.jsm"); /* global EnigmailWindows: false */ Components.utils.import("resource://enigmail/events.jsm"); /*global EnigmailEvents: false */ Components.utils.import("resource://enigmail/keyRing.jsm"); /*global EnigmailKeyRing: false */ Components.utils.import("resource://enigmail/uris.jsm"); /*global EnigmailURIs: false */ Components.utils.import("resource://enigmail/constants.jsm"); /*global EnigmailConstants: false */ Components.utils.import("resource://enigmail/passwords.jsm"); /*global EnigmailPassword: false */ Components.utils.import("resource://enigmail/rules.jsm"); /*global EnigmailRules: false */ Components.utils.import("resource://enigmail/clipboard.jsm"); /*global EnigmailClipboard: false */ Components.utils.import("resource://enigmail/pEpAdapter.jsm"); /*global EnigmailPEPAdapter: false */ Components.utils.import("resource://enigmail/pEpDecrypt.jsm"); /*global EnigmailPEPDecrypt: false */ Components.utils.import("resource://enigmail/wkdLookup.jsm"); /*global EnigmailWkdLookup: false */ Components.utils.import("resource://enigmail/autocrypt.jsm"); /*global EnigmailAutocrypt: false */ Components.utils.import("resource:///modules/jsmime.jsm"); /*global jsmime: false*/ try { Components.utils.import("resource:///modules/MailUtils.js"); /*global MailUtils: false */ } catch (ex) {} if (!Enigmail) var Enigmail = {}; const IOSERVICE_CONTRACTID = "@mozilla.org/network/io-service;1"; const LOCAL_FILE_CONTRACTID = "@mozilla.org/file/local;1"; Enigmail.msg = { editor: null, dirty: null, processed: null, timeoutId: null, sendPgpMime: false, sendMode: null, // the current default for sending a message (0, SIGN, ENCRYPT, or SIGN|ENCRYPT) sendModeDirty: false, // send mode or final send options changed? // processed reasons for encryption: reasonEncrypted: "", reasonSigned: "", // encrypt/sign/pgpmime according to rules? // (1:ENIG_UNDEF(undef/maybe), 0:ENIG_NEVER(never/forceNo), 2:ENIG_ALWAYS(always/forceYes), // 22:ENIG_AUTO_ALWAYS, 99:ENIG_CONFLICT(conflict)) encryptByRules: EnigmailConstants.ENIG_UNDEF, signByRules: EnigmailConstants.ENIG_UNDEF, pgpmimeByRules: EnigmailConstants.ENIG_UNDEF, // forced to encrypt/sign/pgpmime? // (1:ENIG_UNDEF(undef/maybe), 0:ENIG_NEVER(never/forceNo), 2:ENIG_ALWAYS(always/forceYes)) encryptForced: EnigmailConstants.ENIG_UNDEF, signForced: EnigmailConstants.ENIG_UNDEF, pgpmimeForced: EnigmailConstants.ENIG_UNDEF, finalSignDependsOnEncrypt: false, // does signing finally depends on encryption mode? // resulting final encrypt/sign/pgpmime mode: // (-1:ENIG_FINAL_UNDEF, 0:ENIG_FINAL_NO, 1:ENIG_FINAL_YES, 10:ENIG_FINAL_FORCENO, 11:ENIG_FINAL_FORCEYES, 99:ENIG_FINAL_CONFLICT) statusEncrypted: EnigmailConstants.ENIG_FINAL_UNDEF, statusSigned: EnigmailConstants.ENIG_FINAL_UNDEF, statusPGPMime: EnigmailConstants.ENIG_FINAL_UNDEF, statusEncryptedInStatusBar: null, // last statusEncyrpted when processing status buttons // to find possible broken promise of encryption // is OpenPGP encryption possible without displaying the key selection dialog? autoPgpEncryption: false, // processed strings to signal final encrypt/sign/pgpmime state: statusEncryptedStr: "???", statusSignedStr: "???", statusPGPMimeStr: "???", statusSMimeStr: "???", statusInlinePGPStr: "???", statusAttachOwnKey: "???", juniorMode: false, origPepRating: null, sendProcess: false, composeBodyReady: false, identity: null, enableRules: null, modifiedAttach: null, lastFocusedWindow: null, determineSendFlagId: null, trustAllKeys: false, protectHeaders: false, attachOwnKeyObj: { appendAttachment: false, attachedObj: null, attachedKey: null }, keyLookupDone: [], compFieldsEnig_CID: "@mozdev.org/enigmail/composefields;1", saveDraftError: 0, composeStartup: function() { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.composeStartup\n"); function delayedProcessFinalState() { EnigmailTimer.setTimeout(function _f() { Enigmail.msg.processFinalState(); Enigmail.msg.updateStatusBar(); }, 50); } function addSecurityListener(itemId, func) { let s = document.getElementById(itemId); if (s) { s.addEventListener("command", func); } else { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: addSecurityListener - cannot find element " + itemId + "\n"); } } // Relabel SMIME button and menu item var smimeButton = document.getElementById("button-security"); if (smimeButton) { smimeButton.setAttribute("label", "S/MIME"); } var msgId = document.getElementById("msgIdentityPopup"); if (msgId) { msgId.addEventListener("command", Enigmail.msg.setIdentityCallback); } var subj = document.getElementById("msgSubject"); subj.addEventListener('focus', Enigmail.msg.fireSendFlags); // listen to S/MIME changes to potentially display "conflict" message addSecurityListener("menu_securitySign1", delayedProcessFinalState); addSecurityListener("menu_securitySign2", delayedProcessFinalState); addSecurityListener("menu_securityEncryptRequire1", delayedProcessFinalState); addSecurityListener("menu_securityEncryptRequire2", delayedProcessFinalState); this.msgComposeReset(false); // false => not closing => call setIdentityDefaults() this.composeOpen(); this.processFinalState(); this.updateStatusBar(); }, composeUnload: function() { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.composeUnload\n"); //if (gMsgCompose) // gMsgCompose.UnregisterStateListener(Enigmail.composeStateListener); }, handleClick: function(event, modifyType) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.handleClick\n"); switch (event.button) { case 2: // do not process the event any futher // needed on Windows to prevent displaying the context menu event.preventDefault(); this.doPgpButton(); break; case 0: this.doPgpButton(modifyType); break; } }, setIdentityCallback: function(elementId) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.setIdentityCallback: elementId=" + elementId + "\n"); EnigmailTimer.setTimeout(function _f() { Enigmail.msg.setIdentityDefaults(); }, 50); }, /* return whether the account specific setting key is enabled or disabled */ getAccDefault: function(key) { //EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.getAccDefault: identity="+this.identity.key+"("+this.identity.email+") key="+key+"\n"); if (this.isEnigmailEnabled()) { var res = null; switch (key) { case 'sign': res = (this.identity.getIntAttribute("defaultSigningPolicy") > 0); // converts int property to bool property break; case 'encrypt': res = (this.identity.getIntAttribute("defaultEncryptionPolicy") > 0); // converts int property to bool property break; case 'pgpMimeMode': res = this.identity.getBoolAttribute(key); break; case 'signIfNotEnc': res = this.identity.getBoolAttribute("pgpSignPlain"); break; case 'signIfEnc': res = this.identity.getBoolAttribute("pgpSignEncrypted"); break; case 'attachPgpKey': res = this.identity.getBoolAttribute(key); break; } //EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.getAccDefault: "+key+"="+res+"\n"); return res; } else { // every detail is disabled if OpenPGP in general is disabled: switch (key) { case 'sign': case 'encrypt': case 'signIfNotEnc': case 'signIfEnc': case 'pgpMimeMode': case 'attachPgpKey': return false; } } // should not be reached EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.getAccDefault: internal error: invalid key '" + key + "'\n"); return null; }, /** * Determine if any of Enigmail (OpenPGP) or S/MIME encryption is enabled for the account */ getEncryptionEnabled: function() { if (this.juniorMode) return false; let id = getCurrentIdentity(); return ((id.getUnicharAttribute("encryption_cert_name") !== "") || this.isEnigmailEnabled()); }, /** * Determine if any of Enigmail (OpenPGP) or S/MIME signing is enabled for the account */ getSigningEnabled: function() { if (this.juniorMode) return false; let id = getCurrentIdentity(); return ((id.getUnicharAttribute("signing_cert_name") !== "") || this.isEnigmailEnabled()); }, getSmimeSigningEnabled: function() { if (this.juniorMode) return false; let id = getCurrentIdentity(); if (!id.getUnicharAttribute("signing_cert_name")) return false; return id.getBoolPref("sign_mail"); }, setIdentityDefaults: function() { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.setIdentityDefaults\n"); this.identity = getCurrentIdentity(); if (this.isEnigmailEnabled()) { EnigmailFuncs.getSignMsg(this.identity); // convert old acc specific to new acc specific options } else { // reset status strings in menu to useful defaults this.statusEncryptedStr = EnigmailLocale.getString("encryptNo"); this.statusSignedStr = EnigmailLocale.getString("signNo", [""]); this.statusPGPMimeStr = EnigmailLocale.getString("pgpmimeNormal"); this.statusInlinePGPStr = EnigmailLocale.getString("inlinePGPNormal"); this.statusSMimeStr = EnigmailLocale.getString("smimeNormal"); this.statusAttachOwnKey = EnigmailLocale.getString("attachOwnKeyNo"); } if (this.juniorMode) { let pepBc = document.getElementById("enigmail-bc-pepEncrypt"); pepBc.setAttribute("encrypt", this.pepEnabled() ? "true" : "false"); } // reset default send settings, unless we have changed them already if (!this.sendModeDirty) { this.mimePreferOpenPGP = this.identity.getIntAttribute("mimePreferOpenPGP"); this.processAccountSpecificDefaultOptions(); this.determineSendFlags(); // important to use identity specific settings this.processFinalState(); this.updateStatusBar(); } }, // set the current default for sending a message // depending on the identity processAccountSpecificDefaultOptions: function() { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.processAccountSpecificDefaultOptions\n"); const nsIEnigmail = Components.interfaces.nsIEnigmail; const SIGN = nsIEnigmail.SEND_SIGNED; const ENCRYPT = nsIEnigmail.SEND_ENCRYPTED; this.sendMode = 0; if (!this.isEnigmailEnabled()) { return; } if (this.getAccDefault("encrypt")) { this.sendMode |= ENCRYPT; this.reasonEncrypted = EnigmailLocale.getString("reasonEnabledByDefault"); } if (this.getAccDefault("sign")) { this.sendMode |= SIGN; this.reasonSigned = EnigmailLocale.getString("reasonEnabledByDefault"); } this.sendPgpMime = this.getAccDefault("pgpMimeMode"); this.attachOwnKeyObj.appendAttachment = this.getAccDefault("attachPgpKey"); this.setOwnKeyStatus(); this.attachOwnKeyObj.attachedObj = null; this.attachOwnKeyObj.attachedKey = null; this.finalSignDependsOnEncrypt = (this.getAccDefault("signIfEnc") || this.getAccDefault("signIfNotEnc")); }, getOriginalMsgUri: function() { let draftId = gMsgCompose.compFields.draftId; let msgUri = null; if (typeof(draftId) == "string" && draftId.length > 0) { // original message is draft msgUri = draftId.replace(/\?.*$/, ""); } else if (typeof(gMsgCompose.originalMsgURI) == "string" && gMsgCompose.originalMsgURI.length > 0) { // original message is a "true" mail msgUri = gMsgCompose.originalMsgURI; } return msgUri; }, getMsgHdr: function(msgUri) { if (!msgUri) { msgUri = this.getOriginalMsgUri(); } let messenger = Components.classes["@mozilla.org/messenger;1"].getService(Components.interfaces.nsIMessenger); return messenger.messageServiceFromURI(msgUri).messageURIToMsgHdr(msgUri); }, getMsgProperties: function(draft) { EnigmailLog.DEBUG("enigmailMessengerOverlay.js: Enigmail.msg.getMsgProperties:\n"); const nsIEnigmail = Components.interfaces.nsIEnigmail; let msgUri = this.getOriginalMsgUri(); let properties = 0; try { let msgHdr = this.getMsgHdr(msgUri); if (msgHdr) { this.setOriginalSubject(msgHdr.subject); properties = msgHdr.getUint32Property("enigmail"); if (draft) { try { msgHdrToMimeMessage(msgHdr, null, this.getMsgPropertiesCb, true, { examineEncryptedParts: true }); } catch (ex) { EnigmailLog.DEBUG("enigmailMessengerOverlay.js: Enigmail.msg.getMsgProperties: cannot use msgHdrToMimeMessage\n"); } } } } catch (ex) { EnigmailLog.DEBUG("enigmailMessengerOverlay.js: Enigmail.msg.getMsgProperties: got exception '" + ex.toString() + "'\n"); } if (EnigmailURIs.isEncryptedUri(msgUri)) { properties |= nsIEnigmail.DECRYPTION_OKAY; } return properties; }, getMsgPropertiesCb: function(msg, mimeMsg) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.getMsgPropertiesCb\n"); const nsIEnigmail = Components.interfaces.nsIEnigmail; var stat = ""; if (mimeMsg && mimeMsg.headers["x-enigmail-draft-status"]) { stat = String(mimeMsg.headers["x-enigmail-draft-status"]); } else { return; } EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.getMsgPropertiesCb: draftStatus: " + stat + "\n"); if (stat.substr(0, 1) == "N") { // new style drafts (Enigmail 1.7) var enc = "final-encryptDefault"; switch (Number(stat.substr(1, 1))) { case EnigmailConstants.ENIG_NEVER: enc = "final-encryptNo"; break; case EnigmailConstants.ENIG_ALWAYS: enc = "final-encryptYes"; } var sig = "final-signDefault"; switch (Number(stat.substr(2, 1))) { case EnigmailConstants.ENIG_NEVER: sig = "final-signNo"; break; case EnigmailConstants.ENIG_ALWAYS: sig = "final-signYes"; } var pgpMime = "final-pgpmimeDefault"; switch (Number(stat.substr(3, 1))) { case EnigmailConstants.ENIG_NEVER: pgpMime = "final-pgpmimeNo"; break; case EnigmailConstants.ENIG_ALWAYS: pgpMime = "final-pgpmimeYes"; } Enigmail.msg.setFinalSendMode(enc); Enigmail.msg.setFinalSendMode(sig); Enigmail.msg.setFinalSendMode(pgpMime); if (stat.substr(4, 1) == "1") Enigmail.msg.attachOwnKeyObj.appendAttachment = true; } else { // drafts from older versions of Enigmail var flags = Number(stat); if (flags & nsIEnigmail.SEND_SIGNED) Enigmail.msg.setFinalSendMode('final-signYes'); if (flags & nsIEnigmail.SEND_ENCRYPTED) Enigmail.msg.setFinalSendMode('final-encryptYes'); if (flags & nsIEnigmail.SEND_ATTACHMENT) Enigmail.msg.attachOwnKeyObj.appendAttachment = true; } Enigmail.msg.setOwnKeyStatus(); }, setOriginalSubject: function(subject) { const CT = Components.interfaces.nsIMsgCompType; let subjElem = document.getElementById("msgSubject"); let prefix = ""; if (!subjElem) return; switch (gMsgCompose.type) { case CT.ForwardInline: case CT.ForwardAsAttachment: prefix = this.getMailPref("mail.forward_subject_prefix") + ": "; } subject = jsmime.headerparser.decodeRFC2047Words(subject, "utf-8"); switch (gMsgCompose.type) { case CT.Draft: case CT.Template: case CT.ForwardInline: case CT.ForwardAsAttachment: gMsgCompose.compFields.subject = prefix + subject; subjElem.value = prefix + subject; break; } }, setupMenuAndToolbar: function() { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.setupMenuAndToolbar\n"); let toolbarTxt = document.getElementById("enigmail-toolbar-text"); let encBroadcaster = document.getElementById("enigmail-bc-encrypt"); let signBroadcaster = document.getElementById("enigmail-bc-sign"); let attachBroadcaster = document.getElementById("enigmail-bc-attach"); let enigmailMenu = document.getElementById("menu_Enigmail"); let pepBroadcaster = document.getElementById("enigmail-bc-pepEncrypt"); let pepMenu = document.getElementById("menu_EnigmailPep"); let pepStatusbar = document.getElementById("enigmail-pep-statusbar"); if (this.juniorMode) { encBroadcaster.setAttribute("hidden", "true"); signBroadcaster.setAttribute("hidden", "true"); attachBroadcaster.setAttribute("hidden", "true"); if (toolbarTxt) { toolbarTxt.setAttribute("hidden", "true"); } enigmailMenu.setAttribute("hidden", "true"); pepBroadcaster.removeAttribute("hidden"); pepMenu.removeAttribute("hidden"); pepStatusbar.removeAttribute("hidden"); this.setFinalSendMode("final-encryptNo"); this.setFinalSendMode("final-signNo"); this.setFinalSendMode("final-pgpmimeNo"); this.updateStatusBar(); } else { encBroadcaster.removeAttribute("hidden"); signBroadcaster.removeAttribute("hidden"); attachBroadcaster.removeAttribute("hidden"); if (toolbarTxt) { toolbarTxt.removeAttribute("hidden"); } enigmailMenu.removeAttribute("hidden"); pepBroadcaster.setAttribute("hidden", "true"); pepMenu.setAttribute("hidden", "true"); pepStatusbar.setAttribute("hidden", "true"); } }, composeOpen: function() { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.composeOpen\n"); const nsIEnigmail = Components.interfaces.nsIEnigmail; const SIGN = nsIEnigmail.SEND_SIGNED; const ENCRYPT = nsIEnigmail.SEND_ENCRYPTED; var msgFlags; var msgUri = null; var msgIsDraft = false; this.juniorMode = EnigmailPEPAdapter.getPepJuniorMode(); this.setupMenuAndToolbar(); this.determineSendFlagId = null; this.disableSmime = false; this.saveDraftError = 0; this.protectHeaders = EnigmailPrefs.getPref("protectHeaders"); this.enableUndoEncryption(false); this.displayProtectHeadersStatus(); if (this.juniorMode) { this.getOriginalPepMsgRating(); } var toobarElem = document.getElementById("composeToolbar2"); if (toobarElem && (EnigmailOS.getOS() == "Darwin")) { toobarElem.setAttribute("platform", "macos"); } // check rules for status bar icons on each change of the recipients var adrCol = document.getElementById("addressCol2#1"); // recipients field if (adrCol) { let attr = adrCol.getAttribute("oninput"); adrCol.setAttribute("oninput", attr + "; Enigmail.msg.addressOnChange(this);"); attr = adrCol.getAttribute("onchange"); adrCol.setAttribute("onchange", attr + "; Enigmail.msg.addressOnChange(this);"); adrCol.setAttribute("observes", "enigmail-bc-sendprocess"); } adrCol = document.getElementById("addressCol1#1"); // to/cc/bcc/... field if (adrCol) { let attr = adrCol.getAttribute("oncommand"); adrCol.setAttribute("oncommand", attr + "; Enigmail.msg.addressOnChange(this);"); adrCol.setAttribute("observes", "enigmail-bc-sendprocess"); } var draftId = gMsgCompose.compFields.draftId; if (EnigmailPrefs.getPref("keepSettingsForReply") && (!(this.sendMode & ENCRYPT)) || (typeof(draftId) == "string" && draftId.length > 0)) { msgUri = this.getOriginalMsgUri(); if (typeof(draftId) == "string" && draftId.length > 0) { // original message is draft msgIsDraft = true; } if (msgUri) { msgFlags = this.getMsgProperties(msgIsDraft); if (!msgIsDraft) { if (msgFlags & nsIEnigmail.DECRYPTION_OKAY) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.composeOpen: has encrypted originalMsgUri\n"); EnigmailLog.DEBUG("originalMsgURI=" + gMsgCompose.originalMsgURI + "\n"); this.setSendMode('encrypt'); this.disableSmime = true; } else if (msgFlags & (nsIEnigmail.GOOD_SIGNATURE | nsIEnigmail.BAD_SIGNATURE | nsIEnigmail.UNVERIFIED_SIGNATURE)) { this.setSendMode('sign'); } } this.removeAttachedKey(); } } // check for attached signature files and remove them var bucketList = document.getElementById("attachmentBucket"); if (bucketList.hasChildNodes()) { var node = bucketList.firstChild; let nodeNumber = 0; while (node) { if (node.attachment.contentType == "application/pgp-signature") { if (!this.findRelatedAttachment(bucketList, node)) { node = bucketList.removeItemAt(nodeNumber); // Let's release the attachment object held by the node else it won't go away until the window is destroyed node.attachment = null; } } else { ++nodeNumber; } node = node.nextSibling; } if (!bucketList.hasChildNodes()) { try { // TB only UpdateAttachmentBucket(false); } catch (ex) {} } } try { // TB only UpdateAttachmentBucket(bucketList.hasChildNodes()); } catch (ex) {} this.processFinalState(); this.updateStatusBar(); }, getOriginalPepMsgRating: function() { let msgUri = this.getOriginalMsgUri(); let msgHdr = this.getMsgHdr(msgUri); if (msgHdr) { let r = msgHdr.getUint32Property("enigmailPep"); this.origPepRating = (r - (r & 0xFF)) >> 8; } }, // check if an signature is related to another attachment findRelatedAttachment: function(bucketList, node) { // check if filename ends with .sig if (node.attachment.name.search(/\.sig$/i) < 0) return null; var relatedNode = bucketList.firstChild; var findFile = node.attachment.name.toLowerCase(); var baseAttachment = null; while (relatedNode) { if (relatedNode.attachment.name.toLowerCase() + ".sig" == findFile) baseAttachment = relatedNode.attachment; relatedNode = relatedNode.nextSibling; } return baseAttachment; }, msgComposeReopen: function() { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.msgComposeReopen\n"); this.msgComposeReset(false); // false => not closing => call setIdentityDefaults() this.composeOpen(); this.fireSendFlags(); EnigmailTimer.setTimeout(function _f() { EnigmailLog.DEBUG("enigmailMsgComposeOverlay: re-determine send flags\n"); try { this.determineSendFlags(); this.processFinalState(); this.updateStatusBar(); } catch (ex) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay: re-determine send flags - ERROR: " + ex.toString() + "\n"); } }.bind(Enigmail.msg), 1000); }, msgComposeClose: function() { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.msgComposeClose\n"); var ioServ; try { // we should delete the original temporary files of the encrypted or signed // inline PGP attachments (the rest is done automatically) if (this.modifiedAttach) { ioServ = Components.classes[IOSERVICE_CONTRACTID].getService(Components.interfaces.nsIIOService); if (!ioServ) return; for (var i in this.modifiedAttach) { if (this.modifiedAttach[i].origTemp) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.msgComposeClose: deleting " + this.modifiedAttach[i].origUrl + "\n"); var fileUri = ioServ.newURI(this.modifiedAttach[i].origUrl, null, null); var fileHandle = Components.classes[LOCAL_FILE_CONTRACTID].createInstance(Components.interfaces.nsIFile); fileHandle.initWithPath(fileUri.path); if (fileHandle.exists()) fileHandle.remove(false); } } this.modifiedAttach = null; } } catch (ex) { EnigmailLog.ERROR("enigmailMsgComposeOverlay.js: ECSL.ComposeProcessDone: could not delete all files:\n" + ex.toString() + "\n"); } this.msgComposeReset(true); // true => closing => don't call setIdentityDefaults() }, msgComposeReset: function(closing) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.msgComposeReset\n"); this.dirty = 0; this.processed = null; this.timeoutId = null; this.modifiedAttach = null; this.sendMode = 0; this.sendModeDirty = false; this.reasonEncrypted = ""; this.reasonSigned = ""; this.encryptByRules = EnigmailConstants.ENIG_UNDEF; this.signByRules = EnigmailConstants.ENIG_UNDEF; this.pgpmimeByRules = EnigmailConstants.ENIG_UNDEF; this.signForced = EnigmailConstants.ENIG_UNDEF; this.encryptForced = EnigmailConstants.ENIG_UNDEF; this.pgpmimeForced = EnigmailConstants.ENIG_UNDEF; this.finalSignDependsOnEncrypt = false; this.statusSigned = EnigmailConstants.ENIG_FINAL_UNDEF; this.statusEncrypted = EnigmailConstants.ENIG_FINAL_UNDEF; this.statusPGPMime = EnigmailConstants.ENIG_FINAL_UNDEF; this.statusEncryptedStr = "???"; this.statusSignedStr = "???"; this.statusPGPMimeStr = "???"; this.statusInlinePGPStr = "???"; this.statusAttachOwnKey = "???"; this.enableRules = true; this.identity = null; this.sendProcess = false; this.trustAllKeys = false; this.mimePreferOpenPGP = 0; this.origPepRating = null; this.keyLookupDone = []; if (!closing) { this.setIdentityDefaults(); } }, initRadioMenu: function(prefName, optionIds) { EnigmailLog.DEBUG("enigmailMessengerOverlay.js: Enigmail.msg.initRadioMenu: " + prefName + "\n"); var encryptId; var prefValue = EnigmailPrefs.getPref(prefName); if (prefValue >= optionIds.length) return; var menuItem = document.getElementById("enigmail_" + optionIds[prefValue]); if (menuItem) menuItem.setAttribute("checked", "true"); }, usePpgMimeOption: function(value) { EnigmailLog.DEBUG("enigmailMessengerOverlay.js: Enigmail.msg.usePpgMimeOption: " + value + "\n"); EnigmailPrefs.setPref("usePGPMimeOption", value); return true; }, tempTrustAllKeys: function() { this.trustAllKeys = !this.trustAllKeys; }, toggleAttachOwnKey: function() { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.toggleAttachOwnKey\n"); EnigmailCore.getService(window); // make sure Enigmail is loaded and working this.attachOwnKeyObj.appendAttachment = !this.attachOwnKeyObj.appendAttachment; this.setOwnKeyStatus(); }, toggleProtectHeaders: function() { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.toggleProtectHeaders\n"); EnigmailCore.getService(window); // make sure Enigmail is loaded and working this.protectHeaders = !this.protectHeaders; this.displayProtectHeadersStatus(); }, displayProtectHeadersStatus: function() { let bc = document.getElementById("enigmail-bc-protectHdr"); if (this.protectHeaders) { bc.setAttribute("checked", "true"); } else { bc.removeAttribute("checked"); } }, /*** * set broadcaster to display whether the own key is attached or not */ setOwnKeyStatus: function() { let bc = document.getElementById("enigmail-bc-attach"); let attachIcon = document.getElementById("button-enigmail-attach"); if (this.allowAttachOwnKey() === 0) { this.statusAttachOwnKey = EnigmailLocale.getString("attachOwnKeyDisabled"); } else { if (this.attachOwnKeyObj.appendAttachment) { bc.setAttribute("addPubkey", "true"); bc.setAttribute("checked", "true"); this.statusAttachOwnKey = EnigmailLocale.getString("attachOwnKeyYes"); } else { bc.setAttribute("addPubkey", "false"); bc.removeAttribute("checked"); this.statusAttachOwnKey = EnigmailLocale.getString("attachOwnKeyNo"); } } if (attachIcon) attachIcon.setAttribute("tooltiptext", this.statusAttachOwnKey); }, attachOwnKey: function() { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.attachOwnKey:\n"); var userIdValue; if (this.identity.getIntAttribute("pgpKeyMode") > 0) { userIdValue = this.identity.getCharAttribute("pgpkeyId"); if (this.attachOwnKeyObj.attachedKey && (this.attachOwnKeyObj.attachedKey != userIdValue)) { // remove attached key if user ID changed this.removeAttachedKey(); } if (!this.attachOwnKeyObj.attachedKey) { var attachedObj = this.extractAndAttachKey([userIdValue], true); if (attachedObj) { this.attachOwnKeyObj.attachedObj = attachedObj; this.attachOwnKeyObj.attachedKey = userIdValue; } } } else { EnigmailLog.ERROR("enigmailMsgComposeOverlay.js: Enigmail.msg.attachOwnKey: trying to attach unknown own key!\n"); } }, attachKey: function() { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.attachKey: \n"); var resultObj = {}; var inputObj = {}; inputObj.dialogHeader = EnigmailLocale.getString("keysToExport"); inputObj.options = "multisel,allowexpired,nosending"; if (this.trustAllKeys) { inputObj.options += ",trustallkeys"; } var userIdValue = ""; window.openDialog("chrome://enigmail/content/enigmailKeySelection.xul", "", "dialog,modal,centerscreen,resizable", inputObj, resultObj); try { if (resultObj.cancelled) return; this.extractAndAttachKey(resultObj.userList, true); } catch (ex) { // cancel pressed -> do nothing return; } }, extractAndAttachKey: function(uid, warnOnError) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.extractAndAttachKey: \n"); var enigmailSvc = EnigmailCore.getService(window); if (!enigmailSvc) return null; var tmpDir = EnigmailFiles.getTempDir(); var tmpFile; try { tmpFile = Components.classes[LOCAL_FILE_CONTRACTID].createInstance(Components.interfaces.nsIFile); tmpFile.initWithPath(tmpDir); if (!(tmpFile.isDirectory() && tmpFile.isWritable())) { EnigmailDialog.alert(window, EnigmailLocale.getString("noTempDir")); return null; } } catch (ex) { EnigmailLog.writeException("enigmailMsgComposeOverlay.js: Enigmail.msg.extractAndAttachKey", ex); } tmpFile.append("key.asc"); tmpFile.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0x180); // equal 0600 // save file var exitCodeObj = {}; var errorMsgObj = {}; EnigmailKeyRing.extractKey(false, uid.join(" "), tmpFile, exitCodeObj, errorMsgObj); if (exitCodeObj.value !== 0) { if (warnOnError) EnigmailDialog.alert(window, errorMsgObj.value); return null; } // create attachment var ioServ = Components.classes[IOSERVICE_CONTRACTID].getService(Components.interfaces.nsIIOService); var tmpFileURI = ioServ.newFileURI(tmpFile); var keyAttachment = Components.classes["@mozilla.org/messengercompose/attachment;1"].createInstance(Components.interfaces.nsIMsgAttachment); keyAttachment.url = tmpFileURI.spec; if ((uid.length == 1) && (uid[0].search(/^(0x)?[a-fA-F0-9]+$/) === 0)) { keyAttachment.name = uid[0].substr(-16, 16) + ".asc"; if (keyAttachment.name.search(/^0x/) < 0) keyAttachment.name = "0x" + keyAttachment.name; } else { keyAttachment.name = "pgpkeys.asc"; } keyAttachment.temporary = true; keyAttachment.contentType = "application/pgp-keys"; // add attachment to msg this.addAttachment(keyAttachment); try { // TB only ChangeAttachmentBucketVisibility(false); } catch (ex) {} gContentChanged = true; return keyAttachment; }, addAttachment: function(attachment) { if (typeof(AddAttachment) == "undefined") { // TB >= 24 AddAttachments([attachment]); } else { // SeaMonkey AddAttachment(attachment); } }, enableUndoEncryption: function(newStatus) { let eue = document.getElementById("enigmail_undo_encryption"); if (newStatus) { eue.removeAttribute("disabled"); } else eue.setAttribute("disabled", "true"); }, /** * undo the encryption or signing; get back the original (unsigned/unencrypted) text * * useEditorUndo |Number|: > 0 use undo function of editor |n| times * 0: replace text with original text */ undoEncryption: function(useEditorUndo) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.undoEncryption:\n"); if (this.processed) { if (useEditorUndo) { EnigmailTimer.setTimeout(function _f() { Enigmail.msg.editor.undo(useEditorUndo); }, 10); } else { this.replaceEditorText(this.processed.origText); this.enableUndoEncryption(false); } this.processed = null; } else { this.decryptQuote(true); } var node; var nodeNumber; var bucketList = document.getElementById("attachmentBucket"); if (this.modifiedAttach && bucketList && bucketList.hasChildNodes()) { // undo inline encryption of attachments for (var i = 0; i < this.modifiedAttach.length; i++) { node = bucketList.firstChild; nodeNumber = -1; while (node) { ++nodeNumber; if (node.attachment.url == this.modifiedAttach[i].newUrl) { if (this.modifiedAttach[i].encrypted) { node.attachment.url = this.modifiedAttach[i].origUrl; node.attachment.name = this.modifiedAttach[i].origName; node.attachment.temporary = this.modifiedAttach[i].origTemp; node.attachment.contentType = this.modifiedAttach[i].origCType; } else { node = bucketList.removeItemAt(nodeNumber); // Let's release the attachment object held by the node else it won't go away until the window is destroyed node.attachment = null; } // delete encrypted file try { this.modifiedAttach[i].newFile.remove(false); } catch (ex) {} node = null; // next attachment please } else { node = node.nextSibling; } } } } this.removeAttachedKey(); }, removeAttachedKey: function() { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.removeAttachedKey: \n"); var bucketList = document.getElementById("attachmentBucket"); var node = bucketList.firstChild; if (bucketList && bucketList.hasChildNodes() && this.attachOwnKeyObj.attachedObj) { // undo attaching own key var nodeNumber = -1; while (node) { ++nodeNumber; if (node.attachment.url == this.attachOwnKeyObj.attachedObj.url) { node = bucketList.removeItemAt(nodeNumber); // Let's release the attachment object held by the node else it won't go away until the window is destroyed node.attachment = null; this.attachOwnKeyObj.attachedObj = null; this.attachOwnKeyObj.attachedKey = null; node = null; // exit loop } else { node = node.nextSibling; } } if (!bucketList.hasChildNodes()) { try { // TB only ChangeAttachmentBucketVisibility(true); } catch (ex) {} } } }, resetUpdatedFields: function() { this.removeAttachedKey(); // reset subject if (gMsgCompose.compFields.securityInfo instanceof Components.interfaces.nsIEnigMsgCompFields) { let si = gMsgCompose.compFields.securityInfo.QueryInterface(Components.interfaces.nsIEnigMsgCompFields); if (si.originalSubject) { gMsgCompose.compFields.subject = si.originalSubject; } } }, replaceEditorText: function(text) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.replaceEditorText:\n"); this.editorSelectAll(); // Overwrite text in clipboard for security // (Otherwise plaintext will be available in the clipbaord) if (this.editor.textLength > 0) { this.editorInsertText("Enigmail"); } else { this.editorInsertText(" "); } this.editorSelectAll(); this.editorInsertText(text); }, getMsgFolderFromUri: function(uri, checkFolderAttributes) { let msgfolder = null; if (typeof MailUtils != 'undefined') { return MailUtils.getFolderForURI(uri, checkFolderAttributes); } try { // Postbox, older versions of TB let resource = GetResourceFromUri(uri); msgfolder = resource.QueryInterface(Components.interfaces.nsIMsgFolder); if (checkFolderAttributes) { if (!(msgfolder && (msgfolder.parent || msgfolder.isServer))) { msgfolder = null; } } } catch (ex) { //EnigmailLog.DEBUG("failed to get the folder resource\n"); } return msgfolder; }, goAccountManager: function() { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.goAccountManager:\n"); EnigmailCore.getService(window); var currentId = null; var server = null; try { currentId = getCurrentIdentity(); var amService = Components.classes["@mozilla.org/messenger/account-manager;1"].getService(); var servers, folderURI; try { // Gecko >= 20 servers = amService.getServersForIdentity(currentId); folderURI = servers.queryElementAt(0, Components.interfaces.nsIMsgIncomingServer).serverURI; } catch (ex) { servers = amService.GetServersForIdentity(currentId); folderURI = servers.GetElementAt(0).QueryInterface(Components.interfaces.nsIMsgIncomingServer).serverURI; } server = this.getMsgFolderFromUri(folderURI, true).server; } catch (ex) {} window.openDialog("chrome://enigmail/content/am-enigprefs-edit.xul", "", "dialog,modal,centerscreen", { identity: currentId, account: server }); this.setIdentityDefaults(); }, /** * Determine if Enigmail is enabled for the account */ isEnigmailEnabled: function() { if (this.juniorMode) return false; return this.identity.getBoolAttribute("enablePgp"); }, doPgpButton: function(what) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.doPgpButton: what=" + what + "\n"); if (this.isEnigmailEnabled()) { EnigmailCore.getService(window); // try to access Enigmail to launch the wizard if needed } // ignore settings for this account? try { if (!this.getEncryptionEnabled() && !this.getSigningEnabled()) { if (EnigmailDialog.confirmDlg(window, EnigmailLocale.getString("configureNow"), EnigmailLocale.getString("msgCompose.button.configure"))) { // configure account settings for the first time this.goAccountManager(); if (!this.isEnigmailEnabled()) { return; } } else { return; } } } catch (ex) {} switch (what) { case 'sign': case 'encrypt': this.setSendMode(what); break; // menu entries: case 'final-signDefault': case 'final-signYes': case 'final-signNo': case 'final-encryptDefault': case 'final-encryptYes': case 'final-encryptNo': case 'final-pgpmimeDefault': case 'final-pgpmimeYes': case 'final-pgpmimeNo': case 'final-useSmime': case 'toggle-final-sign': case 'toggle-final-encrypt': case 'toggle-final-mime': this.setFinalSendMode(what); break; case 'trustKeys': this.tempTrustAllKeys(); break; case 'nothing': break; case 'displaySecuritySettings': this.displaySecuritySettings(); break; default: this.displaySecuritySettings(); } }, // changes the DEFAULT sendMode // - also called internally for saved emails setSendMode: function(sendMode) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.setSendMode: sendMode=" + sendMode + "\n"); const nsIEnigmail = Components.interfaces.nsIEnigmail; const SIGN = nsIEnigmail.SEND_SIGNED; const ENCRYPT = nsIEnigmail.SEND_ENCRYPTED; var origSendMode = this.sendMode; switch (sendMode) { case 'sign': this.sendMode |= SIGN; break; case 'encrypt': this.sendMode |= ENCRYPT; break; default: EnigmailDialog.alert(window, "Enigmail.msg.setSendMode - unexpected value: " + sendMode); break; } // sendMode changed ? // - sign and send are internal initializations if (!this.sendModeDirty && (this.sendMode != origSendMode) && sendMode != 'sign' && sendMode != 'encrypt') { this.sendModeDirty = true; } this.processFinalState(); this.updateStatusBar(); }, // changes the FINAL sendMode // - triggered by the user interface setFinalSendMode: function(sendMode) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.setFinalSendMode: sendMode=" + sendMode + "\n"); const nsIEnigmail = Components.interfaces.nsIEnigmail; const SIGN = nsIEnigmail.SEND_SIGNED; const ENCRYPT = nsIEnigmail.SEND_ENCRYPTED; switch (sendMode) { // menu entries for final settings: case 'final-encryptDefault': // switch encryption to "use defaults & rules" if (this.encryptForced != EnigmailConstants.ENIG_UNDEF) { // if encrypt/noencrypt forced this.encryptForced = EnigmailConstants.ENIG_UNDEF; // back to defaults/rules } break; case 'final-encryptYes': // switch encryption to "force encryption" if (this.encryptForced != EnigmailConstants.ENIG_ALWAYS) { // if not forced to encrypt this.encryptForced = EnigmailConstants.ENIG_ALWAYS; // force to encrypt } break; case 'final-encryptNo': // switch encryption to "force no to encrypt" if (this.encryptForced != EnigmailConstants.ENIG_NEVER) { // if not forced not to encrypt this.encryptForced = EnigmailConstants.ENIG_NEVER; // force not to encrypt } break; case 'final-signDefault': // switch signing to "use defaults & rules" if (this.signForced != EnigmailConstants.ENIG_UNDEF) { // if sign/nosign forced // re-init if signing depends on encryption if this was broken before this.finalSignDependsOnEncrypt = (this.getAccDefault("signIfEnc") || this.getAccDefault("signIfNotEnc")); this.signForced = EnigmailConstants.ENIG_UNDEF; // back to defaults/rules } break; case 'final-signYes': if (this.signForced != EnigmailConstants.ENIG_ALWAYS) { // if not forced to sign this.signingNoLongerDependsOnEnc(); this.signForced = EnigmailConstants.ENIG_ALWAYS; // force to sign } break; case 'final-signNo': if (this.signForced != EnigmailConstants.ENIG_NEVER) { // if not forced not to sign this.signingNoLongerDependsOnEnc(); this.signForced = EnigmailConstants.ENIG_NEVER; // force not to sign } break; case 'final-pgpmimeDefault': if (this.pgpmimeForced != EnigmailConstants.ENIG_UNDEF) { // if any PGP mode forced this.pgpmimeForced = EnigmailConstants.ENIG_UNDEF; // back to defaults/rules } break; case 'final-pgpmimeYes': if (this.pgpmimeForced != EnigmailConstants.ENIG_ALWAYS) { // if not forced to PGP/Mime this.pgpmimeForced = EnigmailConstants.ENIG_ALWAYS; // force to PGP/Mime } break; case 'final-pgpmimeNo': if (this.pgpmimeForced != EnigmailConstants.ENIG_NEVER) { // if not forced not to PGP/Mime this.pgpmimeForced = EnigmailConstants.ENIG_NEVER; // force not to PGP/Mime } break; case 'final-useSmime': // this.pgpmimeForced = EnigmailConstants.ENIG_FORCE_SMIME; break; // status bar buttons: // - can only switch to force or not to force sign/enc case 'toggle-final-sign': this.signingNoLongerDependsOnEnc(); switch (this.statusSigned) { case EnigmailConstants.ENIG_FINAL_NO: case EnigmailConstants.ENIG_FINAL_FORCENO: this.signForced = EnigmailConstants.ENIG_ALWAYS; // force to sign break; case EnigmailConstants.ENIG_FINAL_YES: case EnigmailConstants.ENIG_FINAL_FORCEYES: this.signForced = EnigmailConstants.ENIG_NEVER; // force not to sign break; case EnigmailConstants.ENIG_FINAL_CONFLICT: this.signForced = EnigmailConstants.ENIG_NEVER; break; } break; case 'toggle-final-encrypt': if (this.juniorMode) { this.onPepEncryptButton(sendMode); return; } switch (this.statusEncrypted) { case EnigmailConstants.ENIG_FINAL_NO: case EnigmailConstants.ENIG_FINAL_FORCENO: this.encryptForced = EnigmailConstants.ENIG_ALWAYS; // force to encrypt break; case EnigmailConstants.ENIG_FINAL_YES: case EnigmailConstants.ENIG_FINAL_FORCEYES: this.encryptForced = EnigmailConstants.ENIG_NEVER; // force not to encrypt break; case EnigmailConstants.ENIG_FINAL_CONFLICT: this.encryptForced = EnigmailConstants.ENIG_NEVER; break; } break; case 'toggle-final-mime': switch (this.statusPGPMime) { case EnigmailConstants.ENIG_FINAL_NO: case EnigmailConstants.ENIG_FINAL_FORCENO: this.pgpmimeForced = EnigmailConstants.ENIG_ALWAYS; // force PGP/MIME break; case EnigmailConstants.ENIG_FINAL_YES: case EnigmailConstants.ENIG_FINAL_FORCEYES: this.pgpmimeForced = EnigmailConstants.ENIG_NEVER; // force Inline-PGP break; case EnigmailConstants.ENIG_FINAL_CONFLICT: this.pgpmimeForced = EnigmailConstants.ENIG_NEVER; break; } break; default: EnigmailDialog.alert(window, "Enigmail.msg.setFinalSendMode - unexpected value: " + sendMode); break; } // this is always a send mode change (only toggle effects) this.sendModeDirty = true; this.determineSendFlags(); //this.processFinalState(); //this.updateStatusBar(); }, /** key function to process the final encrypt/sign/pgpmime state from all settings @param sendFlags: contains the sendFlags if the message is really processed. Optional, can be null - uses as INPUT: - this.sendMode - this.encryptByRules, this.signByRules, pgpmimeByRules - this.encryptForced, this.encryptSigned - uses as OUTPUT: - this.statusEncrypt, this.statusSign, this.statusPGPMime no return value */ processFinalState: function(sendFlags) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.processFinalState()\n"); if (this.juniorMode) return; const nsIEnigmail = Components.interfaces.nsIEnigmail; const SIGN = nsIEnigmail.SEND_SIGNED; const ENCRYPT = nsIEnigmail.SEND_ENCRYPTED; let encFinally = null; let encReason = ""; let signFinally = null; let signReason = ""; let pgpmimeFinally = null; // ------ 1. process OpenPGP status ------ // process resulting encrypt mode if (this.encryptForced == EnigmailConstants.ENIG_NEVER) { // force not to encrypt? encFinally = EnigmailConstants.ENIG_FINAL_FORCENO; encReason = EnigmailLocale.getString("reasonManuallyForced"); } else if (this.encryptForced == EnigmailConstants.ENIG_ALWAYS) { // force to encrypt? encFinally = EnigmailConstants.ENIG_FINAL_FORCEYES; encReason = EnigmailLocale.getString("reasonManuallyForced"); } else switch (this.encryptByRules) { case EnigmailConstants.ENIG_NEVER: encFinally = EnigmailConstants.ENIG_FINAL_NO; encReason = EnigmailLocale.getString("reasonByRecipientRules"); break; case EnigmailConstants.ENIG_UNDEF: if (this.sendMode & ENCRYPT) { encFinally = EnigmailConstants.ENIG_FINAL_YES; if (this.getAccDefault("encrypt")) { encReason = EnigmailLocale.getString("reasonEnabledByDefault"); } } else { encFinally = EnigmailConstants.ENIG_FINAL_NO; } break; case EnigmailConstants.ENIG_ALWAYS: encFinally = EnigmailConstants.ENIG_FINAL_YES; encReason = EnigmailLocale.getString("reasonByRecipientRules"); break; case EnigmailConstants.ENIG_AUTO_ALWAYS: encFinally = EnigmailConstants.ENIG_FINAL_YES; encReason = EnigmailLocale.getString("reasonByAutoEncryption"); break; case EnigmailConstants.ENIG_CONFLICT: encFinally = EnigmailConstants.ENIG_FINAL_CONFLICT; encReason = EnigmailLocale.getString("reasonByConflict"); break; } EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: encrypt=" + ((this.sendMode & ENCRYPT) !== 0) + " encryptByRules=" + this.encryptByRules + " encFinally=" + encFinally + "\n"); EnigmailLog.DEBUG(" encReason=" + encReason + "\n"); // process resulting sign mode if (this.signForced == EnigmailConstants.ENIG_NEVER) { // force not to sign? signFinally = EnigmailConstants.ENIG_FINAL_FORCENO; signReason = EnigmailLocale.getString("reasonManuallyForced"); } else if (this.signForced == EnigmailConstants.ENIG_ALWAYS) { // force to sign? signFinally = EnigmailConstants.ENIG_FINAL_FORCEYES; signReason = EnigmailLocale.getString("reasonManuallyForced"); } else switch (this.signByRules) { case EnigmailConstants.ENIG_NEVER: signFinally = EnigmailConstants.ENIG_FINAL_NO; signReason = EnigmailLocale.getString("reasonByRecipientRules"); break; case EnigmailConstants.ENIG_UNDEF: if (this.sendMode & SIGN) { signFinally = EnigmailConstants.ENIG_FINAL_YES; if (this.getAccDefault("sign")) { signReason = EnigmailLocale.getString("reasonEnabledByDefault"); } } else { signFinally = EnigmailConstants.ENIG_FINAL_NO; } break; case EnigmailConstants.ENIG_ALWAYS: signFinally = EnigmailConstants.ENIG_FINAL_YES; signReason = EnigmailLocale.getString("reasonByRecipientRules"); break; case EnigmailConstants.ENIG_CONFLICT: signFinally = EnigmailConstants.ENIG_FINAL_CONFLICT; signReason = EnigmailLocale.getString("reasonByConflict"); break; } EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: signed=" + ((this.sendMode & SIGN) !== 0) + " signByRules=" + this.signByRules + " signFinally=" + signFinally + "\n"); EnigmailLog.DEBUG(" signReason=" + signReason + "\n"); // process option to finally sign if encrypted/unencrypted // (unless rules force not to sign) //var derivedFromEncMode = false; if (this.finalSignDependsOnEncrypt) { if (this.signByRules == EnigmailConstants.ENIG_UNDEF) { // if final sign mode not clear yet //derivedFromEncMode = true; switch (encFinally) { case EnigmailConstants.ENIG_FINAL_YES: case EnigmailConstants.ENIG_FINAL_FORCEYES: if (this.getAccDefault("signIfEnc")) { signFinally = EnigmailConstants.ENIG_FINAL_YES; signReason = EnigmailLocale.getString("reasonByEncryptionMode"); } break; case EnigmailConstants.ENIG_FINAL_NO: case EnigmailConstants.ENIG_FINAL_FORCENO: if (this.getAccDefault("signIfNotEnc")) { signFinally = EnigmailConstants.ENIG_FINAL_YES; signReason = EnigmailLocale.getString("reasonByEncryptionMode"); } break; case EnigmailConstants.ENIG_FINAL_CONFLICT: if (this.getAccDefault("signIfEnc") && this.getAccDefault("signIfNotEnc")) { signFinally = EnigmailConstants.ENIG_FINAL_YES; signReason = EnigmailLocale.getString("reasonByEncryptionMode"); } else { signFinally = EnigmailConstants.ENIG_FINAL_CONFLICT; } break; } EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: derived signFinally=" + signFinally + "\n"); EnigmailLog.DEBUG(" signReason=" + signReason + "\n"); } } this.statusPGPMime = EnigmailConstants.ENIG_FINAL_UNDEF; // ------ 2. Process S/MIME status ------ if (gSMFields) { let r = this.tryEnablingSMime(encFinally, signFinally); encFinally = r.encFinally; signFinally = r.signFinally; // update the S/MIME GUI elements try { setSecuritySettings("1"); } catch (ex) {} try { setSecuritySettings("2"); } catch (ex) {} } // ------ 3. process final resulting protocol mode (inline-PGP / PGP/MIME / S/MIME) ------ if (this.statusPGPMime !== EnigmailConstants.ENIG_FINAL_SMIME && this.statusPGPMime !== EnigmailConstants.ENIG_FINAL_FORCESMIME) { // process resulting PGP/MIME mode if (this.pgpmimeForced === EnigmailConstants.ENIG_NEVER) { // force not to PGP/Mime? pgpmimeFinally = EnigmailConstants.ENIG_FINAL_FORCENO; } else if (this.pgpmimeForced === EnigmailConstants.ENIG_ALWAYS) { // force to PGP/Mime? pgpmimeFinally = EnigmailConstants.ENIG_FINAL_FORCEYES; } else switch (this.pgpmimeByRules) { case EnigmailConstants.ENIG_NEVER: pgpmimeFinally = EnigmailConstants.ENIG_FINAL_NO; break; case EnigmailConstants.ENIG_UNDEF: pgpmimeFinally = ((this.sendPgpMime || (this.sendMode & nsIEnigmail.SEND_PGP_MIME)) ? EnigmailConstants.ENIG_FINAL_YES : EnigmailConstants.ENIG_FINAL_NO); break; case EnigmailConstants.ENIG_ALWAYS: pgpmimeFinally = EnigmailConstants.ENIG_FINAL_YES; break; case EnigmailConstants.ENIG_CONFLICT: pgpmimeFinally = EnigmailConstants.ENIG_FINAL_CONFLICT; break; } this.statusPGPMime = pgpmimeFinally; } EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: pgpmimeByRules=" + this.pgpmimeByRules + " pgpmimeFinally=" + pgpmimeFinally + "\n"); this.statusEncrypted = encFinally; this.statusSigned = signFinally; this.reasonEncrypted = encReason; this.reasonSigned = signReason; switch (this.statusEncrypted) { case EnigmailConstants.ENIG_FINAL_CONFLICT: case EnigmailConstants.ENIG_FINAL_FORCENO: case EnigmailConstants.ENIG_FINAL_YES: return; } switch (this.statusPGPMime) { case EnigmailConstants.ENIG_FINAL_SMIME: case EnigmailConstants.ENIG_FINAL_FORCESMIME: return; } }, /** * Try to enable S/MIME, repsecting the various Enigmail rules * * @param encFinally: Number - "final" encryption status before applying S/MIME * @param signFinally: Number - "final" signing status before applying S/MIME * * @return Object: * - encFinally: Number - new encryption status after trying S/MIME * - signFinally: Number - new signing status after trying S/MIME */ tryEnablingSMime: function(encFinally, signFinally) { let encryptSmime = false; gSMFields.requireEncryptMessage = false; gSMFields.signMessage = false; // do not try S/MIME encryption if one of the following applies: // - OpenPGP is preferred over S/MIME, and OpenPGP is possible // - OpenPGP is preferred over S/MIME, and OpenPGP is enabled by rules // - encryption is disabled by rules and encryption is not manually enabled // - encryption is manually disabled if (this.pgpmimeForced === EnigmailConstants.ENIG_FORCE_SMIME) { encryptSmime = true; } else if ((this.mimePreferOpenPGP === 1 && (this.autoPgpEncryption || this.encryptByRules === EnigmailConstants.ENIG_ALWAYS)) || (this.encryptByRules === EnigmailConstants.ENIG_NEVER && encFinally !== EnigmailConstants.ENIG_FINAL_FORCEYES) || (this.pgpmimeForced === EnigmailConstants.ENIG_NEVER || this.pgpmimeForced === EnigmailConstants.ENIG_ALWAYS) || encFinally === EnigmailConstants.ENIG_FINAL_FORCENO) { return { encFinally: encFinally, signFinally: signFinally }; } if (!encryptSmime) { if (EnigmailPrefs.getPref("autoSendEncrypted") == 1) { if (this.isSmimeEncryptionPossible()) { if (this.mimePreferOpenPGP === 0) { // S/MIME is preferred and encryption is possible encryptSmime = true; encFinally = EnigmailConstants.ENIG_FINAL_YES; } else if (encFinally === EnigmailConstants.ENIG_FINAL_NO || encFinally === EnigmailConstants.ENIG_FINAL_CONFLICT || !this.autoPgpEncryption) { // Enigmail is preferred but not possible; S/MIME enc. is possible encryptSmime = true; encFinally = EnigmailConstants.ENIG_FINAL_YES; } } } else if (encFinally === EnigmailConstants.ENIG_FINAL_FORCEYES) { if (this.isSmimeEncryptionPossible()) { if (this.mimePreferOpenPGP === 0 || (!this.autoPgpEncryption)) { // S/MIME is preferred and encryption is possible // or PGP/MIME is preferred but impossible encryptSmime = true; } } } } if (encryptSmime) { if (this.pgpmimeForced === EnigmailConstants.ENIG_FORCE_SMIME) { this.statusPGPMime = EnigmailConstants.ENIG_FINAL_FORCESMIME; } else this.statusPGPMime = EnigmailConstants.ENIG_FINAL_SMIME; if (encFinally === EnigmailConstants.ENIG_FINAL_YES || encFinally === EnigmailConstants.ENIG_FINAL_FORCEYES) { gSMFields.requireEncryptMessage = true; } if (signFinally === EnigmailConstants.ENIG_FINAL_YES || signFinally === EnigmailConstants.ENIG_FINAL_FORCEYES) { gSMFields.signMessage = true; } } else { gSMFields.requireEncryptMessage = false; if ((encFinally === EnigmailConstants.ENIG_FINAL_NO || encFinally === EnigmailConstants.ENIG_FINAL_FORCENO) && this.mimePreferOpenPGP === 0 && !this.autoPgpEncryption && (signFinally === EnigmailConstants.ENIG_FINAL_YES || signFinally === EnigmailConstants.ENIG_FINAL_FORCEYES)) { // S/MIME is preferred this.statusPGPMime = EnigmailConstants.ENIG_FINAL_SMIME; gSMFields.signMessage = true; } else { gSMFields.signMessage = false; } } return { encFinally: encFinally, signFinally: signFinally }; }, // process icon/strings of status bar buttons and menu entries according to final encrypt/sign/pgpmime status // - uses as INPUT: // - this.statusEncrypt, this.statusSign, this.statusPGPMime // - uses as OUTPUT: // - resulting icon symbols // - this.statusEncryptStr, this.statusSignStr, this.statusPGPMimeStr, this.statusInlinePGPStr, this.statusAttachOwnKey // - this.statusSMimeStr updateStatusBar: function() { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.updateStatusBar()\n"); this.statusEncryptedInStatusBar = this.statusEncrypted; // to double check broken promise for encryption if (!this.identity) { this.identity = getCurrentIdentity(); } var toolbarTxt = document.getElementById("enigmail-toolbar-text"); var encBroadcaster = document.getElementById("enigmail-bc-encrypt"); var signBroadcaster = document.getElementById("enigmail-bc-sign"); var attachBroadcaster = document.getElementById("enigmail-bc-attach"); let enc = this.getEncryptionEnabled(); let sign = this.getSigningEnabled(); // enigmail disabled for this identity?: if (!enc) { // hide icons if enigmail not enabled encBroadcaster.removeAttribute("encrypted"); encBroadcaster.setAttribute("disabled", "true"); } else { encBroadcaster.removeAttribute("disabled"); } if (!sign) { signBroadcaster.removeAttribute("signed"); signBroadcaster.setAttribute("disabled", "true"); attachBroadcaster.setAttribute("disabled", "true"); } else { signBroadcaster.removeAttribute("disabled"); attachBroadcaster.removeAttribute("disabled"); } if (!(enc || sign)) { if (toolbarTxt) { toolbarTxt.value = EnigmailLocale.getString("msgCompose.toolbarTxt.disabled"); toolbarTxt.removeAttribute("class"); } return; } // process resulting icon symbol and status strings for encrypt mode var encSymbol = null; var doEncrypt = false; var encStr = null; switch (this.statusEncrypted) { case EnigmailConstants.ENIG_FINAL_FORCENO: encSymbol = "forceNo"; encStr = EnigmailLocale.getString("encryptMessageNorm"); break; case EnigmailConstants.ENIG_FINAL_FORCEYES: doEncrypt = true; encSymbol = "forceYes"; encStr = EnigmailLocale.getString("encryptMessageNorm"); break; case EnigmailConstants.ENIG_FINAL_NO: encSymbol = "inactiveNone"; encStr = EnigmailLocale.getString("encryptMessageAuto"); break; case EnigmailConstants.ENIG_FINAL_YES: doEncrypt = true; encSymbol = "activeNone"; encStr = EnigmailLocale.getString("encryptMessageAuto"); break; case EnigmailConstants.ENIG_FINAL_CONFLICT: encSymbol = "inactiveConflict"; encStr = EnigmailLocale.getString("encryptMessageAuto"); break; } var encReasonStr = null; if (doEncrypt) { if (this.reasonEncrypted && this.reasonEncrypted !== "") { encReasonStr = EnigmailLocale.getString("encryptOnWithReason", [this.reasonEncrypted]); } else { encReasonStr = EnigmailLocale.getString("encryptOn"); } } else { if (this.reasonEncrypted && this.reasonEncrypted !== "") { encReasonStr = EnigmailLocale.getString("encryptOffWithReason", [this.reasonEncrypted]); } else { encReasonStr = EnigmailLocale.getString("encryptOff"); } } EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: encSymbol=" + encSymbol + " encReasonStr=" + encReasonStr + "\n"); // update encrypt icon and tooltip/menu-text encBroadcaster.setAttribute("encrypted", encSymbol); var encIcon = document.getElementById("button-enigmail-encrypt"); if (encIcon) { encIcon.setAttribute("tooltiptext", encReasonStr); } this.statusEncryptedStr = encStr; this.setChecked("enigmail-bc-encrypt", doEncrypt); // process resulting icon symbol for sign mode var signSymbol = null; var doSign = false; var signStr = ""; switch (this.statusSigned) { case EnigmailConstants.ENIG_FINAL_FORCENO: signSymbol = "forceNo"; signStr = EnigmailLocale.getString("signMessageNorm"); signReasonStr = EnigmailLocale.getString("signOffWithReason", [this.reasonSigned]); break; case EnigmailConstants.ENIG_FINAL_FORCEYES: doSign = true; signSymbol = "forceYes"; signStr = EnigmailLocale.getString("signMessageNorm"); signReasonStr = EnigmailLocale.getString("signOnWithReason", [this.reasonSigned]); break; case EnigmailConstants.ENIG_FINAL_NO: signSymbol = "inactiveNone"; signStr = EnigmailLocale.getString("signMessageAuto"); signReasonStr = EnigmailLocale.getString("signOffWithReason", [this.reasonSigned]); break; case EnigmailConstants.ENIG_FINAL_YES: doSign = true; signSymbol = "activeNone"; signStr = EnigmailLocale.getString("signMessageAuto"); signReasonStr = EnigmailLocale.getString("signOnWithReason", [this.reasonSigned]); break; case EnigmailConstants.ENIG_FINAL_CONFLICT: signSymbol = "inactiveConflict"; signStr = EnigmailLocale.getString("signMessageAuto"); signReasonStr = EnigmailLocale.getString("signOffWithReason", [this.reasonSigned]); break; } var signReasonStr = null; if (doSign) { if (this.reasonSigned && this.reasonSigned !== "") { signReasonStr = EnigmailLocale.getString("signOnWithReason", [this.reasonSigned]); } else { signReasonStr = signStr; } } else { if (this.reasonSigned && this.reasonSigned !== "") { signReasonStr = EnigmailLocale.getString("signOffWithReason", [this.reasonSigned]); } else { signReasonStr = signStr; } } EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: signSymbol=" + signSymbol + " signReasonStr=" + signReasonStr + "\n"); // update sign icon and tooltip/menu-text signBroadcaster.setAttribute("signed", signSymbol); var signIcon = document.getElementById("button-enigmail-sign"); if (signIcon) { signIcon.setAttribute("tooltiptext", signReasonStr); } this.statusSignedStr = signStr; this.setChecked("enigmail-bc-sign", doSign); // process resulting toolbar message var toolbarMsg = ""; if (doSign && doEncrypt) { toolbarMsg = EnigmailLocale.getString("msgCompose.toolbarTxt.signAndEncrypt"); } else if (doSign) { toolbarMsg = EnigmailLocale.getString("msgCompose.toolbarTxt.signOnly"); } else if (doEncrypt) { toolbarMsg = EnigmailLocale.getString("msgCompose.toolbarTxt.encryptOnly"); } else { toolbarMsg = EnigmailLocale.getString("msgCompose.toolbarTxt.noEncryption"); } if (toolbarTxt) { toolbarTxt.value = toolbarMsg; if (gMsgCompose.compFields.securityInfo) { let si = gMsgCompose.compFields.securityInfo.QueryInterface(Components.interfaces.nsIMsgSMIMECompFields); if (!doSign && !doEncrypt && !(gMsgCompose.compFields.securityInfo instanceof Components.interfaces.nsIMsgSMIMECompFields && (si.signMessage || si.requireEncryptMessage))) { toolbarTxt.setAttribute("class", "enigmailStrong"); } else { toolbarTxt.removeAttribute("class"); } } else { toolbarTxt.removeAttribute("class"); } } // update pgp mime/inline PGP menu-text if (this.statusPGPMime == EnigmailConstants.ENIG_FINAL_YES) { this.statusPGPMimeStr = EnigmailLocale.getString("pgpmimeAuto"); } else { this.statusPGPMimeStr = EnigmailLocale.getString("pgpmimeNormal"); } if (this.statusPGPMime == EnigmailConstants.ENIG_FINAL_NO) { this.statusInlinePGPStr = EnigmailLocale.getString("inlinePGPAuto"); } else { this.statusInlinePGPStr = EnigmailLocale.getString("inlinePGPNormal"); } if (this.statusPGPMime == EnigmailConstants.ENIG_FINAL_SMIME) { this.statusSMimeStr = EnigmailLocale.getString("smimeAuto"); } else { this.statusSMimeStr = EnigmailLocale.getString("smimeNormal"); } if (this.allowAttachOwnKey() === 1) { attachBroadcaster.removeAttribute("disabled"); } else { attachBroadcaster.setAttribute("disabled", "true"); } }, /** * determine if own key may be attached. * @result: Number: * -1: account not enabled for Enigmail * 0: account enabled but key mode set to "by Email address" * 1: account enabled; key specified */ allowAttachOwnKey: function() { let allow = -1; if (this.isEnigmailEnabled()) { allow = 0; if (this.identity.getIntAttribute("pgpKeyMode") > 0) { let keyIdValue = this.identity.getCharAttribute("pgpkeyId"); if (keyIdValue.search(/^ *(0x)?[0-9a-fA-F]* *$/) === 0) { allow = 1; } } } return allow; }, /* compute whether to sign/encrypt according to current rules and sendMode * - without any interaction, just to process resulting status bar icons */ determineSendFlags: function() { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.focusChange: Enigmail.msg.determineSendFlags\n"); let detailsObj = {}; if (this.juniorMode) { this.getPepMessageRating(); return detailsObj; } this.statusEncryptedInStatusBar = null; // to double check broken promise for encryption if (!this.identity) { this.identity = getCurrentIdentity(); } if (this.isEnigmailEnabled()) { var compFields = gMsgCompose.compFields; if (!Enigmail.msg.composeBodyReady) { compFields = Components.classes["@mozilla.org/messengercompose/composefields;1"].createInstance(Components.interfaces.nsIMsgCompFields); } Recipients2CompFields(compFields); gMsgCompose.expandMailingLists(); // process list of to/cc email addresses // - bcc email addresses are ignored, when processing whether to sign/encrypt var toAddrList = []; var arrLen = {}; var recList; if (compFields.to.length > 0) { recList = compFields.splitRecipients(compFields.to, true, arrLen); this.addRecipients(toAddrList, recList); } if (compFields.cc.length > 0) { recList = compFields.splitRecipients(compFields.cc, true, arrLen); this.addRecipients(toAddrList, recList); } this.encryptByRules = EnigmailConstants.ENIG_UNDEF; this.signByRules = EnigmailConstants.ENIG_UNDEF; this.pgpmimeByRules = EnigmailConstants.ENIG_UNDEF; // process rules if (toAddrList.length > 0 && EnigmailPrefs.getPref("assignKeysByRules")) { var matchedKeysObj = {}; var flagsObj = {}; if (EnigmailRules.mapAddrsToKeys(toAddrList.join(", "), false, // no interaction if not all addrs have a key window, matchedKeysObj, // resulting matching keys flagsObj)) { // resulting flags (0/1/2/3 for each type) this.encryptByRules = flagsObj.encrypt; this.signByRules = flagsObj.sign; this.pgpmimeByRules = flagsObj.pgpMime; if (matchedKeysObj.value && matchedKeysObj.value.length > 0) { // replace addresses with results from rules toAddrList = matchedKeysObj.value.split(", "); } } } let validKeyList = Enigmail.hlp.validKeysForAllRecipients(toAddrList.join(", "), detailsObj); this.autoPgpEncryption = (validKeyList !== null); // if not clear whether to encrypt yet, check whether automatically-send-encrypted applies if (toAddrList.length > 0 && this.encryptByRules == EnigmailConstants.ENIG_UNDEF && EnigmailPrefs.getPref("autoSendEncrypted") == 1) { if (validKeyList) { this.encryptByRules = EnigmailConstants.ENIG_AUTO_ALWAYS; } } } else { this.encryptByRules = EnigmailConstants.ENIG_AUTO_NEVER; this.autoPgpEncryption = false; } // process and signal new resulting state this.processFinalState(); this.updateStatusBar(); this.determineSendFlagId = null; return detailsObj; }, setChecked: function(elementId, checked) { let elem = document.getElementById(elementId); if (elem) { if (checked) { elem.setAttribute("checked", "true"); } else elem.removeAttribute("checked"); } }, setMenuSettings: function(postfix) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.setMenuSettings: postfix=" + postfix + "\n"); const nsIEnigmail = Components.interfaces.nsIEnigmail; const SIGN = nsIEnigmail.SEND_SIGNED; const ENCRYPT = nsIEnigmail.SEND_ENCRYPTED; var elem = document.getElementById("enigmail_compose_sign_item" + postfix); if (elem) { elem.setAttribute("label", this.statusSignedStr); switch (this.statusSigned) { case EnigmailConstants.ENIG_FINAL_YES: case EnigmailConstants.ENIG_FINAL_FORCEYES: elem.setAttribute("checked", "true"); break; default: elem.setAttribute("checked", "false"); } } elem = document.getElementById("enigmail_compose_encrypt_item" + postfix); if (elem) { elem.setAttribute("label", this.statusEncryptedStr); switch (this.statusEncrypted) { case EnigmailConstants.ENIG_FINAL_YES: case EnigmailConstants.ENIG_FINAL_FORCEYES: elem.setAttribute("checked", "true"); break; default: elem.setAttribute("checked", "false"); } } elem = document.getElementById("enigmail_compose_pgpmime_item" + postfix); if (elem) { elem.setAttribute("label", this.statusPGPMimeStr); switch (this.statusPGPMime) { case EnigmailConstants.ENIG_FINAL_YES: case EnigmailConstants.ENIG_FINAL_FORCEYES: elem.setAttribute("checked", "true"); break; default: elem.setAttribute("checked", "false"); } elem = document.getElementById("enigmail_compose_inline_item" + postfix); if (elem) { elem.setAttribute("label", this.statusInlinePGPStr); switch (this.statusPGPMime) { case EnigmailConstants.ENIG_FINAL_NO: case EnigmailConstants.ENIG_FINAL_FORCENO: case EnigmailConstants.ENIG_FINAL_CONFLICT: case EnigmailConstants.ENIG_FINAL_UNDEF: elem.setAttribute("checked", "true"); break; default: elem.setAttribute("checked", "false"); } } elem = document.getElementById("enigmail_compose_smime_item" + postfix); if (elem) { elem.setAttribute("label", this.statusSMimeStr); switch (this.statusPGPMime) { case EnigmailConstants.ENIG_FINAL_SMIME: case EnigmailConstants.ENIG_FINAL_FORCESMIME: elem.setAttribute("checked", "true"); break; default: elem.setAttribute("checked", "false"); } } let menuElement = document.getElementById("enigmail_insert_own_key"); if (menuElement) { if (this.identity.getIntAttribute("pgpKeyMode") > 0) { menuElement.setAttribute("checked", this.attachOwnKeyObj.appendAttachment.toString()); menuElement.removeAttribute("disabled"); } else { menuElement.setAttribute("disabled", "true"); } } } }, pepMenuPopup: function() { let encMenu = document.getElementById("enigmail_compose_pep_encrypt"); let hsMenu = document.getElementById("enigmail_composeMenu_pep_handshake"); let pepBc = document.getElementById("enigmail-bc-pepEncrypt"); if (this.pepEnabled()) { encMenu.setAttribute("checked", pepBc.getAttribute("encrypt")); encMenu.removeAttribute("disabled"); hsMenu.removeAttribute("disabled"); } else { encMenu.setAttribute("checked", "false"); encMenu.setAttribute("disabled", "true"); hsMenu.setAttribute("disabled", "true"); } }, /** * Determine if pEp was disabled by the user */ pepEnabled: function() { let id = getCurrentIdentity(); return id.getBoolAttribute("enablePEP"); }, pepDisabledError: function() { EnigmailDialog.alert(window, EnigmailLocale.getString("pep.alert.disabledForIdentity")); }, onPepEncryptMenu: function() { if (!this.pepEnabled()) { this.pepDisabledError(); return; } let pepBc = document.getElementById("enigmail-bc-pepEncrypt"); pepBc.setAttribute("encrypt", pepBc.getAttribute("encrypt") === "true" ? "false" : "true"); this.getPepMessageRating(); }, onPepEncryptButton: function() { this.onPepEncryptMenu(); }, onPepHandshakeButton: function(event) { if (!this.pepEnabled()) { this.pepDisabledError(); return; } event.stopPropagation(); EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.onPepHandshakeButton()\n"); let bc = document.getElementById("enigmail-bc-pepEncrypt"); if (bc.getAttribute("encrypt") === "false") { EnigmailDialog.info(window, EnigmailLocale.getString("handshakeDlg.error.noProtection")); return; } let o = this.compileFromAndTo(); let toAddr = EnigmailFuncs.stripEmail(o.toAddrList.join(",")).split(/,/); if (o.toAddrList.length === 0) { EnigmailDialog.info(window, EnigmailLocale.getString("handshakeDlg.error.noPeers")); return; } let myId = getCurrentIdentity(); let inputObj = { myself: myId.email, addresses: toAddr, direction: 1, parentWindow: window, onComplete: Enigmail.msg.getPepMessageRating.bind(Enigmail.msg) }; window.openDialog("chrome://enigmail/content/pepPrepHandshake.xul", "", "dialog,modal,centerscreen", inputObj); }, displaySecuritySettings: function() { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.displaySecuritySettings\n"); var inputObj = { statusEncrypted: this.statusEncrypted, statusSigned: this.statusSigned, statusPGPMime: this.statusPGPMime, success: false, resetDefaults: false }; window.openDialog("chrome://enigmail/content/enigmailEncryptionDlg.xul", "", "dialog,modal,centerscreen", inputObj); if (!inputObj.success) return; // Cancel pressed if (inputObj.resetDefaults) { // reset everything to defaults this.encryptForced = EnigmailConstants.ENIG_UNDEF; this.signForced = EnigmailConstants.ENIG_UNDEF; this.pgpmimeForced = EnigmailConstants.ENIG_UNDEF; this.finalSignDependsOnEncrypt = true; } else { if (this.signForced != inputObj.sign) { this.dirty = 2; this.signForced = inputObj.sign; this.finalSignDependsOnEncrypt = false; } if (this.encryptForced != inputObj.encrypt || this.pgpmimeForced != inputObj.pgpmime) { this.dirty = 2; } this.encryptForced = inputObj.encrypt; this.pgpmimeForced = inputObj.pgpmime; } this.processFinalState(); this.updateStatusBar(); }, signingNoLongerDependsOnEnc: function() { if (this.finalSignDependsOnEncrypt && (!this.juniorMode)) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.signingNoLongerDependsOnEnc(): unbundle final signing\n"); this.finalSignDependsOnEncrypt = false; EnigmailDialog.alertPref(window, EnigmailLocale.getString("signIconClicked"), "displaySignWarn"); } }, confirmBeforeSend: function(toAddrStr, gpgKeys, sendFlags, isOffline) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.confirmBeforeSend: sendFlags=" + sendFlags + "\n"); // get confirmation before sending message const nsIEnigmail = Components.interfaces.nsIEnigmail; const SIGN = nsIEnigmail.SEND_SIGNED; const ENCRYPT = nsIEnigmail.SEND_ENCRYPTED; // get wording for message status (e.g. " SIGNED ENCRYPTED") var msgStatus = ""; if (sendFlags & (ENCRYPT | SIGN)) { if (this.statusPGPMime === EnigmailConstants.ENIG_FINAL_SMIME || this.statusPGPMime === EnigmailConstants.ENIG_FINAL_FORCESMIME) { msgStatus += " " + EnigmailLocale.getString("statSMIME"); } else if (sendFlags & nsIEnigmail.SEND_PGP_MIME) { msgStatus += " " + EnigmailLocale.getString("statPGPMIME"); } if (sendFlags & SIGN) { msgStatus += " " + EnigmailLocale.getString("statSigned"); } if (sendFlags & ENCRYPT) { msgStatus += " " + EnigmailLocale.getString("statEncrypted"); } } else { msgStatus += " " + EnigmailLocale.getString("statPlain"); } // create message var msgConfirm = ""; if (isOffline || sendFlags & nsIEnigmail.SEND_LATER) { msgConfirm = EnigmailLocale.getString("offlineSave", [msgStatus, EnigmailFuncs.stripEmail(toAddrStr).replace(/,/g, ", ")]); } else { msgConfirm = EnigmailLocale.getString("onlineSend", [msgStatus, EnigmailFuncs.stripEmail(toAddrStr).replace(/,/g, ", ")]); } // add list of keys if (sendFlags & ENCRYPT) { gpgKeys = gpgKeys.replace(/^, /, "").replace(/, $/, ""); // make gpg keys unique let keyList = gpgKeys.split(/[, ]+/).reduce(function _f(p, key) { if (p.indexOf(key) < 0) p.push(key); return p; }, []); if (this.statusPGPMime !== EnigmailConstants.ENIG_FINAL_SMIME && this.statusPGPMime !== EnigmailConstants.ENIG_FINAL_FORCESMIME) { msgConfirm += "\n\n" + EnigmailLocale.getString("encryptKeysNote", [keyList.join(", ")]); } } return EnigmailDialog.confirmDlg(window, msgConfirm, EnigmailLocale.getString((isOffline || sendFlags & nsIEnigmail.SEND_LATER) ? "msgCompose.button.save" : "msgCompose.button.send")); }, addRecipients: function(toAddrList, recList) { for (var i = 0; i < recList.length; i++) { toAddrList.push(EnigmailFuncs.stripEmail(recList[i].replace(/[",]/g, ""))); } }, setDraftStatus: function() { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.setDraftStatus - enabling draft mode\n"); // Draft Status: // N (for new style) plus String of 4 numbers: // 1: encryption // 2: signing // 3: PGP/MIME // 4: attach own key var draftStatus = "N" + this.encryptForced + this.signForced + this.pgpmimeForced + (this.attachOwnKeyObj.appendAttachment ? "1" : "0"); this.setAdditionalHeader("X-Enigmail-Draft-Status", draftStatus); }, getForceRecipientDlg: function() { // force add-rule dialog for each missing key?: let forceRecipientSettings = false; // if keys are ONLY assigned by rules, force add-rule dialog for each missing key if (EnigmailPrefs.getPref("assignKeysByRules") && !EnigmailPrefs.getPref("assignKeysByEmailAddr") && !EnigmailPrefs.getPref("assignKeysManuallyIfMissing") && !EnigmailPrefs.getPref("assignKeysManuallyAlways")) { forceRecipientSettings = true; } return forceRecipientSettings; }, getSenderUserId: function() { var userIdValue = null; if (this.identity.getIntAttribute("pgpKeyMode") > 0) { userIdValue = this.identity.getCharAttribute("pgpkeyId"); if (!userIdValue) { var mesg = EnigmailLocale.getString("composeSpecifyEmail"); var valueObj = { value: userIdValue }; if (EnigmailDialog.promptValue(window, mesg, valueObj)) { userIdValue = valueObj.value; } } if (userIdValue) { this.identity.setCharAttribute("pgpkeyId", userIdValue); } else { this.identity.setIntAttribute("pgpKeyMode", 0); } } if (typeof(userIdValue) != "string") { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.getSenderUserId: type of userIdValue=" + typeof(userIdValue) + "\n"); userIdValue = this.identity.email; } return userIdValue; }, /* process rules and find keys for passed email addresses * This is THE core method to prepare sending encryptes emails. * - it processes the recipient rules (if not disabled) * - it * * @sendFlags: Longint - all current combined/processed send flags (incl. optSendFlags) * @optSendFlags: Longint - may only be SEND_ALWAYS_TRUST or SEND_ENCRYPT_TO_SELF * @gotSendFlags: Longint - initial sendMode of encryptMsg() (0 or SIGN or ENCRYPT or SIGN|ENCRYPT) * @fromAddr: String - from email * @toAddrList: Array - both to and cc receivers * @bccAddrList: Array - bcc receivers * @return: Object: * - sendFlags (Longint) * - toAddrStr comma separated string of unprocessed to/cc emails * - bccAddrStr comma separated string of unprocessed to/cc emails * or null (cancel sending the email) */ keySelection: function(enigmailSvc, sendFlags, optSendFlags, gotSendFlags, fromAddr, toAddrList, bccAddrList) { EnigmailLog.DEBUG("=====> keySelection()\n"); EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.keySelection()\n"); const nsIEnigmail = Components.interfaces.nsIEnigmail; const SIGN = nsIEnigmail.SEND_SIGNED; const ENCRYPT = nsIEnigmail.SEND_ENCRYPTED; var toAddrStr = toAddrList.join(", "); var bccAddrStr = bccAddrList.join(", "); // NOTE: If we only have bcc addresses, we currently do NOT process rules and select keys at all // This is GOOD because sending keys for bcc addresses makes bcc addresses visible // (thus compromising the concept of bcc) // THUS, we disable encryption even though all bcc receivers might want to have it encrypted. if (toAddrStr.length === 0) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.keySelection(): skip key selection because we neither have \"to\" nor \"cc\" addresses\n"); if (this.statusPGPMime == EnigmailConstants.ENIG_FINAL_YES || this.statusPGPMime == EnigmailConstants.ENIG_FINAL_FORCEYES) { sendFlags |= nsIEnigmail.SEND_PGP_MIME; } else if (this.statusPGPMime == EnigmailConstants.ENIG_FINAL_NO || this.statusPGPMime == EnigmailConstants.ENIG_FINAL_FORCENO || this.statusPGPMime == EnigmailConstants.ENIG_FINAL_CONFLICT) { sendFlags &= ~nsIEnigmail.SEND_PGP_MIME; } return { sendFlags: sendFlags, toAddrStr: toAddrStr, bccAddrStr: bccAddrStr }; } EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.keySelection(): toAddrStr=\"" + toAddrStr + "\" bccAddrStr=\"" + bccAddrStr + "\"\n"); var forceRecipientSettings = this.getForceRecipientDlg(); // REPEAT 1 or 2 times: // NOTE: The only way to call this loop twice is to come to the "continue;" statement below, // which forces a second iteration (with forceRecipientSettings==true) var doRulesProcessingAgain; do { doRulesProcessingAgain = false; // process rules if not disabled // - enableRules: rules not temporarily disabled // REPLACES email addresses by keys in its result !!! var refreshKeyList = true; if (EnigmailPrefs.getPref("assignKeysByRules") && this.enableRules) { let result = this.processRules(forceRecipientSettings, sendFlags, optSendFlags, toAddrStr, bccAddrStr); if (!result) { return null; } sendFlags = result.sendFlags; optSendFlags = result.optSendFlags; toAddrStr = result.toAddr; // replace email addresses with rules by the corresponding keys bccAddrStr = result.bccAddr; // replace email addresses with rules by the corresponding keys refreshKeyList = !result.didRefreshKeyList; // if key list refreshed we don't have to do it again } // if encryption is requested for the email: // - encrypt test message for default encryption // - might trigger a second iteration through this loop // - if during its dialog for manual key selection "create per-recipient rules" is pressed // to force manual settings for missing keys // LEAVES remaining email addresses not covered by rules as they are if (sendFlags & ENCRYPT) { let result = this.encryptTestMessage(enigmailSvc, sendFlags, optSendFlags, fromAddr, toAddrStr, bccAddrStr, bccAddrList, refreshKeyList); if (!result) { return null; } sendFlags = result.sendFlags; toAddrStr = result.toAddrStr; bccAddrStr = result.bccAddrStr; if (result.doRulesProcessingAgain) { // start rule processing again ? doRulesProcessingAgain = true; if (result.createNewRule) { forceRecipientSettings = true; } } } } while (doRulesProcessingAgain); EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.keySelection(): return toAddrStr=\"" + toAddrStr + "\" bccAddrStr=\"" + bccAddrStr + "\"\n"); EnigmailLog.DEBUG(" <=== keySelection()\n"); return { sendFlags: sendFlags, toAddrStr: toAddrStr, bccAddrStr: bccAddrStr }; }, /** * Determine if S/MIME or OpenPGP should be used * * @param sendFlags: Number - input send flags. * * @return: Boolean: * 1: use OpenPGP * 0: use S/MIME */ preferPgpOverSmime: function(sendFlags) { const nsIEnigmail = Components.interfaces.nsIEnigmail; if (gMsgCompose.compFields.securityInfo instanceof Components.interfaces.nsIMsgSMIMECompFields && (sendFlags & (nsIEnigmail.SEND_SIGNED | nsIEnigmail.SEND_ENCRYPTED))) { let si = gMsgCompose.compFields.securityInfo.QueryInterface(Components.interfaces.nsIMsgSMIMECompFields); if (si.requireEncryptMessage || si.signMessage) { if (sendFlags & nsIEnigmail.SAVE_MESSAGE) { // use S/MIME if it's enabled for saving drafts return 0; } else { return this.mimePreferOpenPGP; } } } return 1; }, /** * check if S/MIME encryption can be enabled * * @return: Boolean - true: keys for all recipients are available */ isSmimeEncryptionPossible: function() { let ret = false; let id = getCurrentIdentity(); if (id.getUnicharAttribute("encryption_cert_name") === "") return false; // enable encryption if keys for all recipients are available let missingCount = {}; let emailAddresses = {}; try { Components.classes["@mozilla.org/messenger-smime/smimejshelper;1"] .createInstance(Components.interfaces.nsISMimeJSHelper) .getNoCertAddresses(gMsgCompose.compFields, missingCount, emailAddresses); } catch (e) { return false; } if (missingCount.value === 0) { return true; } return false; }, /** * try to apply the OpenPGP rules * * @forceRecipientSetting: Boolean - force manual selection for each missing key? * @sendFlags: Integer - all current combined/processed send flags (incl. optSendFlags) * @optSendFlags: Integer - may only be SEND_ALWAYS_TRUST or SEND_ENCRYPT_TO_SELF * @toAddrStr: String - comma separated string of keys and unprocessed to/cc emails * @bccAddrStr: String - comma separated string of keys and unprocessed bcc emails * @return: { sendFlags, toAddr, bccAddr } * or null (cancel sending the email) */ processRules: function(forceRecipientSettings, sendFlags, optSendFlags, toAddrStr, bccAddrStr) { EnigmailLog.DEBUG("=====> processRules()\n"); EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.processRules(): toAddrStr=\"" + toAddrStr + "\" bccAddrStr=\"" + bccAddrStr + "\" forceRecipientSettings=" + forceRecipientSettings + "\n"); // process defaults const nsIEnigmail = Components.interfaces.nsIEnigmail; const SIGN = nsIEnigmail.SEND_SIGNED; const ENCRYPT = nsIEnigmail.SEND_ENCRYPTED; var didRefreshKeyList = false; // return value to signal whether the key list was refreshed // get keys for to and cc addresses: // - matchedKeysObj will contain the keys and the remaining toAddrStr elements var matchedKeysObj = {}; // returned value for matched keys var flagsObj = {}; // returned value for flags if (!EnigmailRules.mapAddrsToKeys(toAddrStr, forceRecipientSettings, // true => start dialog for addrs without any key window, matchedKeysObj, flagsObj)) { return null; } if (matchedKeysObj.value) { toAddrStr = matchedKeysObj.value; EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.processRules(): after mapAddrsToKeys() toAddrStr=\"" + toAddrStr + "\"\n"); } this.encryptByRules = flagsObj.encrypt; this.signByRules = flagsObj.sign; this.pgpmimeByRules = flagsObj.pgpMime; // if not clear whether to encrypt yet, check whether automatically-send-encrypted applies // - check whether bcc is empty here? if (bccAddrStr.length === 0) if (toAddrStr.length > 0 && this.encryptByRules == EnigmailConstants.ENIG_UNDEF && EnigmailPrefs.getPref("autoSendEncrypted") == 1) { var validKeyList = Enigmail.hlp.validKeysForAllRecipients(toAddrStr); if (validKeyList) { this.encryptByRules = EnigmailConstants.ENIG_AUTO_ALWAYS; toAddrStr = validKeyList.join(", "); } } // process final state this.processFinalState(sendFlags); // final handling of conflicts: // - pgpMime conflicts always result into pgpMime = 0/'never' if (this.statusPGPMime == EnigmailConstants.ENIG_FINAL_CONFLICT) { this.statusPGPMime = EnigmailConstants.ENIG_FINAL_NO; } // - encrypt/sign conflicts result into result 0/'never' // with possible dialog to give a corresponding feedback var conflictFound = false; if (this.statusEncrypted == EnigmailConstants.ENIG_FINAL_CONFLICT) { this.statusEncrypted = EnigmailConstants.ENIG_FINAL_NO; conflictFound = true; } if (this.statusSigned == EnigmailConstants.ENIG_FINAL_CONFLICT) { this.statusSigned = EnigmailConstants.ENIG_FINAL_NO; conflictFound = true; } if (conflictFound) { if (!Enigmail.hlp.processConflicts(this.statusEncrypted == EnigmailConstants.ENIG_FINAL_YES || this.statusEncrypted == EnigmailConstants.ENIG_FINAL_FORCEYES, this.statusSigned == EnigmailConstants.ENIG_FINAL_YES || this.statusSigned == EnigmailConstants.ENIG_FINAL_FORCEYES)) { return null; } } // process final sendMode // ENIG_FINAL_CONFLICT no longer possible switch (this.statusEncrypted) { case EnigmailConstants.ENIG_FINAL_NO: case EnigmailConstants.ENIG_FINAL_FORCENO: sendFlags &= ~ENCRYPT; break; case EnigmailConstants.ENIG_FINAL_YES: case EnigmailConstants.ENIG_FINAL_FORCEYES: sendFlags |= ENCRYPT; break; } switch (this.statusSigned) { case EnigmailConstants.ENIG_FINAL_NO: case EnigmailConstants.ENIG_FINAL_FORCENO: sendFlags &= ~SIGN; break; case EnigmailConstants.ENIG_FINAL_YES: case EnigmailConstants.ENIG_FINAL_FORCEYES: sendFlags |= SIGN; break; } switch (this.statusPGPMime) { case EnigmailConstants.ENIG_FINAL_NO: case EnigmailConstants.ENIG_FINAL_FORCENO: sendFlags &= ~nsIEnigmail.SEND_PGP_MIME; break; case EnigmailConstants.ENIG_FINAL_YES: case EnigmailConstants.ENIG_FINAL_FORCEYES: sendFlags |= nsIEnigmail.SEND_PGP_MIME; break; } // get keys according to rules for bcc addresses: // - matchedKeysObj will contain the keys and the remaining bccAddrStr elements // - NOTE: bcc recipients are ignored when in general computing whether to sign or encrypt or pgpMime if (!EnigmailRules.mapAddrsToKeys(bccAddrStr, forceRecipientSettings, // true => start dialog for addrs without any key window, matchedKeysObj, flagsObj)) { return null; } if (matchedKeysObj.value) { bccAddrStr = matchedKeysObj.value; EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.processRules(): after mapAddrsToKeys() bccAddrStr=\"" + bccAddrStr + "\"\n"); } EnigmailLog.DEBUG(" <=== processRules()\n"); return { sendFlags: sendFlags, optSendFlags: optSendFlags, toAddr: toAddrStr, bccAddr: bccAddrStr, didRefreshKeyList: didRefreshKeyList }; }, /* encrypt a test message to see whether we have all necessary keys * * @sendFlags: all current combined/processed send flags (incl. optSendFlags) * @optSendFlags: may only be SEND_ALWAYS_TRUST or SEND_ENCRYPT_TO_SELF * @fromAddr: from email * @toAddrStr: comma separated string of keys and unprocessed to/cc emails * @bccAddrStr: comma separated string of keys and unprocessed bcc emails * @bccAddrList: bcc receivers * @return: doRulesProcessingAgain: start with rule processing once more * or null (cancel sending the email) */ encryptTestMessage: function(enigmailSvc, sendFlags, optSendFlags, fromAddr, toAddrStr, bccAddrStr, bccAddrList, refresh) { EnigmailLog.DEBUG("=====> encryptTestMessage()\n"); const nsIEnigmail = Components.interfaces.nsIEnigmail; const SIGN = nsIEnigmail.SEND_SIGNED; const ENCRYPT = nsIEnigmail.SEND_ENCRYPTED; var testCipher = null; var testExitCodeObj = {}; var testStatusFlagsObj = {}; var testErrorMsgObj = {}; // get keys for remaining email addresses // - NOTE: This should not be necessary; however, in GPG there is a problem: // Only the first key found for an email is used. // If this is invalid, no other keys are tested. // Thus, WE make it better here in enigmail until the bug is fixed. var details = {}; // will contain msgList[] afterwards if (EnigmailPrefs.getPref("assignKeysByEmailAddr")) { var validKeyList = Enigmail.hlp.validKeysForAllRecipients(toAddrStr, details); if (validKeyList) { toAddrStr = validKeyList.join(", "); } } // encrypt test message for test recipients var testPlain = "Test Message"; var testUiFlags = nsIEnigmail.UI_TEST; var testSendFlags = nsIEnigmail.SEND_TEST | ENCRYPT | optSendFlags; EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.encryptTestMessage(): call encryptMessage() for fromAddr=\"" + fromAddr + "\" toAddrStr=\"" + toAddrStr + "\" bccAddrStr=\"" + bccAddrStr + "\"\n"); testCipher = enigmailSvc.encryptMessage(window, testUiFlags, testPlain, fromAddr, toAddrStr, bccAddrStr, testSendFlags, testExitCodeObj, testStatusFlagsObj, testErrorMsgObj); if (testStatusFlagsObj.value & (nsIEnigmail.INVALID_RECIPIENT | nsIEnigmail.NO_SECKEY)) { // check if own key is invalid EnigmailDialog.alert(window, testErrorMsgObj.value); return null; } // if // - "always ask/manually" (even if all keys were found) or // - unless "ask for missing keys": // - we have an invalid recipient or // - we could not resolve any/all keys // (due to disabled "assignKeysByEmailAddr"" or multiple keys with same trust for a recipient) // start the dialog for user selected keys if (EnigmailPrefs.getPref("assignKeysManuallyAlways") || (((testStatusFlagsObj.value & nsIEnigmail.INVALID_RECIPIENT) || toAddrStr.indexOf('@') >= 0) && EnigmailPrefs.getPref("assignKeysManuallyIfMissing")) || (details && details.errArray && details.errArray.length > 0) ) { // check for invalid recipient keys var resultObj = { foundKeys: false }; var inputObj = {}; inputObj.toAddr = toAddrStr; inputObj.invalidAddr = Enigmail.hlp.getInvalidAddress(testErrorMsgObj.value); if (details && details.errArray && details.errArray.length > 0) { inputObj.errArray = details.errArray; } // prepare dialog options: inputObj.options = "multisel"; if (EnigmailPrefs.getPref("assignKeysByRules")) { inputObj.options += ",rulesOption"; // enable button to create per-recipient rule } if (EnigmailPrefs.getPref("assignKeysManuallyAlways")) { inputObj.options += ",noforcedisp"; } if (!(sendFlags & SIGN)) { inputObj.options += ",unsigned"; } if (this.trustAllKeys) { inputObj.options += ",trustallkeys"; } if (sendFlags & nsIEnigmail.SEND_LATER) { let sendLaterLabel = EnigmailLocale.getString("sendLaterCmd.label"); inputObj.options += ",sendlabel=" + sendLaterLabel; } inputObj.options += ","; inputObj.dialogHeader = EnigmailLocale.getString("recipientsSelectionHdr"); // perform key selection dialog: window.openDialog("chrome://enigmail/content/enigmailKeySelection.xul", "", "dialog,modal,centerscreen,resizable", inputObj, resultObj); // process result from key selection dialog: try { // CANCEL: if (resultObj.cancelled) { return null; } // repeat checking of rules etc. (e.g. after importing new key) if (resultObj.repeatEvaluation) { // THIS is the place that triggers a second iteration let returnObj = { doRulesProcessingAgain: true, createNewRule: false, sendFlags: sendFlags, toAddrStr: toAddrStr, bccAddrStr: bccAddrStr }; // "Create per recipient rule(s)": if (resultObj.perRecipientRules && this.enableRules) { // do an extra round because the user wants to set a PGP rule returnObj.createNewRule = true; } return returnObj; } // process OK button: if (resultObj.encrypt) { sendFlags |= ENCRYPT; // should anyway be set if (bccAddrList.length > 0) { toAddrStr = ""; bccAddrStr = resultObj.userList.join(", "); } else { toAddrStr = resultObj.userList.join(", "); bccAddrStr = ""; } } else { // encryption explicitely turned off sendFlags &= ~ENCRYPT; // counts as forced non-encryption // (no internal error if different state was processed before) this.statusEncrypted = EnigmailConstants.ENIG_FINAL_NO; this.statusEncryptedInStatusBar = EnigmailConstants.ENIG_FINAL_NO; } if (resultObj.sign) { sendFlags |= SIGN; } else { sendFlags &= ~SIGN; } testCipher = "ok"; testExitCodeObj.value = 0; } catch (ex) { // cancel pressed -> don't send mail return null; } } // If test encryption failed and never ask manually, turn off default encryption if ((!testCipher || (testExitCodeObj.value !== 0)) && !EnigmailPrefs.getPref("assignKeysManuallyIfMissing") && !EnigmailPrefs.getPref("assignKeysManuallyAlways")) { sendFlags &= ~ENCRYPT; this.statusEncrypted = EnigmailConstants.ENIG_FINAL_NO; this.statusEncryptedInStatusBar = EnigmailConstants.ENIG_FINAL_NO; EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.encryptTestMessage: No default encryption because test failed\n"); } EnigmailLog.DEBUG(" <=== encryptTestMessage()\n"); return { doRulesProcessingAgain: false, createNewRule: false, sendFlags: sendFlags, toAddrStr: toAddrStr, bccAddrStr: bccAddrStr }; }, /* Manage the wrapping of inline signed mails * * @wrapresultObj: Result: * @wrapresultObj.cancelled, true if send operation is to be cancelled, else false * @wrapresultObj.usePpgMime, true if message send option was changed to PGP/MIME, else false */ wrapInLine: function(wrapresultObj) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: WrapInLine\n"); wrapresultObj.cancelled = false; wrapresultObj.usePpgMime = false; try { const dce = Components.interfaces.nsIDocumentEncoder; var wrapper = gMsgCompose.editor.QueryInterface(Components.interfaces.nsIEditorMailSupport); var editor = gMsgCompose.editor.QueryInterface(Components.interfaces.nsIPlaintextEditor); var encoderFlags = dce.OutputFormatted | dce.OutputLFLineBreak; var wrapWidth = this.getMailPref("mailnews.wraplength"); if (wrapWidth > 0 && wrapWidth < 68 && editor.wrapWidth > 0) { if (EnigmailDialog.confirmDlg(window, EnigmailLocale.getString("minimalLineWrapping", [wrapWidth]))) { wrapWidth = 68; EnigmailPrefs.getPrefRoot().setIntPref("mailnews.wraplength", wrapWidth); } } if (wrapWidth && editor.wrapWidth > 0) { // First use standard editor wrap mechanism: editor.wrapWidth = wrapWidth - 2; wrapper.rewrap(true); editor.wrapWidth = wrapWidth; // Now get plaintext from editor var wrapText = this.editorGetContentAs("text/plain", encoderFlags); // split the lines into an array wrapText = wrapText.split(/\r\n|\r|\n/g); var i = 0; var excess = 0; // inspect all lines of mail text to detect if we still have excessive lines which the "standard" editor wrapper leaves for (i = 0; i < wrapText.length; i++) { if (wrapText[i].length > wrapWidth) { excess = 1; } } if (excess) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Excess lines detected\n"); var resultObj = {}; window.openDialog("chrome://enigmail/content/enigmailWrapSelection.xul", "", "dialog,modal,centerscreen", resultObj); try { if (resultObj.cancelled) { // cancel pressed -> do not send, return instead. wrapresultObj.cancelled = true; return; } } catch (ex) { // cancel pressed -> do not send, return instead. wrapresultObj.cancelled = true; return; } var quote = ""; var limitedLine = ""; var restOfLine = ""; var WrapSelect = resultObj.Select; switch (WrapSelect) { case "0": // Selection: Force rewrap for (i = 0; i < wrapText.length; i++) { if (wrapText[i].length > wrapWidth) { // If the current line is too long, limit it hard to wrapWidth and insert the rest as the next line into wrapText array limitedLine = wrapText[i].slice(0, wrapWidth); restOfLine = wrapText[i].slice(wrapWidth); // We should add quotes at the beginning of "restOfLine", if limitedLine is a quoted line // However, this would be purely academic, because limitedLine will always be "standard"-wrapped // by the editor-rewrapper at the space between quote sign (>) and the quoted text. wrapText.splice(i, 1, limitedLine, restOfLine); } } break; case "1": // Selection: Send as is break; case "2": // Selection: Use MIME wrapresultObj.usePpgMime = true; break; case "3": // Selection: Edit manually -> do not send, return instead. wrapresultObj.cancelled = true; return; } //switch } // Now join all lines together again and feed it back into the compose editor. var newtext = wrapText.join("\n"); this.replaceEditorText(newtext); } } catch (ex) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Exception while wrapping=" + ex + "\n"); } }, // Save draft message. We do not want most of the other processing for encrypted mails here... saveDraftMessage: function() { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: saveDraftMessage()\n"); const nsIEnigmail = Components.interfaces.nsIEnigmail; let doEncrypt = this.isEnigmailEnabled() && this.identity.getBoolAttribute("autoEncryptDrafts"); this.setDraftStatus(); if (!doEncrypt) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: drafts disabled\n"); try { if (gMsgCompose.compFields.securityInfo instanceof Components.interfaces.nsIEnigMsgCompFields) { gMsgCompose.compFields.securityInfo.sendFlags = 0; } } catch (ex) {} return true; } let sendFlags = nsIEnigmail.SEND_PGP_MIME | nsIEnigmail.SEND_ENCRYPTED | nsIEnigmail.SAVE_MESSAGE | nsIEnigmail.SEND_ALWAYS_TRUST; let fromAddr = this.identity.email; let userIdValue = this.getSenderUserId(); if (userIdValue) { fromAddr = userIdValue; } let enigmailSvc = EnigmailCore.getService(window); if (!enigmailSvc) return true; if (this.preferPgpOverSmime(sendFlags) === 0) return true; // use S/MIME // Try to save draft var testCipher = null; var testExitCodeObj = {}; var testStatusFlagsObj = {}; var testErrorMsgObj = {}; // encrypt test message for test recipients var testPlain = "Test Message"; var testUiFlags = nsIEnigmail.UI_TEST; EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.saveDraft(): call encryptMessage() for fromAddr=\"" + fromAddr + "\"\n"); testCipher = enigmailSvc.encryptMessage(null, testUiFlags, testPlain, fromAddr, fromAddr, "", sendFlags | nsIEnigmail.SEND_TEST, testExitCodeObj, testStatusFlagsObj, testErrorMsgObj); if (testStatusFlagsObj.value & (nsIEnigmail.INVALID_RECIPIENT | nsIEnigmail.NO_SECKEY)) { // check if own key is invalid if (testErrorMsgObj.value && testErrorMsgObj.value.length > 0) { ++this.saveDraftError; if (this.saveDraftError === 1) { this.notifyUser(3, EnigmailLocale.getString("msgCompose.cannotSaveDraft"), "saveDraftFailed", testErrorMsgObj.value); } return false; } } let newSecurityInfo; try { if (gMsgCompose.compFields.securityInfo instanceof Components.interfaces.nsIEnigMsgCompFields) { newSecurityInfo = gMsgCompose.compFields.securityInfo; } else { throw "dummy"; } } catch (ex) { try { newSecurityInfo = Components.classes[this.compFieldsEnig_CID].createInstance(Components.interfaces.nsIEnigMsgCompFields); if (newSecurityInfo) { let oldSecurityInfo = gMsgCompose.compFields.securityInfo; newSecurityInfo.init(oldSecurityInfo); gMsgCompose.compFields.securityInfo = newSecurityInfo; } } catch (ex2) { EnigmailLog.writeException("enigmailMsgComposeOverlay.js: Enigmail.msg.saveDraftMessage", ex); return false; } } newSecurityInfo.sendFlags = sendFlags; newSecurityInfo.UIFlags = 0; newSecurityInfo.senderEmailAddr = fromAddr; newSecurityInfo.recipients = fromAddr; newSecurityInfo.bccRecipients = ""; this.dirty = true; return true; }, createEnigmailSecurityFields: function(oldSecurityInfo) { let newSecurityInfo = Components.classes[this.compFieldsEnig_CID].createInstance(Components.interfaces.nsIEnigMsgCompFields); if (!newSecurityInfo) throw Components.results.NS_ERROR_FAILURE; newSecurityInfo.init(oldSecurityInfo); gMsgCompose.compFields.securityInfo = newSecurityInfo; }, isSendConfirmationRequired: function(sendFlags) { // process whether final confirmation is necessary const SIGN = Components.interfaces.nsIEnigmail.SEND_SIGNED; const ENCRYPT = Components.interfaces.nsIEnigmail.SEND_ENCRYPTED; let confirm = false; let confPref = EnigmailPrefs.getPref("confirmBeforeSending"); switch (confPref) { case 0: // never confirm = false; break; case 1: // always confirm = true; break; case 2: // if send encrypted confirm = ((sendFlags & ENCRYPT) == ENCRYPT); break; case 3: // if send unencrypted confirm = ((sendFlags & ENCRYPT) === 0); break; case 4: // if encryption changed due to rules confirm = ((sendFlags & ENCRYPT) != (this.sendMode & ENCRYPT)); break; } // double check that no internal error did result in broken promise of encryption // - if NOT send encrypted // - although encryption was // - the recent processed resulting encryption status or // - was signaled in the status bar but is not the outcome now if ((sendFlags & ENCRYPT) === 0 && this.statusPGPMime !== EnigmailConstants.ENIG_FINAL_SMIME && this.statusPGPMime !== EnigmailConstants.ENIG_FINAL_FORCESMIME && (this.statusEncrypted == EnigmailConstants.ENIG_FINAL_YES || this.statusEncrypted == EnigmailConstants.ENIG_FINAL_FORCEYES || this.statusEncryptedInStatusBar == EnigmailConstants.ENIG_FINAL_YES || this.statusEncryptedInStatusBar == EnigmailConstants.ENIG_FINAL_FORCEYES)) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.isSendConfirmationRequired: promised encryption did not succeed\n"); if (!EnigmailDialog.confirmDlg(window, EnigmailLocale.getString("msgCompose.internalEncryptionError"), EnigmailLocale.getString("msgCompose.button.sendAnyway"))) { return null; // cancel sending } // without canceling sending, force firnal confirmation confirm = true; } return confirm; }, setPepPrivacyLabel: function(rating) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.setPepPrivacyLabel: " + rating + "\n"); let l = document.getElementById("enigmail-pep-privacy-status"); let bc = document.getElementById("enigmail-bc-pepEncrypt"); let color = EnigmailPEPAdapter.calculateColorFromRating(rating); if (bc.getAttribute("encrypt") === "false") { color = "grey"; } else if (rating === 0) { color = "?"; } switch (color) { case "?": l.setAttribute("value", EnigmailLocale.getString("msgCompose.pepSendUnknown")); l.setAttribute("class", "enigmail-statusbar-pep-unsecure"); break; case "green": l.setAttribute("value", EnigmailLocale.getString("msgCompose.pepSendTrusted")); l.setAttribute("class", "enigmail-statusbar-pep-trusted"); break; case "yellow": l.setAttribute("value", EnigmailLocale.getString("msgCompose.pepSendSecure")); l.setAttribute("class", "enigmail-statusbar-pep-secure"); break; default: l.setAttribute("value", EnigmailLocale.getString("msgCompose.pepSendUnsecure")); l.setAttribute("class", "enigmail-statusbar-pep-unsecure"); } }, getPepMessageRating: function() { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.getPepMessageRating\n"); if (this.pepEnabled()) { let rating = 0; let o = this.compileFromAndTo(); if (o) { rating = EnigmailPEPAdapter.getOutgoingMessageRating(o.from, o.toAddrList); } this.setPepPrivacyLabel(rating); this.determineSendFlagId = null; } else { this.setPepPrivacyLabel(0); } }, compileFromAndTo: function() { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.compileFromAndTo\n"); let compFields = gMsgCompose.compFields; let toAddrList = []; let recList; let arrLen = {}; if (!Enigmail.msg.composeBodyReady) { compFields = Components.classes["@mozilla.org/messengercompose/composefields;1"].createInstance(Components.interfaces.nsIMsgCompFields); } Recipients2CompFields(compFields); gMsgCompose.expandMailingLists(); EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: to='" + compFields.to + "'\n"); if (compFields.to.length > 0) { toAddrList = EnigmailFuncs.parseEmails(compFields.to, false); } if (compFields.cc.length > 0) { toAddrList = toAddrList.concat(EnigmailFuncs.parseEmails(compFields.cc, false)); } if (compFields.bcc.length > 0) { toAddrList = toAddrList.concat(EnigmailFuncs.parseEmails(compFields.bcc, false)); } for (let addr of toAddrList) { // determine incomplete addresses --> do not attempt pEp encryption if (addr.email.search(/.@./) < 0) return null; } this.identity = getCurrentIdentity(); let from = { email: this.identity.email, name: this.identity.fullName }; return { from: from, toAddrList: toAddrList }; }, attachPepKey: function() { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.attachPepKey()\n"); if (this.identity.getBoolAttribute("attachPgpKey")) { let id = EnigmailPEPAdapter.getOwnIdentityForEmail(this.identity.email); if (id) { let userIdValue = "0x" + id.fpr; if (this.attachOwnKeyObj.attachedKey && (this.attachOwnKeyObj.attachedKey != userIdValue)) { // remove attached key if user ID changed this.removeAttachedKey(); } if (!this.attachOwnKeyObj.attachedKey) { let attachedObj = this.extractAndAttachKey([userIdValue], false); if (attachedObj) { attachedObj.name = "pEpkey.asc"; this.attachOwnKeyObj.attachedObj = attachedObj; this.attachOwnKeyObj.attachedKey = userIdValue; gMsgCompose.compFields.addAttachment(attachedObj); } } } } }, encryptPepMessage: function(msgSendType) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.encryptPepMessage()\n"); const CiMsgCompDeliverMode = Components.interfaces.nsIMsgCompDeliverMode; const nsIEnigmail = Components.interfaces.nsIEnigmail; let isDraft = false; let compFields = gMsgCompose.compFields; switch (msgSendType) { case CiMsgCompDeliverMode.SaveAsDraft: case CiMsgCompDeliverMode.SaveAsTemplate: case CiMsgCompDeliverMode.AutoSaveAsDraft: EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.encryptPepMessage: detected save draft\n"); return true; } try { // autoEncryptDrafts equals "trusted server" in pEp mode if (this.identity.getBoolAttribute("autoEncryptDrafts")) { EnigmailPEPAdapter.filter.deleteDecryptedCopyFilter(this.identity); } else { EnigmailPEPAdapter.filter.ensureDecryptedCopyFilter(this.identity); } } catch (ex) {} let rating = 0; if (this.pepEnabled()) { let o = this.compileFromAndTo(); if (o) { rating = EnigmailPEPAdapter.getOutgoingMessageRating(o.from, o.toAddrList); let isDraft = (typeof(gMsgCompose.compFields.draftId) === "string" && gMsgCompose.compFields.draftId.length > 0); if (this.origPepRating !== null && !isDraft) { if (this.origPepRating >= 6 && rating < 6 && this.identity.getBoolAttribute("warnWeakReply")) { let msgInput = { msgtext: EnigmailLocale.getString("pep.alert.weakReply"), button1: EnigmailLocale.getString("dlg.button.continue"), cancelButton: EnigmailLocale.getString("dlg.button.cancel"), iconType: EnigmailConstants.ICONTYPE_ALERT, dialogTitle: EnigmailLocale.getString("warning") }; if (EnigmailDialog.msgBox(window, msgInput) !== 0) return false; } } } } EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.encryptPepMessage: rating=" + rating + "\n"); if (rating >= 6) { if (!(compFields.securityInfo instanceof Components.interfaces.nsIEnigMsgCompFields)) { this.createEnigmailSecurityFields(compFields.securityInfo); } let si = compFields.securityInfo.QueryInterface(Components.interfaces.nsIEnigMsgCompFields); if (this.identity.getBoolAttribute("protectSubject")) { si.originalSubject = compFields.subject; compFields.subject = ""; } else { si.originalSubject = null; } let encrypt = document.getElementById("enigmail-bc-pepEncrypt").getAttribute("encrypt"); si.sendFlags = (encrypt === "true" ? nsIEnigmail.SEND_ENCRYPTED : 0); } else { // attach own key this.attachPepKey(); } return true; }, encryptMsg: function(msgSendType) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.encryptMsg: msgSendType=" + msgSendType + ", Enigmail.msg.sendMode=" + this.sendMode + ", Enigmail.msg.statusEncrypted=" + this.statusEncrypted + "\n"); if (this.juniorMode) return this.encryptPepMessage(msgSendType); const nsIEnigmail = Components.interfaces.nsIEnigmail; const SIGN = nsIEnigmail.SEND_SIGNED; const ENCRYPT = nsIEnigmail.SEND_ENCRYPTED; const CiMsgCompDeliverMode = Components.interfaces.nsIMsgCompDeliverMode; var promptSvc = EnigmailDialog.getPromptSvc(); var newSecurityInfo; let arrLen = {}; let toAddrList = []; let recList; let bccAddrList = []; let splitRecipients; var gotSendFlags = this.sendMode; // here we process the final state: if (this.statusEncrypted == EnigmailConstants.ENIG_FINAL_YES || this.statusEncrypted == EnigmailConstants.ENIG_FINAL_FORCEYES) { gotSendFlags |= ENCRYPT; } else if (this.statusEncrypted == EnigmailConstants.ENIG_FINAL_FORCENO) { gotSendFlags &= ~ENCRYPT; } if (this.statusSigned == EnigmailConstants.ENIG_FINAL_YES || this.statusSigned == EnigmailConstants.ENIG_FINAL_FORCEYES) { gotSendFlags |= SIGN; } else if (this.statusSigned == EnigmailConstants.ENIG_FINAL_FORCENO) { gotSendFlags &= ~SIGN; } var ioService = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService); // EnigSend: Handle both plain and encrypted messages below var isOffline = (ioService && ioService.offline); var sendFlags = 0; if (gotSendFlags & SIGN) sendFlags |= SIGN; if (gotSendFlags & ENCRYPT) sendFlags |= ENCRYPT; if (msgSendType === CiMsgCompDeliverMode.Later) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.encryptMsg: adding SEND_LATER\n"); sendFlags |= nsIEnigmail.SEND_LATER; } if (this.statusPGPMime == EnigmailConstants.ENIG_FINAL_SMIME || this.statusPGPMime == EnigmailConstants.ENIG_FINAL_FORCESMIME) { // use S/MIME and return let si = gMsgCompose.compFields.securityInfo.QueryInterface(Components.interfaces.nsIMsgSMIMECompFields); if (sendFlags & SIGN) si.signMessage = true; if (sendFlags & ENCRYPT) si.requireEncryptMessage = true; let conf = this.isSendConfirmationRequired(sendFlags); if (conf === null) return false; if (conf) { msgCompFields = gMsgCompose.compFields; splitRecipients = msgCompFields.splitRecipients; if (msgCompFields.to.length > 0) { recList = splitRecipients(msgCompFields.to, true, arrLen); this.addRecipients(toAddrList, recList); } if (msgCompFields.cc.length > 0) { recList = splitRecipients(msgCompFields.cc, true, arrLen); this.addRecipients(toAddrList, recList); } if (!this.confirmBeforeSend(toAddrList.join(", "), "", sendFlags, isOffline)) { return false; } } return true; } switch (msgSendType) { case CiMsgCompDeliverMode.SaveAsDraft: case CiMsgCompDeliverMode.SaveAsTemplate: case CiMsgCompDeliverMode.AutoSaveAsDraft: EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.encryptMsg: detected save draft\n"); // saving drafts is simpler and works differently than the rest of Enigmail. // All rules except account-settings are ignored. return this.saveDraftMessage(); } this.unsetAdditionalHeader("x-enigmail-draft-status"); var msgCompFields = gMsgCompose.compFields; var newsgroups = msgCompFields.newsgroups; // Check if sending to any newsgroups if (msgCompFields.to === "" && msgCompFields.cc === "" && msgCompFields.bcc === "" && newsgroups === "") { // don't attempt to send message if no recipient specified var bundle = document.getElementById("bundle_composeMsgs"); EnigmailDialog.alert(window, bundle.getString("12511")); return false; } this.identity = getCurrentIdentity(); if (gWindowLocked) { EnigmailDialog.alert(window, EnigmailLocale.getString("windowLocked")); return false; } if (this.dirty) { // make sure the sendFlags are reset before the message is processed // (it may have been set by a previously cancelled send operation!) try { if (gMsgCompose.compFields.securityInfo instanceof Components.interfaces.nsIEnigMsgCompFields) { gMsgCompose.compFields.securityInfo.sendFlags = 0; gMsgCompose.compFields.securityInfo.originalSubject = gMsgCompose.compFields.subject; } else if (!gMsgCompose.compFields.securityInfo) { throw "dummy"; } } catch (ex) { try { newSecurityInfo = Components.classes[this.compFieldsEnig_CID].createInstance(Components.interfaces.nsIEnigMsgCompFields); if (newSecurityInfo) { newSecurityInfo.sendFlags = 0; newSecurityInfo.originalSubject = gMsgCompose.compFields.subject; gMsgCompose.compFields.securityInfo = newSecurityInfo; } } catch (ex2) { EnigmailLog.writeException("enigmailMsgComposeOverlay.js: Enigmail.msg.encryptMsg", ex2); } } } this.dirty = 1; var enigmailSvc = EnigmailCore.getService(window); if (!enigmailSvc) { var msg = EnigmailLocale.getString("sendUnencrypted"); if (EnigmailCore.getEnigmailService() && EnigmailCore.getEnigmailService().initializationError) { msg = EnigmailCore.getEnigmailService().initializationError + "\n\n" + msg; } return EnigmailDialog.confirmDlg(window, msg, EnigmailLocale.getString("msgCompose.button.send")); } try { this.modifiedAttach = null; EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.encryptMsg: currentId=" + this.identity + ", " + this.identity.email + "\n"); var fromAddr = this.identity.email; if (!this.isEnigmailEnabled()) return true; var optSendFlags = 0; var inlineEncAttach = false; // request or preference to always accept (even non-authenticated) keys? if (this.trustAllKeys) { optSendFlags |= nsIEnigmail.SEND_ALWAYS_TRUST; } else { var acceptedKeys = EnigmailPrefs.getPref("acceptedKeys"); switch (acceptedKeys) { case 0: // accept valid/authenticated keys only break; case 1: // accept all but revoked/disabled/expired keys optSendFlags |= nsIEnigmail.SEND_ALWAYS_TRUST; break; default: EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.encryptMsg: INVALID VALUE for acceptedKeys: \"" + acceptedKeys + "\"\n"); break; } } if (EnigmailPrefs.getPref("encryptToSelf")) { optSendFlags |= nsIEnigmail.SEND_ENCRYPT_TO_SELF; } sendFlags |= optSendFlags; var userIdValue = this.getSenderUserId(); if (userIdValue) { fromAddr = userIdValue; } - EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.encryptMsg:gMsgCompose=" + gMsgCompose + "\n"); + //EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.encryptMsg:gMsgCompose=" + gMsgCompose + "\n"); splitRecipients = msgCompFields.splitRecipients; if (msgCompFields.to.length > 0) { recList = splitRecipients(msgCompFields.to, true, arrLen); this.addRecipients(toAddrList, recList); } if (msgCompFields.cc.length > 0) { recList = splitRecipients(msgCompFields.cc, true, arrLen); this.addRecipients(toAddrList, recList); } // special handling of bcc: // - note: bcc and encryption is a problem // - but bcc to the sender is fine if (msgCompFields.bcc.length > 0) { recList = splitRecipients(msgCompFields.bcc, true, arrLen); var bccLC = EnigmailFuncs.stripEmail(msgCompFields.bcc).toLowerCase(); EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.encryptMsg: BCC: " + bccLC + "\n"); var selfBCC = this.identity.email && (this.identity.email.toLowerCase() == bccLC); if (selfBCC) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.encryptMsg: Self BCC\n"); this.addRecipients(toAddrList, recList); } else if (sendFlags & ENCRYPT) { // BCC and encryption var dummy = { value: null }; var hideBccUsers = promptSvc.confirmEx(window, EnigmailLocale.getString("enigConfirm"), EnigmailLocale.getString("sendingHiddenRcpt"), (promptSvc.BUTTON_TITLE_IS_STRING * promptSvc.BUTTON_POS_0) + (promptSvc.BUTTON_TITLE_CANCEL * promptSvc.BUTTON_POS_1) + (promptSvc.BUTTON_TITLE_IS_STRING * promptSvc.BUTTON_POS_2), EnigmailLocale.getString("sendWithShownBcc"), null, EnigmailLocale.getString("sendWithHiddenBcc"), null, dummy); switch (hideBccUsers) { case 2: this.addRecipients(bccAddrList, recList); this.addRecipients(toAddrList, recList); break; case 0: this.addRecipients(toAddrList, recList); break; case 1: return false; } } } if (newsgroups) { toAddrList.push(newsgroups); if (sendFlags & ENCRYPT) { if (!EnigmailPrefs.getPref("encryptToNews")) { EnigmailDialog.alert(window, EnigmailLocale.getString("sendingNews")); return false; } else if (!EnigmailDialog.confirmPref(window, EnigmailLocale.getString("sendToNewsWarning"), "warnOnSendingNewsgroups", EnigmailLocale.getString("msgCompose.button.send"))) { return false; } } } var usePGPMimeOption = EnigmailPrefs.getPref("usePGPMimeOption"); if (this.sendPgpMime) { // Use PGP/MIME sendFlags |= nsIEnigmail.SEND_PGP_MIME; } var result = this.keySelection(enigmailSvc, sendFlags, // all current combined/processed send flags (incl. optSendFlags) optSendFlags, // may only be SEND_ALWAYS_TRUST or SEND_ENCRYPT_TO_SELF gotSendFlags, // initial sendMode (0 or SIGN or ENCRYPT or SIGN|ENCRYPT) fromAddr, toAddrList, bccAddrList); if (!result) { return false; } var toAddrStr; var bccAddrStr; sendFlags = result.sendFlags; toAddrStr = result.toAddrStr; bccAddrStr = result.bccAddrStr; if (this.preferPgpOverSmime(sendFlags) === 0) { // use S/MIME sendFlags = 0; return true; } if (this.attachOwnKeyObj.appendAttachment) this.attachOwnKey(); var bucketList = document.getElementById("attachmentBucket"); var hasAttachments = ((bucketList && bucketList.hasChildNodes()) || gMsgCompose.compFields.attachVCard); EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: hasAttachments = " + hasAttachments + "\n"); if (hasAttachments && (sendFlags & (ENCRYPT | SIGN)) && !(sendFlags & nsIEnigmail.SEND_PGP_MIME)) { let inputObj = { pgpMimePossible: true, inlinePossible: true, restrictedScenario: false, reasonForCheck: "" }; // init reason for dialog to be able to use the right labels if (sendFlags & ENCRYPT) { if (sendFlags & SIGN) { inputObj.reasonForCheck = "encryptAndSign"; } else { inputObj.reasonForCheck = "encrypt"; } } else { if (sendFlags & SIGN) { inputObj.reasonForCheck = "sign"; } } // determine if attachments are all local files (currently the only // supported kind of attachments) var node = bucketList.firstChild; while (node) { if (node.attachment.url.substring(0, 7) != "file://") { inputObj.inlinePossible = false; } node = node.nextSibling; } if (inputObj.pgpMimePossible || inputObj.inlinePossible) { let resultObj = { selected: EnigmailPrefs.getPref("encryptAttachments") }; //skip or not var skipCheck = EnigmailPrefs.getPref("encryptAttachmentsSkipDlg"); if (skipCheck == 1) { if ((resultObj.selected == 2 && inputObj.pgpMimePossible === false) || (resultObj.selected == 1 && inputObj.inlinePossible === false)) { //add var to disable remember box since we're dealing with restricted scenarios... inputObj.restrictedScenario = true; resultObj.selected = -1; window.openDialog("chrome://enigmail/content/enigmailAttachmentsDialog.xul", "", "dialog,modal,centerscreen", inputObj, resultObj); } } else { resultObj.selected = -1; window.openDialog("chrome://enigmail/content/enigmailAttachmentsDialog.xul", "", "dialog,modal,centerscreen", inputObj, resultObj); } if (resultObj.selected < 0) { // dialog cancelled return false; } else if (resultObj.selected == 1) { // encrypt attachments inlineEncAttach = true; } else if (resultObj.selected == 2) { // send as PGP/MIME sendFlags |= nsIEnigmail.SEND_PGP_MIME; } else if (resultObj.selected == 3) { // cancel the encryption/signing for the whole message sendFlags &= ~ENCRYPT; sendFlags &= ~SIGN; } } else { if (sendFlags & ENCRYPT) { if (!EnigmailDialog.confirmDlg(window, EnigmailLocale.getString("attachWarning"), EnigmailLocale.getString("msgCompose.button.send"))) return false; } } } var usingPGPMime = (sendFlags & nsIEnigmail.SEND_PGP_MIME) && (sendFlags & (ENCRYPT | SIGN)); // ----------------------- Rewrapping code, taken from function "encryptInline" // Check wrapping, if sign only and inline and plaintext if ((sendFlags & SIGN) && !(sendFlags & ENCRYPT) && !(usingPGPMime) && !(gMsgCompose.composeHTML)) { var wrapresultObj = {}; this.wrapInLine(wrapresultObj); if (wrapresultObj.usePpgMime) { sendFlags |= nsIEnigmail.SEND_PGP_MIME; usingPGPMime = nsIEnigmail.SEND_PGP_MIME; } if (wrapresultObj.cancelled) { return false; } } var uiFlags = nsIEnigmail.UI_INTERACTIVE; if (usingPGPMime) uiFlags |= nsIEnigmail.UI_PGP_MIME; if ((sendFlags & (ENCRYPT | SIGN)) && usingPGPMime) { // Use PGP/MIME EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.encryptMsg: Using PGP/MIME, flags=" + sendFlags + "\n"); var oldSecurityInfo = gMsgCompose.compFields.securityInfo; EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.encryptMsg: oldSecurityInfo = " + oldSecurityInfo + "\n"); if (!oldSecurityInfo) { try { newSecurityInfo = oldSecurityInfo.QueryInterface(Components.interfaces.nsIEnigMsgCompFields); } catch (ex) {} } if (!newSecurityInfo) { this.createEnigmailSecurityFields(oldSecurityInfo); newSecurityInfo = gMsgCompose.compFields.securityInfo.QueryInterface(Components.interfaces.nsIEnigMsgCompFields); } newSecurityInfo.originalSubject = gMsgCompose.compFields.subject; newSecurityInfo.originalReferences = gMsgCompose.compFields.references; if (this.protectHeaders) { sendFlags |= nsIEnigmail.ENCRYPT_HEADERS; if (sendFlags & ENCRYPT) { gMsgCompose.compFields.subject = ""; if (EnigmailPrefs.getPref("protectReferencesHdr")) { gMsgCompose.compFields.references = ""; } } } newSecurityInfo.sendFlags = sendFlags; newSecurityInfo.UIFlags = uiFlags; newSecurityInfo.senderEmailAddr = fromAddr; newSecurityInfo.recipients = toAddrStr; newSecurityInfo.bccRecipients = bccAddrStr; EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.encryptMsg: securityInfo = " + newSecurityInfo + "\n"); } else if (!this.processed && (sendFlags & (ENCRYPT | SIGN))) { // use inline PGP var sendInfo = { sendFlags: sendFlags, inlineEncAttach: inlineEncAttach, fromAddr: fromAddr, toAddr: toAddrStr, bccAddr: bccAddrStr, uiFlags: uiFlags, bucketList: bucketList }; if (!this.encryptInline(sendInfo)) { return false; } } // update the list of attachments Attachments2CompFields(msgCompFields); let confirm = this.isSendConfirmationRequired(sendFlags); if (confirm === null) return false; // perform confirmation dialog if necessary/requested if (confirm) { if (!this.confirmBeforeSend(toAddrList.join(", "), toAddrStr + ", " + bccAddrStr, sendFlags, isOffline)) { if (this.processed) { this.undoEncryption(0); } else { this.removeAttachedKey(); } return false; } } else if ((sendFlags & nsIEnigmail.SEND_WITH_CHECK) && !this.messageSendCheck()) { // Abort send if (this.processed) { this.undoEncryption(0); } else { this.removeAttachedKey(); } return false; } if (msgCompFields.characterSet != "ISO-2022-JP") { if ((usingPGPMime && ((sendFlags & (ENCRYPT | SIGN)))) || ((!usingPGPMime) && (sendFlags & ENCRYPT))) { try { // make sure plaintext is not changed to 7bit if (typeof(msgCompFields.forceMsgEncoding) == "boolean") { msgCompFields.forceMsgEncoding = true; EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.encryptMsg: enabled forceMsgEncoding\n"); } } catch (ex) {} } } } catch (ex) { EnigmailLog.writeException("enigmailMsgComposeOverlay.js: Enigmail.msg.encryptMsg", ex); let msg = EnigmailLocale.getString("signFailed"); if (EnigmailCore.getEnigmailService() && EnigmailCore.getEnigmailService().initializationError) { msg += "\n" + EnigmailCore.getEnigmailService().initializationError; } return EnigmailDialog.confirmDlg(window, msg, EnigmailLocale.getString("msgCompose.button.sendUnencrypted")); } // The encryption process for PGP/MIME messages follows "here". It's // called automatically from nsMsgCompose->sendMsg(). // registration for this is dome in chrome.manifest return true; }, encryptInline: function(sendInfo) { // sign/encrpyt message using inline-PGP const dce = Components.interfaces.nsIDocumentEncoder; const nsIEnigmail = Components.interfaces.nsIEnigmail; const SIGN = nsIEnigmail.SEND_SIGNED; const ENCRYPT = nsIEnigmail.SEND_ENCRYPTED; var enigmailSvc = EnigmailCore.getService(window); if (!enigmailSvc) return false; if (gMsgCompose.composeHTML) { var errMsg = EnigmailLocale.getString("hasHTML"); EnigmailDialog.alertCount(window, "composeHtmlAlertCount", errMsg); } try { var convert = DetermineConvertibility(); if (convert == Components.interfaces.nsIMsgCompConvertible.No) { if (!EnigmailDialog.confirmDlg(window, EnigmailLocale.getString("strippingHTML"), EnigmailLocale.getString("msgCompose.button.sendAnyway"))) { return false; } } } catch (ex) { EnigmailLog.writeException("enigmailMsgComposeOverlay.js: Enigmail.msg.encryptInline", ex); } try { if (this.getMailPref("mail.strictly_mime")) { if (EnigmailDialog.confirmPref(window, EnigmailLocale.getString("quotedPrintableWarn"), "quotedPrintableWarn")) { EnigmailPrefs.getPrefRoot().setBoolPref("mail.strictly_mime", false); } } } catch (ex) {} var sendFlowed; try { sendFlowed = this.getMailPref("mailnews.send_plaintext_flowed"); } catch (ex) { sendFlowed = true; } var encoderFlags = dce.OutputFormatted | dce.OutputLFLineBreak; var wrapper = gMsgCompose.editor.QueryInterface(Components.interfaces.nsIEditorMailSupport); var editor = gMsgCompose.editor.QueryInterface(Components.interfaces.nsIPlaintextEditor); var wrapWidth = 72; if (!(sendInfo.sendFlags & ENCRYPT)) { // signed messages only if (gMsgCompose.composeHTML) { // enforce line wrapping here // otherwise the message isn't signed correctly try { wrapWidth = this.getMailPref("editor.htmlWrapColumn"); if (wrapWidth > 0 && wrapWidth < 68 && gMsgCompose.wrapLength > 0) { if (EnigmailDialog.confirmDlg(window, EnigmailLocale.getString("minimalLineWrapping", [wrapWidth]))) { EnigmailPrefs.getPrefRoot().setIntPref("editor.htmlWrapColumn", 68); } } if (EnigmailPrefs.getPref("wrapHtmlBeforeSend")) { if (wrapWidth) { editor.wrapWidth = wrapWidth - 2; // prepare for the worst case: a 72 char's long line starting with '-' wrapper.rewrap(false); } } } catch (ex) {} } else { // plaintext: Wrapping code has been moved to superordinate function encryptMsg to enable interactive format switch } } var exitCodeObj = {}; var statusFlagsObj = {}; var errorMsgObj = {}; var exitCode; // Get plain text // (Do we need to set the nsIDocumentEncoder.* flags?) var origText = this.editorGetContentAs("text/plain", encoderFlags); if (!origText) origText = ""; if (origText.length > 0) { // Sign/encrypt body text var escText = origText; // Copy plain text for possible escaping if (sendFlowed && !(sendInfo.sendFlags & ENCRYPT)) { // Prevent space stuffing a la RFC 2646 (format=flowed). //EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: escText["+encoderFlags+"] = '"+escText+"'\n"); escText = escText.replace(/^From /gm, "~From "); escText = escText.replace(/^>/gm, "|"); escText = escText.replace(/^[ \t]+$/gm, ""); escText = escText.replace(/^ /gm, "~ "); //EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: escText = '"+escText+"'\n"); // Replace plain text and get it again this.replaceEditorText(escText); escText = this.editorGetContentAs("text/plain", encoderFlags); } // Replace plain text and get it again (to avoid linewrapping problems) this.replaceEditorText(escText); escText = this.editorGetContentAs("text/plain", encoderFlags); //EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: escText["+encoderFlags+"] = '"+escText+"'\n"); // Encrypt plaintext var charset = this.editorGetCharset(); EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.encryptMsg: charset=" + charset + "\n"); // Encode plaintext to charset from unicode var plainText = (sendInfo.sendFlags & ENCRYPT) ? EnigmailData.convertFromUnicode(origText, charset) : EnigmailData.convertFromUnicode(escText, charset); var cipherText = enigmailSvc.encryptMessage(window, sendInfo.uiFlags, plainText, sendInfo.fromAddr, sendInfo.toAddr, sendInfo.bccAddr, sendInfo.sendFlags, exitCodeObj, statusFlagsObj, errorMsgObj); exitCode = exitCodeObj.value; //EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: cipherText = '"+cipherText+"'\n"); if (cipherText && (exitCode === 0)) { // Encryption/signing succeeded; overwrite plaintext if (gMsgCompose.composeHTML) { // workaround for Thunderbird bug (TB adds an extra space in front of the text) cipherText = "\n" + cipherText; } else cipherText = cipherText.replace(/\r\n/g, "\n"); if ((sendInfo.sendFlags & ENCRYPT) && charset && (charset.search(/^us-ascii$/i) !== 0)) { // Add Charset armor header for encrypted blocks cipherText = cipherText.replace(/(-----BEGIN PGP MESSAGE----- *)(\r?\n)/, "$1$2Charset: " + charset + "$2"); } // Decode ciphertext from charset to unicode and overwrite this.replaceEditorText(EnigmailData.convertToUnicode(cipherText, charset)); this.enableUndoEncryption(true); // Save original text (for undo) this.processed = { "origText": origText, "charset": charset }; } else { // Restore original text this.replaceEditorText(origText); this.enableUndoEncryption(false); if (sendInfo.sendFlags & (ENCRYPT | SIGN)) { // Encryption/signing failed /*if (statusFlagsObj.statusMsg) { // check if own key is invalid let s = new RegExp("^(\\[GNUPG:\\] )?INV_(RECP|SGNR) [0-9]+ \\?", "m"); if (statusFlagsObj.statusMsg.search(s) >= 0) { errorMsgObj.value += "\n\n" + EnigmailLocale.getString("keyError.resolutionAction"); } }*/ this.sendAborted(window, errorMsgObj); return false; } } } if (sendInfo.inlineEncAttach) { // encrypt attachments this.modifiedAttach = []; exitCode = this.encryptAttachments(sendInfo.bucketList, this.modifiedAttach, window, sendInfo.uiFlags, sendInfo.fromAddr, sendInfo.toAddr, sendInfo.bccAddr, sendInfo.sendFlags, errorMsgObj); if (exitCode !== 0) { this.modifiedAttach = null; this.sendAborted(window, errorMsgObj); if (this.processed) { this.undoEncryption(0); } else { this.removeAttachedKey(); } return false; } } return true; }, sendAborted: function(window, errorMsgObj) { if (errorMsgObj && errorMsgObj.value) { var txt = errorMsgObj.value; var txtLines = txt.split(/\r?\n/); var errorMsg = ""; for (var i = 0; i < txtLines.length; ++i) { var line = txtLines[i]; var tokens = line.split(/ /); // process most important business reasons for invalid recipient (and sender) errors: if (tokens.length == 3 && (tokens[0] == "INV_RECP" || tokens[0] == "INV_SGNR")) { var reason = tokens[1]; var key = tokens[2]; if (reason == "10") { errorMsg += EnigmailLocale.getString("keyNotTrusted", [key]) + "\n"; } else if (reason == "1") { errorMsg += EnigmailLocale.getString("keyNotFound", [key]) + "\n"; } else if (reason == "4") { errorMsg += EnigmailLocale.getString("keyRevoked", [key]) + "\n"; } else if (reason == "5") { errorMsg += EnigmailLocale.getString("keyExpired", [key]) + "\n"; } } } if (errorMsg !== "") { txt = errorMsg + "\n" + txt; } EnigmailDialog.info(window, EnigmailLocale.getString("sendAborted") + txt); } else { EnigmailDialog.info(window, EnigmailLocale.getString("sendAborted") + "\n" + EnigmailLocale.getString("msgCompose.internalError")); } }, getMailPref: function(prefName) { let prefRoot = EnigmailPrefs.getPrefRoot(); var prefValue = null; try { var prefType = prefRoot.getPrefType(prefName); // Get pref value switch (prefType) { case prefRoot.PREF_BOOL: prefValue = prefRoot.getBoolPref(prefName); break; case prefRoot.PREF_INT: prefValue = prefRoot.getIntPref(prefName); break; case prefRoot.PREF_STRING: prefValue = prefRoot.getCharPref(prefName); break; default: prefValue = undefined; break; } } catch (ex) { // Failed to get pref value EnigmailLog.ERROR("enigmailMsgComposeOverlay.js: Enigmail.msg.getMailPref: unknown prefName:" + prefName + " \n"); } return prefValue; }, messageSendCheck: function() { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.messageSendCheck\n"); try { var warn = this.getMailPref("mail.warn_on_send_accel_key"); if (warn) { var checkValue = { value: false }; var bundle = document.getElementById("bundle_composeMsgs"); var buttonPressed = EnigmailDialog.getPromptSvc().confirmEx(window, bundle.getString('sendMessageCheckWindowTitle'), bundle.getString('sendMessageCheckLabel'), (EnigmailDialog.getPromptSvc().BUTTON_TITLE_IS_STRING * EnigmailDialog.getPromptSvc().BUTTON_POS_0) + (EnigmailDialog.getPromptSvc().BUTTON_TITLE_CANCEL * EnigmailDialog.getPromptSvc().BUTTON_POS_1), bundle.getString('sendMessageCheckSendButtonLabel'), null, null, bundle.getString('CheckMsg'), checkValue); if (buttonPressed !== 0) { return false; } if (checkValue.value) { EnigmailPrefs.getPrefRoot().setBoolPref("mail.warn_on_send_accel_key", false); } } } catch (ex) {} return true; }, /** * set non-standard message Header * (depending on TB version) * * hdr: String: header type (e.g. X-Enigmail-Version) * val: String: header data (e.g. 1.2.3.4) */ setAdditionalHeader: function(hdr, val) { if ("otherRandomHeaders" in gMsgCompose.compFields) { // TB <= 36 gMsgCompose.compFields.otherRandomHeaders += hdr + ": " + val + "\r\n"; } else { gMsgCompose.compFields.setHeader(hdr, val); } }, unsetAdditionalHeader: function(hdr) { if ("otherRandomHeaders" in gMsgCompose.compFields) { // TB <= 36 let h = gMsgCompose.compFields.otherRandomHeaders; let r = new RegExp("^(" + hdr + ":)(.*)$", "im"); let m = h.replace(r, "").replace(/(\r\n)+/, "\r\n"); gMsgCompose.compFields.otherRandomHeaders = m; } else { gMsgCompose.compFields.deleteHeader(hdr); } }, modifyCompFields: function() { const HEADERMODE_KEYID = 0x01; const HEADERMODE_URL = 0x10; try { if (!this.identity) { this.identity = getCurrentIdentity(); } if (this.isEnigmailEnabled()) { if (EnigmailPrefs.getPref("addHeaders")) { this.setAdditionalHeader("X-Enigmail-Version", EnigmailApp.getVersion()); } var pgpHeader = ""; var openPgpHeaderMode = this.identity.getIntAttribute("openPgpHeaderMode"); if (openPgpHeaderMode & HEADERMODE_KEYID) { let key = EnigmailKeyRing.getKeyById(this.identity.getCharAttribute("pgpkeyId")); if (key && key.fpr && key.fpr.length > 0) { pgpHeader += "id=" + key.fpr; } } if (openPgpHeaderMode & HEADERMODE_URL) { if (pgpHeader.indexOf("=") > 0) pgpHeader += ";\r\n\t"; pgpHeader += "url=" + this.identity.getCharAttribute("openPgpUrlName"); } if (openPgpHeaderMode === 0) { pgpHeader = "preference=signencrypt"; } if (pgpHeader.length > 0) { this.setAdditionalHeader("OpenPGP", pgpHeader); } this.setAutocryptHeader(); } } catch (ex) { EnigmailLog.writeException("enigmailMsgComposeOverlay.js: Enigmail.msg.modifyCompFields", ex); } }, setAutocryptHeader: function() { if (EnigmailPEPAdapter.usingPep() || EnigmailPrefs.getPref("autocryptMode") === 0) { return; } if (!this.identity) { this.identity = getCurrentIdentity(); } let key; if (this.identity.getIntAttribute("pgpKeyMode") > 0) { key = EnigmailKeyRing.getKeyById(this.identity.getCharAttribute("pgpkeyId")); } else { key = EnigmailKeyRing.getSecretKeyByUserId(this.identity.email); } if (key) { let k = key.getMinimalPubKey(); if (k.exitCode === 0) { let keyData = k.keyData.replace(/(.{72})/g, " $1\r\n"); this.setAdditionalHeader('Autocrypt', 'addr="' + this.identity.email + '"; type=1; keydata=\r\n' + keyData); } } }, /** * Handle the 'compose-send-message' event from TB */ sendMessageListener: function(event) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.sendMessageListener\n"); // Do nothing if a compatible version of the "SendLater" addon is installed. // SendLater will call handleSendMessageEvent when needed. try { if (typeof(Sendlater3Composing.callEnigmail) === "function") { return; } } catch (ex) {} Enigmail.msg.handleSendMessageEvent(event); }, /** * Perform handling of the compose-send-message' event from TB (or SendLater) */ handleSendMessageEvent: function(event) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.handleSendMessageEvent\n"); let msgcomposeWindow = document.getElementById("msgcomposeWindow"); let sendMsgType = Number(msgcomposeWindow.getAttribute("msgtype")); if (!(this.sendProcess && sendMsgType == Components.interfaces.nsIMsgCompDeliverMode.AutoSaveAsDraft)) { this.sendProcess = true; let bc = document.getElementById("enigmail-bc-sendprocess"); try { this.modifyCompFields(); bc.setAttribute("disabled", "true"); if (!this.encryptMsg(sendMsgType)) { this.resetUpdatedFields(); event.preventDefault(); event.stopPropagation(); } } catch (ex) {} bc.removeAttribute("disabled"); } else { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.sendMessageListener: sending in progress - autosave aborted\n"); event.preventDefault(); event.stopPropagation(); } 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 nsIEnigmail = Components.interfaces.nsIEnigmail; const SIGN = nsIEnigmail.SEND_SIGNED; const ENCRYPT = nsIEnigmail.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 = enigmailSvc.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"); var menuElement = document.getElementById("enigmail_" + attrName); var oldValue = EnigmailPrefs.getPref(attrName); EnigmailPrefs.setPref(attrName, !oldValue); }, toggleAccountAttr: function(attrName) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.toggleAccountAttr('" + attrName + "')\n"); var oldValue = this.identity.getBoolAttribute(attrName); this.identity.setBoolAttribute(attrName, !oldValue); }, decryptQuote: function(interactive) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.decryptQuote: " + interactive + "\n"); const nsIEnigmail = Components.interfaces.nsIEnigmail; if (gWindowLocked || this.processed) return; var enigmailSvc = EnigmailCore.getService(window); if (!enigmailSvc) return; const dce = Components.interfaces.nsIDocumentEncoder; var encoderFlags = dce.OutputFormatted | dce.OutputLFLineBreak; var docText = this.editorGetContentAs("text/plain", encoderFlags); var blockBegin = docText.indexOf("-----BEGIN PGP "); if (blockBegin < 0) return; // Determine indentation string var indentBegin = docText.substr(0, blockBegin).lastIndexOf("\n"); var indentStr = docText.substring(indentBegin + 1, blockBegin); EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.decryptQuote: indentStr='" + indentStr + "'\n"); var beginIndexObj = {}; var endIndexObj = {}; var indentStrObj = {}; var blockType = EnigmailArmor.locateArmoredBlock(docText, 0, indentStr, beginIndexObj, endIndexObj, indentStrObj); if ((blockType != "MESSAGE") && (blockType != "SIGNED MESSAGE")) return; var beginIndex = beginIndexObj.value; var endIndex = endIndexObj.value; var head = docText.substr(0, beginIndex); var tail = docText.substr(endIndex + 1); var pgpBlock = docText.substr(beginIndex, endIndex - beginIndex + 1); var indentRegexp; if (indentStr) { if (indentStr == "> ") { // replace ">> " with "> > " to allow correct quoting pgpBlock = pgpBlock.replace(/^>>/gm, "> >"); } // Delete indentation indentRegexp = new RegExp("^" + indentStr, "gm"); pgpBlock = pgpBlock.replace(indentRegexp, ""); //tail = tail.replace(indentRegexp, ""); if (indentStr.match(/[ \t]*$/)) { indentStr = indentStr.replace(/[ \t]*$/gm, ""); indentRegexp = new RegExp("^" + indentStr + "$", "gm"); pgpBlock = pgpBlock.replace(indentRegexp, ""); } // Handle blank indented lines pgpBlock = pgpBlock.replace(/^[ \t]*>[ \t]*$/gm, ""); //tail = tail.replace(/^[ \t]*>[ \t]*$/g, ""); // Trim leading space in tail tail = tail.replace(/^\s*\n/m, "\n"); } if (tail.search(/\S/) < 0) { // No non-space characters in tail; delete it tail = ""; } //EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.decryptQuote: pgpBlock='"+pgpBlock+"'\n"); var charset = this.editorGetCharset(); EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.decryptQuote: charset=" + charset + "\n"); // Encode ciphertext from unicode to charset var cipherText = EnigmailData.convertFromUnicode(pgpBlock, charset); if ((!this.getMailPref("mailnews.reply_in_default_charset")) && (blockType == "MESSAGE")) { // set charset according to PGP block, if available (encrypted messages only) let armorHeaders = EnigmailArmor.getArmorHeaders(cipherText); if ("charset" in armorHeaders) { charset = armorHeaders.charset; gMsgCompose.SetDocumentCharset(charset); } } // Decrypt message var signatureObj = {}; signatureObj.value = ""; var exitCodeObj = {}; var statusFlagsObj = {}; var userIdObj = {}; var keyIdObj = {}; var sigDetailsObj = {}; var errorMsgObj = {}; var blockSeparationObj = {}; var encToDetailsObj = {}; var uiFlags = nsIEnigmail.UI_UNVERIFIED_ENC_OK; var plainText = ""; if (EnigmailPEPAdapter.usingPep()) { let msgHdr = this.getMsgHdr(); let addresses = { from: null, to: EnigmailFuncs.parseEmails(msgHdr.recipients), cc: EnigmailFuncs.parseEmails(msgHdr.ccList) }; let fromAddr = EnigmailFuncs.parseEmails(msgHdr.author); if (fromAddr.length > 0) addresses.from = fromAddr[0]; let pEpResult = EnigmailPEPDecrypt.decryptMessageData(false, cipherText, addresses); if (pEpResult && pEpResult.longmsg.length > 0) { plainText = pEpResult.longmsg; exitCodeObj.value = 0; errorMsgObj.value = ""; } } else { plainText = enigmailSvc.decryptMessage(window, uiFlags, cipherText, signatureObj, exitCodeObj, statusFlagsObj, keyIdObj, userIdObj, sigDetailsObj, errorMsgObj, blockSeparationObj, encToDetailsObj); } // Decode plaintext from charset to unicode plainText = EnigmailData.convertToUnicode(plainText, charset).replace(/\r\n/g, "\n"); if (EnigmailPrefs.getPref("keepSettingsForReply")) { if (statusFlagsObj.value & nsIEnigmail.DECRYPTION_OKAY) this.setSendMode('encrypt'); } var exitCode = exitCodeObj.value; if (exitCode !== 0) { // Error processing var errorMsg = errorMsgObj.value; var statusLines = errorMsg.split(/\r?\n/); var displayMsg; if (statusLines && statusLines.length) { // Display only first ten lines of error message while (statusLines.length > 10) statusLines.pop(); displayMsg = statusLines.join("\n"); if (interactive) EnigmailDialog.info(window, displayMsg); } } if (blockType == "MESSAGE" && exitCode === 0 && plainText.length === 0) { plainText = " "; } if (!plainText) { if (blockType != "SIGNED MESSAGE") return; // Extract text portion of clearsign block plainText = EnigmailArmor.extractSignaturePart(pgpBlock, nsIEnigmail.SIGNATURE_TEXT); } const nsIMsgCompType = Components.interfaces.nsIMsgCompType; var doubleDashSeparator = EnigmailPrefs.getPref("doubleDashSeparator"); if (gMsgCompose.type != nsIMsgCompType.Template && gMsgCompose.type != nsIMsgCompType.Draft && doubleDashSeparator) { var signOffset = plainText.search(/[\r\n]-- +[\r\n]/); if (signOffset < 0 && blockType == "SIGNED MESSAGE") { signOffset = plainText.search(/[\r\n]--[\r\n]/); } if (signOffset > 0) { // Strip signature portion of quoted message plainText = plainText.substr(0, signOffset + 1); } } var clipBoard = Components.classes["@mozilla.org/widget/clipboard;1"]. getService(Components.interfaces.nsIClipboard); var data; if (clipBoard.supportsSelectionClipboard()) { // get the clipboard contents for selected text (X11) data = EnigmailClipboard.getClipboardContent(window, Components.interfaces.nsIClipboard.kSelectionClipboard); } // Replace encrypted quote with decrypted quote (destroys selection clipboard on X11) this.editorSelectAll(); //EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.decryptQuote: plainText='"+plainText+"'\n"); if (head) this.editorInsertText(head); var quoteElement; if (indentStr) { quoteElement = this.editorInsertAsQuotation(plainText); } else { this.editorInsertText(plainText); } if (tail) this.editorInsertText(tail); if (clipBoard.supportsSelectionClipboard()) { // restore the clipboard contents for selected text (X11) EnigmailClipboard.setClipboardContent(data, clipBoard.kSelectionClipboard); } if (interactive) return; // Position cursor var replyOnTop = 1; try { replyOnTop = this.identity.replyOnTop; } catch (ex) {} if (!indentStr || !quoteElement) replyOnTop = 1; EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.decryptQuote: replyOnTop=" + replyOnTop + ", quoteElement=" + quoteElement + "\n"); var nsISelectionController = Components.interfaces.nsISelectionController; if (this.editor.selectionController) { var selection = this.editor.selectionController; selection.completeMove(false, false); // go to start; switch (replyOnTop) { case 0: // Position after quote this.editor.endOfDocument(); if (tail) { for (let cPos = 0; cPos < tail.length; cPos++) { selection.characterMove(false, false); // move backwards } } break; case 2: // Select quote if (head) { for (let cPos = 0; cPos < head.length; cPos++) { selection.characterMove(true, false); } } selection.completeMove(true, true); if (tail) { for (let cPos = 0; cPos < tail.length; cPos++) { selection.characterMove(false, true); // move backwards } } break; default: // Position at beginning of document if (this.editor) { this.editor.beginningOfDocument(); } } this.editor.selectionController.scrollSelectionIntoView(nsISelectionController.SELECTION_NORMAL, nsISelectionController.SELECTION_ANCHOR_REGION, true); } this.processFinalState(); this.updateStatusBar(); }, editorInsertText: function(plainText) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.editorInsertText\n"); if (this.editor) { var mailEditor; try { mailEditor = this.editor.QueryInterface(Components.interfaces.nsIEditorMailSupport); mailEditor.insertTextWithQuotations(plainText); } catch (ex) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.editorInsertText: no mail editor\n"); this.editor.insertText(plainText); } } }, editorInsertAsQuotation: function(plainText) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.editorInsertAsQuotation\n"); if (this.editor) { var mailEditor; try { mailEditor = this.editor.QueryInterface(Components.interfaces.nsIEditorMailSupport); } catch (ex) {} if (!mailEditor) return 0; EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.editorInsertAsQuotation: mailEditor=" + mailEditor + "\n"); mailEditor.insertAsQuotation(plainText); return 1; } return 0; }, /** * Display a notification to the user at the bottom of the window * * @param priority: Number - Priority of the message [1 = high (error) ... 3 = low (info)] * @param msgText: String - Text to be displayed in notification bar * @param messageId: String - Unique message type identification * @param detailsText: String - optional text to be displayed by clicking on "Details" button. * if null or "", then the Detail button will no be displayed. */ notifyUser: function(priority, msgText, messageId, detailsText) { let notif = document.getElementById("attachmentNotificationBox"); let prio; switch (priority) { case 1: prio = notif.PRIORITY_CRITICAL_MEDIUM; break; case 3: prio = notif.PRIORITY_INFO_MEDIUM; break; default: prio = notif.PRIORITY_WARNING_MEDIUM; } let buttonArr = []; if (detailsText && detailsText.length > 0) { buttonArr.push({ accessKey: EnigmailLocale.getString("msgCompose.detailsButton.accessKey"), label: EnigmailLocale.getString("msgCompose.detailsButton.label"), callback: function(aNotificationBar, aButton) { EnigmailDialog.info(window, detailsText); } }); } notif.appendNotification(msgText, messageId, null, prio, buttonArr); }, editorSelectAll: function() { if (this.editor) { this.editor.selectAll(); } }, editorGetCharset: function() { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.editorGetCharset\n"); return this.editor.documentCharacterSet; }, editorGetContentAs: function(mimeType, flags) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.editorGetContentAs\n"); if (this.editor) { return this.editor.outputToString(mimeType, flags); } return null; }, addrOnChangeTimer: null, addressOnChange: function(element) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.addressOnChange\n"); if (!this.addrOnChangeTimer) { var self = this; this.addrOnChangeTimer = EnigmailTimer.setTimeout(function _f() { self.fireSendFlags(); self.addrOnChangeTimer = null; }, 250); } }, focusChange: function() { // call original TB function CommandUpdate_MsgCompose(); var focusedWindow = top.document.commandDispatcher.focusedWindow; // we're just setting focus to where it was before if (focusedWindow == Enigmail.msg.lastFocusedWindow) { // skip return; } Enigmail.msg.lastFocusedWindow = focusedWindow; Enigmail.msg.fireSendFlags(); }, fireSendFlags: function() { try { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: Enigmail.msg.fireSendFlags\n"); if (!this.determineSendFlagId) { this.determineSendFlagId = EnigmailEvents.dispatchEvent( function _sendFlagWrapper() { Enigmail.msg.determineSendFlags(); Enigmail.msg.fireSearchKeys(); }, 0); } } catch (ex) {} }, /** * Merge multiple Re: Re: into one Re: in message subject */ fixMessageSubject: function() { let subjElem = document.getElementById("msgSubject"); if (subjElem) { let r = subjElem.value.replace(/^(Re: )+(.*)/, "Re: $2"); if (r !== subjElem.value) { subjElem.value = r; if (typeof subjElem.oninput === "function") subjElem.oninput(); } } }, fireSearchKeys: function() { if (this.isEnigmailEnabled()) { if (this.searchKeysTimeout) return; let self = this; this.searchKeysTimeout = EnigmailTimer.setTimeout(function _f() { self.searchKeysTimeout = null; Enigmail.msg.findMissingKeys(); }, 20000); // 20 Seconds } }, /** * Determine if all addressees have a valid key ID; if not, attempt to * import them via WKD or Autocrypt. */ findMissingKeys: function() { const nsIEnigmail = Components.interfaces.nsIEnigmail; try { if (this.juniorMode) return; EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: findMissingKeys()\n"); let self = this; let missingKeys = this.determineSendFlags(); if ("errArray" in missingKeys && missingKeys.errArray.length > 0) { let missingEmails = missingKeys.errArray.map(function(i) { return i.addr.toLowerCase().trim(); }); let lookupList = []; // only search for keys not checked before for (let k of missingEmails) { if (this.keyLookupDone.indexOf(k) < 0) { lookupList.push(k); this.keyLookupDone.push(k); } } if (lookupList.length > 0) { new Promise((resolve, reject) => { if (EnigmailPrefs.getPref("autocryptMode") === 0) { resolve([]); return; } EnigmailAutocrypt.importAutocryptKeys(lookupList). then(foundKeys => { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: findMissingKeys: got " + foundKeys.length + " autocrypt keys\n"); if (foundKeys.length > 0) { self.determineSendFlags(); } resolve(foundKeys); }); }). then((foundKeys) => { if (EnigmailPrefs.getPref("autoWkdLookup") === 0) return; if (foundKeys.length >= lookupList.length) return; EnigmailWkdLookup.findKeys(lookupList). then((foundKeys) => { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: findMissingKeys: wkd got " + foundKeys + "\n"); if (foundKeys) { self.determineSendFlags(); } }); }). catch(err => { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: findMissingKeys: error " + err + "\n"); }); } } } catch (ex) {} } }; Enigmail.composeStateListener = { NotifyComposeFieldsReady: function() { // Note: NotifyComposeFieldsReady is only called when a new window is created (i.e. not in case a window object is reused). EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: ECSL.NotifyComposeFieldsReady\n"); try { Enigmail.msg.editor = gMsgCompose.editor.QueryInterface(Components.interfaces.nsIEditor); } catch (ex) {} if (!Enigmail.msg.editor) return; Enigmail.msg.fixMessageSubject(); function enigDocStateListener() {} enigDocStateListener.prototype = { QueryInterface: function(iid) { if (!iid.equals(Components.interfaces.nsIDocumentStateListener) && !iid.equals(Components.interfaces.nsISupports)) throw Components.results.NS_ERROR_NO_INTERFACE; return this; }, NotifyDocumentCreated: function() { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: EDSL.NotifyDocumentCreated\n"); }, NotifyDocumentWillBeDestroyed: function() { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: EDSL.enigDocStateListener.NotifyDocumentWillBeDestroyed\n"); }, NotifyDocumentStateChanged: function(nowDirty) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: EDSL.enigDocStateListener.NotifyDocumentStateChanged\n"); } }; var docStateListener = new enigDocStateListener(); Enigmail.msg.editor.addDocumentStateListener(docStateListener); }, ComposeProcessDone: function(aResult) { // Note: called after a mail was sent (or saved) EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: ECSL.ComposeProcessDone: " + aResult + "\n"); if (aResult != Components.results.NS_OK) { if (Enigmail.msg.processed) { Enigmail.msg.undoEncryption(4); } Enigmail.msg.removeAttachedKey(); } // ensure that securityInfo is set back to S/MIME flags (especially required if draft was saved) if (gSMFields) gMsgCompose.compFields.securityInfo = gSMFields; }, NotifyComposeBodyReady: function() { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: ECSL.ComposeBodyReady\n"); var isEmpty, isEditable; isEmpty = Enigmail.msg.editor.documentIsEmpty; isEditable = Enigmail.msg.editor.isDocumentEditable; Enigmail.msg.composeBodyReady = true; EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: EDSL.ComposeBodyReady: isEmpty=" + isEmpty + ", isEditable=" + isEditable + "\n"); //FIXME EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: EDSL.ComposeBodyReady: disableSmime=" + Enigmail.msg.disableSmime + "\n"); if (Enigmail.msg.disableSmime) { if (gMsgCompose && gMsgCompose.compFields && gMsgCompose.compFields.securityInfo) { let si = gMsgCompose.compFields.securityInfo.QueryInterface(Components.interfaces.nsIMsgSMIMECompFields); si.signMessage = false; si.requireEncryptMessage = false; } else { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: EDSL.ComposeBodyReady: could not disable S/MIME\n"); } } if (!isEditable || isEmpty) return; // Required for TB < 48 (with window recycling) Enigmail.msg.fixMessageSubject(); if (!Enigmail.msg.timeoutId && !Enigmail.msg.dirty) { Enigmail.msg.timeoutId = EnigmailTimer.setTimeout(function() { Enigmail.msg.decryptQuote(false); }, 0); } }, SaveInFolderDone: function(folderURI) { //EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: ECSL.SaveInFolderDone\n"); } }; window.addEventListener("load", function _enigmail_composeStartup(event) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: got load event\n"); Enigmail.msg.composeStartup(event); }, false); window.addEventListener("unload", function _enigmail_composeUnload(event) { Enigmail.msg.composeUnload(event); }, false); // Handle recycled windows // TB < 47 window.addEventListener('compose-window-close', function _enigmail_msgComposeClose(event) { Enigmail.msg.msgComposeClose(event); }, true); // TB >= 48 window.addEventListener('compose-window-unload', function _enigmail_msgComposeDestory(event) { Enigmail.msg.msgComposeClose(event); }, true); // TB < 47 only window.addEventListener('compose-window-reopen', function _enigmail_msgComposeReopen(event) { Enigmail.msg.msgComposeReopen(event); }, true); // Listen to message sending event window.addEventListener('compose-send-message', function _enigmail_sendMessageListener(event) { Enigmail.msg.sendMessageListener(event); }, true); window.addEventListener('compose-window-init', function _enigmail_composeWindowInit(event) { EnigmailLog.DEBUG("enigmailMsgComposeOverlay.js: _enigmail_composeWindowInit\n"); gMsgCompose.RegisterStateListener(Enigmail.composeStateListener); Enigmail.msg.composeBodyReady = false; }, true);