Skip to content

Instantly share code, notes, and snippets.

@shirosaki
Created September 10, 2011 06:14
Show Gist options
  • Save shirosaki/1208000 to your computer and use it in GitHub Desktop.
Save shirosaki/1208000 to your computer and use it in GitHub Desktop.
add enigmail feature to quick reply
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:
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