Created
September 10, 2011 06:14
-
-
Save shirosaki/1208000 to your computer and use it in GitHub Desktop.
add enigmail feature to quick reply
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/send.js b/send.js | |
index f62ad60..b05ce57 100644 | |
--- a/send.js | |
+++ b/send.js | |
@@ -192,6 +192,7 @@ function initCompose(aMsgComposeService, aParams, aWindow, aDocShell) { | |
* @param composeParameters.returnReceipt (optional) | |
* @param composeParameters.receiptType (optional) | |
* @param composeParameters.requestDsn (optional) | |
+ * @param composeParameters.securityInfo (optional) | |
* | |
* @param sendingParameters | |
* @param sendingParameters.deliverType See Ci.nsIMsgCompDeliverMode | |
@@ -252,7 +253,9 @@ function sendMessage(params, | |
fields.DSN = ("requestDsn" in params) | |
? params.requestDsn | |
: identity.requestDSN; | |
- | |
+ if ("securityInfo" in params) | |
+ fields.securityInfo = params.securityInfo; | |
+ | |
let references = []; | |
switch (compType) { | |
case mCompType.New: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/content/stub.compose-ui.js b/content/stub.compose-ui.js | |
index 736f7fe..68f199e 100644 | |
--- a/content/stub.compose-ui.js | |
+++ b/content/stub.compose-ui.js | |
@@ -56,6 +56,7 @@ Cu.import("resource://conversations/stdlib/send.js"); | |
Cu.import("resource://conversations/stdlib/compose.js"); | |
Cu.import("resource://conversations/log.js"); | |
Cu.import("resource://conversations/misc.js"); | |
+Cu.import("resource://conversations/hook.js"); | |
let Log = setupLogging("Conversations.Stub.Compose"); | |
@@ -572,6 +573,18 @@ ComposeSession.prototype = { | |
let body = "\n" | |
+ htmlToPlainText('<blockquote type="cite">'+aBody+'</blockquote>') | |
.trim(); | |
+ let node = getActiveEditor(); | |
+ let newBody = {}; | |
+ try { | |
+ [h.onReplyComposed(node, body, $, newBody) for each ([, h] in Iterator(getHooks())) | |
+ if (typeof(h.onReplyComposed) == "function")]; | |
+ } catch (e) { | |
+ Log.warn("Plugin returned an error:", e); | |
+ dumpCallStack(e); | |
+ } | |
+ if (newBody.value) { | |
+ body = "\n" + newBody.value; | |
+ } | |
// Old way: | |
// let body = citeString("\n"+htmlToPlainText(aBody).trim()); | |
let quoteblock = // body already starts with a newline | |
@@ -599,7 +612,6 @@ ComposeSession.prototype = { | |
}; | |
// The user might be fast and might have started typing something | |
// already | |
- let node = getActiveEditor(); | |
let txt = node.value; | |
let val = null; | |
let pos = null; | |
@@ -651,6 +663,20 @@ ComposeSession.prototype = { | |
? Ci.nsIMsgCompType.ForwardInline | |
: Ci.nsIMsgCompType.ReplyAll; // ReplyAll, Reply... ends up the same | |
+ let encryptStatus = {}; | |
+ if (!popOut) { | |
+ try { | |
+ [h.onMessageBeforeSend(ed, self, $, encryptStatus) | |
+ for each ([, h] in Iterator(getHooks())) | |
+ if (typeof(h.onMessageBeforeSend) == "function")]; | |
+ } catch (e) { | |
+ Log.warn("Plugin returned an error:", e); | |
+ dumpCallStack(e); | |
+ } | |
+ if (encryptStatus.processing === false) | |
+ return; | |
+ } | |
+ | |
return sendMessage({ | |
urls: [msgHdrGetUri(self.params.msgHdr)], | |
identity: self.params.identity, | |
@@ -658,6 +684,7 @@ ComposeSession.prototype = { | |
cc: JSON.parse($("#cc").val()).join(","), | |
bcc: JSON.parse($("#bcc").val()).join(","), | |
subject: self.params.subject, | |
+ securityInfo: encryptStatus.securityInfo, | |
}, { | |
compType: compType, | |
deliverType: deliverMode, | |
diff --git a/modules/plugins/enigmail.js b/modules/plugins/enigmail.js | |
index a282e55..24184e6 100644 | |
--- a/modules/plugins/enigmail.js | |
+++ b/modules/plugins/enigmail.js | |
@@ -102,6 +102,7 @@ try { | |
} | |
let enigmailSvc; | |
+let global = this; | |
window.addEventListener("load", function () { | |
if (hasEnigmail) { | |
enigmailSvc = EnigmailCommon.getService(window); | |
@@ -109,6 +110,14 @@ window.addEventListener("load", function () { | |
Log.debug("Error loading the Enigmail service. Is Enigmail disabled?\n"); | |
hasEnigmail = false; | |
} | |
+ try { | |
+ let loader = Services.scriptloader; | |
+ loader.loadSubScript("chrome://enigmail/content/enigmailMsgComposeOverlay.js", global); | |
+ loader.loadSubScript("chrome://enigmail/content/enigmailMsgComposeHelper.js", global); | |
+ } catch (e) { | |
+ hasEnigmail = false; | |
+ Log.debug("Enigmail script doesn't seem to be loaded. Error: " + e); | |
+ } | |
} | |
}, false); | |
@@ -133,20 +142,19 @@ function tryEnigmail(bodyElement, aMsgWindow) { | |
var tail = ""; | |
var msgText = bodyElement.textContent; | |
var startOffset = msgText.indexOf("-----BEGIN PGP"); | |
+ var indentMatches = msgText.match(/\n(.*)-----BEGIN PGP/); | |
+ var indent = ""; | |
+ if (indentMatches && (indentMatches.length > 1)) { | |
+ indent = indentMatches[1]; | |
+ } | |
head = msgText.substring(0, startOffset).replace(/^[\n\r\s]*/,""); | |
head = head.replace(/[\n\r\s]*$/,""); | |
- var endStart = msgText.indexOf("-----END PGP"); | |
+ var endStart = msgText.indexOf("\n"+indent+"-----END PGP") + 1; | |
var nextLine = msgText.substring(endStart).search(/[\n\r]/); | |
if (nextLine > 0) { | |
tail = msgText.substring(endStart+nextLine).replace(/^[\n\r\s]*/,""); | |
} | |
- var decryptedText = | |
- enigmailSvc.decryptMessage(window, 0, bodyElement.textContent, | |
- signatureObj, exitCodeObj, | |
- statusFlagsObj, keyIdObj, userIdObj, sigDetailsObj, | |
- errorMsgObj, blockSeparationObj); | |
- | |
var charset = aMsgWindow ? aMsgWindow.mailCharacterSet : ""; | |
var subText = msgText.substr(startOffset); | |
var matches = subText.match(/\n[> ]*Charset: *(.*) *\n/i); | |
@@ -155,6 +163,14 @@ function tryEnigmail(bodyElement, aMsgWindow) { | |
charset = matches[1]; | |
} | |
+ msgText = EnigmailCommon.convertFromUnicode(msgText, "UTF-8"); | |
+ | |
+ var decryptedText = | |
+ enigmailSvc.decryptMessage(window, 0, msgText, | |
+ signatureObj, exitCodeObj, | |
+ statusFlagsObj, keyIdObj, userIdObj, sigDetailsObj, | |
+ errorMsgObj, blockSeparationObj); | |
+ | |
var msgRfc822Text = ""; | |
if (head || tail) { | |
if (head) { | |
@@ -182,6 +198,9 @@ function tryEnigmail(bodyElement, aMsgWindow) { | |
} | |
return statusFlagsObj.value; | |
} | |
+ else { | |
+ Log.error("Enigmail error: "+exitCodeObj.value+" --- "+errorMsgObj.value+"\n"); | |
+ } | |
} catch (ex) { | |
dumpCallStack(ex); | |
Log.error("Enigmail error: "+ex+" --- "+errorMsgObj.value+"\n"); | |
@@ -189,6 +208,194 @@ function tryEnigmail(bodyElement, aMsgWindow) { | |
} | |
} | |
+// This code is a part of encryptMsg() in enigmailMsgComposeOverlay.js. | |
+function checkPerRecipientRules(params, aResultParams) { | |
+ let { | |
+ sendFlags, | |
+ optSendFlags, | |
+ gotSendFlags, | |
+ fromAddr, | |
+ toAddr, | |
+ bccAddr, | |
+ enableRules, | |
+ sendModeDirty, | |
+ signPlain, | |
+ } = params; | |
+ | |
+ const nsIEnigmail = Ci.nsIEnigmail; | |
+ const SIGN = nsIEnigmail.SEND_SIGNED; | |
+ const ENCRYPT = nsIEnigmail.SEND_ENCRYPTED; | |
+ | |
+ var recipientsSelection = EnigmailCommon.getPref("recipientsSelection"); | |
+ var testCipher = null; | |
+ | |
+ var notSignedIfNotEnc= (sendModeDirty < 2 && (! signPlain)); | |
+ | |
+ if (toAddr.length>=1) { | |
+ | |
+ EnigmailCommon.DEBUG_LOG("enigmailMsgComposeOverlay.js: Enigmail.msg.encryptMsg: toAddr="+toAddr+"\n"); | |
+ var repeatSelection = 0; | |
+ while (repeatSelection < 2) { | |
+ if (recipientsSelection != 3 && recipientsSelection != 4 | |
+ && enableRules) { | |
+ var matchedKeysObj = new Object; | |
+ var flagsObj = new Object; | |
+ if (!Enigmail.hlp.getRecipientsKeys(toAddr, | |
+ (repeatSelection == 1), | |
+ true, | |
+ matchedKeysObj, | |
+ flagsObj)) { | |
+ return false; | |
+ } | |
+ if (matchedKeysObj.value) toAddr=matchedKeysObj.value; | |
+ | |
+ if (flagsObj.value) { | |
+ switch (flagsObj.sign) { | |
+ case 0: | |
+ sendFlags &= ~SIGN; | |
+ break; | |
+ case 2: | |
+ sendFlags |= SIGN; | |
+ break; | |
+ } | |
+ | |
+ switch (flagsObj.encrypt) { | |
+ case 0: | |
+ sendFlags &= ~ENCRYPT; | |
+ break; | |
+ case 2: | |
+ sendFlags |= ENCRYPT; | |
+ break; | |
+ } | |
+ | |
+ switch (flagsObj.pgpMime) { | |
+ case 0: | |
+ sendFlags &= ~nsIEnigmail.SEND_PGP_MIME; | |
+ break; | |
+ case 2: | |
+ sendFlags |= nsIEnigmail.SEND_PGP_MIME; | |
+ break; | |
+ } | |
+ } | |
+ | |
+ if (!Enigmail.hlp.getRecipientsKeys(bccAddr, | |
+ (repeatSelection==1), | |
+ true, | |
+ matchedKeysObj, | |
+ flagsObj)) { | |
+ return false; | |
+ } | |
+ if (matchedKeysObj.value) bccAddr=matchedKeysObj.value; | |
+ // bcc recipients are part of "normal" recipients as well; no need to do furter processing of flags etc. | |
+ } | |
+ repeatSelection++; | |
+ | |
+ if (sendFlags & ENCRYPT) { | |
+ // Encrypt or sign test message for default encryption | |
+ | |
+ var testExitCodeObj = new Object(); | |
+ var testStatusFlagsObj = new Object(); | |
+ var testErrorMsgObj = new Object(); | |
+ | |
+ var testPlain = "Test Message"; | |
+ var testUiFlags = nsIEnigmail.UI_TEST; | |
+ var testSendFlags = nsIEnigmail.SEND_TEST | ENCRYPT | | |
+ optSendFlags ; | |
+ | |
+ // test recipients | |
+ testCipher = enigmailSvc.encryptMessage(window, testUiFlags, null, | |
+ testPlain, | |
+ fromAddr, toAddr, bccAddr, | |
+ testSendFlags, | |
+ testExitCodeObj, | |
+ testStatusFlagsObj, | |
+ testErrorMsgObj); | |
+ | |
+ if (testStatusFlagsObj.value) { | |
+ // check if own key is invalid | |
+ let errLines = testErrorMsgObj.value.split(/\r?\n/); | |
+ let s = new RegExp("INV_(RECP|SGNR) [0-9]+ \<?" + fromAddr + "\>?"); | |
+ for (let l = 0; l < errLines.length; l++) { | |
+ if (errLines[l].search(s) == 0) { | |
+ EnigmailCommon.alert(window, EnigmailCommon.getString("errorKeyUnusable", [ fromAddr ])); | |
+ return false; | |
+ } | |
+ } | |
+ } | |
+ | |
+ | |
+ if ((recipientsSelection == 4) || | |
+ ((testStatusFlagsObj.value & nsIEnigmail.INVALID_RECIPIENT) && | |
+ (recipientsSelection == 2 || recipientsSelection == 3))) { | |
+ // check for invalid recipient keys | |
+ var resultObj = new Object(); | |
+ var inputObj = new Object(); | |
+ inputObj.toAddr = toAddr; | |
+ inputObj.invalidAddr = Enigmail.hlp.getInvalidAddress(testErrorMsgObj.value); | |
+ inputObj.options = "multisel"; | |
+ if (recipientsSelection==2) | |
+ inputObj.options += ",rulesOption" | |
+ if (notSignedIfNotEnc) | |
+ inputObj.options += ",notsigned"; | |
+ if (recipientsSelection == 4) | |
+ inputObj.options += ",noforcedisp"; | |
+ inputObj.dialogHeader = EnigmailCommon.getString("recipientsSelectionHdr"); | |
+ | |
+ window.openDialog("chrome://enigmail/content/enigmailUserSelection.xul","", "dialog,modal,centerscreen", inputObj, resultObj); | |
+ try { | |
+ if (resultObj.cancelled) { | |
+ return false; | |
+ } | |
+ if (resultObj.perRecipientRules && enableRules) { | |
+ // do an extra round because the user want to set a PGP rule | |
+ continue; | |
+ } | |
+ if (! resultObj.encrypt) { | |
+ // encryption explicitely turned off | |
+ sendFlags &= ~ENCRYPT; | |
+ if (notSignedIfNotEnc) sendFlags &= ~SIGN; | |
+ } | |
+ else { | |
+ if (bccAddrList.length > 0) { | |
+ bccAddr = resultObj.userList.join(", "); | |
+ toAddr = ""; | |
+ } | |
+ else { | |
+ toAddr = resultObj.userList.join(", "); | |
+ bccAddr = ""; | |
+ } | |
+ } | |
+ testCipher = "ok"; | |
+ testExitCodeObj.value = 0; | |
+ } catch (ex) { | |
+ // cancel pressed -> don't send mail | |
+ return false; | |
+ } | |
+ } | |
+ if ((!testCipher || (testExitCodeObj.value != 0)) && recipientsSelection == 5) { | |
+ // Test encryption failed; turn off default encryption | |
+ sendFlags &= ~ENCRYPT; | |
+ EnigmailCommon.DEBUG_LOG("enigmailMsgComposeOverlay.js: Enigmail.msg.encryptMsg: No default encryption because test failed\n"); | |
+ } | |
+ } | |
+ repeatSelection = 2; | |
+ } | |
+ | |
+ if ((gotSendFlags & ENCRYPT) && | |
+ !(sendFlags & ENCRYPT)) { | |
+ // Default encryption turned off; turn off signing as well | |
+ if (sendModeDirty < 2 && (! signPlain)) { | |
+ sendFlags &= ~SIGN; | |
+ } | |
+ } | |
+ } | |
+ aResultParams.sendFlags = sendFlags; | |
+ aResultParams.toAddr = toAddr; | |
+ aResultParams.bccAddr = bccAddr; | |
+ | |
+ return true; | |
+} | |
+ | |
let enigmailHook = { | |
_domNode: null, | |
@@ -225,6 +432,153 @@ let enigmailHook = { | |
} | |
} | |
}, | |
+ | |
+ onMessageBeforeSend: function _enigmailHook_onMessageBeforeSend(aEditor, aComposeSession, $, aStatus) { | |
+ if (hasEnigmail) { | |
+ const nsIEnigmail = Ci.nsIEnigmail; | |
+ const SIGN = nsIEnigmail.SEND_SIGNED; | |
+ const ENCRYPT = nsIEnigmail.SEND_ENCRYPTED; | |
+ | |
+ let uiFlags = nsIEnigmail.UI_INTERACTIVE; | |
+ | |
+ let identity = aComposeSession.params.identity; | |
+ let fromAddr = identity.email; | |
+ | |
+ // set default depending on the identity | |
+ Enigmail.msg.identity = identity; | |
+ Enigmail.msg.setSendDefaultOptions.call(Enigmail.msg); | |
+ let sendFlags = Enigmail.msg.sendMode; | |
+ if (Enigmail.msg.sendPgpMime) { | |
+ // Use PGP/MIME | |
+ sendFlags |= nsIEnigmail.SEND_PGP_MIME; | |
+ } | |
+ | |
+ let optSendFlags = 0; | |
+ if (EnigmailCommon.getPref("alwaysTrustSend")) { | |
+ optSendFlags |= nsIEnigmail.SEND_ALWAYS_TRUST; | |
+ } | |
+ if (EnigmailCommon.getPref("encryptToSelf") || (sendFlags & nsIEnigmail.SAVE_MESSAGE)) { | |
+ optSendFlags |= nsIEnigmail.SEND_ENCRYPT_TO_SELF; | |
+ } | |
+ let gotSendFlags = sendFlags; | |
+ sendFlags |= optSendFlags; | |
+ | |
+ let toAddrList = $.map( | |
+ $.merge(JSON.parse($("#to").val()), JSON.parse($("#cc").val())), | |
+ function (val) { | |
+ return EnigmailFuncs.stripEmail(val); | |
+ }); | |
+ let toAddr = toAddrList.join(", "); | |
+ let bccAddr = $.map(JSON.parse($("#bcc").val()), function (val) { | |
+ return EnigmailFuncs.stripEmail(val); | |
+ }).join(", "); | |
+ Log.error("to: " + toAddr); | |
+ Log.error("bcc: " + bccAddr); | |
+ | |
+ let resultParams = {}; | |
+ let ret = checkPerRecipientRules({ | |
+ sendFlags: sendFlags, | |
+ optSendFlags: optSendFlags, | |
+ gotSendFlags: gotSendFlags, | |
+ fromAddr: fromAddr, | |
+ toAddr: toAddr, | |
+ bccAddr: bccAddr, | |
+ enableRules: true, | |
+ sendModeDirty: 0, | |
+ signPlain: identity.getBoolAttribute("pgpSignPlain") | |
+ }, resultParams); | |
+ | |
+ if (ret) { | |
+ sendFlags = resultParams.sendFlags; | |
+ toAddr = resultParams.toAddr; | |
+ bccAddr = resultParams.bccAddr; | |
+ } | |
+ else { | |
+ aStatus.processing = false; | |
+ return false; | |
+ } | |
+ | |
+ let statusFlagsObj = {}; | |
+ let exitCodeObj = {}; | |
+ let errorMsgObj = {}; | |
+ | |
+ try { | |
+ let origText; | |
+ | |
+ let usingPGPMime = (sendFlags & nsIEnigmail.SEND_PGP_MIME) && | |
+ (sendFlags & (ENCRYPT | SIGN)); | |
+ if (usingPGPMime) { | |
+ uiFlags |= nsIEnigmail.UI_PGP_MIME; | |
+ | |
+ let newSecurityInfo = Cc[Enigmail.msg.compFieldsEnig_CID] | |
+ .createInstance(Ci.nsIEnigMsgCompFields); | |
+ newSecurityInfo.sendFlags = sendFlags; | |
+ newSecurityInfo.UIFlags = uiFlags; | |
+ newSecurityInfo.senderEmailAddr = fromAddr; | |
+ newSecurityInfo.recipients = toAddr; | |
+ newSecurityInfo.bccRecipients = bccAddr; | |
+ newSecurityInfo.hashAlgorithm = | |
+ Enigmail.msg.mimeHashAlgo[EnigmailCommon.getPref("mimeHashAlgorithm")]; | |
+ | |
+ aStatus.securityInfo = newSecurityInfo; | |
+ } | |
+ else if (sendFlags & (ENCRYPT | SIGN)) { | |
+ // inline-PGP | |
+ let plainText = aEditor.value; | |
+ let charset = "UTF-8"; | |
+ origText = plainText; | |
+ plainText= EnigmailCommon.convertFromUnicode(plainText, charset); | |
+ let cipherText = enigmailSvc.encryptMessage(window, uiFlags, null, | |
+ plainText, fromAddr, toAddr, bccAddr, | |
+ sendFlags, exitCodeObj, statusFlagsObj, errorMsgObj); | |
+ | |
+ let exitCode = exitCodeObj.value; | |
+ if (cipherText && (exitCode == 0)) { | |
+ if ((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"); | |
+ } | |
+ cipherText = EnigmailCommon.convertToUnicode(cipherText, charset); | |
+ | |
+ aEditor.value = cipherText; | |
+ } | |
+ } | |
+ | |
+ if ((!(sendFlags & nsIEnigmail.SAVE_MESSAGE)) && | |
+ EnigmailCommon.getPref("confirmBeforeSend")) { | |
+ if (!Enigmail.msg.confirmBeforeSend(toAddrList.join(", "), toAddr+", "+bccAddr, | |
+ sendFlags, Services.io.offline)) { | |
+ if (origText) { | |
+ aEditor.value = origText; | |
+ } | |
+ aStatus.processing = false; | |
+ return false; | |
+ } | |
+ } | |
+ return true; | |
+ } catch (ex) { | |
+ dumpCallStack(ex); | |
+ Log.error("Enigmail encrypt error: "+ex+" --- "+errorMsgObj.value+"\n"); | |
+ return null; | |
+ } | |
+ } | |
+ }, | |
+ | |
+ onReplyComposed: function _enigmailHook_onReplyComposed(aNode, aOrigBody, $, aNewBody) { | |
+ if (hasEnigmail && aOrigBody.indexOf("-----BEGIN PGP") >= 0) { | |
+ // find original message node and get decrypted text | |
+ let $body = $(aNode).parents("li.message:first") | |
+ .find("iframe:first").contents() | |
+ .find("body").clone(); | |
+ | |
+ // remove "- show quoted text -" | |
+ $body.find("div.showhidequote").remove(); | |
+ | |
+ aNewBody.value = $body.text().replace(/^/mg, '>'); | |
+ } | |
+ }, | |
} | |
registerHook(enigmailHook); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment