diff options
author | Patrick Brunschwig <[email protected]> | 2019-06-14 21:49:26 +0200 |
---|---|---|
committer | Patrick Brunschwig <[email protected]> | 2019-06-14 21:49:26 +0200 |
commit | 74648654b0fede31f59e79a01df19917d6c16e3b (patch) | |
tree | 4b3458885bff1c48c1a71b8346e56dee2d95ab81 | |
parent | 349a641892b7f2834f0662a3e703624de45d673d (diff) | |
download | enigmail-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.jsm | 86 | ||||
-rw-r--r-- | package/mime.jsm | 2 | ||||
-rw-r--r-- | package/persistentCrypto.jsm | 1050 |
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"); |