aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPatrick Brunschwig <[email protected]>2019-06-14 21:49:26 +0200
committerPatrick Brunschwig <[email protected]>2019-06-14 21:49:26 +0200
commit74648654b0fede31f59e79a01df19917d6c16e3b (patch)
tree4b3458885bff1c48c1a71b8346e56dee2d95ab81
parent349a641892b7f2834f0662a3e703624de45d673d (diff)
downloadenigmail-74648654b0fede31f59e79a01df19917d6c16e3b.tar.gz
enigmail-74648654b0fede31f59e79a01df19917d6c16e3b.tar.bz2
enigmail-74648654b0fede31f59e79a01df19917d6c16e3b.zip
new implementation of reparing Exchange messages using JSMime lib
instead of the Gloda MIME parser.
-rw-r--r--package/fixExchangeMsg.jsm86
-rw-r--r--package/mime.jsm2
-rw-r--r--package/persistentCrypto.jsm1050
3 files changed, 397 insertions, 741 deletions
diff --git a/package/fixExchangeMsg.jsm b/package/fixExchangeMsg.jsm
index 4792a128..f5246a1e 100644
--- a/package/fixExchangeMsg.jsm
+++ b/package/fixExchangeMsg.jsm
@@ -98,46 +98,17 @@ var EnigmailFixExchangeMsg = {
EnigmailLog.DEBUG("*** start data ***\n'" + data + "'\n***end data***\n");
}
- self.determineCreatorApp(data);
+ try {
+ let msg = self.getRepairedMessage(data);
- let hdrEnd = data.search(/\r?\n\r?\n/);
-
- if (hdrEnd <= 0) {
- // cannot find end of header data
- reject(0);
- return;
- }
-
- let hdrLines = data.substr(0, hdrEnd).split(/\r?\n/);
- let hdrObj = self.getFixedHeaderData(hdrLines);
-
- if (hdrObj.headers.length === 0 || hdrObj.boundary.length === 0) {
- reject(1);
+ if (msg) {
+ resolve(msg);
+ } else
+ reject(2);
return;
- }
- let boundary = hdrObj.boundary;
- let body;
-
- switch (self.brokenByApp) {
- case "exchange":
- body = self.getCorrectedExchangeBodyData(data.substr(hdrEnd + 2), boundary);
- break;
- case "iPGMail":
- body = self.getCorrectediPGMailBodyData(data.substr(hdrEnd + 2), boundary);
- break;
- default:
- EnigmailLog.ERROR("fixExchangeMsg.jsm: getMessageBody: unknown appType " + self.brokenByApp + "\n");
- reject(99);
- return;
- }
-
- if (body) {
- resolve(hdrObj.headers + "\r\n" + body);
- return;
- } else {
- reject(2);
- return;
+ } catch (ex) {
+ reject(ex);
}
}
);
@@ -153,6 +124,45 @@ var EnigmailFixExchangeMsg = {
);
},
+ getRepairedMessage: function(data) {
+ this.determineCreatorApp(data);
+
+ let hdrEnd = data.search(/\r?\n\r?\n/);
+
+ if (hdrEnd <= 0) {
+ // cannot find end of header data
+ throw 0;
+ }
+
+ let hdrLines = data.substr(0, hdrEnd).split(/\r?\n/);
+ let hdrObj = this.getFixedHeaderData(hdrLines);
+
+ if (hdrObj.headers.length === 0 || hdrObj.boundary.length === 0) {
+ throw 1;
+ }
+
+ let boundary = hdrObj.boundary;
+ let body;
+
+ switch (this.brokenByApp) {
+ case "exchange":
+ body = this.getCorrectedExchangeBodyData(data.substr(hdrEnd + 2), boundary);
+ break;
+ case "iPGMail":
+ body = this.getCorrectediPGMailBodyData(data.substr(hdrEnd + 2), boundary);
+ break;
+ default:
+ EnigmailLog.ERROR("fixExchangeMsg.jsm: getRepairedMessage: unknown appType " + self.brokenByApp + "\n");
+ throw 99;
+ }
+
+ if (body) {
+ return hdrObj.headers + "\r\n" + body;
+ } else {
+ throw 2;
+ }
+ },
+
determineCreatorApp: function(msgData) {
// perform extra testing if iPGMail is assumed
if (this.brokenByApp === "exchange") return;
@@ -347,7 +357,7 @@ var EnigmailFixExchangeMsg = {
let p0 = body.search(/^-----BEGIN PGP MESSAGE-----$/m);
let p1 = body.search(/^-----END PGP MESSAGE-----$/m);
- ok = (p0 >= 0 && p1 > p0 + 4);
+ ok = (p0 >= 0 && p1 > p0 + 32);
}
return ok;
} catch (x) {}
diff --git a/package/mime.jsm b/package/mime.jsm
index d44f371a..71b937c1 100644
--- a/package/mime.jsm
+++ b/package/mime.jsm
@@ -143,7 +143,7 @@ var EnigmailMime = {
header = hdrValue.join("").split(" ");
}
else {
- header = hdrValue.split(" ");
+ header = hdrValue.split(/ +/);
}
let line = "";
diff --git a/package/persistentCrypto.jsm b/package/persistentCrypto.jsm
index 6baf0756..ebcfcdd7 100644
--- a/package/persistentCrypto.jsm
+++ b/package/persistentCrypto.jsm
@@ -10,7 +10,6 @@
var EXPORTED_SYMBOLS = ["EnigmailPersistentCrypto"];
const EnigmailLazy = ChromeUtils.import("chrome://enigmail/content/modules/lazy.jsm").EnigmailLazy;
-const AddonManager = ChromeUtils.import("resource://gre/modules/AddonManager.jsm").AddonManager;
const EnigmailLog = ChromeUtils.import("chrome://enigmail/content/modules/log.jsm").EnigmailLog;
const EnigmailArmor = ChromeUtils.import("chrome://enigmail/content/modules/armor.jsm").EnigmailArmor;
const EnigmailLocale = ChromeUtils.import("chrome://enigmail/content/modules/locale.jsm").EnigmailLocale;
@@ -20,27 +19,15 @@ const EnigmailTb60Compat = ChromeUtils.import("chrome://enigmail/content/modules
const EnigmailCore = ChromeUtils.import("chrome://enigmail/content/modules/core.jsm").EnigmailCore;
const EnigmailGpg = ChromeUtils.import("chrome://enigmail/content/modules/gpg.jsm").EnigmailGpg;
const EnigmailStreams = ChromeUtils.import("chrome://enigmail/content/modules/streams.jsm").EnigmailStreams;
-const EnigmailPassword = ChromeUtils.import("chrome://enigmail/content/modules/passwords.jsm").EnigmailPassword;
const EnigmailMime = ChromeUtils.import("chrome://enigmail/content/modules/mime.jsm").EnigmailMime;
const EnigmailData = ChromeUtils.import("chrome://enigmail/content/modules/data.jsm").EnigmailData;
-const EnigmailAttachment = ChromeUtils.import("chrome://enigmail/content/modules/attachment.jsm").EnigmailAttachment;
const EnigmailTimer = ChromeUtils.import("chrome://enigmail/content/modules/timer.jsm").EnigmailTimer;
const EnigmailConstants = ChromeUtils.import("chrome://enigmail/content/modules/constants.jsm").EnigmailConstants;
const jsmime = ChromeUtils.import("resource:///modules/jsmime.jsm").jsmime;
const EnigmailStdlib = ChromeUtils.import("chrome://enigmail/content/modules/stdlib.jsm").EnigmailStdlib;
const EnigmailEncryption = ChromeUtils.import("chrome://enigmail/content/modules/encryption.jsm").EnigmailEncryption;
-const NetUtil = ChromeUtils.import("resource://gre/modules/NetUtil.jsm").NetUtil;
-
-const {
- MimeBody,
- MimeUnknown,
- MimeMessageAttachment,
- MsgHdrToMimeMessage,
- MimeMessage,
- MimeContainer
-} = ChromeUtils.import("resource:///modules/gloda/mimemsg.js");
-
-const getGpgAgent = EnigmailLazy.loader("enigmail/gpgAgent.jsm", "EnigmailGpgAgent");
+
+const getFixExchangeMsg = EnigmailLazy.loader("enigmail/fixExchangeMsg.jsm", "EnigmailFixExchangeMsg");
const getDecryption = EnigmailLazy.loader("enigmail/decryption.jsm", "EnigmailDecryption");
const getDialog = EnigmailLazy.loader("enigmail/dialog.jsm", "EnigmailDialog");
@@ -87,12 +74,19 @@ var EnigmailPersistentCrypto = {
dispatchMessages: function(aMsgHdrs, targetFolder, copyListener, move, targetKey) {
EnigmailLog.DEBUG("persistentCrypto.jsm: dispatchMessages()\n");
+ let enigmailSvc = EnigmailCore.getService();
+ if (copyListener && !enigmailSvc) {
+ // could not initiate Enigmail - do nothing
+ copyListener.OnStopCopy(0);
+ return;
+ }
+
if (copyListener) {
copyListener.OnStartCopy();
}
let promise = EnigmailPersistentCrypto.cryptMessage(aMsgHdrs[0], targetFolder, move, targetKey);
- var processNext = function(data) {
+ let processNext = function(data) {
aMsgHdrs.splice(0, 1);
if (aMsgHdrs.length > 0) {
EnigmailPersistentCrypto.dispatchMessages(aMsgHdrs, targetFolder, copyListener, move, targetKey);
@@ -131,15 +125,21 @@ var EnigmailPersistentCrypto = {
const msgSvc = Cc["@mozilla.org/messenger;1"].createInstance(Ci.nsIMessenger).messageServiceFromURI(msgUriSpec);
+ let urlObj = {};
+ msgSvc.GetUrlForUri(msgUriSpec, urlObj, null);
+
+ let msgUrl = urlObj.value.spec;
+
+
const crypt = new CryptMessageIntoFolder(destFolder, move, resolve, targetKey);
try {
- MsgHdrToMimeMessage(hdr, crypt, crypt.messageParseCallback, true, {
- examineEncryptedParts: false,
- partsOnDemand: false
- });
+ EnigmailMime.getMimeTreeFromUrl(msgUrl, true,
+ function f_(mime) {
+ crypt.messageParseCallback(mime, hdr);
+ });
} catch (ex) {
- reject("msgHdrsDeleteoMimeMessage failed");
+ reject("msgHdrsDeleteoMimeMessage failed: " + ex.toString());
}
return;
}
@@ -152,267 +152,51 @@ function CryptMessageIntoFolder(destFolder, move, resolve, targetKey) {
this.move = move;
this.resolve = resolve;
this.targetKey = targetKey;
+ this.messageDecrypted = false;
- this.foundPGP = 0;
- this.mime = null;
- this.hdr = null;
+ this.mimeTree = null;
this.decryptionTasks = [];
this.subject = "";
}
CryptMessageIntoFolder.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'));
+ messageParseCallback: async function(mimeTree, msgHdr) {
+ this.mimeTree = mimeTree;
+ this.hdr = msgHdr;
+ await this.decryptMimeTree(mimeTree);
- if (!ct) {
- this.resolve(true);
- return;
- }
-
-
- this.walkMimeTree(this.mime, this.mime);
+ let msg = "";
- this.decryptINLINE(this.mime);
- if (this.foundPGP < 0) {
- // decryption failed
+ // Encrypt the message if a target key is given.
+ if (this.targetKey) {
+ msg = this.encryptToKey(mimeTree);
+ if (!msg) {
+ // do nothing (still better than destroying the message)
this.resolve(true);
return;
+ } else {
+ this.messageDecrypted = true;
}
-
-
- for (let i in this.mime.allAttachments) {
- let a = this.mime.allAttachments[i];
- let suffixIndexEnd = a.name.toLowerCase().lastIndexOf('.pgp');
- if (suffixIndexEnd < 0) {
- suffixIndexEnd = a.name.toLowerCase().lastIndexOf('.asc');
- }
-
- if (suffixIndexEnd > 0 &&
- a.contentType.search(/application\/pgp-signature/i) < 0) {
-
- // possible OpenPGP attachment
- let p = self.decryptAttachment(a, a.name.substring(0, suffixIndexEnd));
- this.decryptionTasks.push(p);
- } else {
- let p = this.readAttachment(a);
- this.decryptionTasks.push(p);
- }
- }
-
- Promise.all(this.decryptionTasks).then(
- function(tasks) {
- self.allTasks = tasks;
- for (let a in tasks) {
- switch (tasks[a].status) {
- case STATUS_NOT_REQUIRED:
- tasks[a].name = tasks[a].origName;
- break;
- case STATUS_OK:
- ++self.foundPGP;
- break;
- case STATUS_FAILURE:
- // attachment did not decrypt successfully
- self.resolve(true);
- return;
- default:
- // no valid result?!
- tasks[a].name = tasks[a].origName;
- }
- }
-
- if (self.foundPGP === 0 && !self.targetKey) {
- self.resolve(true);
- return;
- }
-
- // No dest folder. Let's use the same folder.
- if (!self.destFolder) {
- // We need to use the URI and not the folderURL folderURL
- // would work for IMAP but fail for Local Folders.
- self.destFolder = hdr.folder.URI;
- }
-
- let rfc822Headers;
- if (self.targetKey) {
- // If we encrypt we don't want to include all headers
- // int the encrypted message. So we only pass
- // content headers and store the rest.
- rfc822Headers = mime.headers;
- var contentHeaders = [];
-
- contentHeaders["content-type"] = getHeaderValue(self.mime, 'content-type');
- contentHeaders["content-transfer-encoding"] = getHeaderValue(self.mime, 'content-transfer-encoding');
- contentHeaders["content-disposition"] = getHeaderValue(self.mime, 'content-disposition');
- self.mime.headers = contentHeaders;
- }
-
- // Build the new message
- let msg = "";
- if (self.foundPGP) {
- // A decrypted message
- msg = self.mimeToString(self.mime, true);
- } else {
- // Not found pgp. Copy the msg for encryption
- // we avoid mimeToString as mimeToString is not
- // really a direct conversion but has awareness of
- // crypto tasks and will not work properly for messages
- // that are not encrypted.
- EnigmailLog.DEBUG("persistentCrypto.jsm: did not find encryption. Using original.\n");
- var folder = hdr.folder;
- var stream = folder.getMsgInputStream(hdr, {});
-
- var messageSize = folder.hasMsgOffline(hdr.messageKey) ? hdr.offlineMessageSize : hdr.messageSize;
- var scriptInput = Components.classes["@mozilla.org/scriptableinputstream;1"].createInstance();
- try {
- msg = NetUtil.readInputStreamToString(stream, messageSize);
- } catch (ex) {
- EnigmailLog.DEBUG("persistentCrypto.jsm: failed to get plain data: " + ex + "\n");
- // Uhm,.. What to do? Ok let's give mimeToString a chance.
- msg = self.mimeToString(self.mime, true);
- }
- stream.close();
- }
-
- if (!msg || msg === "") {
- // no message data found
- self.resolve(true);
- return;
- }
-
- // Encrypt the message if a target key is given.
- if (self.targetKey) {
- msg = self.encryptToKey(rfc822Headers, msg);
- if (!msg) {
- // do nothing (still better than destroying the message)
- self.resolve(true);
- return;
- }
- }
-
- //XXX Do we wanna use the tmp for this?
- var tempFile = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties).get("TmpD", Ci.nsIFile);
- tempFile.append("message.eml");
- tempFile.createUnique(0, 0o600);
-
- // ensure that file gets deleted on exit, if something goes wrong ...
- var extAppLauncher = Cc["@mozilla.org/mime;1"].getService(Ci.nsPIExternalAppLauncher);
-
- var foStream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
- foStream.init(tempFile, 2, 0x200, false); // open as "write only"
- foStream.write(msg, msg.length);
- foStream.close();
-
- extAppLauncher.deleteTemporaryFileOnExit(tempFile);
-
- //
- // This was taken from the HeaderToolsLite Example Addon "original by Frank DiLecce"
- //
- // this is interesting: nsIMsgFolder.copyFileMessage seems to have a bug on Windows, when
- // the nsIFile has been already used by foStream (because of Windows lock system?), so we
- // must initialize another nsIFile object, pointing to the temporary file
- var fileSpec = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
- fileSpec.initWithPath(tempFile.path);
-
- const copySvc = Cc["@mozilla.org/messenger/messagecopyservice;1"].getService(Ci.nsIMsgCopyService);
-
- var copyListener = {
- QueryInterface: function(iid) {
- if (iid.equals(Ci.nsIMsgCopyServiceListener) || iid.equals(Ci.nsISupports)) {
- return this;
- }
- EnigmailLog.DEBUG("persistentCrypto.jsm: copyListener error\n");
- throw Components.results.NS_NOINTERFACE;
- },
- GetMessageId: function(messageId) {},
- OnProgress: function(progress, progressMax) {},
- OnStartCopy: function() {
- EnigmailLog.DEBUG("persistentCrypto.jsm: copyListener: OnStartCopy()\n");
- },
- SetMessageKey: function(key) {
- EnigmailLog.DEBUG("persistentCrypto.jsm: copyListener: SetMessageKey(" + key + ")\n");
- },
- OnStopCopy: function(statusCode) {
- EnigmailLog.DEBUG("persistentCrypto.jsm: copyListener: OnStopCopy()\n");
- if (statusCode !== 0) {
- EnigmailLog.DEBUG("persistentCrypto.jsm: Error copying message: " + statusCode + "\n");
- try {
- tempFile.remove(false);
- } catch (ex) {
- try {
- fileSpec.remove(false);
- } catch (e2) {
- EnigmailLog.DEBUG("persistentCrypto.jsm: Could not delete temp file\n");
- }
- }
- self.resolve(true);
- return;
- }
- EnigmailLog.DEBUG("persistentCrypto.jsm: Copy complete\n");
-
- if (self.move) {
- deleteOriginalMail(self.hdr);
- }
-
- try {
- tempFile.remove(false);
- } catch (ex) {
- try {
- fileSpec.remove(false);
- } catch (e2) {
- EnigmailLog.DEBUG("persistentCrypto.jsm: Could not delete temp file\n");
- }
- }
-
- EnigmailLog.DEBUG("persistentCrypto.jsm: Cave Johnson. We're done\n");
- self.resolve(true);
- }
- };
-
- EnigmailLog.DEBUG("persistentCrypto.jsm: copySvc ready for copy\n");
- try {
- if (self.mime.headers.subject) {
- self.hdr.subject = self.mime.headers.subject.join();
- }
- } catch (ex) {}
-
- copySvc.CopyFileMessage(fileSpec, EnigmailTb60Compat.getExistingFolder(self.destFolder), self.hdr,
- false, 0, "", copyListener, null);
- }
- ).catch(
- function catchErr(errorMsg) {
- EnigmailLog.DEBUG("persistentCrypto.jsm: Promise.catchErr: " + errorMsg + "\n");
- self.resolve(false);
- }
- );
- } catch (ex) {
- EnigmailLog.DEBUG("persistentCrypto.jsm: messageParseCallback: caught error " + ex.toString() + "\n");
- self.resolve(false);
+ } else if (this.messageDecrypted) {
+ msg = this.mimeToString(mimeTree, true);
}
+
+ if (this.messageDecrypted) {
+ this.resolve(await this.storeMessage(msg));
+ } else
+ this.resolve(true);
},
- encryptToKey: function(rfc822Headers, inputMsg) {
+ encryptToKey: function(mimeTree) {
let exitCodeObj = {};
let statusFlagsObj = {};
let errorMsgObj = {};
EnigmailLog.DEBUG("persistentCrypto.jsm: Encrypting message.\n");
+
+ let inputMsg = this.mimeToString(mimeTree, false);
+
+
let encmsg = "";
try {
encmsg = EnigmailEncryption.encryptMessage(null,
@@ -434,6 +218,8 @@ CryptMessageIntoFolder.prototype = {
// Build the pgp-encrypted mime structure
let msg = "";
+ let rfc822Headers = []; // FIXME
+
// First the original headers
for (let header in rfc822Headers) {
if (header != "content-type" &&
@@ -484,7 +270,7 @@ CryptMessageIntoFolder.prototype = {
type: "attachment",
data: data,
name: strippedName ? strippedName : attachment.name,
- partName: attachment.partName,
+ partNum: attachment.partNum,
origName: attachment.name,
status: STATUS_NOT_REQUIRED
};
@@ -505,138 +291,25 @@ CryptMessageIntoFolder.prototype = {
);
},
- decryptAttachment: function(attachment, strippedName) {
- var self = this;
-
- return new Promise(
- function(resolve, reject) {
- EnigmailLog.DEBUG("persistentCrypto.jsm: decryptAttachment\n");
- self.readAttachment(attachment, strippedName).then(
- function(o) {
- var attachmentHead = o.data.substr(0, 30);
- if (attachmentHead.match(/-----BEGIN PGP \w{5,10} 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 & 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", [attachment.name, self.subject]);
-
- if (!getDialog().confirmDlg(null, msg,
- EnigmailLocale.getString("dlg.button.retry"), EnigmailLocale.getString("dlg.button.skip"))) {
- o.status = STATUS_FAILURE;
- resolve(o);
- return;
- }
- } else if (statusFlagsObj.value & EnigmailConstants.DECRYPTION_INCOMPLETE) {
- // failure; message not complete
- EnigmailLog.DEBUG("persistentCrypto.jsm: decryptAttachment: decryption incomplete\n");
- o.status = STATUS_FAILURE;
- resolve(o);
- return;
- } else {
- // there is nothing to be decrypted
- EnigmailLog.DEBUG("persistentCrypto.jsm: decryptAttachment: no decryption required\n");
- o.status = STATUS_NOT_REQUIRED;
- resolve(o);
- return;
- }
-
- } while (exitCode !== 0);
-
-
- EnigmailLog.DEBUG("persistentCrypto.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
+ /**
+ * Walk through the MIME message structure and decrypt the body if there is something to decrypt
*/
+ decryptMimeTree: async function(mimePart) {
+ EnigmailLog.DEBUG("persistentCrypto.jsm: decryptMimeTree:\n");
- // the sunny world of PGP/MIME
-
- walkMimeTree: function(mime, parent) {
- EnigmailLog.DEBUG("persistentCrypto.jsm: walkMimeTree:\n");
- let ct = getContentType(getHeaderValue(mime, 'content-type'));
-
- EnigmailLog.DEBUG("persistentCrypto.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.isBrokenByExchange(mimePart)) {
+ this.fixExchangeMessage(mimePart);
}
- if (this.isPgpMime(mime) || this.isSMime(mime)) {
- 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");
- mime.isBrokenByExchange = true;
- mime.parts[0].parts[2].name = "ignore.txt";
- this.decryptionTasks.push(p);
- } else if (typeof(mime.body) == "string") {
- EnigmailLog.DEBUG(" body size: " + mime.body.length + "\n");
+
+ if (this.isPgpMime(mimePart)) {
+ this.decryptPGPMIME(mimePart);
+ } else {
+ this.decryptINLINE(mimePart);
}
- for (var i in mime.parts) {
- this.walkMimeTree(mime.parts[i], mime);
+ for (let i in mimePart.subParts) {
+ await this.decryptMimeTree(mimePart.subParts[i]);
}
},
@@ -649,24 +322,23 @@ CryptMessageIntoFolder.prototype = {
* - application/octet-stream Attachment with name "encrypted.asc" having the encrypted content in base64
* - see:
* - https://www.enigmail.net/forum/viewtopic.php?f=4&t=425
- * - https://sourceforge.net/p/enigmail/forum/support/thread/4add2b69/
+ * - https://sourceforge.net/p/enigmail/forum/support/thread/4add2b69/
*/
isBrokenByExchange: function(mime) {
EnigmailLog.DEBUG("persistentCrypto.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) {
+ if (mime.subParts && mime.subParts.length === 3 &&
+ mime.fullContentType.toLowerCase().indexOf("multipart/mixed") >= 0 &&
+ mime.subParts[0].subParts.length === 0 &&
+ mime.subParts[0].fullContentType.search(/multipart\/encrypted/i) < 0 &&
+ mime.subParts[0].fullContentType.toLowerCase().indexOf("text/plain") >= 0 &&
+ mime.subParts[1].fullContentType.toLowerCase().indexOf("application/pgp-encrypted") >= 0 &&
+ mime.subParts[1].fullContentType.toLowerCase().search(/multipart\/encrypted/i) < 0 &&
+ mime.subParts[1].fullContentType.toLowerCase().search(/PGPMIME Versions? Identification/i) >= 0 &&
+ mime.subParts[2].fullContentType.toLowerCase().indexOf("application/octet-stream") >= 0 &&
+ mime.subParts[2].fullContentType.toLowerCase().indexOf("encrypted.asc") >= 0) {
EnigmailLog.DEBUG("persistentCrypto.jsm: isBrokenByExchange: found message broken by MS-Exchange\n");
return true;
@@ -676,180 +348,131 @@ CryptMessageIntoFolder.prototype = {
return false;
},
- isPgpMime: function(mime) {
- EnigmailLog.DEBUG("persistentCrypto.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("persistentCrypto.jsm: isPgpMime:"+ex+"\n");
- }
- return false;
- },
-
- // smime-type=enveloped-data
- isSMime: function(mime) {
- EnigmailLog.DEBUG("persistentCrypto.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;
+ isPgpMime: function(mimePart) {
+ EnigmailLog.DEBUG("persistentCrypto.jsm: isPgpMime()\n");
- if (ct.toLowerCase() == "application/pkcs7-mime" && pt == "enveloped-data") {
+ if (mimePart.headers.has("content-type")) {
+ if (mimePart.headers.get("content-type").type.toLowerCase() === "multipart/encrypted" &&
+ mimePart.headers.get("content-type").get("protocol").toLowerCase() === "application/pgp-encrypted" &&
+ mimePart.subParts.length === 2) {
return true;
}
- } catch (ex) {
- EnigmailLog.DEBUG("persistentCrypto.jsm: isSMime:" + ex + "\n");
}
return false;
},
- decryptPGPMIME: function(mime, part) {
- EnigmailLog.DEBUG("persistentCrypto.jsm: decryptPGPMIME: part=" + part + "\n");
-
- var self = this;
+ decryptPGPMIME: async function(mimePart) {
+ EnigmailLog.DEBUG("persistentCrypto.jsm: decryptPGPMIME(" + mimePart.partNum + ")\n");
- return new Promise(
- function(resolve, reject) {
- var m = Cc["@mozilla.org/messenger/mimeheaders;1"].createInstance(Ci.nsIMimeHeaders);
+ if (!mimePart.subParts[1]) throw "Not a correct PGP/MIME message";
- 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);
+ const uiFlags = EnigmailConstants.UI_INTERACTIVE | EnigmailConstants.UI_UNVERIFIED_ENC_OK |
+ EnigmailConstants.UI_IGNORE_MDC_ERROR;
+ let exitCodeObj = {};
+ let statusFlagsObj = {};
+ let userIdObj = {};
+ let sigDetailsObj = {};
+ let errorMsgObj = {};
+ let keyIdObj = {};
+ let blockSeparationObj = {
+ value: ""
+ };
+ let encToDetailsObj = {};
+ var signatureObj = {};
+ signatureObj.value = "";
- let op = (u.value.spec.indexOf("?") > 0 ? "&" : "?");
- let url = u.value.spec + op + 'part=' + part + "&header=enigmailConvert";
- EnigmailLog.DEBUG("persistentCrypto.jsm: getting data from URL " + url + "\n");
+ let data = getDecryption().decryptMessage(null, uiFlags, mimePart.subParts[1].body, signatureObj, exitCodeObj, statusFlagsObj,
+ keyIdObj, userIdObj, sigDetailsObj, errorMsgObj, blockSeparationObj, encToDetailsObj);
- let s = EnigmailStreams.newStringStreamListener(
- function analyzeDecryptedData(data) {
- EnigmailLog.DEBUG("persistentCrypto.jsm: analyzeDecryptedData: got " + data.length + " bytes\n");
+ if (!data || data.length === 0) {
+ if (statusFlagsObj.value & EnigmailConstants.DISPLAY_MESSAGE) {
+ getDialog().alert(null, errorMsgObj.value);
+ throw "Decryption impossible";
+ }
+ }
- if (EnigmailLog.getLogLevel() > 5) {
- EnigmailLog.DEBUG("*** start data ***\n'" + data + "'\n***end data***\n");
- }
+ EnigmailLog.DEBUG("persistentCrypto.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];
+ if (data.length === 0) {
+ // fail if no data found
+ return;
+ }
- let o = {
- type: "mime",
- name: "",
- origName: "",
- data: "",
- partName: part,
- status: STATUS_OK
- };
+ let bodyIndex = data.search(/\n\s*\r?\n/);
+ if (bodyIndex < 0) {
+ bodyIndex = 0;
+ } else {
+ ++bodyIndex;
+ }
- if (data.length === 0) {
- // fail if no data found
- o.status = STATUS_FAILURE;
- resolve(o);
- return;
- }
+ if (data.substr(bodyIndex).search(/\r?\n$/) === 0) {
+ 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;
+ let m = Cc["@mozilla.org/messenger/mimeheaders;1"].createInstance(Ci.nsIMimeHeaders);
+ m.initialize(data.substr(0, bodyIndex));
+ let ct = m.extractHeader("content-type", false) || "";
+ let part = mimePart.partNum;
- }
- 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("") === "p≡p") {
- let subject = getPepSubject(data);
- if (subject) {
- self.mime.headers.subject = [subject];
- }
- }
- }
+ 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) || "";
+ this.mimeTree.headers._rawHeaders.set("subject", [subject]);
+ }
+ } else if (this.mimeTree.headers.get("subject") === "p≡p") {
+ let subject = getPepSubject(data);
+ if (subject) {
+ this.mimeTree.headers._rawHeaders.set("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 + "\r\n";
- o.data += "Content-Type: " + ct + "\r\n";
-
- let h = m.headerNames;
- while (h.hasMore()) {
- let hdr = h.getNext();
- if (hdr.search(/^content-type$/i) < 0) {
- try {
- EnigmailLog.DEBUG("persistentCrypto.jsm: getUnstructuredHeader: hdr=" + hdr + "\n");
- let hdrVal = m.getUnstructuredHeader(hdr.toLowerCase());
- o.data += hdr + ": " + hdrVal + "\r\n";
- } catch (ex) {
- try {
- let hdrVal = m.getRawHeader(hdr.toLowerCase());
- o.data += hdr + ": " + hdrVal + "\r\n";
- } catch (ex) {
- EnigmailLog.DEBUG("persistentCrypto.jsm: getUnstructuredHeader: exception " + ex.toString() + "\n");
- }
- }
- }
- }
+ let boundary = getBoundary(mimePart);
+ if (!boundary)
+ boundary = EnigmailMime.createBoundary();
+
+ // append relevant headers
+ mimePart.headers.get("content-type").type = "multipart/mixed";
+ mimePart.headers._rawHeaders.set("content-type", ['multipart/mixed; boundary="' + boundary + '"']);
+ mimePart.subParts = [{
+ body: data,
+ decryptedPgpMime: true,
+ partNum: mimePart.partNum + ".1",
+ headers: {
+ _rawHeaders: new Map(),
+ get: function() {
+ return null;
+ },
+ has: function() {
+ return false;
+ }
+ },
+ subParts: []
+ }];
- EnigmailLog.DEBUG("persistentCrypto.jsm: getUnstructuredHeader: done\n");
- o.data += data.substr(bodyIndex);
- if (subpart) {
- subpart.body = undefined;
- subpart.headers['content-type'] = ct;
- }
+ this.messageDecrypted = true;
+ },
- resolve(o);
- }
- );
+ decryptINLINE: function(mimePart) {
+ EnigmailLog.DEBUG("persistentCrypto.jsm: decryptINLINE()\n");
- try {
- var channel = EnigmailStreams.createChannel(url);
- channel.asyncOpen(s, null);
- } catch (e) {
- EnigmailLog.DEBUG("persistentCrypto.jsm: decryptPGPMIME: exception " + e.toString() + "\n");
- }
- }
- );
- },
+ if (("decryptedPgpMime" in mimePart) && mimePart.decryptedPgpMime) {
+ return 0;
+ }
- //inline wonderland
- decryptINLINE: function(mime) {
- EnigmailLog.DEBUG("persistentCrypto.jsm: decryptINLINE:\n");
- if (typeof mime.body !== 'undefined') {
- let ct = getContentType(getHeaderValue(mime, 'content-type'));
+ if ("body" in mimePart && mimePart.body.length > 0) {
+ let ct = getContentType(mimePart);
- if (ct == "text/html") {
- mime.body = this.stripHTMLFromArmoredBlocks(mime.body);
+ if (ct === "text/html") {
+ mimePart.body = this.stripHTMLFromArmoredBlocks(mimePart.body);
}
var exitCodeObj = {};
@@ -869,7 +492,7 @@ CryptMessageIntoFolder.prototype = {
EnigmailConstants.UI_IGNORE_MDC_ERROR;
var plaintexts = [];
- var blocks = EnigmailArmor.locateArmoredBlocks(mime.body);
+ var blocks = EnigmailArmor.locateArmoredBlocks(mimePart.body);
var tmp = [];
for (let i = 0; i < blocks.length; i++) {
@@ -889,7 +512,7 @@ CryptMessageIntoFolder.prototype = {
for (let i = 0; i < blocks.length; i++) {
let plaintext = null;
do {
- let ciphertext = mime.body.substring(blocks[i].begin, blocks[i].end + 1);
+ let ciphertext = mimePart.body.substring(blocks[i].begin, blocks[i].end + 1);
if (ciphertext.length === 0) {
break;
@@ -907,7 +530,7 @@ CryptMessageIntoFolder.prototype = {
if (!plaintext || plaintext.length === 0) {
if (statusFlagsObj.value & EnigmailConstants.DISPLAY_MESSAGE) {
getDialog().alert(null, errorMsgObj.value);
- this.foundPGP = -1;
+ this.messageDecrypted = false;
return -1;
}
@@ -921,26 +544,33 @@ CryptMessageIntoFolder.prototype = {
if (!getDialog().confirmDlg(null, msg,
EnigmailLocale.getString("dlg.button.retry"), EnigmailLocale.getString("dlg.button.skip"))) {
- this.foundPGP = -1;
+ this.messageDecrypted = false;
return -1;
}
} else if (statusFlagsObj.value & EnigmailConstants.DECRYPTION_INCOMPLETE) {
- this.foundPGP = -1;
+ this.messageDecrypted = false;
return -1;
+ } else {
+ plaintext = " ";
}
}
- if (ct == "text/html") {
+ if (ct === "text/html") {
plaintext = plaintext.replace(/\n/ig, "<br/>\n");
}
- if (i == 0 && this.mime.headers.subject && this.mime.headers.subject[0] === "pEp" &&
- mime.partName.length > 0 && mime.partName.search(/[^01.]/) < 0) {
+ let subject = "";
+ if (this.mimeTree.headers.has("subject")) {
+ subject = this.mimeTree.headers.get("subject");
+ }
+
+ if (i == 0 && this.mimeTree.headers.subject === "pEp" &&
+ mimePart.partNum.length > 0 && mimePart.partNum.search(/[^01.]/) < 0) {
let m = EnigmailMime.extractSubjectFromBody(plaintext);
if (m) {
plaintext = m.messageBody;
- this.mime.headers.subject = [m.subject];
+ this.mimeTree.headers._rawHeaders.set("subject", [m.subject]);
}
}
@@ -952,46 +582,35 @@ CryptMessageIntoFolder.prototype = {
- var decryptedMessage = mime.body.substring(0, blocks[0].begin) + plaintexts[0];
+ var decryptedMessage = mimePart.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 += mimePart.body.substring(blocks[i - 1].end + 1, blocks[i].begin + 1) + plaintexts[i];
}
- decryptedMessage += mime.body.substring(blocks[(blocks.length - 1)].end + 1);
+ decryptedMessage += mimePart.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);
+ mimePart.headers._rawHeaders.set('content-transfer-encoding', ['base64']);
+ mimePart.body = EnigmailData.encodeBase64(decryptedMessage);
} else {
- mime.body = decryptedMessage;
- mime.headers['content-transfer-encoding'] = ['8bit'];
+ mimePart.body = decryptedMessage;
+ mimePart.headers._rawHeaders.set('content-transfer-encoding', ['8bit']);
}
- let origCharset = getCharset(getHeaderValue(mime, 'content-type'));
+ let origCharset = getCharset(getHeaderValue(mimePart, 'content-type'));
if (origCharset) {
- mime.headers['content-type'] = getHeaderValue(mime, 'content-type').replace(origCharset, charset);
+ mimePart.headers_rawHeaders.set('content-type', getHeaderValue(mimePart, 'content-type').replace(origCharset, charset));
} else {
- mime.headers['content-type'] = getHeaderValue(mime, 'content-type') + "; charset=" + charset;
+ mimePart.headers._rawHeaders.set('content-type', getHeaderValue(mimePart, 'content-type') + "; charset=" + charset);
}
- this.foundPGP = 1;
+ this.messageDecrypted = true;
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'));
+ let ct = getContentType(mimePart);
EnigmailLog.DEBUG("persistentCrypto.jsm: Decryption skipped: " + ct + "\n");
return 0;
@@ -1029,130 +648,156 @@ CryptMessageIntoFolder.prototype = {
*
******/
- mimeToString: function(mime, topLevel) {
- EnigmailLog.DEBUG("persistentCrypto.jsm: mimeToString: part: '" + mime.partName + "', is of type '" + typeof(mime) + "'\n");
+ mimeToString: function(mimePart, includeHeaders) {
+ EnigmailLog.DEBUG("persistentCrypto.jsm: mimeToString: part: '" + mimePart.partNum + "'\n");
+
+ let msg = "";
+ let rawHdr = mimePart.headers._rawHeaders;
- let ct = getContentType(getHeaderValue(mime, 'content-type'));
+ if (includeHeaders && rawHdr.size > 0) {
+ for (let hdr of rawHdr.keys()) {
+ msg += formatMimeHeader(hdr, rawHdr.get(hdr)) + "\r\n";
+ }
- if (!ct) {
- return "";
+ msg += "\r\n";
}
- let boundary = getBoundary(getHeaderValue(mime, 'content-type'));
+ if (mimePart.body.length > 0) {
+ msg += mimePart.body;
+ }
- let msg = "";
+ if (mimePart.subParts.length > 0) {
+ let boundary = EnigmailMime.getBoundary(rawHdr.get("content-type").join(""));
+
+ for (let i in mimePart.subParts) {
+ msg += `--${boundary}\r\n`;
+ msg += this.mimeToString(mimePart.subParts[i], true);
+ }
- if (mime.isBrokenByExchange) {
- EnigmailLog.DEBUG("persistentCrypto.jsm: mimeToString: MS-Exchange fix\n");
- for (let j in this.allTasks) {
- if (this.allTasks[j].partName == mime.parts[0].partName) {
+ if (msg.search(/[\r\n]$/) < 0) {
+ msg += "\r\n";
+ }
- boundary = EnigmailMime.createBoundary();
+ msg += `--${boundary}--\r\n`;
+ }
+ return msg;
+ },
- msg += getRfc822Headers(mime.headers, ct, "content-type");
- msg += 'Content-Type: multipart/mixed; boundary="' + boundary + '"\r\n\r\n';
+ storeMessage: function(msg) {
+ let self = this;
- 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 new Promise((resolve, reject) => {
+ //XXX Do we wanna use the tmp for this?
+ let tempFile = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties).get("TmpD", Ci.nsIFile);
+ tempFile.append("message.eml");
+ tempFile.createUnique(0, 384); // == 0600, octal is deprecated
- 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) {
+ // ensure that file gets deleted on exit, if something goes wrong ...
+ let extAppLauncher = Cc["@mozilla.org/mime;1"].getService(Ci.nsPIExternalAppLauncher);
- let a = this.allTasks[j];
- EnigmailLog.DEBUG("persistentCrypto.jsm: mimeToString: attaching " + j + " as '" + a.name + "'\n");
+ let foStream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
+ foStream.init(tempFile, 2, 0x200, false); // open as "write only"
+ foStream.write(msg, msg.length);
+ foStream.close();
- for (let header in mime.headers) {
- if (!(a.status == STATUS_OK && header == "content-type")) {
- msg += prettyPrintHeader(header, mime.headers[header]) + "\r\n";
- }
- }
+ extAppLauncher.deleteTemporaryFileOnExit(tempFile);
- 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";
- }
+ //
+ // This was taken from the HeaderToolsLite Example Addon "original by Frank DiLecce"
+ //
+ // this is interesting: nsIMsgFolder.copyFileMessage seems to have a bug on Windows, when
+ // the nsIFile has been already used by foStream (because of Windows lock system?), so we
+ // must initialize another nsIFile object, pointing to the temporary file
+ let fileSpec = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
+ fileSpec.initWithPath(tempFile.path);
- msg += "Content-Transfer-Encoding: base64\r\n\r\n";
- msg += EnigmailData.encodeBase64(a.data) + "\r\n";
+ const copySvc = Cc["@mozilla.org/messenger/messagecopyservice;1"].getService(Ci.nsIMsgCopyService);
+ let copyListener = {
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIMsgCopyServiceListener) || iid.equals(Ci.nsISupports)) {
+ return this;
}
- }
- }
- } 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;
- }
+ EnigmailLog.DEBUG("persistentCrypto.jsm: copyListener error\n");
+ throw Components.results.NS_NOINTERFACE;
+ },
+ GetMessageId: function(messageId) {},
+ OnProgress: function(progress, progressMax) {},
+ OnStartCopy: function() {
+ EnigmailLog.DEBUG("persistentCrypto.jsm: copyListener: OnStartCopy()\n");
+ },
+ SetMessageKey: function(key) {
+ EnigmailLog.DEBUG("persistentCrypto.jsm: copyListener: SetMessageKey(" + key + ")\n");
+ },
+ OnStopCopy: function(statusCode) {
+ EnigmailLog.DEBUG("persistentCrypto.jsm: copyListener: OnStopCopy()\n");
+ if (statusCode !== 0) {
+ EnigmailLog.DEBUG("persistentCrypto.jsm: Error copying message: " + statusCode + "\n");
+ try {
+ tempFile.remove(false);
+ } catch (ex) {
+ try {
+ fileSpec.remove(false);
+ } catch (e2) {
+ EnigmailLog.DEBUG("persistentCrypto.jsm: Could not delete temp file\n");
+ }
+ }
+ resolve(true);
+ return;
+ }
+ EnigmailLog.DEBUG("persistentCrypto.jsm: Copy complete\n");
- subct = mime.parts[0].headers['content-transfer-encoding'];
- if (subct) {
- mime.headers['content-transfer-encoding'] = subct;
- }
+ if (self.move) {
+ deleteOriginalMail(self.hdr);
+ }
- msg += getRfc822Headers(mime.headers, ct);
+ try {
+ tempFile.remove(false);
+ } catch (ex) {
+ try {
+ fileSpec.remove(false);
+ } catch (e2) {
+ EnigmailLog.DEBUG("persistentCrypto.jsm: Could not delete temp file\n");
+ }
+ }
- msg += "\r\n" + mime.parts[0].body + "\r\n";
+ EnigmailLog.DEBUG("persistentCrypto.jsm: Cave Johnson. We're done\n");
+ resolve(true);
+ }
+ };
- return msg;
- } else {
- if (!topLevel && (mime instanceof MimeMessage)) {
- let mimeName = mime.name;
- if (!mimeName || mimeName === "") {
- mimeName = getHeaderValue(mime, 'subject') + ".eml";
+ EnigmailLog.DEBUG("persistentCrypto.jsm: copySvc ready for copy\n");
+ try {
+ if (self.mimeTree.headers.has("subject")) {
+ self.hdr.subject = self.mimeTree.headers.get("subject");
}
+ } catch (ex) {}
- 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';
- }
+ copySvc.CopyFileMessage(fileSpec, EnigmailTb60Compat.getExistingFolder(self.destFolder), self.hdr,
+ false, 0, "", copyListener, null);
+ });
+ },
- msg += getRfc822Headers(mime.headers, ct);
+ fixExchangeMessage: function(mimePart) {
+ EnigmailLog.DEBUG("persistentCrypto.jsm: fixExchangeMessage()\n");
- msg += "\r\n";
+ const FixEx = getFixExchangeMsg();
- 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";
- }
- }
+ let msg = this.mimeToString(mimePart, true);
+ let app = FixEx.determineCreatorApp(msg);
- 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";
- }
- }
+ try {
+ let fixedMsg = FixEx.getRepairedMessage(msg);
+ let replacement = EnigmailMime.getMimeTree(fixedMsg, true);
- if (ct.indexOf("multipart/") === 0 && !(mime instanceof MimeContainer)) {
- if (!mime.noBottomBoundary) {
- msg += "--" + boundary + "--\r\n";
+ for (let i in replacement) {
+ mimePart[i] = replacement[i];
}
- }
- return msg;
+ } catch (ex) {}
}
};
+
/**
* Format a mime header
*
@@ -1166,6 +811,9 @@ function formatHeader(headerLabel) {
}
function formatMimeHeader(headerLabel, headerValue) {
+ if (Array.isArray(headerValue)) {
+ headerValue = headerValue.join("");
+ }
if (headerLabel.search(/^(sender|from|reply-to|to|cc|bcc)$/i) === 0) {
return formatHeader(headerLabel) + ": " + EnigmailMime.formatHeaderData(EnigmailMime.formatEmailAddress(headerValue));
} else {
@@ -1191,9 +839,10 @@ function getHeaderValue(mimeStruct, header) {
EnigmailLog.DEBUG("persistentCrypto.jsm: getHeaderValue: '" + header + "'\n");
try {
- if (header in mimeStruct.headers) {
- if (typeof mimeStruct.headers[header] == "string") {
- return mimeStruct.headers[header];
+ if (mimeStruct.headers.has(header)) {
+ let hdrVal = mimeStruct.headers.get(header);
+ if (typeof hdrVal == "string") {
+ return hdrVal;
} else {
return mimeStruct.headers[header].join(" ");
}
@@ -1238,56 +887,53 @@ function getRfc822Headers(headerArr, contentType, ignoreHeadersArr) {
return hdrs;
}
-function getContentType(shdr) {
+function getContentType(mime) {
try {
- shdr = String(shdr);
- return shdr.match(/([A-z-]+\/[A-z-]+)/)[1].toLowerCase();
+ if (mime.headers.has("content-type")) {
+ return mime.headers.get("content-type").type.toLowerCase();
+ }
} catch (e) {
EnigmailLog.DEBUG("persistentCrypto.jsm: getContentType: " + e + "\n");
- return null;
}
+ return null;
}
// return the content of the boundary parameter
-function getBoundary(shdr) {
+function getBoundary(mime) {
try {
- shdr = String(shdr);
- return EnigmailMime.getBoundary(shdr);
+ if (mime.headers.has("content-type")) {
+ return mime.headers.get("content-type").get("boundary");
+ }
} catch (e) {
EnigmailLog.DEBUG("persistentCrypto.jsm: getBoundary: " + e + "\n");
- return null;
}
+ return null;
}
-function getCharset(shdr) {
+function getCharset(mime) {
try {
- shdr = String(shdr);
- return EnigmailMime.getParameter(shdr, 'charset').toLowerCase();
+ if (mime.headers.has("content-type")) {
+ let c = mime.headers.get("content-type").get("charset");
+ if (c) return c.toLowerCase();
+ }
} catch (e) {
EnigmailLog.DEBUG("persistentCrypto.jsm: getCharset: " + e + "\n");
- return null;
}
+ return null;
}
-function getProtocol(shdr) {
+function getProtocol(mime) {
try {
- shdr = String(shdr);
- return EnigmailMime.getProtocol(shdr).toLowerCase();
+ if (mime.headers.has("content-type")) {
+ let c = mime.headers.get("content-type").get("protocol");
+ if (c) return c.toLowerCase();
+ }
} catch (e) {
EnigmailLog.DEBUG("persistentCrypto.jsm: getProtocol: " + e + "\n");
- return "";
}
+ return "";
}
-function getSMimeProtocol(shdr) {
- try {
- shdr = String(shdr);
- return shdr.match(/smime-type="?([A-z0-9'()+_,-./:=?]+)"?/)[1].toLowerCase();
- } catch (e) {
- EnigmailLog.DEBUG("persistentCrypto.jsm: getSMimeProtocol: " + e + "\n");
- return "";
- }
-}
function getPepSubject(mimeString) {
EnigmailLog.DEBUG("persistentCrypto.jsm: getPepSubject()\n");