aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPatrick Brunschwig <[email protected]>2019-06-15 21:06:40 +0200
committerPatrick Brunschwig <[email protected]>2019-06-15 21:06:40 +0200
commit6b3e1ea554a563531c0d43dff68ee19274d077ce (patch)
tree0950c3331143192ec5805d7725768dae680d0106
parent74648654b0fede31f59e79a01df19917d6c16e3b (diff)
downloadenigmail-filter-v2.tar.gz
enigmail-filter-v2.tar.bz2
enigmail-filter-v2.zip
completed re-implementation of new persistent decryption/encryptionfilter-v2
-rw-r--r--package/persistentCrypto.jsm199
-rw-r--r--package/tests/persistentCrypto-test.js95
-rw-r--r--package/tests/resources/encrypted-pgpmime-email.eml44
3 files changed, 274 insertions, 64 deletions
diff --git a/package/persistentCrypto.jsm b/package/persistentCrypto.jsm
index ebcfcdd7..1e5cd8c2 100644
--- a/package/persistentCrypto.jsm
+++ b/package/persistentCrypto.jsm
@@ -30,6 +30,7 @@ const EnigmailEncryption = ChromeUtils.import("chrome://enigmail/content/modules
const getFixExchangeMsg = EnigmailLazy.loader("enigmail/fixExchangeMsg.jsm", "EnigmailFixExchangeMsg");
const getDecryption = EnigmailLazy.loader("enigmail/decryption.jsm", "EnigmailDecryption");
const getDialog = EnigmailLazy.loader("enigmail/dialog.jsm", "EnigmailDialog");
+const getGpgAgent = EnigmailLazy.loader("enigmail/gpgAgent.jsm", "EnigmailGpgAgent");
const STATUS_OK = 0;
const STATUS_FAILURE = 1;
@@ -163,6 +164,11 @@ CryptMessageIntoFolder.prototype = {
messageParseCallback: async function(mimeTree, msgHdr) {
this.mimeTree = mimeTree;
this.hdr = msgHdr;
+
+ if (mimeTree.headers.has("subject")) {
+ this.subject = mimeTree.headers.get("subject");
+ }
+
await this.decryptMimeTree(mimeTree);
let msg = "";
@@ -259,39 +265,6 @@ CryptMessageIntoFolder.prototype = {
return msg;
},
- readAttachment: function(attachment, strippedName) {
- return new Promise(
- function(resolve, reject) {
- EnigmailLog.DEBUG("persistentCrypto.jsm: readAttachment\n");
- let o;
- var f = function _cb(data) {
- EnigmailLog.DEBUG("persistentCrypto.jsm: readAttachment - got data (" + data.length + ")\n");
- o = {
- type: "attachment",
- data: data,
- name: strippedName ? strippedName : attachment.name,
- partNum: attachment.partNum,
- 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.createChannel(msgUri);
- channel.asyncOpen(bufferListener, msgUri);
- } catch (ex) {
- reject(o);
- }
- }
- );
- },
-
-
/**
* Walk through the MIME message structure and decrypt the body if there is something to decrypt
*/
@@ -304,6 +277,8 @@ CryptMessageIntoFolder.prototype = {
if (this.isPgpMime(mimePart)) {
this.decryptPGPMIME(mimePart);
+ } else if (getAttachmentName(mimePart)) {
+ this.decryptAttachment(mimePart);
} else {
this.decryptINLINE(mimePart);
}
@@ -412,7 +387,6 @@ CryptMessageIntoFolder.prototype = {
if (data.substr(bodyIndex).search(/\r?\n$/) === 0) {
return;
-
}
let m = Cc["@mozilla.org/messenger/mimeheaders;1"].createInstance(Ci.nsIMimeHeaders);
@@ -461,6 +435,101 @@ CryptMessageIntoFolder.prototype = {
this.messageDecrypted = true;
},
+ decryptAttachment: function(mimePart) {
+
+ EnigmailLog.DEBUG("persistentCrypto.jsm: decryptAttachment()\n");
+ let attachmentHead = mimePart.body.substr(0, 30);
+ if (attachmentHead.search(/-----BEGIN PGP \w{5,10} KEY BLOCK-----/) >= 0) {
+ // attachment appears to be a PGP key file, we just go-a-head
+ return;
+ }
+
+ let attachmentName = getAttachmentName(mimePart);
+ if (attachmentName.search(/\.(pgp|asc|gpg)$/) < 0 &&
+ mimePart.body.search(/^-----BEGIN PGP ENCRYPTED MESSAGE-----$/m) < 0) {
+ return;
+ }
+
+ attachmentName = attachmentName.replace(/\.(pgp|asc|gpg)$/, "");
+
+ var enigmailSvc = EnigmailCore.getService();
+ var args = EnigmailGpg.getStandardArgs(true);
+ args.push("-d");
+
+ var statusMsgObj = {};
+ var cmdLineObj = {};
+ var exitCode = -1;
+ var statusFlagsObj = {};
+ var errorMsgObj = {};
+ statusFlagsObj.value = 0;
+
+ var listener = EnigmailExecution.newSimpleListener(
+ function _stdin(pipe) {
+ pipe.write(mimePart.body);
+ pipe.close();
+
+ }
+ );
+
+ do {
+ var proc = EnigmailExecution.execStart(getGpgAgent().agentPath, args, false, null, listener, statusFlagsObj);
+ if (!proc) {
+ return;
+ }
+ // Wait for child STDOUT to close
+ proc.wait();
+ EnigmailExecution.execEnd(listener, statusFlagsObj, statusMsgObj, cmdLineObj, errorMsgObj);
+
+ if ((listener.stdoutData && listener.stdoutData.length > 0) ||
+ (statusFlagsObj.value & EnigmailConstants.DECRYPTION_OKAY)) {
+ EnigmailLog.DEBUG("persistentCrypto.jsm: decryptAttachment: decryption OK\n");
+ exitCode = 0;
+ } else if (statusFlagsObj.value & (EnigmailConstants.DECRYPTION_FAILED | EnigmailConstants.MISSING_MDC)) {
+ EnigmailLog.DEBUG("persistentCrypto.jsm: decryptAttachment: decryption without MDC protection\n");
+ exitCode = 0;
+ } else if (statusFlagsObj.value & EnigmailConstants.DECRYPTION_FAILED) {
+ EnigmailLog.DEBUG("persistentCrypto.jsm: decryptAttachment: decryption failed\n");
+ // since we cannot find out if the user wants to cancel
+ // we should ask
+ let msg = EnigmailLocale.getString("converter.decryptAtt.failed", [attachmentName, this.subject]);
+
+ if (!getDialog().confirmDlg(null, msg,
+ EnigmailLocale.getString("dlg.button.retry"), EnigmailLocale.getString("dlg.button.skip"))) {
+ return;
+ }
+ } else if (statusFlagsObj.value & EnigmailConstants.DECRYPTION_INCOMPLETE) {
+ // failure; message not complete
+ EnigmailLog.DEBUG("persistentCrypto.jsm: decryptAttachment: decryption incomplete\n");
+ return;
+ } else {
+ // there is nothing to be decrypted
+ EnigmailLog.DEBUG("persistentCrypto.jsm: decryptAttachment: no decryption required\n");
+ return;
+ }
+
+ } while (exitCode !== 0);
+
+
+ EnigmailLog.DEBUG("persistentCrypto.jsm: decryptAttachment: decrypted to " + listener.stdoutData.length + " bytes\n");
+ this.decryptedMessage = true;
+ mimePart.body = listener.stdoutData;
+ mimePart.headers._rawHeaders.set("content-disposition", `attachment; filename="${attachmentName}"`);
+ mimePart.headers._rawHeaders.set("content-transfer-encoding", ["base64"]);
+ let origCt = mimePart.headers.get("content-type");
+ let ct = origCt.type;
+
+
+ for (let i of origCt.entries()) {
+ if (i[0].toLowerCase() === "name") {
+ i[1] = i[1].replace(/\.(pgp|asc|gpg)$/, "");
+ }
+ ct += `; ${i[0]}="${i[1]}"`;
+ }
+
+ mimePart.headers._rawHeaders.set("content-type", [ct]);
+ },
+
+
decryptINLINE: function(mimePart) {
EnigmailLog.DEBUG("persistentCrypto.jsm: decryptINLINE()\n");
@@ -593,11 +662,10 @@ CryptMessageIntoFolder.prototype = {
let j = decryptedMessage.search(/[^\x01-\x7F]/); // eslint-disable-line no-control-regex
if (j >= 0) {
mimePart.headers._rawHeaders.set('content-transfer-encoding', ['base64']);
- mimePart.body = EnigmailData.encodeBase64(decryptedMessage);
} else {
- mimePart.body = decryptedMessage;
mimePart.headers._rawHeaders.set('content-transfer-encoding', ['8bit']);
}
+ mimePart.body = decryptedMessage;
let origCharset = getCharset(getHeaderValue(mimePart, 'content-type'));
if (origCharset) {
@@ -663,7 +731,20 @@ CryptMessageIntoFolder.prototype = {
}
if (mimePart.body.length > 0) {
- msg += mimePart.body;
+ let encoding = getTransferEncoding(mimePart);
+ if (!encoding) encoding = "8bit";
+
+ if (encoding === "quoted-printable") {
+ mimePart.headers._rawHeaders.set("content-transfer-encoding", ["base64"]);
+ encoding = "base64";
+ }
+
+ if (encoding === "base64") {
+ msg += EnigmailData.encodeBase64(mimePart.body);
+ } else {
+ msg += mimePart.body;
+ }
+
}
if (mimePart.subParts.length > 0) {
@@ -672,10 +753,9 @@ CryptMessageIntoFolder.prototype = {
for (let i in mimePart.subParts) {
msg += `--${boundary}\r\n`;
msg += this.mimeToString(mimePart.subParts[i], true);
- }
-
- if (msg.search(/[\r\n]$/) < 0) {
- msg += "\r\n";
+ if (msg.search(/[\r\n]$/) < 0) {
+ msg += "\r\n";
+ }
}
msg += `--${boundary}--\r\n`;
@@ -889,7 +969,7 @@ function getRfc822Headers(headerArr, contentType, ignoreHeadersArr) {
function getContentType(mime) {
try {
- if (mime.headers.has("content-type")) {
+ if (mime && ("headers" in mime) && mime.headers.has("content-type")) {
return mime.headers.get("content-type").type.toLowerCase();
}
} catch (e) {
@@ -901,7 +981,7 @@ function getContentType(mime) {
// return the content of the boundary parameter
function getBoundary(mime) {
try {
- if (mime.headers.has("content-type")) {
+ if (mime && ("headers" in mime) && mime.headers.has("content-type")) {
return mime.headers.get("content-type").get("boundary");
}
} catch (e) {
@@ -912,7 +992,7 @@ function getBoundary(mime) {
function getCharset(mime) {
try {
- if (mime.headers.has("content-type")) {
+ if (mime && ("headers" in mime) && mime.headers.has("content-type")) {
let c = mime.headers.get("content-type").get("charset");
if (c) return c.toLowerCase();
}
@@ -924,7 +1004,7 @@ function getCharset(mime) {
function getProtocol(mime) {
try {
- if (mime.headers.has("content-type")) {
+ if (mime && ("headers" in mime) && mime.headers.has("content-type")) {
let c = mime.headers.get("content-type").get("protocol");
if (c) return c.toLowerCase();
}
@@ -934,6 +1014,35 @@ function getProtocol(mime) {
return "";
}
+function getTransferEncoding(mime) {
+ try {
+ if (mime && ("headers" in mime) && mime.headers._rawHeaders.has("content-transfer-encoding")) {
+ let c = mime.headers._rawHeaders.get("content-transfer-encoding")[0];
+ if (c) return c.toLowerCase();
+ }
+ } catch (e) {
+ EnigmailLog.DEBUG("persistentCrypto.jsm: getTransferEncoding: " + e + "\n");
+ }
+ return "8Bit";
+}
+
+
+function getAttachmentName(mime) {
+ try {
+ if (mime && ("headers" in mime) && mime.headers.has("content-disposition")) {
+ let c = mime.headers.get("content-disposition")[0];
+ if (c) {
+ if (c.search(/^attachment/i) === 0) {
+ return EnigmailMime.getParameter(c, "filename");
+ }
+ }
+ }
+ } catch (e) {
+ EnigmailLog.DEBUG("persistentCrypto.jsm: getAttachmentName: " + e + "\n");
+ }
+ return null;
+}
+
function getPepSubject(mimeString) {
EnigmailLog.DEBUG("persistentCrypto.jsm: getPepSubject()\n");
diff --git a/package/tests/persistentCrypto-test.js b/package/tests/persistentCrypto-test.js
index 454c772a..f264d356 100644
--- a/package/tests/persistentCrypto-test.js
+++ b/package/tests/persistentCrypto-test.js
@@ -12,7 +12,7 @@
do_load_module("file://" + do_get_cwd().path + "/testHelper.js"); /*global TestHelper: false, component: false, withTestGpgHome: false, withEnigmail: false */
TestHelper.loadDirectly("tests/mailHelper.js"); /*global MailHelper: false */
-testing("persistentCrypto.jsm"); /*global EnigmailPersistentCrypto: false, Promise: false */
+testing("persistentCrypto.jsm"); /*global EnigmailPersistentCrypto: false, Promise: false, EnigmailMime: false */
var EnigmailKeyRing = component("enigmail/keyRing.jsm").EnigmailKeyRing;
/*global MsgHdrToMimeMessage: false, MimeMessage: false, MimeContainer: false, EnigmailStreams: false */
@@ -70,7 +70,7 @@ test(withTestGpgHome(withEnigmail(function messageIsMovedAndDecrypted() {
loadSecretKey();
MailHelper.cleanMailFolder(MailHelper.rootFolder);
const sourceFolder = MailHelper.createMailFolder("source-box");
- MailHelper.loadEmailToMailFolder("resources/encrypted-email.eml", sourceFolder);
+ MailHelper.loadEmailToMailFolder("resources/encrypted-pgpmime-email.eml", sourceFolder);
const header = MailHelper.fetchFirstMessageHeaderIn(sourceFolder);
const targetFolder = MailHelper.createMailFolder("target-box");
@@ -85,20 +85,29 @@ test(withTestGpgHome(withEnigmail(function messageIsMovedAndDecrypted() {
const dispatchedHeader = MailHelper.fetchFirstMessageHeaderIn(targetFolder);
Assert.ok(dispatchedHeader !== null);
+
+ let msgUriSpec = dispatchedHeader.folder.getUriForMsg(dispatchedHeader);
+ const msgSvc = Cc["@mozilla.org/messenger;1"].createInstance(Ci.nsIMessenger).messageServiceFromURI(msgUriSpec);
+
+ let urlObj = {};
+ msgSvc.GetUrlForUri(msgUriSpec, urlObj, null);
+
do_test_pending();
- MsgHdrToMimeMessage(
- dispatchedHeader,
- null,
- function(header, mime) {
- Assert.ok(!mime.isEncrypted);
- Assert.assertContains(mime.parts[0].body, "This is encrypted");
+ EnigmailMime.getMimeTreeFromUrl(
+ urlObj.value.spec,
+ true,
+ function(mimeTree) {
+ Assert.equal(mimeTree.subParts.length, 1);
+ if (mimeTree.subParts.length > 0) {
+ Assert.assertContains(mimeTree.subParts[0].body, "This message is encrypted");
+ }
do_test_finished();
},
false
);
})));
-/*
+
test(withTestGpgHome(withEnigmail(function messageWithAttachemntIsMovedAndDecrypted() {
loadSecretKey();
loadPublicKey();
@@ -118,23 +127,71 @@ test(withTestGpgHome(withEnigmail(function messageWithAttachemntIsMovedAndDecryp
const dispatchedHeader = MailHelper.fetchFirstMessageHeaderIn(targetFolder);
Assert.ok(dispatchedHeader !== null);
+ let msgUriSpec = dispatchedHeader.folder.getUriForMsg(dispatchedHeader);
+ const msgSvc = Cc["@mozilla.org/messenger;1"].createInstance(Ci.nsIMessenger).messageServiceFromURI(msgUriSpec);
+
+ let urlObj = {};
+ msgSvc.GetUrlForUri(msgUriSpec, urlObj, null);
+
do_test_pending();
- MsgHdrToMimeMessage(
- dispatchedHeader,
- null,
- function(header, mime) {
- Assert.ok(!mime.isEncrypted);
- Assert.assertContains(mime.parts[0].parts[0].body, "This is encrypted");
- const atts = extractAttachments(mime);
- Assert.ok(!atts[0].isEncrypted);
- Assert.assertContains(atts[0].body, "This is an attachment.");
+ EnigmailMime.getMimeTreeFromUrl(
+ urlObj.value.spec,
+ true,
+ function(mimeTree) {
+ Assert.assertContains(mimeTree.subParts[0].body, "This is encrypted");
+ Assert.equal(mimeTree.subParts.length, 2);
+ if (mimeTree.subParts.length >= 2) {
+ Assert.assertContains(mimeTree.subParts[1].body, "This is an attachment.");
+ }
do_test_finished();
},
false
);
})));
-*/
+test(withTestGpgHome(withEnigmail(function messageWithAttachemntIsMovedAndReEncrypted() {
+ loadSecretKey();
+ loadPublicKey();
+ MailHelper.cleanMailFolder(MailHelper.getRootFolder());
+ const sourceFolder = MailHelper.createMailFolder("source-box");
+ MailHelper.loadEmailToMailFolder("resources/encrypted-email-with-attachment.eml", sourceFolder);
+
+ const header = MailHelper.fetchFirstMessageHeaderIn(sourceFolder);
+ const targetFolder = MailHelper.createMailFolder("target-box");
+ const move = true;
+ copyListener.OnStopCopy = function(statusCode) {
+ inspector.exitNestedEventLoop();
+ };
+
+ let keyObj = EnigmailKeyRing.getKeyById("0x65537E212DC19025AD38EDB2781617319CE311C4");
+ EnigmailPersistentCrypto.dispatchMessages([header], targetFolder.URI, copyListener, move, keyObj);
+ inspector.enterNestedEventLoop(0);
+
+ const dispatchedHeader = MailHelper.fetchFirstMessageHeaderIn(targetFolder);
+ Assert.ok(dispatchedHeader !== null);
+
+ let msgUriSpec = dispatchedHeader.folder.getUriForMsg(dispatchedHeader);
+ const msgSvc = Cc["@mozilla.org/messenger;1"].createInstance(Ci.nsIMessenger).messageServiceFromURI(msgUriSpec);
+
+ let urlObj = {};
+ msgSvc.GetUrlForUri(msgUriSpec, urlObj, null);
+
+ do_test_pending();
+ EnigmailMime.getMimeTreeFromUrl(
+ urlObj.value.spec,
+ true,
+ function(mimeTree) {
+ Assert.assertContains(mimeTree.headers._rawHeaders.get("content-type")[0], "multipart/encrypted");
+ Assert.assertContains(mimeTree.subParts[0].body, "Version: 1");
+ Assert.equal(mimeTree.subParts.length, 2);
+ if (mimeTree.subParts.length >= 2) {
+ Assert.assertContains(mimeTree.subParts[1].body, "---BEGIN PGP MESSAGE---");
+ }
+ do_test_finished();
+ },
+ false
+ );
+})));
var loadSecretKey = function() {
const secretKey = do_get_file("resources/dev-strike.sec", false);
diff --git a/package/tests/resources/encrypted-pgpmime-email.eml b/package/tests/resources/encrypted-pgpmime-email.eml
new file mode 100644
index 00000000..f3c8800b
--- /dev/null
+++ b/package/tests/resources/encrypted-pgpmime-email.eml
@@ -0,0 +1,44 @@
+To: Tester 2 <[email protected]>
+From: Tester 1 <[email protected]>
+Message-ID: <[email protected]>
+Date: Tue, 19 Jun 2018 23:39:32 +0530
+User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:52.0)
+ Gecko/20100101 Thunderbird/52.8.0
+MIME-Version: 1.0
+Subject: Encrypted Message
+Content-Type: multipart/encrypted;
+ protocol="application/pgp-encrypted";
+ boundary="UxEMgGKKbt9SDjSozkXfqI0l07sqCV5I4"
+
+This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156)
+--UxEMgGKKbt9SDjSozkXfqI0l07sqCV5I4
+Content-Type: application/pgp-encrypted
+Content-Description: PGP/MIME version identification
+
+Version: 1
+
+--UxEMgGKKbt9SDjSozkXfqI0l07sqCV5I4
+Content-Type: application/octet-stream; name="encrypted.asc"
+Content-Description: OpenPGP encrypted message
+Content-Disposition: inline; filename="encrypted.asc"
+
+-----BEGIN PGP MESSAGE-----
+
+hQIMA9U1Yju2Dp5xAQ/6AnDaC/oxD65YSD6I1e5JX3vAzOgB0TL4eTPabl/KZJ6K
+8eAWiZubWF7i/EWlEXOYZbWycZC0Vo0mUlSiye4EXB/dnY369r/2UIF6kAEW0d7J
+LwMbmg0WMcL+liZSa7HyxajYn8Zb7CohNI4l41KjjYGAbKiJNaK442eEvAQ3fuNp
+5/KvV10jwS6fGV3caouTa8aCT8r2U5FdxowuYGGvazLCfPI9aTnz84JgeTutZfW5
+B/yBBNDeRxmUEa9c3k+8aH9c5DKYBH4Chp5EMfvlTE/pZdhRd2RcTZsR637FQVgO
+FpTHKvOqQNJla4WqGeH9iHOLJh9OAhHkRaiMbbo+072KfwNHqDD4iwjQ04jFIph1
+zImMJg79PTgSsjcTRTdvkQiHaIx7pVW1m212JEl4km1lmT/C6NCHxTYfNLooEAs1
+6YPbaARnbFTTWjJXfEto7s2PZoX1pZ7GG1MNVpAL6J/puUnmVktj+87bJLkxHQVu
+wf3Qq+vcFqB5t63quPMbGbXnHh4P9V+ceWhUhNFSutKWcCmeQL4vxCtL9eOphHEb
+1YDHb4FpSlpVJyVUmLh7g/1X3IMCS1HKpiIqJHAUu4FfouAprL5CAfFmCeCRMkqs
+/4iR3KtpnwRPwNDgY8Gfy7CL0IdvWZdpyLFaFOPr604Kz2JsJwyxbu1opM3acILS
+bgFYYz3p3HwqnGdbnch3+5H3XEToOaeXsH51zSUkR/oq/wNXDWKMKiU5hIUkHYq1
+NsC04Y4EHsbpDPo/GqkcdXEx7Dg0sgohqYqoU+iyq+jjyOEZxNEUdboMadxIm879
+nu672xjC+wg8rBvFr8jh
+=q7pm
+-----END PGP MESSAGE-----
+
+--UxEMgGKKbt9SDjSozkXfqI0l07sqCV5I4--