Page MenuHome GnuPG

decryptPermanently.jsm
No OneTemporary

decryptPermanently.jsm

/*global Components: false*/
/*jshint -W097 */
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
"use strict";
var EXPORTED_SYMBOLS = ["EnigmailDecryptPermanently"];
const Cu = Components.utils;
Cu.import("resource://gre/modules/AddonManager.jsm"); /*global AddonManager: false */
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); /*global XPCOMUtils: false */
Cu.import("resource://enigmail/log.jsm"); /*global EnigmailLog: false */
Cu.import("resource://enigmail/armor.jsm"); /*global EnigmailArmor: false */
Cu.import("resource://enigmail/locale.jsm"); /*global EnigmailLocale: false */
Cu.import("resource://enigmail/execution.jsm"); /*global EnigmailExecution: false */
Cu.import("resource://enigmail/dialog.jsm"); /*global EnigmailDialog: false */
Cu.import("resource://enigmail/glodaUtils.jsm"); /*global GlodaUtils: false */
Cu.import("resource://enigmail/promise.jsm"); /*global Promise: false */
Cu.import("resource:///modules/MailUtils.js"); /*global MailUtils: false */
Cu.import("resource://enigmail/core.jsm"); /*global EnigmailCore: false */
Cu.import("resource://enigmail/gpgAgent.jsm"); /*global EnigmailGpgAgent: false */
Cu.import("resource://enigmail/gpg.jsm"); /*global EnigmailGpg: false */
Cu.import("resource://enigmail/streams.jsm"); /*global EnigmailStreams: false */
Cu.import("resource://enigmail/passwords.jsm"); /*global EnigmailPassword: false */
Cu.import("resource://enigmail/mime.jsm"); /*global EnigmailMime: false */
Cu.import("resource://enigmail/data.jsm"); /*global EnigmailData: false */
Cu.import("resource://enigmail/attachment.jsm"); /*global EnigmailAttachment: false */
/*global MimeBody: false, MimeUnknown: false, MimeMessageAttachment: false */
/*global msgHdrToMimeMessage: false, MimeMessage: false, MimeContainer: false */
Cu.import("resource://enigmail/glodaMime.jsm");
var EC = EnigmailCore;
const Cc = Components.classes;
const Ci = Components.interfaces;
const nsIEnigmail = Components.interfaces.nsIEnigmail;
const STATUS_OK = 0;
const STATUS_FAILURE = 1;
const STATUS_NOT_REQUIRED = 2;
const IOSERVICE_CONTRACTID = "@mozilla.org/network/io-service;1";
/*
* Decrypt a message and copy it to a folder
*
* @param nsIMsgDBHdr hdr Header of the message
* @param String destFolder Folder URI
* @param Boolean move If true the original message will be deleted
*
* @return a Promise that we do that
*/
const EnigmailDecryptPermanently = {
/***
* dispatchMessages
*
* Because Thunderbird throws all messages at once at us thus we have to rate limit the dispatching
* of the message processing. Because there is only a negligible performance gain when dispatching
* several message at once we serialize to not overwhelm low power devices.
*
* The function is implemented such that the 1st call (requireSync == true) is a synchronous function,
* while any other call is asynchronous. This is required to make the filters work correctly in case
* there are other filters that work on the message. (see bug 374).
*
* Parameters
* aMsgHdrs: Array of nsIMsgDBHdr
* targetFolder: String; target folder URI
* move: Boolean: type of action; true = "move" / false = "copy"
* requireSync: Boolean: true = require function to behave synchronously
* false = async function (no useful return value)
*
**/
dispatchMessages: function(aMsgHdrs, targetFolder, move, requireSync) {
var inspector = Cc["@mozilla.org/jsinspector;1"].getService(Ci.nsIJSInspector);
var promise = EnigmailDecryptPermanently.decryptMessage(aMsgHdrs[0], targetFolder, move);
var done = false;
var processNext = function(data) {
aMsgHdrs.splice(0, 1);
if (aMsgHdrs.length > 0) {
EnigmailDecryptPermanently.dispatchMessages(aMsgHdrs, targetFolder, move, false);
}
else {
// last message was finished processing
done = true;
inspector.exitNestedEventLoop();
}
};
promise.then(processNext);
promise.catch(function(err) {
processNext(null);
});
if (requireSync && !done) {
// wait here until all messages processed, such that the function returns
// synchronously
inspector.enterNestedEventLoop({
value: 0
});
}
},
decryptMessage: function(hdr, destFolder, move) {
return new Promise(
function(resolve, reject) {
let msgUriSpec = hdr.folder.getUriForMsg(hdr);
const msgSvc = Cc["@mozilla.org/messenger;1"].createInstance(Ci.nsIMessenger).
messageServiceFromURI(msgUriSpec);
const decrypt = new DecryptMessageIntoFolder(destFolder, move, resolve);
try {
msgHdrToMimeMessage(hdr, decrypt, decrypt.messageParseCallback, true, {
examineEncryptedParts: false,
partsOnDemand: false
});
}
catch (ex) {
reject("msgHdrToMimeMessage failed");
}
return;
}
);
}
};
function DecryptMessageIntoFolder(destFolder, move, resolve) {
this.destFolder = destFolder;
this.move = move;
this.resolve = resolve;
this.foundPGP = 0;
this.mime = null;
this.hdr = null;
this.decryptionTasks = [];
this.subject = "";
}
DecryptMessageIntoFolder.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'));
if (!ct) {
this.resolve(true);
return;
}
this.walkMimeTree(this.mime, this.mime);
this.decryptINLINE(this.mime);
if (this.foundPGP < 0) {
// decryption failed
this.resolve(true);
return;
}
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.resolve(true);
return;
}
var msg = self.mimeToString(self.mime, true);
if (!msg || msg === "") {
// no message data found
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, 384); // == 0600, octal is deprecated
// 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("decryptPermanently.jsm: copyListener error\n");
throw Components.results.NS_NOINTERFACE;
},
GetMessageId: function(messageId) {},
OnProgress: function(progress, progressMax) {},
OnStartCopy: function() {},
SetMessageKey: function(key) {},
OnStopCopy: function(statusCode) {
if (statusCode !== 0) {
//XXX complain?
EnigmailLog.DEBUG("decryptPermanently.jsm: Error copying message: " + statusCode + "\n");
try {
tempFile.remove(false);
}
catch (ex) {
try {
fileSpec.remove(false);
}
catch (e2) {
EnigmailLog.DEBUG("decryptPermanently.jsm: Could not delete temp file\n");
}
}
self.resolve(true);
return;
}
EnigmailLog.DEBUG("decryptPermanently.jsm: Copy complete\n");
if (self.move) {
EnigmailLog.DEBUG("decryptPermanently.jsm: Delete original\n");
var folderInfoObj = {};
self.hdr.folder.getDBFolderInfoAndDB(folderInfoObj).DeleteMessage(self.hdr.messageKey, null, true);
}
try {
tempFile.remove(false);
}
catch (ex) {
try {
fileSpec.remove(false);
}
catch (e2) {
EnigmailLog.DEBUG("decryptPermanently.jsm: Could not delete temp file\n");
}
}
EnigmailLog.DEBUG("decryptPermanently.jsm: Cave Johnson. We're done\n");
self.resolve(true);
}
};
copySvc.CopyFileMessage(fileSpec, MailUtils.getFolderForURI(self.destFolder, false), self.hdr,
false, 0, null, copyListener, null);
}
).catch(
function catchErr(errorMsg) {
EnigmailLog.DEBUG("decryptPermanently.jsm: Promise.catchErr: " + errorMsg + "\n");
self.resolve(false);
}
);
}
catch (ex) {
EnigmailLog.DEBUG("decryptPermanently.jsm: messageParseCallback: caught error " + ex.toString() + "\n");
self.resolve(false);
}
},
readAttachment: function(attachment, strippedName) {
return new Promise(
function(resolve, reject) {
EnigmailLog.DEBUG("decryptPermanently.jsm: readAttachment\n");
let o;
var f = function _cb(data) {
EnigmailLog.DEBUG("decryptPermanently.jsm: readAttachment - got data (" + data.length + ")\n");
o = {
type: "attachment",
data: data,
name: strippedName ? strippedName : attachment.name,
partName: attachment.partName,
origName: attachment.name,
status: STATUS_NOT_REQUIRED
};
resolve(o);
};
try {
var bufferListener = EnigmailStreams.newStringStreamListener(f);
var ioServ = Cc[IOSERVICE_CONTRACTID].getService(Components.interfaces.nsIIOService);
var msgUri = ioServ.newURI(attachment.url, null, null);
var channel = EnigmailStreams.createChannelFromURI(msgUri);
channel.asyncOpen(bufferListener, msgUri);
}
catch (ex) {
reject(o);
}
}
);
},
decryptAttachment: function(attachment, strippedName) {
var self = this;
return new Promise(
function(resolve, reject) {
EnigmailLog.DEBUG("decryptPermanently.jsm: decryptAttachment\n");
self.readAttachment(attachment, strippedName).then(
function(o) {
var attachmentHead = o.data.substr(0, 30);
if (attachmentHead.match(/\-\-\-\-\-BEGIN PGP \w+ 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(EnigmailGpgAgent.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 & nsIEnigmail.DECRYPTION_OKAY)) {
EnigmailLog.DEBUG("decryptPermanently.jsm: decryptAttachment: decryption OK\n");
exitCode = 0;
}
else if (statusFlagsObj.value & nsIEnigmail.DECRYPTION_FAILED) {
EnigmailLog.DEBUG("decryptPermanently.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 (!EnigmailDialog.confirmDlg(null, msg,
EnigmailLocale.getString("dlg.button.retry"), EnigmailLocale.getString("dlg.button.skip"))) {
o.status = STATUS_FAILURE;
resolve(o);
return;
}
}
else if (statusFlagsObj.value & nsIEnigmail.DECRYPTION_INCOMPLETE) {
// failure; message not complete
EnigmailLog.DEBUG("decryptPermanently.jsm: decryptAttachment: decryption incomplete\n");
o.status = STATUS_FAILURE;
resolve(o);
return;
}
else {
// there is nothing to be decrypted
EnigmailLog.DEBUG("decryptPermanently.jsm: decryptAttachment: no decryption required\n");
o.status = STATUS_NOT_REQUIRED;
resolve(o);
return;
}
} while (exitCode !== 0);
EnigmailLog.DEBUG("decryptPermanently.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
*/
// the sunny world of PGP/MIME
walkMimeTree: function(mime, parent) {
EnigmailLog.DEBUG("decryptPermanently.jsm: walkMimeTree:\n");
let ct = getContentType(getHeaderValue(mime, 'content-type'));
EnigmailLog.DEBUG("decryptPermanently.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.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");
}
for (var i in mime.parts) {
this.walkMimeTree(mime.parts[i], mime);
}
},
/***
*
* Detect if mime part is PGP/MIME message that got modified by MS-Exchange:
*
* - multipart/mixed Container with
* - application/pgp-encrypted Attachment with name "PGPMIME Version Identification"
* - application/octet-stream Attachment with name "encrypted.asc" having the encrypted content in base64
* - see:
* - http://www.mozilla-enigmail.org/forum/viewtopic.php?f=4&t=425
* - http://sourceforge.net/p/enigmail/forum/support/thread/4add2b69/
*/
isBrokenByExchange: function(mime) {
EnigmailLog.DEBUG("decryptPermanently.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) {
EnigmailLog.DEBUG("decryptPermanently.jsm: isBrokenByExchange: found message broken by MS-Exchange\n");
return true;
}
}
catch (ex) {}
return false;
},
isPgpMime: function(mime) {
EnigmailLog.DEBUG("decryptPermanently.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("decryptPermanently.jsm: isPgpMime:"+ex+"\n");
}
return false;
},
// smime-type=enveloped-data
isSMime: function(mime) {
EnigmailLog.DEBUG("decryptPermanently.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;
if (ct.toLowerCase() == "application/pkcs7-mime" && pt == "enveloped-data") {
return true;
}
}
catch (ex) {
EnigmailLog.DEBUG("decryptPermanently.jsm: isSMime:" + ex + "\n");
}
return false;
},
decryptPGPMIME: function(mime, part) {
EnigmailLog.DEBUG("decryptPermanently.jsm: decryptPGPMIME: part=" + part + "\n");
var self = this;
return new Promise(
function(resolve, reject) {
var m = Cc["@mozilla.org/messenger/mimeheaders;1"].createInstance(Ci.nsIMimeHeaders);
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);
let op = (u.value.spec.indexOf("?") > 0 ? "&" : "?");
let url = u.value.spec + op + 'part=' + part + "&header=enigmailConvert";
EnigmailLog.DEBUG("decryptPermanently.jsm: getting data from URL " + url + "\n");
let s = EnigmailStreams.newStringStreamListener(
function analyzeDecryptedData(data) {
EnigmailLog.DEBUG("decryptPermanently.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];
let o = {
type: "mime",
name: "",
origName: "",
data: "",
partName: part,
status: STATUS_OK
};
if (data.length === 0) {
// fail if no data found
o.status = STATUS_FAILURE;
resolve(o);
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;
}
m.initialize(data.substr(0, bodyIndex));
let ct = m.extractHeader("content-type", false) || "";
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 + "\n";
o.data += "Content-Type: " + ct + "\n";
let h = m.extractHeader("content-transfer-encoding", false);
if (h) o.data += "content-transfer-encoding: " + h + "\n";
h = m.extractHeader("content-description", true);
if (h) o.data += "content-description: " + h + "\n";
o.data += data.substr(bodyIndex);
if (subpart) {
subpart.body = undefined;
subpart.headers['content-type'] = ct;
}
resolve(o);
}
);
try {
var channel = EnigmailStreams.createChannel(url);
channel.asyncOpen(s, null);
}
catch (e) {
EnigmailLog.DEBUG("decryptPermanently.jsm: decryptPGPMIME: exception " + e + "\n");
}
}
);
},
//inline wonderland
decryptINLINE: function(mime) {
EnigmailLog.DEBUG("decryptPermanently.jsm: decryptINLINE:\n");
if (typeof mime.body !== 'undefined') {
let ct = getContentType(getHeaderValue(mime, 'content-type'));
if (ct == "text/html") {
mime.body = this.stripHTMLFromArmoredBlocks(mime.body);
}
var enigmailSvc = EnigmailCore.getService();
var exitCodeObj = {};
var statusFlagsObj = {};
var userIdObj = {};
var sigDetailsObj = {};
var errorMsgObj = {};
var keyIdObj = {};
var blockSeparationObj = {
value: ""
};
var encToDetailsObj = {};
var signatureObj = {};
signatureObj.value = "";
var uiFlags = nsIEnigmail.UI_INTERACTIVE | nsIEnigmail.UI_UNVERIFIED_ENC_OK;
var plaintexts = [];
var blocks = EnigmailArmor.locateArmoredBlocks(mime.body);
var tmp = [];
for (let i = 0; i < blocks.length; i++) {
if (blocks[i].blocktype == "MESSAGE") {
tmp.push(blocks[i]);
}
}
blocks = tmp;
if (blocks.length < 1) {
return 0;
}
let charset = "utf-8";
for (let i = 0; i < blocks.length; i++) {
let plaintext = null;
do {
let ciphertext = mime.body.substring(blocks[i].begin, blocks[i].end + 1);
if (ciphertext.length === 0) {
break;
}
let hdr = ciphertext.search(/(\r\r|\n\n|\r\n\r\n)/);
if (hdr > 0) {
let chset = ciphertext.substr(0, hdr).match(/^(charset:)(.*)$/mi);
if (chset && chset.length == 3) {
charset = chset[2].trim();
}
}
plaintext = enigmailSvc.decryptMessage(null, uiFlags, ciphertext, signatureObj, exitCodeObj, statusFlagsObj,
keyIdObj, userIdObj, sigDetailsObj, errorMsgObj, blockSeparationObj, encToDetailsObj);
if (!plaintext || plaintext.length === 0) {
if (statusFlagsObj.value & nsIEnigmail.DISPLAY_MESSAGE) {
EnigmailDialog.alert(null, errorMsgObj.value);
this.foundPGP = -1;
return -1;
}
if (statusFlagsObj.value & nsIEnigmail.DECRYPTION_FAILED) {
// since we cannot find out if the user wants to cancel
// we should ask
let msg = EnigmailLocale.getString("converter.decryptBody.failed", this.subject);
if (!EnigmailDialog.confirmDlg(null, msg,
EnigmailLocale.getString("dlg.button.retry"), EnigmailLocale.getString("dlg.button.skip"))) {
this.foundPGP = -1;
return -1;
}
}
else if (statusFlagsObj.value & nsIEnigmail.DECRYPTION_INCOMPLETE) {
this.foundPGP = -1;
return -1;
}
}
if (ct == "text/html") {
plaintext = plaintext.replace(/\n/ig, "<br/>\n");
}
if (plaintext) {
plaintexts.push(plaintext);
}
} while (!plaintext || plaintext === "");
}
var decryptedMessage = mime.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 += mime.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);
}
else {
mime.body = decryptedMessage;
mime.headers['content-transfer-encoding'] = ['8bit'];
}
let origCharset = getCharset(getHeaderValue(mime, 'content-type'));
if (origCharset) {
mime.headers['content-type'] = getHeaderValue(mime, 'content-type').replace(origCharset, charset);
}
else {
mime.headers['content-type'] = getHeaderValue(mime, 'content-type') + "; charset=" + charset;
}
this.foundPGP = 1;
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'));
EnigmailLog.DEBUG("decryptPermanently.jsm: Decryption skipped: " + ct + "\n");
return 0;
},
stripHTMLFromArmoredBlocks: function(text) {
var index = 0;
var begin = text.indexOf("-----BEGIN PGP");
var end = text.indexOf("-----END PGP");
while (begin > -1 && end > -1) {
let sub = text.substring(begin, end);
sub = sub.replace(/(<([^>]+)>)/ig, "");
sub = sub.replace(/&[A-z]+;/ig, "");
text = text.substring(0, begin) + sub + text.substring(end);
index = end + 10;
begin = text.indexOf("-----BEGIN PGP", index);
end = text.indexOf("-----END PGP", index);
}
return text;
},
/******
*
* We have the technology we can rebuild.
*
* Function to reassemble the message from the MIME Tree
* into a String.
*
******/
mimeToString: function(mime, topLevel) {
EnigmailLog.DEBUG("decryptPermanently.jsm: mimeToString: part: '" + mime.partName + "', is of type '" + typeof(mime) + "'\n");
let ct = getContentType(getHeaderValue(mime, 'content-type'));
if (!ct) {
return "";
}
let boundary = getBoundary(getHeaderValue(mime, 'content-type'));
let msg = "";
if (mime.isBrokenByExchange) {
EnigmailLog.DEBUG("decryptPermanently.jsm: mimeToString: MS-Exchange fix\n");
for (let j in this.allTasks) {
if (this.allTasks[j].partName == mime.parts[0].partName) {
boundary = EnigmailMime.createBoundary();
msg += getRfc822Headers(mime.headers, ct, "content-type");
msg += 'Content-Type: multipart/mixed; boundary="' + boundary + '"\r\n\r\n';
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 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) {
let a = this.allTasks[j];
EnigmailLog.DEBUG("decryptPermanently.jsm: mimeToString: attaching " + j + " as '" + a.name + "'\n");
for (let header in mime.headers) {
if (!(a.status == STATUS_OK && header == "content-type")) {
msg += prettyPrintHeader(header, mime.headers[header]) + "\r\n";
}
}
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";
}
msg += "Content-Transfer-Encoding: base64\r\n\r\n";
msg += EnigmailData.encodeBase64(a.data) + "\r\n";
}
}
}
}
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;
}
subct = mime.parts[0].headers['content-transfer-encoding'];
if (subct) {
mime.headers['content-transfer-encoding'] = subct;
}
msg += getRfc822Headers(mime.headers, ct);
msg += "\r\n" + mime.parts[0].body + "\r\n";
return msg;
}
else {
if (!topLevel && (mime instanceof MimeMessage)) {
let mimeName = mime.name;
if (!mimeName || mimeName === "") {
mimeName = getHeaderValue(mime, 'subject') + ".eml";
}
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';
}
msg += getRfc822Headers(mime.headers, ct);
msg += "\r\n";
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";
}
}
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";
}
}
if (ct.indexOf("multipart/") === 0 && !(mime instanceof MimeContainer)) {
if (!mime.noBottomBoundary) {
msg += "--" + boundary + "--\r\n";
}
}
return msg;
}
};
/**
* Format a mime header
*
* e.g. content-type -> Content-Type
*/
function formatHeader(headerLabel) {
return headerLabel.replace(/^.|(\-.)/g, function(match) {
return match.toUpperCase();
});
}
function formatMimeHeader(headerLabel, headerValue) {
if (headerLabel.search(/^(sender|from|reply-to|to|cc|bcc)$/i) === 0) {
return formatHeader(headerLabel) + ": " + EnigmailMime.formatHeaderData(EnigmailMime.formatEmailAddress(headerValue));
}
else {
return formatHeader(headerLabel) + ": " + EnigmailMime.formatHeaderData(EnigmailMime.encodeHeaderValue(headerValue));
}
}
function prettyPrintHeader(headerLabel, headerData) {
let hdrData = "";
if (Array.isArray(headerData)) {
let h = [];
for (let i in headerData) {
h.push(formatMimeHeader(headerLabel, GlodaUtils.deMime(headerData[i])));
}
return h.join("\r\n");
}
else {
return formatMimeHeader(headerLabel, GlodaUtils.deMime(String(headerData)));
}
}
function getHeaderValue(mimeStruct, header) {
EnigmailLog.DEBUG("decryptPermanently.jsm: getHeaderValue: '" + header + "'\n");
try {
if (header in mimeStruct.headers) {
if (typeof mimeStruct.headers[header] == "string") {
return mimeStruct.headers[header];
}
else {
return mimeStruct.headers[header].join(" ");
}
}
else {
return "";
}
}
catch (ex) {
EnigmailLog.DEBUG("decryptPermanently.jsm: getHeaderValue: header not present\n");
return "";
}
}
/***
* get the formatted headers for MimeMessage objects
*
* @headerArr: Array of headers (key/value pairs), such as mime.headers
* @ignoreHeadersArr: Array of headers to exclude from returning
*
* @return: String containing formatted header list
*/
function getRfc822Headers(headerArr, contentType, ignoreHeadersArr) {
let hdrs = "";
let ignore = [];
if (contentType.indexOf("multipart/") >= 0) {
ignore = ['content-transfer-encoding',
'content-disposition',
'content-description'
];
}
if (ignoreHeadersArr) {
ignore = ignore.concat(ignoreHeadersArr);
}
for (let i in headerArr) {
if (ignore.indexOf(i) < 0) {
hdrs += prettyPrintHeader(i, headerArr[i]) + "\r\n";
}
}
return hdrs;
}
function getContentType(shdr) {
try {
shdr = String(shdr);
return shdr.match(/([A-z-]+\/[A-z-]+)/)[1].toLowerCase();
}
catch (e) {
EnigmailLog.DEBUG("decryptPermanently.jsm: getContentType: " + e + "\n");
return null;
}
}
// return the content of the boundary parameter
function getBoundary(shdr) {
try {
shdr = String(shdr);
return shdr.match(/boundary="?([A-z0-9'()+_,-.\/:=?]+)"?/i)[1];
}
catch (e) {
EnigmailLog.DEBUG("decryptPermanently.jsm: getBoundary: " + e + "\n");
return null;
}
}
function getCharset(shdr) {
try {
shdr = String(shdr);
return shdr.match(/charset="?([A-z0-9'()+_,-.\/:=?]+)"?/)[1].toLowerCase();
}
catch (e) {
EnigmailLog.DEBUG("decryptPermanently.jsm: getCharset: " + e + "\n");
return null;
}
}
function getProtocol(shdr) {
try {
shdr = String(shdr);
return shdr.match(/protocol="?([A-z0-9'()+_,-.\/:=?]+)"?/)[1].toLowerCase();
}
catch (e) {
EnigmailLog.DEBUG("decryptPermanently.jsm: getProtocol: " + e + "\n");
return "";
}
}
function getSMimeProtocol(shdr) {
try {
shdr = String(shdr);
return shdr.match(/smime-type="?([A-z0-9'()+_,-.\/:=?]+)"?/)[1].toLowerCase();
}
catch (e) {
EnigmailLog.DEBUG("decryptPermanently.jsm: getSMimeProtocol: " + e + "\n");
return "";
}
}

File Metadata

Mime Type
text/plain
Expires
Sat, May 10, 9:06 AM (7 h, 40 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
a5/bd/e5d3d782e4568ae2f5ea3c0c17ca

Event Timeline