diff --git a/package/Makefile b/package/Makefile
index 407e374d..574815dd 100644
--- a/package/Makefile
+++ b/package/Makefile
@@ -1,111 +1,112 @@
# 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/.
DEPTH = ..
include $(DEPTH)/config/autoconf.mk
PREF_JS_EXPORTS = $(srcdir)/prefs/enigmail.js
COMPFILES = \
enigmail.js \
pgpmimeHandler.js \
mimeEncrypt.js \
prefs-service.js \
msgCompFields.js
PREFFILES = prefs/enigmail.js
MODFILES = \
addrbook.jsm \
app.jsm \
armor.jsm \
attachment.jsm \
autocrypt.jsm \
card.jsm \
clipboard.jsm \
commandLine.jsm \
configBackup.jsm \
configure.jsm \
constants.jsm \
core.jsm \
data.jsm \
decryption.jsm \
decryptPermanently.jsm \
+ encryptPermanently.jsm \
dialog.jsm \
encryption.jsm \
errorHandling.jsm \
events.jsm \
execution.jsm \
funcs.jsm \
files.jsm \
filters.jsm \
fixExchangeMsg.jsm \
gpgAgent.jsm \
glodaMime.jsm \
glodaUtils.jsm \
gpg.jsm \
hash.jsm \
httpProxy.jsm \
installGnuPG.jsm \
installPep.jsm \
key.jsm \
keyEditor.jsm \
keyRing.jsm \
keyUsability.jsm \
keyRefreshService.jsm \
keyserver.jsm \
keyserverUris.jsm \
lazy.jsm \
locale.jsm \
log.jsm \
mime.jsm \
mimeDecrypt.jsm \
mimeVerify.jsm \
os.jsm \
openpgp.jsm \
passwordCheck.jsm \
passwords.jsm \
pEp.jsm \
pEpAdapter.jsm \
pEpDecrypt.jsm \
pEpFilter.jsm \
pEpListener.jsm \
pEpKeySync.jsm \
pipeConsole.jsm \
prefs.jsm \
protocolHandler.jsm \
rng.jsm \
rules.jsm \
send.jsm \
socks5Proxy.jsm \
stdlib.jsm \
streams.jsm \
system.jsm \
time.jsm \
timer.jsm \
tor.jsm \
trust.jsm \
uris.jsm \
verify.jsm \
versioning.jsm \
webKey.jsm \
wkdLookup.jsm \
windows.jsm \
wksMimeHandler.jsm \
zbase32.jsm
all: deploy
deploy: $(PREFFILES) $(COMPFILES) $(MODFILES)
$(DEPTH)/util/install -m 644 $(DIST)/components $(COMPFILES)
$(DEPTH)/util/install -m 644 $(DIST)/defaults/preferences $(PREFFILES)
$(DEPTH)/util/install -m 644 $(DIST)/modules $(MODFILES)
clean:
$(DEPTH)/util/install -u $(DIST)/components $(COMPFILES)
$(DEPTH)/util/install -u $(DIST)/defaults/preferences $(PREFFILES)
$(DEPTH)/util/install -u $(DIST)/modules $(MODFILES)
diff --git a/package/constants.jsm b/package/constants.jsm
index 3f6f427e..93967c34 100644
--- a/package/constants.jsm
+++ b/package/constants.jsm
@@ -1,54 +1,55 @@
/*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 = ["EnigmailConstants"];
const EnigmailConstants = {
POSSIBLE_PGPMIME: -2081,
// possible values for
// - encryptByRule, signByRules, pgpmimeByRules
// - encryptForced, signForced, pgpmimeForced (except CONFLICT)
// NOTE:
// - values 0/1/2 are used with this fixed semantics in the persistent rules
// - see also enigmailEncryptionDlg.xul
ENIG_NEVER: 0,
ENIG_UNDEF: 1,
ENIG_ALWAYS: 2,
ENIG_FORCE_SMIME: 3,
ENIG_AUTO_ALWAYS: 22,
ENIG_CONFLICT: 99,
ENIG_FINAL_UNDEF: -1,
ENIG_FINAL_NO: 0,
ENIG_FINAL_YES: 1,
ENIG_FINAL_FORCENO: 10,
ENIG_FINAL_FORCEYES: 11,
ENIG_FINAL_SMIME: 97, // use S/MIME (automatically chosen)
ENIG_FINAL_FORCESMIME: 98, // use S/MIME (forced by user)
ENIG_FINAL_CONFLICT: 99,
MIME_HANDLER_UNDEF: 0,
MIME_HANDLER_SMIME: 1,
MIME_HANDLER_PGPMIME: 2,
ICONTYPE_INFO: 1,
ICONTYPE_QUESTION: 2,
ICONTYPE_ALERT: 3,
ICONTYPE_ERROR: 4,
FILTER_MOVE_DECRYPT: "enigmail@enigmail.net#filterActionMoveDecrypt",
FILTER_COPY_DECRYPT: "enigmail@enigmail.net#filterActionCopyDecrypt",
+ FILTER_ENCRYPT: "enigmail@enigmail.net#filterActionEncrypt",
nsIEnigmail: Components.interfaces.nsIEnigmail
};
diff --git a/package/decryptPermanently.jsm b/package/decryptPermanently.jsm
index 296b5866..55ef0a8b 100644
--- a/package/decryptPermanently.jsm
+++ b/package/decryptPermanently.jsm
@@ -1,1254 +1,1256 @@
/*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://enigmail/lazy.jsm"); /*global EnigmailLazy: false */
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:///modules/MailUtils.js"); /*global MailUtils: false */
Cu.import("resource://enigmail/core.jsm"); /*global EnigmailCore: 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 */
Cu.import("resource://enigmail/timer.jsm"); /*global EnigmailTimer: false */
Cu.import("resource:///modules/jsmime.jsm"); /*global jsmime: false*/
/*global MimeBody: false, MimeUnknown: false, MimeMessageAttachment: false */
/*global msgHdrToMimeMessage: false, MimeMessage: false, MimeContainer: false */
Cu.import("resource://enigmail/glodaMime.jsm");
const getGpgAgent = EnigmailLazy.loader("enigmail/gpgAgent.jsm", "EnigmailGpgAgent");
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 asynchronously.
*
* Parameters
* aMsgHdrs: Array of nsIMsgDBHdr
* targetFolder: String; target folder URI
* copyListener: listener for async request (nsIMsgCopyServiceListener)
* move: Boolean: type of action; true = "move" / false = "copy"
*
**/
dispatchMessages: function(aMsgHdrs, targetFolder, copyListener, move) {
EnigmailLog.DEBUG("decryptPermanently.jsm: dispatchMessages()\n");
if (copyListener) {
copyListener.OnStartCopy();
}
let promise = EnigmailDecryptPermanently.decryptMessage(aMsgHdrs[0], targetFolder, move);
var processNext = function(data) {
aMsgHdrs.splice(0, 1);
if (aMsgHdrs.length > 0) {
EnigmailDecryptPermanently.dispatchMessages(aMsgHdrs, targetFolder, move);
}
else {
// last message was finished processing
if (copyListener) {
copyListener.OnStopCopy(0);
}
EnigmailLog.DEBUG("decryptPermanently.jsm: dispatchMessages - DONE\n");
}
};
promise.then(processNext);
promise.catch(function(err) {
processNext(null);
});
},
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 = {
+ newMessageKey: null,
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() {
EnigmailLog.DEBUG("decryptPermanently.jsm: copyListener: OnStartCopy()\n");
},
SetMessageKey: function(key) {
EnigmailLog.DEBUG("decryptPermanently.jsm: copyListener: SetMessageKey(" + key + ")\n");
+ copyListener.newMessageKey = key;
},
OnStopCopy: function(statusCode) {
EnigmailLog.DEBUG("decryptPermanently.jsm: copyListener: OnStopCopy()\n");
if (statusCode !== 0) {
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);
+ self.resolve(copyListener.newMessageKey);
return;
}
EnigmailLog.DEBUG("decryptPermanently.jsm: Copy complete\n");
if (self.move) {
deleteOriginalMail(self.hdr);
}
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);
+ self.resolve(copyListener.newMessageKey);
}
};
EnigmailLog.DEBUG("decryptPermanently.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, MailUtils.getFolderForURI(self.destFolder, false), self.hdr,
false, 0, "", 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(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 & 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) || "";
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("") === "pEp") {
let subject = getPepSubject(data);
if (subject) {
self.mime.headers.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 + "\n";
o.data += "Content-Type: " + ct + "\n";
let h = m.headerNames;
while (h.hasMore()) {
let hdr = h.getNext();
if (hdr.search(/^content-type$/i) < 0) {
try {
EnigmailLog.DEBUG("decryptPermanently.jsm: getUnstructuredHeader: hdr=" + hdr + "\n");
let hdrVal = m.getUnstructuredHeader(hdr.toLowerCase());
o.data += hdr + ": " + hdrVal + "\n";
}
catch (ex) {
EnigmailLog.DEBUG("decryptPermanently.jsm: getUnstructuredHeader: exception " + ex.toString() + "\n");
}
}
}
EnigmailLog.DEBUG("decryptPermanently.jsm: getUnstructuredHeader: done\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.toString() + "\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, "
\n");
}
if (i == 0 && this.mime.headers.subject && this.mime.headers.subject[0] === "pEp" &&
mime.partName.length > 0 && mime.partName.search(/[^01.]/) < 0) {
let m = EnigmailMime.extractSubjectFromBody(plaintext);
if (m) {
plaintext = m.messageBody;
this.mime.headers.subject = [m.subject];
}
}
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 EnigmailMime.getBoundary(shdr);
}
catch (e) {
EnigmailLog.DEBUG("decryptPermanently.jsm: getBoundary: " + e + "\n");
return null;
}
}
function getCharset(shdr) {
try {
shdr = String(shdr);
return EnigmailMime.getParameter(shdr, 'charset').toLowerCase();
}
catch (e) {
EnigmailLog.DEBUG("decryptPermanently.jsm: getCharset: " + e + "\n");
return null;
}
}
function getProtocol(shdr) {
try {
shdr = String(shdr);
return EnigmailMime.getProtocol(shdr).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 "";
}
}
function getPepSubject(mimeString) {
EnigmailLog.DEBUG("decryptPermanently.jsm: getPepSubject()\n");
let subject = null;
let emitter = {
ct: "",
firstPlainText: false,
startPart: function(partNum, headers) {
EnigmailLog.DEBUG("decryptPermanently.jsm: getPepSubject.startPart: partNum=" + partNum + "\n");
try {
this.ct = String(headers.getRawHeader("content-type")).toLowerCase();
if (!subject && !this.firstPlainText) {
let s = headers.getRawHeader("subject");
if (s) {
subject = String(s);
this.firstPlainText = true;
}
}
}
catch (ex) {
this.ct = "";
}
},
endPart: function(partNum) {},
deliverPartData: function(partNum, data) {
EnigmailLog.DEBUG("decryptPermanently.jsm: getPepSubject.deliverPartData: partNum=" + partNum + " ct=" + this.ct + "\n");
if (!this.firstPlainText && this.ct.search(/^text\/plain/) === 0) {
// check data
this.firstPlainText = true;
let o = EnigmailMime.extractSubjectFromBody(data);
if (o) {
subject = o.subject;
}
}
}
};
let opt = {
strformat: "unicode",
bodyformat: "decode"
};
try {
let p = new jsmime.MimeParser(emitter, opt);
p.deliverData(mimeString);
}
catch (ex) {}
return subject;
}
/**
* Lazy deletion of original messages
*/
function deleteOriginalMail(msgHdr) {
EnigmailLog.DEBUG("decryptPermanently.jsm: deleteOriginalMail(" + msgHdr.messageKey + ")\n");
let delMsg = function() {
try {
EnigmailLog.DEBUG("decryptPermanently.jsm: deleting original message " + msgHdr.messageKey + "\n");
let folderInfoObj = {};
msgHdr.folder.getDBFolderInfoAndDB(folderInfoObj).DeleteMessage(msgHdr.messageKey, null, true);
}
catch (e) {
EnigmailLog.DEBUG("decryptPermanently.jsm: deletion failed. Error: " + e.toString() + "\n");
}
};
EnigmailTimer.setTimeout(delMsg, 500);
}
diff --git a/package/decryptPermanently.jsm b/package/encryptPermanently.jsm
similarity index 81%
copy from package/decryptPermanently.jsm
copy to package/encryptPermanently.jsm
index 296b5866..fb1cd773 100644
--- a/package/decryptPermanently.jsm
+++ b/package/encryptPermanently.jsm
@@ -1,1254 +1,1119 @@
/*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"];
+var EXPORTED_SYMBOLS = ["EnigmailEncryptPermanently"];
const Cu = Components.utils;
Cu.import("resource://enigmail/lazy.jsm"); /*global EnigmailLazy: false */
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:///modules/MailUtils.js"); /*global MailUtils: false */
Cu.import("resource://enigmail/core.jsm"); /*global EnigmailCore: 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 */
Cu.import("resource://enigmail/timer.jsm"); /*global EnigmailTimer: false */
+Cu.import("resource://enigmail/encryption.jsm"); /*global EnigmailEncryption: false */
Cu.import("resource:///modules/jsmime.jsm"); /*global jsmime: false*/
/*global MimeBody: false, MimeUnknown: false, MimeMessageAttachment: false */
/*global msgHdrToMimeMessage: false, MimeMessage: false, MimeContainer: false */
Cu.import("resource://enigmail/glodaMime.jsm");
const getGpgAgent = EnigmailLazy.loader("enigmail/gpgAgent.jsm", "EnigmailGpgAgent");
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 = {
+const EnigmailEncryptPermanently = {
/***
* 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 asynchronously.
*
* Parameters
* aMsgHdrs: Array of nsIMsgDBHdr
* targetFolder: String; target folder URI
* copyListener: listener for async request (nsIMsgCopyServiceListener)
* move: Boolean: type of action; true = "move" / false = "copy"
*
**/
dispatchMessages: function(aMsgHdrs, targetFolder, copyListener, move) {
- EnigmailLog.DEBUG("decryptPermanently.jsm: dispatchMessages()\n");
+ EnigmailLog.DEBUG("encryptPermanently.jsm: dispatchMessages()\n");
if (copyListener) {
copyListener.OnStartCopy();
}
- let promise = EnigmailDecryptPermanently.decryptMessage(aMsgHdrs[0], targetFolder, move);
+ let promise = EnigmailEncryptPermanently.encryptMessage(aMsgHdrs[0], targetFolder, move);
var processNext = function(data) {
aMsgHdrs.splice(0, 1);
if (aMsgHdrs.length > 0) {
- EnigmailDecryptPermanently.dispatchMessages(aMsgHdrs, targetFolder, move);
+ EnigmailEncryptPermanently.dispatchMessages(aMsgHdrs, targetFolder, move);
}
else {
// last message was finished processing
if (copyListener) {
copyListener.OnStopCopy(0);
}
- EnigmailLog.DEBUG("decryptPermanently.jsm: dispatchMessages - DONE\n");
+ EnigmailLog.DEBUG("encryptPermanently.jsm: dispatchMessages - DONE\n");
}
};
promise.then(processNext);
promise.catch(function(err) {
processNext(null);
});
},
- decryptMessage: function(hdr, destFolder, move) {
+ encryptMessage: function(msgHdr, encryptTo) {
return new Promise(
function(resolve, reject) {
- let msgUriSpec = hdr.folder.getUriForMsg(hdr);
+ let msgUriSpec = msgHdr.folder.getUriForMsg(msgHdr);
const msgSvc = Cc["@mozilla.org/messenger;1"].createInstance(Ci.nsIMessenger).messageServiceFromURI(msgUriSpec);
- const decrypt = new DecryptMessageIntoFolder(destFolder, move, resolve);
+ const encrypt = new EncryptMessageIntoFolder(resolve,encryptTo);
try {
- msgHdrToMimeMessage(hdr, decrypt, decrypt.messageParseCallback, true, {
+ msgHdrToMimeMessage(msgHdr, encrypt, encrypt.messageParseCallback, true, {
examineEncryptedParts: false,
partsOnDemand: false
});
}
catch (ex) {
reject("msgHdrToMimeMessage failed");
}
return;
}
);
}
};
-function DecryptMessageIntoFolder(destFolder, move, resolve) {
- this.destFolder = destFolder;
- this.move = move;
+function EncryptMessageIntoFolder(resolve,encryptTo) {
this.resolve = resolve;
this.foundPGP = 0;
this.mime = null;
this.hdr = null;
- this.decryptionTasks = [];
+ this.encryptionTasks = [];
this.subject = "";
+ this.fromEmail = "";
+ this.encryptTo = encryptTo;
}
-DecryptMessageIntoFolder.prototype = {
+EncryptMessageIntoFolder.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() {
- EnigmailLog.DEBUG("decryptPermanently.jsm: copyListener: OnStartCopy()\n");
- },
- SetMessageKey: function(key) {
- EnigmailLog.DEBUG("decryptPermanently.jsm: copyListener: SetMessageKey(" + key + ")\n");
- },
- OnStopCopy: function(statusCode) {
- EnigmailLog.DEBUG("decryptPermanently.jsm: copyListener: OnStopCopy()\n");
- if (statusCode !== 0) {
- 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) {
- deleteOriginalMail(self.hdr);
- }
-
- 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);
- }
- };
-
- EnigmailLog.DEBUG("decryptPermanently.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, MailUtils.getFolderForURI(self.destFolder, false), self.hdr,
- false, 0, "", 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);
- }
- },
+ 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'));
+ this.fromEmail = GlodaUtils.deMime(getHeaderValue(mime, 'from'));
+
+ if (!ct) {
+ this.resolve(true);
+ return;
+ }
+
+ this.walkMimeTree(this.mime, this.mime);
+
+ EnigmailLog.DEBUG("encrypt to: " + this.encryptTo + ", from: " + this.fromEmail + "\n");
+ let exitCode = {};
+ let errorMsg = {};
+
+ let msg = EnigmailEncryption.encryptMessage(
+ null,
+ nsIEnigmail.UI_UNVERIFIED_ENC_OK,
+ this.mimeToString(mime),
+ this.encryptTo,
+ this.fromEmail,
+ "",
+ nsIEnigmail.SAVE_MESSAGE | nsIEnigmail.SEND_ENCRYPTED |
+ nsIEnigmail.SEND_ALWAYS_TRUST | nsIEnigmail.SEND_PGP_MIME,
+ exitCode, {}, errorMsg);
+ let outHandler = {
+ 'deliverData': function(s) { EnigmailLog.DEBUG("got: " + s + "\n"); },
+ 'deliverEOF': function() { EnigmailLog.DEBUG("got EOF\n"); }
+ };
+ let enc = Cc["@enigmail.net/enigmail/composesecure;1"].createInstance(Ci.nsIMsgComposeSecure);
+ enc.beginCryptoEncapsulation({},"",null,null,null,false);
+ EnigmailLog.DEBUG("emit: " + jsmime.headeremitter.emitStructuredHeader("content-type", "lala", {}));
+
+ EnigmailLog.DEBUG("########## msg ###########\n");
+ EnigmailLog.DEBUG(msg);
+ EnigmailLog.DEBUG("########## end ###########\n");
+ EnigmailLog.DEBUG("exit: " + JSON.stringify(exitCode) + "\n");
+ EnigmailLog.DEBUG("error: " + JSON.stringify(errorMsg) + "\n");
+ }
+ catch(ex) {
+ EnigmailLog.DEBUG("caught: " + ex + "\n");
+ }
+ },
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(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 & 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)) {
+ EnigmailLog.DEBUG("pgp mime part " + mime.partName + "\n");
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) || "";
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("") === "pEp") {
let subject = getPepSubject(data);
if (subject) {
self.mime.headers.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 + "\n";
o.data += "Content-Type: " + ct + "\n";
let h = m.headerNames;
while (h.hasMore()) {
let hdr = h.getNext();
if (hdr.search(/^content-type$/i) < 0) {
try {
EnigmailLog.DEBUG("decryptPermanently.jsm: getUnstructuredHeader: hdr=" + hdr + "\n");
let hdrVal = m.getUnstructuredHeader(hdr.toLowerCase());
o.data += hdr + ": " + hdrVal + "\n";
}
catch (ex) {
EnigmailLog.DEBUG("decryptPermanently.jsm: getUnstructuredHeader: exception " + ex.toString() + "\n");
}
}
}
EnigmailLog.DEBUG("decryptPermanently.jsm: getUnstructuredHeader: done\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.toString() + "\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, "
\n");
}
if (i == 0 && this.mime.headers.subject && this.mime.headers.subject[0] === "pEp" &&
mime.partName.length > 0 && mime.partName.search(/[^01.]/) < 0) {
let m = EnigmailMime.extractSubjectFromBody(plaintext);
if (m) {
plaintext = m.messageBody;
this.mime.headers.subject = [m.subject];
}
}
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 EnigmailMime.getBoundary(shdr);
}
catch (e) {
EnigmailLog.DEBUG("decryptPermanently.jsm: getBoundary: " + e + "\n");
return null;
}
}
function getCharset(shdr) {
try {
shdr = String(shdr);
return EnigmailMime.getParameter(shdr, 'charset').toLowerCase();
}
catch (e) {
EnigmailLog.DEBUG("decryptPermanently.jsm: getCharset: " + e + "\n");
return null;
}
}
function getProtocol(shdr) {
try {
shdr = String(shdr);
return EnigmailMime.getProtocol(shdr).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 "";
}
}
function getPepSubject(mimeString) {
EnigmailLog.DEBUG("decryptPermanently.jsm: getPepSubject()\n");
let subject = null;
let emitter = {
ct: "",
firstPlainText: false,
startPart: function(partNum, headers) {
EnigmailLog.DEBUG("decryptPermanently.jsm: getPepSubject.startPart: partNum=" + partNum + "\n");
try {
this.ct = String(headers.getRawHeader("content-type")).toLowerCase();
if (!subject && !this.firstPlainText) {
let s = headers.getRawHeader("subject");
if (s) {
subject = String(s);
this.firstPlainText = true;
}
}
}
catch (ex) {
this.ct = "";
}
},
endPart: function(partNum) {},
deliverPartData: function(partNum, data) {
EnigmailLog.DEBUG("decryptPermanently.jsm: getPepSubject.deliverPartData: partNum=" + partNum + " ct=" + this.ct + "\n");
if (!this.firstPlainText && this.ct.search(/^text\/plain/) === 0) {
// check data
this.firstPlainText = true;
let o = EnigmailMime.extractSubjectFromBody(data);
if (o) {
subject = o.subject;
}
}
}
};
let opt = {
strformat: "unicode",
bodyformat: "decode"
};
try {
let p = new jsmime.MimeParser(emitter, opt);
p.deliverData(mimeString);
}
catch (ex) {}
return subject;
}
/**
* Lazy deletion of original messages
*/
function deleteOriginalMail(msgHdr) {
EnigmailLog.DEBUG("decryptPermanently.jsm: deleteOriginalMail(" + msgHdr.messageKey + ")\n");
let delMsg = function() {
try {
EnigmailLog.DEBUG("decryptPermanently.jsm: deleting original message " + msgHdr.messageKey + "\n");
let folderInfoObj = {};
msgHdr.folder.getDBFolderInfoAndDB(folderInfoObj).DeleteMessage(msgHdr.messageKey, null, true);
}
catch (e) {
EnigmailLog.DEBUG("decryptPermanently.jsm: deletion failed. Error: " + e.toString() + "\n");
}
};
EnigmailTimer.setTimeout(delMsg, 500);
}
diff --git a/package/encryption.jsm b/package/encryption.jsm
index 647ca6fd..6e995d98 100644
--- a/package/encryption.jsm
+++ b/package/encryption.jsm
@@ -1,491 +1,493 @@
/*global Components: false, EnigmailCore: false, EnigmailLog: false, EnigmailPrefs: false, EnigmailApp: false, EnigmailLocale: false, EnigmailDialog: 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 = ["EnigmailEncryption"];
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
+Cu.import("resource://enigmail/lazy.jsm"); /*global EnigmailLazy: false */
Cu.import("resource://enigmail/core.jsm");
Cu.import("resource://enigmail/data.jsm"); /*global EnigmailData: false */
Cu.import("resource://enigmail/log.jsm");
Cu.import("resource://enigmail/prefs.jsm");
Cu.import("resource://enigmail/app.jsm");
Cu.import("resource://enigmail/locale.jsm");
Cu.import("resource://enigmail/dialog.jsm");
Cu.import("resource://enigmail/gpgAgent.jsm"); /*global EnigmailGpgAgent: false */
Cu.import("resource://enigmail/gpg.jsm"); /*global EnigmailGpg: false */
Cu.import("resource://enigmail/errorHandling.jsm"); /*global EnigmailErrorHandling: false */
Cu.import("resource://enigmail/execution.jsm"); /*global EnigmailExecution: false */
Cu.import("resource://enigmail/files.jsm"); /*global EnigmailFiles: false */
Cu.import("resource://enigmail/passwords.jsm"); /*global EnigmailPassword: false */
Cu.import("resource://enigmail/funcs.jsm"); /*global EnigmailFuncs: false */
Cu.import("resource://enigmail/keyRing.jsm"); /*global EnigmailKeyRing: false */
+const getGpgAgent = EnigmailLazy.loader("enigmail/gpgAgent.jsm", "EnigmailGpgAgent");
const nsIEnigmail = Ci.nsIEnigmail;
var EC = EnigmailCore;
const gMimeHashAlgorithms = [null, "sha1", "ripemd160", "sha256", "sha384", "sha512", "sha224", "md5"];
const ENC_TYPE_MSG = 0;
const ENC_TYPE_ATTACH_BINARY = 1;
const ENC_TYPE_ATTACH_ASCII = 2;
const GPG_COMMENT_OPT = "Using GnuPG with %s - http://www.enigmail.net/";
const EnigmailEncryption = {
getEncryptCommand: function(fromMailAddr, toMailAddr, bccMailAddr, hashAlgorithm, sendFlags, isAscii, errorMsgObj) {
EnigmailLog.DEBUG("encryption.jsm: getEncryptCommand: hashAlgorithm=" + hashAlgorithm + "\n");
try {
fromMailAddr = EnigmailFuncs.stripEmail(fromMailAddr);
toMailAddr = EnigmailFuncs.stripEmail(toMailAddr);
bccMailAddr = EnigmailFuncs.stripEmail(bccMailAddr);
}
catch (ex) {
errorMsgObj.value = EnigmailLocale.getString("invalidEmail");
return null;
}
var defaultSend = sendFlags & nsIEnigmail.SEND_DEFAULT;
var signMsg = sendFlags & nsIEnigmail.SEND_SIGNED;
var encryptMsg = sendFlags & nsIEnigmail.SEND_ENCRYPTED;
var usePgpMime = sendFlags & nsIEnigmail.SEND_PGP_MIME;
var useDefaultComment = false;
try {
useDefaultComment = EnigmailPrefs.getPref("useDefaultComment");
}
catch (ex) {}
var hushMailSupport = false;
try {
hushMailSupport = EnigmailPrefs.getPref("hushMailSupport");
}
catch (ex) {}
var detachedSig = (usePgpMime || (sendFlags & nsIEnigmail.SEND_ATTACHMENT)) && signMsg && !encryptMsg;
var toAddrList = toMailAddr.split(/\s*,\s*/);
var bccAddrList = bccMailAddr.split(/\s*,\s*/);
var k;
var encryptArgs = EnigmailGpg.getStandardArgs(true);
if (!useDefaultComment)
encryptArgs = encryptArgs.concat(["--comment", GPG_COMMENT_OPT.replace(/%s/, EnigmailApp.getName())]);
var angledFromMailAddr = ((fromMailAddr.search(/^0x/) === 0) || hushMailSupport) ?
fromMailAddr : "<" + fromMailAddr + ">";
angledFromMailAddr = angledFromMailAddr.replace(/(["'`])/g, "\\$1");
if (signMsg && hashAlgorithm) {
encryptArgs = encryptArgs.concat(["--digest-algo", hashAlgorithm]);
}
if (encryptMsg) {
switch (isAscii) {
case ENC_TYPE_MSG:
encryptArgs.push("-a");
encryptArgs.push("-t");
break;
case ENC_TYPE_ATTACH_ASCII:
encryptArgs.push("-a");
}
encryptArgs.push("--encrypt");
if (signMsg)
encryptArgs.push("--sign");
if (sendFlags & nsIEnigmail.SEND_ALWAYS_TRUST) {
encryptArgs.push("--trust-model");
encryptArgs.push("always");
}
if ((sendFlags & nsIEnigmail.SEND_ENCRYPT_TO_SELF) && fromMailAddr)
encryptArgs = encryptArgs.concat(["--encrypt-to", angledFromMailAddr]);
for (k = 0; k < toAddrList.length; k++) {
toAddrList[k] = toAddrList[k].replace(/'/g, "\\'");
if (toAddrList[k].length > 0) {
encryptArgs.push("-r");
if (toAddrList[k].search(/^GROUP:/) === 0) {
// groups from gpg.conf file
encryptArgs.push(toAddrList[k].substr(6));
}
else {
encryptArgs.push((hushMailSupport || (toAddrList[k].search(/^0x/) === 0)) ? toAddrList[k] : "<" + toAddrList[k] + ">");
}
}
}
for (k = 0; k < bccAddrList.length; k++) {
bccAddrList[k] = bccAddrList[k].replace(/'/g, "\\'");
if (bccAddrList[k].length > 0) {
encryptArgs.push("--hidden-recipient");
encryptArgs.push((hushMailSupport || (bccAddrList[k].search(/^0x/) === 0)) ? bccAddrList[k] : "<" + bccAddrList[k] + ">");
}
}
}
else if (detachedSig) {
encryptArgs = encryptArgs.concat(["-s", "-b"]);
switch (isAscii) {
case ENC_TYPE_MSG:
encryptArgs = encryptArgs.concat(["-a", "-t"]);
break;
case ENC_TYPE_ATTACH_ASCII:
encryptArgs.push("-a");
}
}
else if (signMsg) {
encryptArgs = encryptArgs.concat(["-t", "--clearsign"]);
}
if (fromMailAddr) {
encryptArgs = encryptArgs.concat(["-u", angledFromMailAddr]);
}
return encryptArgs;
},
/**
* Determine if the sender key ID or user ID can be used for signing and/or encryption
*
* @param sendFlags: Number - the send Flags; need to contain SEND_SIGNED and/or SEND_ENCRYPTED
* @param fromMailAddr: String - the sender email address or key ID
*
* @return Object:
* - keyId: String - the found key ID, or null if fromMailAddr is not valid
* - errorMsg: String - the erorr message if key not valid, or null if key is valid
*/
determineOwnKeyUsability: function(sendFlags, fromMailAddr) {
EnigmailLog.DEBUG("encryption.jsm: determineOwnKeyUsability: sendFlags=" + sendFlags + ", sender=" + fromMailAddr + "\n");
let keyList = [];
let ret = {
keyId: null,
errorMsg: null
};
let sign = (sendFlags & nsIEnigmail.SEND_SIGNED ? true : false);
let encrypt = (sendFlags & nsIEnigmail.SEND_ENCRYPTED ? true : false);
if (fromMailAddr.search(/^(0x)?[A-Z0-9]+$/) === 0) {
// key ID specified
let key = EnigmailKeyRing.getKeyById(fromMailAddr);
keyList.push(key);
}
else {
// email address specified
keyList = EnigmailKeyRing.getKeysByUserId(fromMailAddr);
}
if (keyList.length === 0) {
ret.errorMsg = EnigmailLocale.getString("errorOwnKeyUnusable", fromMailAddr);
return ret;
}
if (sign) {
keyList = keyList.reduce(function _f(p, keyObj) {
if (keyObj.getSigningValidity().keyValid) p.push(keyObj);
return p;
}, []);
}
if (encrypt) {
keyList = keyList.reduce(function _f(p, keyObj) {
if (keyObj && keyObj.getEncryptionValidity().keyValid) p.push(keyObj);
return p;
}, []);
}
if (keyList.length === 0) {
if (sign) {
ret.errorMsg = EnigmailErrorHandling.determineInvSignReason(fromMailAddr);
}
else {
ret.errorMsg = EnigmailErrorHandling.determineInvRcptReason(fromMailAddr);
}
}
else {
ret.keyId = keyList[0].fpr;
}
return ret;
},
encryptMessageStart: function(win, uiFlags, fromMailAddr, toMailAddr, bccMailAddr,
hashAlgorithm, sendFlags, listener, statusFlagsObj, errorMsgObj) {
EnigmailLog.DEBUG("encryption.jsm: encryptMessageStart: uiFlags=" + uiFlags + ", from " + fromMailAddr + " to " + toMailAddr + ", hashAlgorithm=" + hashAlgorithm + " (" + EnigmailData.bytesToHex(
EnigmailData.pack(sendFlags, 4)) + ")\n");
let keyUseability = this.determineOwnKeyUsability(sendFlags, fromMailAddr);
if (!keyUseability.keyId) {
EnigmailLog.DEBUG("encryption.jsm: encryptMessageStart: own key invalid\n");
errorMsgObj.value = keyUseability.errorMsg;
statusFlagsObj.value = nsIEnigmail.INVALID_RECIPIENT | nsIEnigmail.NO_SECKEY | nsIEnigmail.DISPLAY_MESSAGE;
return null;
}
var pgpMime = uiFlags & nsIEnigmail.UI_PGP_MIME;
var hashAlgo = gMimeHashAlgorithms[EnigmailPrefs.getPref("mimeHashAlgorithm")];
if (hashAlgorithm) {
hashAlgo = hashAlgorithm;
}
errorMsgObj.value = "";
if (!sendFlags) {
EnigmailLog.DEBUG("encryption.jsm: encryptMessageStart: NO ENCRYPTION!\n");
errorMsgObj.value = EnigmailLocale.getString("notRequired");
return null;
}
if (!EnigmailCore.getService(win)) {
EnigmailLog.ERROR("encryption.jsm: encryptMessageStart: not yet initialized\n");
errorMsgObj.value = EnigmailLocale.getString("notInit");
return null;
}
var encryptArgs = EnigmailEncryption.getEncryptCommand(fromMailAddr, toMailAddr, bccMailAddr, hashAlgo, sendFlags, ENC_TYPE_MSG, errorMsgObj);
if (!encryptArgs)
return null;
var signMsg = sendFlags & nsIEnigmail.SEND_SIGNED;
- var proc = EnigmailExecution.execStart(EnigmailGpgAgent.agentPath, encryptArgs, signMsg, win, listener, statusFlagsObj);
+ var proc = EnigmailExecution.execStart(getGpgAgent().agentPath, encryptArgs, signMsg, win, listener, statusFlagsObj);
if (statusFlagsObj.value & nsIEnigmail.MISSING_PASSPHRASE) {
EnigmailLog.ERROR("encryption.jsm: encryptMessageStart: Error - no passphrase supplied\n");
errorMsgObj.value = "";
}
if (pgpMime && errorMsgObj.value) {
EnigmailDialog.alert(win, errorMsgObj.value);
}
return proc;
},
encryptMessageEnd: function(fromMailAddr, stderrStr, exitCode, uiFlags, sendFlags, outputLen, retStatusObj) {
EnigmailLog.DEBUG("encryption.jsm: encryptMessageEnd: uiFlags=" + uiFlags + ", sendFlags=" + EnigmailData.bytesToHex(EnigmailData.pack(sendFlags, 4)) + ", outputLen=" + outputLen + "\n");
var pgpMime = uiFlags & nsIEnigmail.UI_PGP_MIME;
var defaultSend = sendFlags & nsIEnigmail.SEND_DEFAULT;
var signMsg = sendFlags & nsIEnigmail.SEND_SIGNED;
var encryptMsg = sendFlags & nsIEnigmail.SEND_ENCRYPTED;
retStatusObj.statusFlags = 0;
retStatusObj.errorMsg = "";
retStatusObj.blockSeparation = "";
if (!EnigmailCore.getService().initialized) {
EnigmailLog.ERROR("encryption.jsm: encryptMessageEnd: not yet initialized\n");
retStatusObj.errorMsg = EnigmailLocale.getString("notInit");
return -1;
}
EnigmailErrorHandling.parseErrorOutput(stderrStr, retStatusObj);
exitCode = EnigmailExecution.fixExitCode(exitCode, retStatusObj);
if ((exitCode === 0) && !outputLen) {
exitCode = -1;
}
if (exitCode !== 0 && (signMsg || encryptMsg)) {
// GnuPG might return a non-zero exit code, even though the message was correctly
// signed or encryped -> try to fix the exit code
var correctedExitCode = 0;
if (signMsg) {
if (!(retStatusObj.statusFlags & nsIEnigmail.SIG_CREATED)) correctedExitCode = exitCode;
}
if (encryptMsg) {
if (!(retStatusObj.statusFlags & nsIEnigmail.END_ENCRYPTION)) correctedExitCode = exitCode;
}
exitCode = correctedExitCode;
}
EnigmailLog.DEBUG("encryption.jsm: encryptMessageEnd: command execution exit code: " + exitCode + "\n");
if (retStatusObj.statusFlags & nsIEnigmail.DISPLAY_MESSAGE) {
if (retStatusObj.extendedStatus.search(/\bdisp:/) >= 0) {
retStatusObj.errorMsg = retStatusObj.statusMsg;
}
else {
if (fromMailAddr.search(/^0x/) === 0) {
fromMailAddr = fromMailAddr.substr(2);
}
if (fromMailAddr.search(/^[A-F0-9]{8,40}$/i) === 0) {
fromMailAddr = "[A-F0-9]+" + fromMailAddr;
}
let s = new RegExp("^(\\[GNUPG:\\] )?INV_(RECP|SGNR) [0-9]+ (\\<|0x)?" + fromMailAddr + "\\>?", "m");
if (retStatusObj.statusMsg.search(s) >= 0) {
retStatusObj.errorMsg += "\n\n" + EnigmailLocale.getString("keyError.resolutionAction");
}
else if (retStatusObj.statusMsg.length > 0) {
retStatusObj.errorMsg = retStatusObj.statusMsg;
}
}
}
else if (retStatusObj.statusFlags & nsIEnigmail.INVALID_RECIPIENT) {
retStatusObj.errorMsg = retStatusObj.statusMsg;
}
else if (exitCode !== 0) {
retStatusObj.errorMsg = EnigmailLocale.getString("badCommand");
}
return exitCode;
},
encryptMessage: function(parent, uiFlags, plainText, fromMailAddr, toMailAddr, bccMailAddr, sendFlags,
exitCodeObj, statusFlagsObj, errorMsgObj) {
EnigmailLog.DEBUG("enigmail.js: Enigmail.encryptMessage: " + plainText.length + " bytes from " + fromMailAddr + " to " + toMailAddr + " (" + sendFlags + ")\n");
exitCodeObj.value = -1;
statusFlagsObj.value = 0;
errorMsgObj.value = "";
if (!plainText) {
EnigmailLog.DEBUG("enigmail.js: Enigmail.encryptMessage: NO ENCRYPTION!\n");
exitCodeObj.value = 0;
EnigmailLog.DEBUG(" <=== encryptMessage()\n");
return plainText;
}
var defaultSend = sendFlags & nsIEnigmail.SEND_DEFAULT;
var signMsg = sendFlags & nsIEnigmail.SEND_SIGNED;
var encryptMsg = sendFlags & nsIEnigmail.SEND_ENCRYPTED;
if (encryptMsg) {
// First convert all linebreaks to newlines
plainText = plainText.replace(/\r\n/g, "\n");
plainText = plainText.replace(/\r/g, "\n");
// we need all data in CRLF according to RFC 4880
plainText = plainText.replace(/\n/g, "\r\n");
}
var listener = EnigmailExecution.newSimpleListener(
function _stdin(pipe) {
pipe.write(plainText);
pipe.close();
},
function _done(exitCode) {});
var proc = EnigmailEncryption.encryptMessageStart(parent, uiFlags,
fromMailAddr, toMailAddr, bccMailAddr,
null, sendFlags,
listener, statusFlagsObj, errorMsgObj);
if (!proc) {
exitCodeObj.value = -1;
EnigmailLog.DEBUG(" <=== encryptMessage()\n");
return "";
}
// Wait for child pipes to close
proc.wait();
var retStatusObj = {};
exitCodeObj.value = EnigmailEncryption.encryptMessageEnd(fromMailAddr, EnigmailData.getUnicodeData(listener.stderrData), listener.exitCode,
uiFlags, sendFlags,
listener.stdoutData.length,
retStatusObj);
statusFlagsObj.value = retStatusObj.statusFlags;
statusFlagsObj.statusMsg = retStatusObj.statusMsg;
errorMsgObj.value = retStatusObj.errorMsg;
if ((exitCodeObj.value === 0) && listener.stdoutData.length === 0)
exitCodeObj.value = -1;
if (exitCodeObj.value === 0) {
// Normal return
EnigmailLog.DEBUG(" <=== encryptMessage()\n");
return EnigmailData.getUnicodeData(listener.stdoutData);
}
// Error processing
EnigmailLog.DEBUG("enigmail.js: Enigmail.encryptMessage: command execution exit code: " + exitCodeObj.value + "\n");
return "";
},
encryptAttachment: function(parent, fromMailAddr, toMailAddr, bccMailAddr, sendFlags, inFile, outFile,
exitCodeObj, statusFlagsObj, errorMsgObj) {
EnigmailLog.DEBUG("encryption.jsm: EnigmailEncryption.encryptAttachment infileName=" + inFile.path + "\n");
statusFlagsObj.value = 0;
sendFlags |= nsIEnigmail.SEND_ATTACHMENT;
let asciiArmor = false;
try {
asciiArmor = EnigmailPrefs.getPrefBranch().getBoolPref("inlineAttachAsciiArmor");
}
catch (ex) {}
const asciiFlags = (asciiArmor ? ENC_TYPE_ATTACH_ASCII : ENC_TYPE_ATTACH_BINARY);
let args = EnigmailEncryption.getEncryptCommand(fromMailAddr, toMailAddr, bccMailAddr, "", sendFlags, asciiFlags, errorMsgObj);
if (!args) {
return null;
}
const signMessage = (sendFlags & nsIEnigmail.SEND_SIGNED);
if (signMessage) {
args = args.concat(EnigmailPassword.command());
}
//const inFilePath = EnigmailFiles.getEscapedFilename(EnigmailFiles.getFilePathReadonly(inFile.QueryInterface(Ci.nsIFile)));
const fileContents = EnigmailFiles.readBinaryFile(inFile.QueryInterface(Ci.nsIFile));
const inFileName = inFile.QueryInterface(Ci.nsIFile).leafName;
const outFilePath = EnigmailFiles.getEscapedFilename(EnigmailFiles.getFilePathReadonly(outFile.QueryInterface(Ci.nsIFile)));
args = args.concat(["--yes", "-o", outFilePath, "--set-filename", inFileName]);
let cmdErrorMsgObj = {};
const msg = EnigmailExecution.execCmd(EnigmailGpgAgent.agentPath, args, fileContents, exitCodeObj, statusFlagsObj, {}, cmdErrorMsgObj);
if (exitCodeObj.value !== 0) {
if (cmdErrorMsgObj.value) {
errorMsgObj.value = EnigmailFiles.formatCmdLine(EnigmailGpgAgent.agentPath, args);
errorMsgObj.value += "\n" + cmdErrorMsgObj.value;
}
else {
errorMsgObj.value = "An unknown error has occurred";
}
return "";
}
return msg;
},
registerOn: function(target) {
target.encryptMessage = EnigmailEncryption.encryptMessage;
target.encryptAttachment = EnigmailEncryption.encryptAttachment;
}
};
diff --git a/package/filters.jsm b/package/filters.jsm
index 1f23a3d1..5faf0871 100644
--- a/package/filters.jsm
+++ b/package/filters.jsm
@@ -1,347 +1,403 @@
/*global Components: false, EnigmailDecryptPermanently: false, EnigmailCore: false, EnigmailLog: false, EnigmailLocale: false, EnigmailLazy: 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 = ["EnigmailFilters"];
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://enigmail/lazy.jsm");
Cu.import("resource://enigmail/locale.jsm");
Cu.import("resource://enigmail/core.jsm");
-Cu.import("resource://enigmail/decryptPermanently.jsm");
+Cu.import("resource://enigmail/decryptPermanently.jsm"); /* global EnigmailDecryptPermanently: false */
+Cu.import("resource://enigmail/encryptPermanently.jsm"); /* global EnigmailEncryptPermanently: false */
Cu.import("resource://enigmail/log.jsm");
Cu.import("resource://enigmail/funcs.jsm"); /* global EnigmailFuncs: false */
Cu.import("resource://enigmail/streams.jsm"); /* global EnigmailStreams: false */
Cu.import("resource://enigmail/constants.jsm"); /* global EnigmailConstants: false */
Cu.import("resource://enigmail/data.jsm"); /* global EnigmailData: false */
+Cu.import("resource://enigmail/keyRing.jsm"); /* global EnigmailKeyRing: false */
Cu.import("resource:///modules/jsmime.jsm"); /*global jsmime: false*/
const getDialog = EnigmailLazy.loader("enigmail/dialog.jsm", "EnigmailDialog");
var gNewMailListenerInitiated = false;
/**
* filter action for creating a decrypted version of the mail and
* deleting the original mail at the same time
*/
const filterActionMoveDecrypt = {
id: EnigmailConstants.FILTER_MOVE_DECRYPT,
name: EnigmailLocale.getString("filter.decryptMove.label"),
value: "movemessage",
apply: function(aMsgHdrs, aActionValue, aListener, aType, aMsgWindow) {
EnigmailLog.DEBUG("filters.jsm: filterActionMoveDecrypt: Move to: " + aActionValue + "\n");
var msgHdrs = [];
for (var i = 0; i < aMsgHdrs.length; i++) {
msgHdrs.push(aMsgHdrs.queryElementAt(i, Ci.nsIMsgDBHdr));
}
EnigmailDecryptPermanently.dispatchMessages(msgHdrs, aActionValue, aListener, true);
},
isValidForType: function(type, scope) {
return true;
},
validateActionValue: function(value, folder, type) {
getDialog().alert(null, EnigmailLocale.getString("filter.decryptMove.warnExperimental"));
if (value === "") {
return EnigmailLocale.getString("filter.folderRequired");
}
return null;
},
allowDuplicates: false,
isAsync: true,
needsBody: true
};
/**
* filter action for creating a decrypted copy of the mail, leaving the original
* message untouched
*/
const filterActionCopyDecrypt = {
id: EnigmailConstants.FILTER_COPY_DECRYPT,
name: EnigmailLocale.getString("filter.decryptCopy.label"),
value: "copymessage",
apply: function(aMsgHdrs, aActionValue, aListener, aType, aMsgWindow) {
EnigmailLog.DEBUG("filters.jsm: filterActionCopyDecrypt: Copy to: " + aActionValue + "\n");
var msgHdrs = [];
for (var i = 0; i < aMsgHdrs.length; i++) {
msgHdrs.push(aMsgHdrs.queryElementAt(i, Ci.nsIMsgDBHdr));
}
EnigmailDecryptPermanently.dispatchMessages(msgHdrs, aActionValue, aListener, false);
},
isValidForType: function(type, scope) {
return true;
},
validateActionValue: function(value, folder, type) {
if (value === "") {
return EnigmailLocale.getString("filter.folderRequired");
}
return null;
},
allowDuplicates: false,
isAsync: true,
needsBody: true
};
+/**
+ * filter action for to encrypt a mail to a specific key
+ */
+const filterActionEncrypt = {
+ id: EnigmailConstants.FILTER_ENCRYPT,
+ name: EnigmailLocale.getString("filter.encrypt.label"),
+ apply: function(aMsgHdrs, aActionValue, aListener, aType, aMsgWindow) {
+ EnigmailLog.DEBUG("filters.jsm: filterActionEncrypt: Encrypt to: " + aActionValue + "\n");
+
+ var msgHdrs = [];
+
+ for (var i = 0; i < aMsgHdrs.length; i++) {
+ let msg = aMsgHdrs.queryElementAt(i, Ci.nsIMsgDBHdr);
+
+ EnigmailDecryptPermanently.decryptMessage(msg,msg.folder.folderURL,true)
+ .then(function(value) {
+ EnigmailLog.DEBUG("decrypt: " + JSON.stringify(value) + "\n");
+ let newMsg = msg.folder.GetMessageHeader(value);
+ EnigmailEncryptPermanently.encryptMessage(newMsg,aActionValue);
+ });
+ }
+ },
+
+ isValidForType: function(type, scope) {
+ return true;
+ },
+
+ validateActionValue: function(value, folder, type) {
+
+ EnigmailLog.DEBUG("filters.jsm: validateActionValue: Encrypt to: " + value + "\n");
+ if (value === "") {
+ return EnigmailLocale.getString("filter.keyRequired");
+ }
+
+ let keyFound = EnigmailKeyRing.getKeyById(value);
+
+ if (keyFound === null) {
+ EnigmailLog.DEBUG("filters.jsm: failed to find key by id: " + value + "\n");
+ keyFound = EnigmailKeyRing.getValidKeyForRecipient(value);
+ }
+
+ if (keyFound === null) {
+ return EnigmailLocale.getString("filter.keyNotFound", [value]);
+ }
+
+ return null;
+ },
+
+ allowDuplicates: false,
+ isAsync: true,
+ needsBody: true
+};
+
function initNewMailListener() {
EnigmailLog.DEBUG("filters.jsm: initNewMailListener()\n");
if (!gNewMailListenerInitiated) {
let notificationService = Cc["@mozilla.org/messenger/msgnotificationservice;1"]
.getService(Ci.nsIMsgFolderNotificationService);
notificationService.addListener(newMailListener, notificationService.msgAdded);
}
gNewMailListenerInitiated = true;
}
function getIdentityForSender(senderEmail, msgServer) {
let accountManager = Cc["@mozilla.org/messenger/account-manager;1"].getService(Ci.nsIMsgAccountManager);
let identities = accountManager.getIdentitiesForServer(msgServer);
for (let i = 0; i < identities.length; i++) {
let id = identities.queryElementAt(i, Ci.nsIMsgIdentity);
if (id.email.toLowerCase() === senderEmail.toLowerCase()) {
return id;
}
}
return null;
}
var consumerList = [];
function JsmimeEmitter(requireBody) {
this.requireBody = requireBody;
this.mimeTree = {
partNum: "",
headers: null,
body: "",
parent: null,
subParts: []
};
this.stack = [];
this.currPartNum = "";
}
JsmimeEmitter.prototype = {
createPartObj: function(partNum, headers, parent) {
return {
partNum: partNum,
headers: headers,
body: "",
parent: parent,
subParts: []
};
},
getMimeTree: function() {
return this.mimeTree.subParts[0];
},
/** JSMime API **/
startMessage: function() {
this.currentPart = this.mimeTree;
},
endMessage: function() {},
startPart: function(partNum, headers) {
EnigmailLog.DEBUG("filters.jsm: JsmimeEmitter.startPart: partNum=" + partNum + "\n");
//this.stack.push(partNum);
let newPart = this.createPartObj(partNum, headers, this.currentPart);
if (partNum.indexOf(this.currPartNum) === 0) {
// found sub-part
this.currentPart.subParts.push(newPart);
}
else {
// found same or higher level
this.currentPart.subParts.push(newPart);
}
this.currPartNum = partNum;
this.currentPart = newPart;
},
endPart: function(partNum) {
EnigmailLog.DEBUG("filters.jsm: JsmimeEmitter.startPart: partNum=" + partNum + "\n");
this.currentPart = this.currentPart.parent;
},
deliverPartData: function(partNum, data) {
EnigmailLog.DEBUG("filters.jsm: JsmimeEmitter.deliverPartData: partNum=" + partNum + "\n");
if (this.requireBody) {
if (typeof(data) === "string") {
this.currentPart.body += data;
}
else {
this.currentPart.body += EnigmailData.arrayBufferToString(data);
}
}
}
};
function processIncomingMail(url, requireBody, aMsgHdr) {
EnigmailLog.DEBUG("filters.jsm: processIncomingMail()\n");
let inputStream = EnigmailStreams.newStringStreamListener(msgData => {
let opt = {
strformat: "unicode",
bodyformat: "decode"
};
try {
let e = new JsmimeEmitter(requireBody);
let p = new jsmime.MimeParser(e, opt);
p.deliverData(msgData);
for (let c of consumerList) {
try {
c.consumeMessage(e.getMimeTree(), msgData, aMsgHdr);
}
catch (ex) {
EnigmailLog.DEBUG("filters.jsm: processIncomingMail: exception: " + ex.toString() + "\n");
}
}
}
catch (ex) {}
});
try {
let channel = EnigmailStreams.createChannel(url);
channel.asyncOpen(inputStream, null);
}
catch (e) {
EnigmailLog.DEBUG("filters.jsm: processIncomingMail: open stream exception " + e.toString() + "\n");
}
}
function getRequireMessageProcessing(aMsgHdr) {
let isInbox = aMsgHdr.folder.getFlag(Ci.nsMsgFolderFlags.CheckNew) || aMsgHdr.folder.getFlag(Ci.nsMsgFolderFlags.Inbox);
let requireBody = false;
let inboxOnly = true;
let selfSentOnly = false;
let processReadMail = false;
for (let c of consumerList) {
if (!c.incomingMailOnly) {
inboxOnly = false;
}
if (!c.unreadOnly) {
processReadMail = true;
}
if (!c.headersOnly) {
requireBody = true;
}
if (c.selfSentOnly) {
selfSentOnly = true;
}
}
if (!processReadMail && aMsgHdr.isRead) return null;
if (inboxOnly && !isInbox) return null;
if (selfSentOnly) {
let sender = EnigmailFuncs.parseEmails(aMsgHdr.author, true);
let id = null;
if (sender && sender[0]) {
id = getIdentityForSender(sender[0].email, aMsgHdr.folder.server);
}
if (!id) return null;
}
EnigmailLog.DEBUG("filters.jsm: getRequireMessageProcessing: author: " + aMsgHdr.author + "\n");
let messenger = Cc["@mozilla.org/messenger;1"].getService(Ci.nsIMessenger);
let msgSvc = messenger.messageServiceFromURI(aMsgHdr.folder.getUriForMsg(aMsgHdr));
let u = {};
msgSvc.GetUrlForUri(aMsgHdr.folder.getUriForMsg(aMsgHdr), u, null);
let op = (u.value.spec.indexOf("?") > 0 ? "&" : "?");
let url = u.value.spec + op + "header=enigmailFilter";
return {
url: url,
requireBody: requireBody
};
}
const newMailListener = {
msgAdded: function(aMsgHdr) {
EnigmailLog.DEBUG("filters.jsm: newMailListener.msgAdded() - got new mail in " + aMsgHdr.folder.prettiestName + "\n");
if (consumerList.length === 0) return;
let ret = getRequireMessageProcessing(aMsgHdr);
if (ret) {
processIncomingMail(ret.url, ret.requireBody, aMsgHdr);
}
}
};
/**
messageStructure - Object:
- partNum: String - MIME part number
- headers: Object(nsIStructuredHeaders) - MIME part headers
- body: String or typedarray - the body part
- parent: Object(messageStructure) - link to the parent part
- subParts: Array of Object(messageStructure) - array of the sub-parts
*/
const EnigmailFilters = {
registerAll: function() {
var filterService = Cc["@mozilla.org/messenger/services/filters;1"].getService(Ci.nsIMsgFilterService);
filterService.addCustomAction(filterActionMoveDecrypt);
filterService.addCustomAction(filterActionCopyDecrypt);
+ filterService.addCustomAction(filterActionEncrypt);
initNewMailListener();
},
/**
* add a new consumer to listen to new mails
*
* @param consumer - Object
* - headersOnly: Boolean - needs full message body? [FUTURE]
* - incomingMailOnly: Boolean - only work on folder(s) that obtain new mail
* (Inbox and folders that listen to new mail)
* - unreadOnly: Boolean - only process unread mails
* - selfSentOnly: Boolean - only process mails with sender Email == Account Email
* - consumeMessage: function(messageStructure, rawMessageData, nsIMsgHdr)
*/
addNewMailConsumer: function(consumer) {
EnigmailLog.DEBUG("filters.jsm: addNewMailConsumer()\n");
consumerList.push(consumer);
},
removeNewMailConsumer: function(consumer) {
}
};
diff --git a/ui/locale/en-US/enigmail.properties b/ui/locale/en-US/enigmail.properties
index 77485451..367fac51 100644
--- a/ui/locale/en-US/enigmail.properties
+++ b/ui/locale/en-US/enigmail.properties
@@ -1,772 +1,775 @@
Enigmail=Enigmail
#####################################################################
# Strings used within enigmailCommon.js and enigmailCommon.jsm
#####################################################################
enigAlert=Enigmail Alert
enigConfirm=Enigmail Confirm
enigInfo=Enigmail Information
enigError=Enigmail Error
enigPrompt=Enigmail Prompt
dlgYes=&Yes
dlgNo=&No
dlgKeepSetting=Remember my answer and do not ask me again
dlgNoPrompt=Do not show me this dialog again
dlg.button.delete=&Delete
dlg.button.cancel=&Cancel
dlg.button.close=&Close
dlg.button.continue=Con&tinue
dlg.button.skip=&Skip
dlg.button.overwrite=&Overwrite
dlg.button.view=&View
dlg.button.retry=&Retry
dlg.button.ignore=&Ignore
dlg.button.ok=&OK
repeatPrefix=\n\nThis alert will repeat %S
repeatSuffixSingular=more time.
repeatSuffixPlural=more times.
noRepeat=\n\nThis alert will not repeat until you upgrade Enigmail.
pgpNotSupported=You seem to be using Enigmail together with PGP 6.x\n\nUnfortunately, PGP 6.x has a number of issues that prevent Enigmail from working correctly. Therefore, Enigmail does not support PGP 6.x anymore; please switch to GnuPG (GPG) instead.\n\nIf you need help on switching to GnuPG, check the Help section of the Enigmail homepage.
initErr.howToFixIt=In order to use Enigmail, GnuPG is required. If you did not install GnuPG yet, the easiest way to do this is using the "Setup Wizard" button below.
initErr.setupWizard.button=&Setup Wizard
passphraseCleared=The passphrase has been cleared.
cannotClearPassphrase=You are using a non-standard tool (such as gnome-keyring) for passphrase handling. Clearing the passphrase is therefore not possible from within Enigmail.
noPhotoAvailable=No Photo available
debugLog.title=Enigmail Debug Log
error.photoPathNotReadable=Photo path '%S' is not readable
generalError=Error: %S
# Strings in configure.jsm
enigmailCommon.versionSignificantlyChanged=This new version of Enigmail has significant changes in the handling of preferences and options. We tried to transfer the old settings to this new version. However, we cannot cover all cases automatically. Please double check the resulting new preferences and options.
enigmailCommon.checkPreferences=Check Preferences ...
preferences.defaultToPgpMime=We have changed the default message encoding in Enigmail from Inline-PGP to PGP/MIME. We recommend you keep this as default.\n\nIf you still wish to use Inline-PGP by default, you can do so in the Account Settings under OpenPGP Security.
#####################################################################
# Strings in enigmailAbout.js
#####################################################################
usingVersion=Running Enigmail version %S
enigmailPepVersion=Enigmail/p≡p version %S
usingAgent=Using %1$S executable %2$S to encrypt and decrypt
agentError=ERROR: Failed to access Enigmail core service!
#####################################################################
# Strings in enigmailKeygen.js
#####################################################################
accessError=Error in accessing Enigmail service
onlyGPG=Key generation only works with GnuPG (not with PGP)!
keygenComplete=Key generation completed! Identity <%S> will be used for signing.
revokeCertRecommended=We highly recommend to create a revocation certificate for your key. This certificate can be used to invalidate your key, e.g. in case your secret key gets lost or compromised. Do you want to create such a revocation certificate now?
keyMan.button.generateCert=&Generate Certificate
genCompleteNoSign=Key generation completed!
genGoing=Key generation already in progress!
passNoMatch=Passphrase entries do not match; please re-enter
passCheckBox=Please check box if specifying no passphrase for key
passUserName=Please specify user name for this identity
keygen.missingUserName=There is no name specified for the selected account/identity. Please enter a value in the field "Your name" in the account settings.
keygen.passCharProblem=You are using special characters in your passphrase. Unfortunately, this can cause troubles for other applications. We recommend you choose a passphrase consisting only of any of these characters:\na-z A-Z 0-9 /.;:-,!?(){}[]%*
passSpaceProblem=Due to technical reasons, your passphrase may not start or end with a space character.
changePassFailed=Changing the passphrase failed.
keyConfirm=Generate public and private keys for '%S'?
keyMan.button.generateKey=&Generate Key
keyAbort=Abort key generation?
keyMan.button.generateKeyAbort=&Abort Key Generation
keyMan.button.generateKeyContinue=&Continue Key Generation
expiryTooLong=You cannot create a key that expires in more than 100 years.
expiryTooLongShorter=You cannot create a key that expires in more than 90 years.
expiryTooShort=Your key must be valid for at least one day.
dsaSizeLimit=DSA signing keys are limited to 3072 bits. The key size will be reduced accordingly.
keyGenFailed=The key generation failed. Please check the Enigmail console (Menu Enigmail > Debugging Enigmail) for details.
setKeyExpirationDateFailed=The expiration date could not be changed
# Strings in enigmailMessengerOverlay.js
securityInfo=Enigmail Security Info\n\n
enigHeader=Enigmail:
enigContentNote=Enigmail: *Attachments to this message have not been signed or encrypted*\r\n\r\n
possiblyPgpMime=Possibly PGP/MIME encrypted or signed message; use 'Decrypt/Verify' function to verify
noDecrypted=No decrypted message to save!\nUse Save command from File menu
noMessage=No message to save!
useButton=Please use 'Decrypt/Verify' function to decrypt message
saveHeader=Enigmail: Save decrypted message
saveAttachmentHeader=Enigmail: Save decrypted attachment
noTempDir=Could not find a temporary directory to write to\nPlease set the TEMP environment variable
attachmentPgpKey=The attachment '%S' you are opening appears to be an OpenPGP key file.\n\nClick 'Import' to import the keys contained or 'View' to view the file contents in a browser window
beginPgpPart=********* *BEGIN ENCRYPTED or SIGNED PART* *********
endPgpPart=********** *END ENCRYPTED or SIGNED PART* **********
notePartEncrypted=Enigmail: *Parts of the message have NOT been signed or encrypted*
noteCutMessage=Enigmail: *Multiple message blocks found -- decryption/verification aborted*
decryptOkNoSig=Warning\n\nDecryption was successful, but the signature could not be verified correctly
msgOvl.button.contAnyway=&Continue Anyway
signature.verifiedOK=The signature for attachment %S was successfully verified
signature.verifyFailed=The signature for attachment %S could not be verified
attachment.noMatchToSignature=Could not match attachment '%S' to a signature file
attachment.noMatchFromSignature=Could not match signature file '%S' to an attachment
fixBrokenExchangeMsg.failed=Did not succeed to repair message.
enigmail.msgViewColumn.label=Enigmail
enigmailPep.msgViewColumn.label=Enigmail/p≡p
wksNoIdentity=This key is not linked to any of your email accounts. Please add an account for at least one of the following email addresse(s):\n\n%S
wksConfirmSuccess=Confirmation email sent.
wksConfirmFailure=Sending the confirmation email failed.
fileSaveError=Error in saving to file %S
autocrypt.importSetupKey.invalidMessage=Error - could not read setup message. The message seems to be corrupted. Please try to create a new setup message on your "other" device.
autocrypt.importSetupKey.invalidKey=Error - the key could not be imported. The key is either not supported by your version of GnuPG, or it got corrupted.
autocrypt.importSetupKey.wrongPasswd=The password you entered is wrong. Do you want to retry?
autocrypt.importSetupKey.success=The Autocrypt setup message was processed successfully. Autocrypt is now available for your account '%S'.
#####################################################################
# Strings in enigmailMsgComposeOverlay.js
#####################################################################
keysToExport=Select OpenPGP Keys to Insert
keysToUse=Select OpenPGP Key(s) to use for %S
pubKey=Public key for %S\n
windowLocked=Compose window is locked; send cancelled
sendUnencrypted=Failed to initialize Enigmail.\nSend unencrypted message?
composeSpecifyEmail=Please specify your primary email address, which will be used to choose the signing key for outgoing messages.\n If you leave it blank, the FROM address of the message will be used to choose the signing key.
sendingHiddenRcpt=This message has BCC (blind copy) recipients. If this message is encrypted, it is possible to hide the BCC recipients but users of some products (e.g. PGP Corp.) will not be able to decrypt the message. Given this, we recommend to avoid BCC-emails with encrypted messages.
sendWithHiddenBcc=Hide BCC recipients
sendWithShownBcc=Encrypt normally
sendingNews=Encrypted send operation aborted.\n\nThis message cannot be encrypted because there are newsgroup recipients. Please re-send the message without encryption.
sendToNewsWarning=Warning: you are about to send an encrypted email to a newsgroup.\n\nThis is discouraged because it only makes sense if all members of the group can decrypt the message, i.e. the message needs to be encrypted with the keys of all group participants. Please send this message only if you know exactly what you are doing.\n\nContinue?
hasHTML=HTML mail warning:\nThis message may contain HTML, which could cause signing/encryption to fail. To avoid this in the future, you should press the SHIFT key when clicking on the Compose/Reply button to send signed mail.\nIf you sign mail by default, you should uncheck the 'Compose Messages in HTML' preference box to permanently disable HTML mail for this mail account.
strippingHTML=Message contains HTML formatting information that will be lost when converting to plain text for signing/encryption. Do you wish to proceed?
msgCompose.button.sendAnyway=&Send Message Anyway
attachWarning=Attachments to this message are not local, they cannot be encrypted. In order to encrypt the attachments, store them as local files first and attach these files. Do you wish to send the message anyway?
quotedPrintableWarn=You have enabled 'quoted-printable' encoding for sending messages. This may result in incorrect decryption and/or verification of your message.\nDo you wish to turn off sending 'quoted-printable' messages now?
minimalLineWrapping=You have set line wrapping to %S characters. For correct encryption and/or signing, this value needs to be at least 68.\nDo you wish to change line wrapping to 68 characters now?
warning=Warning
signIconClicked=You have manually modified signing. Therefore, while you are composing this message, (de)activating signing does not depend anymore on (de)activating encryption.
errorOwnKeyUnusable=The key ID '%S' configured for the current identity does not yield a usable OpenPGP key.\n\nPlease ensure that you have a valid, not expired OpenPGP key and that your account settings point to that key.\nIf your key is not expired, then check if you did set Owner trust to full or ultimate.
msgCompose.cannotSaveDraft=Error while saving draft
msgCompose.internalEncryptionError=Internal Error: promised encryption disabled
msgCompose.internalError=An internal error has occurred.
msgCompose.toolbarTxt.signAndEncrypt=This message will be signed and encrypted
msgCompose.toolbarTxt.signOnly=This message will be signed
msgCompose.toolbarTxt.encryptOnly=This message will be encrypted
msgCompose.toolbarTxt.noEncryption=This message will be unsigned and unencrypted
msgCompose.toolbarTxt.disabled=Enigmail is disabled for the selected identity
msgCompose.encryptedSubjectStub=Encrypted Message
msgCompose.detailsButton.label=Details ...
msgCompose.detailsButton.accessKey=D
msgCompose.pepSendUnknown=Unknown
msgCompose.pepSendUnsecure=Unsecure
msgCompose.pepSendSecure=Secure
msgCompose.pepSendTrusted=Secure & Trusted
pep.alert.disabledForIdentity=p≡p is disabled for the current identity. Please enable p≡p via the Enigmail/p≡p preferences.
pep.alert.weakReply=You’re about to forward or reply to a secure message as insecure. If you choose to proceed, confidential information might be leaked putting you and your communication partner at risk. Are you sure you want to continue?
# note: should end with double newline:
sendAborted=Send operation aborted.\n\n
# details:
keyNotTrusted=Not enough trust for key '%S'
keyNotFound=Key '%S' not found
keyRevoked=Key '%S' revoked
keyExpired=Key '%S' expired
statPGPMIME=PGP/MIME
statSMIME=S/MIME
statSigned=SIGNED
statEncrypted=ENCRYPTED
statPlain=UNSIGNED and UNENCRYPTED
offlineSave=Save %1$S message to %2$S in Unsent Messages folder?
onlineSend=Send %1$S message to %2$S?
encryptKeysNote=Note: The message is encrypted for the following User ID's / Keys: %S
hiddenKey=
signFailed=Error in Enigmail; Encryption/signing failed; send unencrypted message?
msgCompose.button.sendUnencrypted=&Send Unencrypted Message
recipientsSelectionHdr=Select Recipients for Encryption
configureNow=You did not yet configure Enigmail security for the selected identity. Do you want to do this now?
# encryption/signing status and associated reasons:
encryptMessageAuto=Encrypt Message (auto)
encryptMessageNorm=Encrypt Message
signMessageAuto=Sign Message (auto)
signMessageNorm=Sign Message
encryptOff=Encryption: OFF
encryptOnWithReason=Encryption: ON (%S)
encryptOffWithReason=Encryption: OFF (%S)
encryptOn=Encryption: ON
signOn=Signing: ON
signOff=Signing: OFF
signOnWithReason=Signing: ON (%S)
signOffWithReason=Signing: OFF (%S)
reasonEnabledByDefault=enabled by default
reasonManuallyForced=manually forced
reasonByRecipientRules=forced by recipient rules
reasonByAutoEncryption=forced by auto encryption
reasonByConflict=due to conflict in recipient rules
reasonByEncryptionMode=due to encryption mode
# should not be used anymore:
encryptYes=Message will be encrypted
encryptNo=Message will not be encrypted
# should not be used anymore:
signYes=Message will be signed
signNo=Message will not be signed
# PGP/MIME status:
pgpmimeNormal=Protocol: PGP/MIME
inlinePGPNormal=Protocol: Inline PGP
smimeNormal=Protocol: S/MIME
pgpmimeAuto=Protocol: PGP/MIME (auto)
inlinePGPAuto=Protocol: Inline PGP (auto)
smimeAuto=Protocol: S/MIME (auto)
# should not be used anymore
pgpmimeYes=PGP/MIME will be used
pgpmimeNo=Inline PGP will be used
# Attach own key status (tooltip strings):
attachOwnKeyNo=Your own key will not be attached
attachOwnKeyYes=Your own key will be attached
attachOwnKeyDisabled=Your own key cannot be attached. You have to select a specific key\nin the OpenPGP section of the Account Settings to enable this feature.
rulesConflict=Conflicting per-recipient rules detected\n%S\n\nSend message with these settings?
msgCompose.button.configure=&Configure
msgCompose.button.send=&Send Message
msgCompose.button.save=&Save Message
# Strings in enigmailMsgHdrViewOverlay.js
keyNeeded=Public key %S needed to verify signature
keyUsed=Public key %S used to verify signature
clickDecrypt=; use 'Decrypt/Verify' function
clickDecryptRetry=; use 'Decrypt/Verify' function to retry
clickDetailsButton=; click on 'Details' button for more information
clickImportButton=; click on the 'Import Key' button to import the key
keyTypeUnsupported=; the key type is not supported by your version of GnuPG
msgPart=Part of the message %S
msgSigned=signed
msgSignedUnkownKey=signed with unknown key
msgEncrypted=encrypted
msgSignedAndEnc=signed and encrypted
unverifiedSig=Unverified signature
incompleteDecrypt=Decryption incomplete
needKey=Error - no matching private/secret key found to decrypt message
failedDecrypt=Error - decryption failed
badPhrase=Error - bad passphrase
failedDecryptVerify=Error - decryption/verification failed
viewInfo=; View > Message security info for details
decryptedMsg=Decrypted message
decryptedMsgWithFormatError=Decrypted message (restored broken PGP email format probably caused by an old Exchange server, so that the result might not be perfect to read)
usedAlgorithms=Used Algorithms: %1$S and %2$S
pepStatusInfo.text=p≡p Message Status.
pepStatusInfo.title.m3=Under Attack
pepStatusInfo.info.m3=This message is not secure and has been tampered with.
pepStatusInfo.title.m1=Mistrusted
pepStatusInfo.info.m1=This message has a communication partner that has previously been marked as mistrusted
pepStatusInfo.title.r0=Unknown
pepStatusInfo.info.r0=This message does not contain enough information to determine if it is secure.
pepStatusInfo.title.r1=Cannot Decrypt
pepStatusInfo.info.r1=This message cannot be decrypted because the key is not available.
pepStatusInfo.title.r2=Cannot Decrypt
pepStatusInfo.info.r2=This message cannot be decrypted because the key is not available.
pepStatusInfo.title.r3=Unsecure
pepStatusInfo.info.r3=This message is unsecure.
pepStatusInfo.title.r4=Unsecure for Some
pepStatusInfo.info.r4=This message is unsecure for some communication partners.
pepStatusInfo.title.r5=Unreliable Security
pepStatusInfo.info.r5=This message has unreliable protection.
pepStatusInfo.title.r6=Secure...
pepStatusInfo.info.r6=This message is secure but you still need to verify the identity of your communication partner.
pepStatusInfo.title.r7=Secure & Trusted
pepStatusInfo.info.r7=This message is secure and trusted.
pepStatusInfo.color.green=Green
pepStatusInfo.color.yellow=Yellow
pepStatusInfo.color.red=Red
pepRevokeTrust.question=Do you really want to cancel the trust for %S?
pepRevokeMistrust.question=Do you really want to re-trust the key for %S?
pepRevokeTrust.doRevoke=Cancel &trust
wksConfirmationReq=Webkey Directory Confirmation Request
wksConfirmationReq.message=This message has been sent by your email provider to confirm deployment of your OpenPGP public key\nin their Web Key Directory.\nProviding your public key helps others to discover your key and thus being able to encrypt messages to you.\n\nIf you want to deploy your key in the Web Key Directory now, please click on the button "Confirm Request" in the status bar.\nOtherwise, simply ignore this message.
wksConfirmationReq.button.label=Confirm Request
autocryptSetupReq=Perform Autocrypt Setup
autocryptSetupReq.button.label=Start Setup
autocryptSetupReq.setupMsg.desc=This message contains all information to transfer your Autocrypt settings along with your secret key securely from your original device.
autocryptSetupReq.setupMsg.backup=You can keep this message and use it as a backup for your secret key. If you want to do this, you should write down the backup code and store it securely.
autocryptSetupReq.message.import=To import the settings and key(s) in Enigmail, please click on the "Start Setup" button in the status bar.
autocryptSetupReq.message.sent=Please click on the message on your new device and follow the instuctions to import the settings.
# strings in pref-enigmail.js
oldGpgVersion20=Enigmail initialization failed.\n\nYou are using GnuPG version %1$S, which is not supported anymore. Enigmail requires GnuPG version %2$S or newer. Please upgrade your GnuPG installation, or Enigmail will not work.
locateGpg=Locate GnuPG program
invalidGpgPath=GnuPG cannot be executed with the path provided. Enigmail is therefore deactivated until you change the path to GnuPG again or until you restart the application.
warningsAreReset=All warnings have been reset.
prefs.gpgFound=GnuPG was found in %S
prefs.gpgNotFound=Could not find GnuPG
prefs.warnAskNever=Warning: activating this option will result in unencrypted emails without any further information if there is no key for one of the recipients -- Enigmail will not inform you if this happens!
prefs.warnIdleTimeForUnknownAgent=Cannot connect to gpg-agent. Maybe your system uses a specialized tool for passphrase handling (e.g. gnome-keyring, seahorse-agent, KDE wallet manager, ...). Unfortunately Enigmail cannot control the passphrase timeout for the tool you are using. Therefore the respective timeout settings in Enigmail are disregarded.
prefEnigmail.oneKeyserverOnly=Error - you can only specify one keyserver for automatic downloading of missing OpenPGP keys.
# Strings used in components/enigmail.js
# (said file also re-uses some strings from above)
enterAdminPin=Please type in the ADMIN PIN of your SmartCard
enterCardPin=Please type your SmartCard PIN
notInit=Error - Enigmail service not yet initialized
badCommand=Error - encryption command failed
cmdLine=command line and output:
notRequired=Error - no encryption required
notComplete=Error - key generation not yet completed
invalidEmail=Error - invalid email address(es)
noPassphrase=Error - no passphrase supplied
noPGPblock=Error - No valid armored OpenPGP data block found
unverifiedReply=Indented message part (reply) was probably modified
keyInMessageBody=Key in message body found. Click 'Import Key' to import the key
sigMismatch=Error - Signature mismatch
cantImport=Error in importing public key\n\n
doImportOne=Import %1$S (%2$S)?
doImportMultiple=Import the following keys?\n\n%S
previewFailed=Can't read public key file.
# Strings used in errorHandling.jsm
sc.wrongCardAvailable=The SmartCard %1$S found in your reader cannot be used to process the message.\nPlease insert your SmartCard %2$S and repeat the operation.
sc.insertCard=The operation requires your SmartCard %S.\nPlease insert the required SmartCard and repeat the operation.
sc.removeCard=The operation requires no SmartCard to be in the reader.\nPlease remove your SmartCard and repeat the operation.
sc.noCardAvailable=No SmartCard could be found in your reader\nPlease insert your SmartCard and repeat the operation.
sc.noReaderAvailable=Your SmartCard reader could not be accessed\nPlease attach your SmartCard reader, insert your card, and repeat the operation.
keyError.keySpecNotFound=The email address "%S" cannot be matched to a key on your keyring.
keyError.keyIdNotFound=The configured key ID "%S" cannot be found on your keyring.
keyError.resolutionAction=Please select a valid key in the OpenPGP section of your Account Settings.
missingPassphrase=Missing passphrase
errorHandling.gpgAgentInvalid=Your system is running a version of gpg-agent that is not suitable for your GnuPG version.
errorHandling.gpgAgentError=GnuPG reported an error in the communication with gpg-agent (a component of GnuPG).
errorHandling.dirmngrError=GnuPG reported an error in the communication with dirmngr (a component of GnuPG).
errorHandling.pinentryError=GnuPG cannot query your passphrase via pinentry.
errorHandling.readFaq=This is a system setup or configuration error that prevents Enigmail from working properly and cannot be fixed automatically.\n\nWe strongly recommend that you consult our support web site at https://enigmail.net/faq.
gpgNotFound=Unable to locate GnuPG program '%S'.\nMake sure you have set the GnuPG executable path correctly in the Enigmail Preferences.
gpgNotInPath=Unable to locate GnuPG executable in the PATH.\nMake sure you have set the GnuPG executable path correctly in the Enigmail Preferences.
enigmailNotAvailable=Enigmail core Service not available
gpgAgentNotStarted=Could not start the gpg-agent program which is needed for your GnuPG version %S.
prefUntrusted=UNTRUSTED
prefRevoked=REVOKED KEY
prefExpiredKey=EXPIRED KEY
prefExpired=EXPIRED
prefGood=Good signature from %S
prefBad=BAD signature from %S
failCancel=Error - Key receive cancelled by user
failNoServer=Error - No keyserver specified to receive key from
failNoID=Error - No key ID specified to receive key for
failKeyExtract=Error - key extraction command failed
notFirstBlock=Error - First OpenPGP block not public key block
importKeyConfirm=Import public key(s) embedded in message?
failKeyImport=Error - key importing failed
fileWriteFailed=Failed to write to file %S
importKey=Import public key %S from keyserver:
uploadKey=Send public key %S to keyserver:
keyId=Key ID
keyAndSigDate=Key ID: 0x%1$S / Signed on: %2$S
keyFpr=Key fingerprint: %S
noEmailProvided=You did not provide an email address!
keyAlreadySigned=The key is already signed, you cannot sign it twice.
#####################################################################
# Strings used in enigmailKeySelection.js
#####################################################################
selKeyExpired=expired %S
createdHeader=Created
atLeastOneKey=No key selected! You have to select at least one key to accept this dialog
fewerKeysThanRecipients=You have selected a smaller number of keys than recipients. Are you sure that the list of keys to encrypt is complete?
userSel.button.goBack=Select more Keys
userSel.secretKeySel.title=Select a Secret OpenPGP Key to Sign Your Messages
userSel.problemNoKey=No valid key
userSel.problemMultipleKeys=Multiple keys
# should be same as thunderbird ENTITY sendLaterCmd.label:
sendLaterCmd.label=Send Later
# Strings used in enigmailAttachmentDialog.js
pgpMimeNote=NOTE: PGP/MIME is only supported by a limited number of mail clients! On Windows only Mozilla/Thunderbird, Sylpheed, Pegasus and Mulberry are known to support this standard; on Linux/UNIX and Mac OS X most popular mail clients support it. If you are unsure, select the %S option.
first=first
second=second
# Strings used in am-enigprefs.js / enigmailEditIdentity.js
encryptKeyHeader=Select OpenPGP Key for Encryption
identityName=Identity: %S
# Strings used in enigmailSingleRcptSettings.js
noEncryption=You have activated encryption, but you did not select a key. In order to encrypt mails sent to %1$S, you need to specify one or several valid key(s) from your key list. Do you want to disable encryption for %2$S?
noKeyToUse=(none - no encryption)
noEmptyRule=The Rule may not be empty! Please set an email address in the Rule field.
invalidAddress=The email address(es) you have entered are not valid. You should not set the names of the recipients, just the email addresses. E.g.:\nInvalid: Some Name \nValid: some.name@address.net
noCurlyBrackets=The curly brackets {} have a special meaning and should not be used in the email addresses. If you want to modify the matching behavior for this rule, use the 'Apply rule if recipient ...' option.\nMore information is available from the Help button.
# Strings used in enigmailRulesEditor.js
never=Never
always=Always
possible=Possible
deleteRule=Really delete the selected rule?
nextRcpt=(Next recipient)
negateRule=Not
addKeyToRule=Add key %1$S (%2$S) to per-recipient rule
# Strings used in enigmailSearchKey.js
needOnline=The function you have selected is not available in offline mode. Please go online and try again.
protocolNotSupported=The protocol '%S://' that you have selected is not supported for downloading OpenPGP keys.
gpgkeysDisabled=It might help to enable the option 'extensions.enigmail.useGpgKeysTool'.
noKeyserverConn=Could not connect to keyserver at %S.
keyDownloadFailed=Failed to download key from keyserver. Status message is:\n%S
internalError=An internal error occurred. The keys could not be downloaded or imported.
noKeyFound=We are sorry, could not find any key that would match the specified search criteria.
# gpgkeys_%S is one of the gpg command line tools gpgkeys_hkp, gpgkeys_ldap, etc.
gpgKeysFailed=Failed to search or download key from keyserver: gpgkeys_%S could not be executed.
# Strings in enigmailEditKeyTrustDlg.xul
setKeyTrustFailed=Setting owner trust failed
# Strings in enigmailSignKeyDlg.js
signKeyFailed=Key signing failed
alreadySigned.label=Note: the key %S is already signed with the selected private key.
alreadySignedexportable.label=Note: the key %S is already signed exportable with the selected private key. A local signature does not make sense.
partlySigned.label=Note: some user IDs of key %S are already signed with the selected private key.
noTrustedOwnKeys=No eligible key found for signing! You need at least one fully trusted private key in order to sign keys.
# Strings in enigmailKeyManager.js
keyMan.loadingKeys=Loading keys, please wait ...
keyValid.unknown=unknown
keyValid.invalid=invalid
keyValid.disabled=disabled
keyValid.revoked=revoked
keyValid.expired=expired
keyValid.noSubkey=no valid subkey
keyTrust.untrusted=untrusted
keyTrust.marginal=marginal
keyTrust.full=trusted
keyTrust.ultimate=ultimate
keyTrust.group=(group)
keyType.public=pub
keyType.publicAndSec=pub/sec
keyMan.enableKey=Enable Key
keyMan.disableKey=Disable Key
userAtt.photo=User attribute (JPEG image)
asciiArmorFile=ASCII Armored Files (*.asc)
importKeyFile=Import OpenPGP Key File
gnupgFile=GnuPG Files
saveRevokeCertAs=Create & Save Revocation Certificate
revokeCertOK=The revocation certificate has been successfully created. You can use it to invalidate your public key, e.g. in case you would lose your secret key.\n\nPlease transfer it to a medium which can be stored away safely such as a CD or USB stick. If somebody gains access to this certificate they can use it to render your key unusable.
revokeCertFailed=The revocation certificate could not be created.
addUidOK=User ID added successfully
addUidFailed=Adding the User ID failed
noKeySelected=You should select at least one key in order to perform the selected operation
exportToFile=Export Public Key To File
exportKeypairToFile=Export Secret and Public Key To File
exportSecretKey=Do you want to include the secret key in the saved OpenPGP key file?
saveKeysOK=The keys were successfully saved
saveKeysFailed=Saving the keys failed
importKeysFailed=Importing the keys failed
enableKeyFailed=Enabling/disabling the keys failed
specificPubKeyFilename=%1$S (0x%2$S) pub
specificPubSecKeyFilename=%1$S (0x%2$S) pub-sec
defaultPubKeyFilename=Exported-public-keys
defaultPubSecKeyFilename=Exported-public-and-secret-keys
noSecretKeys=No secret keys found.\n\nDo you want to generate your own key now?
sendKeysOk=Key(s) sent successfully
sendKeysFailed=Sending of keys failed
receiveKeysOk=Key(s) updated successfully
receiveKeysFailed=Downloading of keys failed
importFromClip=Do you want to import some key(s) from clipboard?
importFromUrl=Download public key from this URL:
copyToClipbrdFailed=Could not copy the selected key(s) to the clipboard.
copyToClipbrdOK=Key(s) copied to clipboard
deleteSecretKey=WARNING: You are about to delete a secret key!\nIf you delete your secret key, you will no longer be able to decrypt any messages encrypted for that key, and you cannot revoke your key anymore.\n\nDo you really want to delete BOTH, the secret key and the public key\n'%S'?
deleteMix=WARNING: You are about to delete secret keys!\nIf you delete your secret key, you will no longer be able to decrypt any messages encrypted for that key.\n\nDo you really want to delete BOTH, the selected secret and public keys?
deletePubKey=Do you want to delete the public key\n'%S'?
deleteSelectedPubKey=Do you want to delete the public keys?
deleteKeyFailed=The key could not be deleted.
revokeKeyQuestion=You are about to revoke the key '%S'.\n\nYou will no longer be able to sign with this key, and once distributed, others will no longer be able to encrypt with that key. You can still use the key to decrypt old messages.\n\nDo you want to proceed?
revokeKeyOk=The key has been revoked. If your key is available on a key server, it is recommended to re-upload it, so that others can see the revocation.
revokeKeyFailed=The key could not be revoked.
revokeKeyNotPresent=You have no key (0x%S) which fits to this revocation certificate!\n\nIf you have lost your key, you must import it (e.g. from a keyserver) before the revocation certificate!
revokeKeyAlreadyRevoked=The key 0x%S has already been revoked.
refreshAllQuestion=You did not select any key. Would you like to refresh ALL keys?
refreshKeyServiceOn.warn=Warning: Your keys are currently being refreshed in the background as safely as possible.\nRefreshing all your keys at once will unnecessarily reveal information about you.\nDo you really want to do this?
refreshKey.warn=Warning: depending on the number of keys and the connection speed, refreshing all keys could be quite a lengthy process!
downloadContactsKeys.warn=Warning: depending on the number of contacts and the connection speed, downloading all keys could be quite a lengthy process!
downloadContactsKeys.importFrom=Import contacts from address book '%S'?
keyMan.button.exportSecKey=Export &Secret Keys
keyMan.button.exportPubKey=Export &Public Keys Only
keyMan.button.import=&Import
keyMan.button.refreshAll=&Refresh All Keys
keyMan.button.revokeKey=&Revoke Key
keyMan.button.skip=&Skip Key
keylist.noOtherUids=Has no other identities
keylist.hasOtherUids=Also known as
keylist.noPhotos=No photograph available
keylist.hasPhotos=Photographs
keyMan.addphoto.filepicker.title=Select photo to add
keyMan.addphoto.warnLargeFile=The file you have chosen is larger than 25 kB.\nIt is not recommended to add very large files because the keys get very large by this.
keyMan.addphoto.noJpegFile=The selected file does not appear to be a JPEG file. Please choose a different file.
keyMan.addphoto.failed=The photo could not be added.
noWksIdentity=The key %S does not have a WKS identity.
keyman.addBlacklistKey.msg=Do you really want to stop pEp using the key "%1$S (%2$S)" for encrypting messages?
keyman.removeBlacklistKey.msg=Do you want allow pEp to use key "%1$S (%2$S)" for future messages?
keyman.addBlacklistKey.button=&Blacklist the key
keyman.removeBlacklistKey.button=&Remove key from Blacklist
# Strings in enigmailManageUidDlg.xul
changePrimUidFailed=Changing the primary User ID failed
changePrimUidOK=The primary user ID was changed successfully
deleteUidFailed=Deleting the user ID %S failed
deleteUidOK=User ID %S was deleted successfully
revokeUidFailed=Revoking the user ID %S failed
revokeUidOK=User ID %S was revoked successfully. If your key is available on a key server, it is recommended to re-upload it, so that others can see the revocation.
revokeUidQuestion=Do you really want to revoke the user ID %S?
deleteUidQuestion=Do you really want to delete the user ID %S?\n\nPlease note: if you have submitted your public key to a key server, deleting a user ID will not change anything. In this case you should use 'Revoke user ID'.
# Strings in enigmailKeyImportInfo.xul
importInfoTitle=SUCCESS! Keys imported
importInfoSuccess=\u2705
importInfoBits=Bits
importInfoCreated=Created
importInfoFpr=Fingerprint
importInfoDetails=(Details)
importInfoNoKeys=No keys imported.
# Strings in enigmailKeyDetailsDlg.xul
keyTypePublic=public key
keyTypePrimary=primary key
keyTypeSubkey=subkey
keyTypePair=key pair
keyExpiryNever=never
keyAlgorithm_1=RSA
keyAlgorithm_2=RSA
keyAlgorithm_3=RSA
keyAlgorithm_16=ELG
keyAlgorithm_17=DSA
keyAlgorithm_18=ECDH
keyAlgorithm_19=ECDSA
keyAlgorithm_20=ELG
keyAlgorithm_22=EDDSA
keyUsageEncrypt=Encrypt
keyUsageSign=Sign
keyUsageCertify=Certify
keyUsageAuthentication=Authentication
keyDoesNotExpire=Key does not expire
# Strings in enigmailGenCardKey.xul
keygen.started=Please wait while the key is being generated ....
keygen.completed=Key Generated. The new Key ID is: 0x%S
keygen.keyBackup=The key is backed up as %S
keygen.passRequired=Please specify a passphrase if you want to create a backup copy of your key outside your SmartCard.
# Strings in enigmailSetCardPin.xul
cardPin.processFailed=Failed to change PIN
# Strings in enigRetrieveProgres.xul
keyserverProgress.refreshing=Refreshing keys, please wait ...
keyserverProgress.uploading=Uploading keys, please wait ...
keyserverProgress.wksCheck=Checking provider for Webkey Service support ...
keyserverProgress.noWks=Your provider does not support the Webkey Service
keyserverProgress.wksUploadFailed=Could not upload your key to the Webkey Service
keyserverProgress.wksUploadCompleted=Your public key was successfully submitted to your provider. You will receive an email to confirm that you initiated the upload.
keyserverTitle.refreshing=Refresh Keys
keyserverTitle.uploading=Key Upload
# Strings in enigmailSetupWizard
passphrase.min8keys=Your passphrase should contain at least 8 characters!
setupWizard.reallyCancel=Do you really want to close the Enigmail Setup Wizard?
setupWizard.invalidGpg=The file you specified is not a GnuPG executable. Please specify a different file.
setupWizard.specifyFile=You need to at least specify a public key file in order to proceed.
setupWizard.installFailed=It seems that the installation was not successful. Please either retry the installation, or install GnuPG manually and locate it using the Browse button.
setupWizard.downloadForbidden=For your own security, we will not download GnuPG. Please visit https://gnupg.org/ in order to download GnuPG.
setupWizard.downloadImpossible=We cannot download GnuPG currently. Please try later or visit https://gnupg.org/ in order to download GnuPG.
setupWizard.hashSumError=The wizard could not verify the integrity of the downloaded file. The file may be broken or manipulated. Do you want to continue the installation anyway?
setupWizard.importSettingsFile=Specify backup file to load from
setupWizard.invalidSettingsFile=The specified file is not a correct Enigmail Settings backup file.
setupWizard.gpgConfExists=The GnuPG config file already exists. Do you want to overwrite it with the one from your old installation?
setupWizard.noGpgHomeDir=It appears that you configured a specificy %S to use for GnuPG. However, this is not a directory - you cannot use it.
setupWizard.unmachtedIds=The following identities of your old setup could not be matched:\n%S\nThe settings for these identities were skipped.
# Strings in installGnuPG.jsm
installGnuPG.downloadFailed=An error occurred while trying to download GnuPG. Please check the console log for further details.
installGnuPG.installFailed=An error occurred while installing GnuPG. Please check the console log for further details.
# Strings in enigmailAddUidDlg.xul
addUidDlg.nameOrEmailError=You have to fill in a name and an email address
addUidDlg.nameMinLengthError=The name must at least have 5 characters
addUidDlg.invalidEmailError=You must specify a valid email address
# Strings in enigmailCardDetails.js
Carddetails.NoASCII=OpenPGP Smartcards only support ASCII characters in Firstname/Name.
# network error types
errorType.SecurityCertificate=The security certificate presented by the web service is not valid.
errorType.SecurityProtocol=The security protocol used by the web service is unknown.
errorType.Network=A network error has occurred.
# filter stuff
filter.folderRequired=You must select a target folder.
filter.decryptMove.label=Decrypt permanently (Enigmail)
filter.decryptCopy.label=Create decrypted Copy (Enigmail)
filter.decryptMove.warnExperimental=Warning - the filter action "Decrypt permanently" may lead to destroyed messages.\n\nWe strongly recommend that you first try the "Create decrypted Copy" filter, test the result carefully, and only start using this filter once you are satisified with the result.
+filter.encrypt.label=Encrypt to (Enigmail)
+filter.keyRequired=You must select a recipient key.
+filter.keyNotFound=Could not find an encryption key for "%S".
filter.tempPepFilterDesc=Temporary filter to store sent message unencrypted
# strings in enigmailConvert.jsm
converter.decryptBody.failed=Could not decrypt message with subject\n"%S".\nDo you want to retry with a different passphrase or do you want to skip the message?
converter.decryptAtt.failed=Could not decrypt attachment "%1$S"\nof message with subject\n"%2$S".\nDo you want to retry with a different passphrase or do you want to skip the message?
saveLogFile.title=Save Log File
# strings in gpg.jsm
unknownSigningAlg=Unknown signing algorithm (ID: %S)
unknownHashAlg=Unknown cryptographic hash (ID: %S)
# strings in keyRing.jsm
keyring.photo=Photo
keyRing.pubKeyRevoked=The key %1$S (key ID %2$S) is revoked.
keyRing.pubKeyExpired=The key %1$S (key ID %2$S) has expired.
keyRing.pubKeyNotForSigning=The key %1$S (key ID %2$S) cannot be used for signing.
keyRing.pubKeyNotForEncryption=The key %1$S (key ID %2$S) cannot be used for encryption.
keyRing.keyDisabled=The key %1$S (key ID %2$S) is disabled; it cannot be used.
keyRing.keyNotTrusted=The key %1$S (key ID %2$S) is not trusted enough. Please set the trust level of your key to "ultimate" to use it for signing.
keyRing.keyInvalid=The key %1$S (key ID %2$S) is not valid. Please consider verifying it correctly. Alternatively use "Convenient encryption settings".
keyRing.signSubKeysRevoked=All signing-subkeys of key %1$S (key ID %2$S) are revoked.
keyRing.signSubKeysExpired=All signing-subkeys of key %1$S (key ID %2$S) have expired.
keyRing.signSubKeysUnusable=All signing-subkeys of key %1$S (key ID %2$S) are revoked, expired or otherwise unusable.
keyRing.encSubKeysRevoked=All encryption subkeys of key %1$S (key ID %2$S) are revoked.
keyRing.encSubKeysExpired=All encryption subkeys of key %1$S (key ID %2$S) have expired.
keyRing.noSecretKey=You do not seem to have the secret key for %1$S (key ID %2$S) on your keyring; you cannot use the key for signing.
keyRing.encSubKeysUnusable=All encryption subkeys of key %1$S (key ID %2$S) are revoked, expired or otherwise unusable.
#strings in exportSettingsWizard.js
cannotWriteToFile=Cannot save to file '%S'. Please select a different file.
dataExportError=An error occurred during exporting your data.
enigmailSettings=EnigmailSettings
defaultBackupFileName=Enigmail-export
specifyExportFile=Specify file name for exporting
homedirParamNotSUpported=Additional params that configure paths such as --homedir and --keyring are not supported for exporting/restoring your settings. Please use alternative methods such as setting the environment variable GNUPGHOME.
#strings in expiry.jsm
expiry.keyExpiresSoon=Your key %1$S will expire in less than %2$S days.\n\nWe recommend that you create a new key pair and configure the corresponding accounts to use the new key.
expiry.keysExpireSoon=The following of your keys will expire in less than %1$S days:\n%2$S. We recommend that you create new keys and configure your accounts to use the new keys.
expiry.keyMissingOwnerTrust=Your secret key %S has missing trust.\n\nWe recommend that you set "You rely on certifications" to ultimate in key properties.
expiry.keysMissingOwnerTrust=The following of your secret keys have missing trust.\n%S.\nWe recommend that you set "You rely on certifications" to ultimate in key properties.
expiry.OpenKeyManager=Open Enigmail Key Management
expiry.OpenKeyProperties=Open Key Properties
#strings in pEpDecrypt.jsm
pEpDecrypt.cannotDecrypt=This is an encrypted message. Unfortunately you don't have the private key to decrypt the message.
#strings in gpgAgent.jsm
gpghomedir.notexists=The directory '%S' containing your OpenPGP keys does not exist and cannot be created.
gpghomedir.notwritable=The directory '%S' containing your OpenPGP keys is not writable.
gpghomedir.notdirectory=The directory '%S' containing your OpenPGP keys is a file instead of a directory.
gpghomedir.notusable=Please fix the directory permissions or change the location of your GnuPG "home" directory. GnuPG cannot work corretly otherwise.
#strings in pepTrustWords.js
pepTrustWords.cannotVerifyOwnId=Cannot verify p≡p Trustwords for own account.
pepTrustWords.cannotFindKey=Cannot find key for %S.
pepTrustWords.cannotStoreChange=Could not change trust for %S.
pepTrustWords.generalFailure=Cannot obtain trustwords for %S.
pepTrustWords.partnerFingerprint=Fingerprint for %S:
#strings in mimeWkdHandler.jsm
wkdMessage.body.req=Your email provider processed your request to upload your public key to the OpenPGP Web Key Directory.\n\nPlease click the confirmation button in the Enigmail header to complete the publishing of your public key.
wkdMessage.body.process=This is an email related to the automatic processing to upload your public key to the OpenPGP Web Key Directory.\n\nYou do not need to take any manual action at this point.
#strings in pepHandshake.js
pepPrivacyStatus.RatingBrokenSuggestion=Either you or the sender should resend the message.
pepPrivacyStatus.RatingHaveNoKeySuggestionOutgoing=If you composed this message, your key is not available.
pepPrivacyStatus.RatingMistrustSuggestion=Re-establish the connection with your communication partner and try to complete another handshake.
pepPrivacyStatus.RatingReliableSuggestion=Complete a handshake with your communication partner by exchanging trustwords in person or over the phone. A handshake is needed only once per partner and will ensure secure and trusted communication.
pepPrivacyStatus.RatingTrustedSuggestion=No action needed!
pepPrivacyStatus.RatingUndefinedSuggestionIncoming=Be aware this message may not be secure.
pepPrivacyStatus.RatingUndefinedSuggestionOutgoing=Please add the necessary information.
pepPrivacyStatus.RatingUnderAttackSuggestion=Separately verify the content of this message with your communication partner.
pepPrivacyStatus.RatingUnencryptedForSomeSuggestion=Make sure the privacy status for each communication partner listed is at least secure.
pepPrivacyStatus.RatingUnencryptedSuggestion=Please ask your communication partner to use an encryption solution or install p≡p.
pepPrivacyStatus.RatingUnreliableSuggestion=This message has no reliable encryption or no signature. Ask your communication partner to upgrade their encryption solution or install p≡p.
pepPrivacyStatus.RatingBrokenExplanation=This message has broken encryption or formatting.
pepPrivacyStatus.RatingHaveNoKeyExplanation=This message cannot be decrypted because the key is not available.
pepPrivacyStatus.RatingMistrustExplanation=This message has a communication partner that has previously been marked as mistrusted.
pepPrivacyStatus.RatingReliableExplanation=This message is secure but you still need to verify the identity of your communication partner.
pepPrivacyStatus.RatingTrustedExplanation=This message is secure and trusted.
pepPrivacyStatus.RatingUndefinedExplanation=This message does not contain enough information to determine if it is secure.
pepPrivacyStatus.RatingUnderAttackExplanation=This message is not secure and has been tampered with.
pepPrivacyStatus.RatingUnencryptedExplanation=This message is unsecure.
pepPrivacyStatus.RatingUnencryptedForSomeExplanation=This message is unsecure for some communication partners.
pepPrivacyStatus.RatingUnreliableExplanation=This message has unreliable protection.
pepPrivacyStatus.RatingBrokenText=Broken
pepPrivacyStatus.RatingHaveNoKeyText=Cannot Decrypt
pepPrivacyStatus.RatingMistrustText=Mistrusted
pepPrivacyStatus.RatingReliableText=Secure
pepPrivacyStatus.RatingTrustedText=Secure & Trusted
pepPrivacyStatus.RatingUndefinedText=Unknown
pepPrivacyStatus.RatingUnderAttackText=Under Attack
pepPrivacyStatus.RatingUnencryptedForSomeText=Unsecure for Some
pepPrivacyStatus.RatingUnencryptedText=Unsecure
pepPrivacyStatus.RatingUnreliableText=Unreliable Security
handshakeDlg.button.initHandshake=Handshake...
handshakeDlg.button.stopTrust=Stop Trusting
handshakeDlg.button.reTrust=Stop Mistrusting
handshakeDlg.label.outgoingMessage=Outgoing message
handshakeDlg.label.incomingMessage=Incoming message
handshakeDlg.error.noPeers=Cannot handshake without any correspondents.
handshakeDlg.error.noProtection=Please enable protection in order to use the Handshake function.
enigmail.acSetupPasswd.descEnterPasswd=Please enter the setup code that is displayed on the other device
enigmail.acSetupPasswd.descCopyPasswd=Please enter the setup code below on your other device to proceed with the setup
#strings in autocrypt.jsm
autocrypt.setupMsg.subject=Autocrypt Setup Message
autocrypt.setupMsg.msgBody=To set up your new device for Autocrypt, please follow the instuctions that should be presented by your new device.
autocrypt.setupMsg.fileTxt=This is the Autocrypt setup file used to transfer settings and keys between clients. You can decrypt it using the password presented on your old device, and then import the contained key into your keyring.
diff --git a/ui/skin/common/enigmail-common.css b/ui/skin/common/enigmail-common.css
index d41c7eaf..57413cab 100644
--- a/ui/skin/common/enigmail-common.css
+++ b/ui/skin/common/enigmail-common.css
@@ -1,326 +1,330 @@
/*
* 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/.
*/
/**
* Common Enigmail styles for all platforms
*/
.action-box { width: 100px }
#expandedEnigmailBox {
background-color:#d8e0e8;
color: black;
}
.enigmailHeaderName {
color: #888a85; /* lower contrast, TB3 */
text-align: right;
background-color: transparent;
-moz-margin-end: 2px;
}
.enigmailHeaderNameBox {
width: 7.7em;
background-color:#d8e0e8;
color: black;
}
.enigmailHeaderValue {
min-width: 50px;
white-space: normal;
color: black;
line-height: 1.4em;
-moz-appearance: none !important;
-moz-appearance: none;
padding: 0px !important;
margin: 0px !important;
-moz-margin-start: 3px !important;
border: none !important;
background-color: transparent;
}
.enigmailHeaderSpacer {
width: 2px;
}
.enigmailHeaderBoxLabelNoSignature {
background-color:#d8e0e8;
color: black;
}
.enigmailHeaderBoxLabelSignatureOk {
background-color:#ccffcc;
color: black;
}
.enigmailHeaderBoxLabelSignatureVerified {
background-color:#ccffff;
color: black;
}
.enigmailHeaderBoxLabelSignatureNotOk {
background-color:#FFD8FE;
color: black;
}
.enigmailHeaderBoxLabelSignatureUnknown {
background-color:#FFFFC9;
color: black;
}
.enigmailHeaderBoxLabelBuggyMailFormat {
background-color:#FFD8FE;
color: black;
}
.enigmailLink {
text-decoration: underline;
color: blue;
cursor: pointer;
}
.enigmailStrong {
font-weight: bold;
color: red;
}
.enigmailUidInactive {
color: gray;
font-style: italic;
}
.enigMsgHdrView-flat-button {
min-width: 1px !important;
-moz-appearance: none;
color: black;
background-color: #DDDDDD;
border: 2px solid transparent;
margin-top: 2px;
margin-bottom: 2px;
padding: 0 2px;
border-radius: 2px;
-moz-border-top-colors: transparent threedhighlight threedlightshadow;
-moz-border-right-colors: transparent threeddarkshadow threedshadow;
-moz-border-bottom-colors: transparent threeddarkshadow threedshadow;
-moz-border-left-colors: transparent threedhighlight threedlightshadow;
min-height: 1ex;
}
.enigMsgHdrView-flat-button:hover {
background-color: #CCCCCC;
}
.enigmailToolbar {
-moz-appearance: none;
}
.enigmailTitle {
font-size: larger;
font-weight: bold;
}
#messagepanebox[enigSigned="ok"] #messagepane {
color: red;
}
.enigmailMessagePane {
margin: 6px;
-moz-user-focus: normal;
-moz-user-select: text;
cursor: text !important;
white-space: pre-wrap;
unicode-bidi: -moz-plaintext;
}
/***************************************************
* Various other styles
***************************************************/
.enigmailDialogTitle {
font-size: 120%;
font-weight: bold;
padding-bottom: 6px;
}
.enigmailDialogBody {
-moz-user-focus: normal;
-moz-user-select: text;
cursor: text !important;
white-space: pre-wrap;
unicode-bidi: -moz-plaintext;
}
.enigmailPrefsTitle {
font-weight: bold;
height: 25px;
}
.enigmailKeyImportHeader {
font-weight: bold;
color: #888;
}
.enigmailKeyImportUserId {
font-weight: bold;
}
.enigmailKeyImportCaption {
list-style-image: url("chrome://enigmail/skin/importSuccess.png");
max-height: 2em;
max-width: 2em;
}
.enigmailKeyImportDetails {
color: blue;
}
.enigmailKeyImportDetails:hover {
text-decoration: underline;
}
treechildren::-moz-tree-cell-text(enigmailSubkeyTitle) {
font-weight: bold;
}
treechildren::-moz-tree-cell-text(enigmailOwnKey) {
font-weight: bold;
}
treechildren::-moz-tree-cell-text(enigKeyInactive) {
color: gray;
font-style: italic;
}
treechildren::-moz-tree-column(enigDontEncrypt) {
background-color: rgb(90%, 90%, 90%);
}
treechildren::-moz-tree-cell-text(fixedWidthFont) {
font-family: "Courier New", Courier, monospace;
}
.enigmailExpandViewButton {
width: 9px; /* The image's width is 9 pixels */
list-style-image: url("chrome://enigmail/skin/twisty-clsd.png");
}
.enigmailCollapseViewButton {
width: 9px; /* The image's width is 9 pixels */
list-style-image: url("chrome://enigmail/skin/twisty-open.png");
}
.enigmailWarningIcon {
list-style-image: url("chrome://enigmail/skin/warning-16.png");
}
treechildren::-moz-tree-image(enigSignedEncrypted)
{
list-style-image: url("chrome://enigmail/skin/col-encrypted-signed.png");
}
treechildren::-moz-tree-image(enigSigned)
{
list-style-image: url("chrome://enigmail/skin/enigSignOk.png");
}
treechildren::-moz-tree-image(enigEncrypted)
{
list-style-image: url("chrome://enigmail/skin/enigEncOk.png");
}
/*
the following styles are available for the key trust
columnm in the key manager:
enigmail_keyValid_unknown
enigmail_keyValid_invalid
enigmail_keyValid_disabled
enigmail_keyValid_revoked
enigmail_keyValid_expired
enigmail_keyTrust_untrusted
enigmail_keyTrust_marginal
enigmail_keyTrust_full
enigmail_keyTrust_ultimate
enigmail_keyTrust_unknown
They can be applied using:
treechildren::-moz-tree-cell(STYLE) {}
treechildren::-moz-tree-cell-text(STYLE) {}
*/
/******************************
* Rules for filter actions
******************************/
.ruleactiontarget[type="enigmail@enigmail.net#filterActionMoveDecrypt"] {
-moz-binding: url("chrome://messenger/content/searchWidgets.xml#ruleactiontarget-folder");
}
.ruleactiontarget[type="enigmail@enigmail.net#filterActionCopyDecrypt"] {
-moz-binding: url("chrome://messenger/content/searchWidgets.xml#ruleactiontarget-folder");
}
+.ruleactiontarget[type="enigmail@enigmail.net#filterActionEncrypt"] {
+ -moz-binding: url("chrome://messenger/content/searchWidgets.xml#ruleactiontarget-forwardto");
+}
+
.enigmailPassphraseQuality {
margin: 2px 4px;
min-width: 128px;
height: 12px;
background-color: #C21540;
}
.enigmailPassphraseQuality[value="medium"] {
background-color: #5885C4;
}
.enigmailPassphraseQuality[value="high"] {
background-color: #64C4A1;
}
.enigmailPassphraseQuality[value="excellent"] {
background-color: #1A9C2A;
}
/* a spinning wheel circle */
.enigmailWheel {
list-style-image: url("chrome://enigmail/skin/spinning-wheel.png");
max-width: 100%;
max-height: 100%;
}
.enigmailSpinning {
animation: enigmailDoRotation 1.4s infinite linear;
transform: translateZ(0);
}
@keyframes enigmailDoRotation {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
/***************************************************
* Styles for Setup Wizard and Key Generation
***************************************************/
#passphraseBox #passphraseError
{
margin-top: 3px;
margin-bottom: 3px;
color: red;
}
#passphraseBox #passphraseErrorRepeat
{
margin-top: 3px;
margin-bottom: 3px;
color: red;
}
diff --git a/util/genxpi b/util/genxpi
index 5afabac1..f52edec6 100755
--- a/util/genxpi
+++ b/util/genxpi
@@ -1,166 +1,167 @@
#!/bin/sh
# 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/.
#
# This script generates the Enigmail XPI
#
echo "genxpi: Generating $1 in $3"
if [ $# -lt 5 ]; then
echo "Wrong number of parameters"
exit 1
fi
xpiFile=$1
xpiVersion="$2"
distDir="$3"
srcDir=$4
xpiModule=$5
enableLang=$6
cd ${srcDir}
cwd=`pwd`
cd "$distDir"
targetDir=`pwd`
cd "$cwd"
cp ${srcDir}/package/install.rdf ${targetDir}/install.rdf
# Prepare chrome.manifest
cat ${srcDir}/package/chrome.manifest \
> ${targetDir}/chrome.manifest
# Prepare languages other than en-US
if [ "$enableLang" = "yes" ]; then
if [ -s ${srcDir}/lang/current-languages.txt ]; then
echo '' >> ${targetDir}/chrome.manifest
echo '# Additional languages' >> ${targetDir}/chrome.manifest
for lang in `cat ${srcDir}/lang/current-languages.txt`; do
echo 'locale enigmail '$lang' jar:chrome/enigmail.jar!/locale/'$lang'/' >> ${targetDir}/chrome.manifest
done
fi
fi
# cd ${srcDir}/package
cd "$targetDir"
mkdir -p "${targetDir}/wrappers"
cp ${cwd}/util/gpg-agent-wrapper wrappers
echo "Creating ${xpiFile} file"
zip --must-match\
../${xpiFile} \
components/${xpiModule}.xpt \
components/${xpiModule}.js \
components/prefs-service.js \
components/msgCompFields.js \
components/pgpmimeHandler.js \
components/mimeEncrypt.js \
defaults/preferences/enigmail.js \
modules/addrbook.jsm \
modules/app.jsm \
modules/armor.jsm \
modules/attachment.jsm \
modules/autocrypt.jsm \
modules/card.jsm \
modules/clipboard.jsm \
modules/commandLine.jsm \
modules/configure.jsm \
modules/constants.jsm \
modules/data.jsm \
modules/decryption.jsm \
modules/decryptPermanently.jsm \
+ modules/encryptPermanently.jsm \
modules/dialog.jsm \
modules/encryption.jsm \
modules/core.jsm \
modules/configBackup.jsm \
modules/errorHandling.jsm \
modules/funcs.jsm \
modules/gpgAgent.jsm \
modules/protocolHandler.jsm \
modules/events.jsm \
modules/execution.jsm \
modules/files.jsm \
modules/filters.jsm \
modules/fixExchangeMsg.jsm \
modules/glodaMime.jsm \
modules/glodaUtils.jsm \
modules/gpg.jsm \
modules/hash.jsm \
modules/httpProxy.jsm \
modules/installGnuPG.jsm \
modules/installPep.jsm \
modules/key.jsm \
modules/keyEditor.jsm \
modules/keyRing.jsm \
modules/keyUsability.jsm \
modules/keyRefreshService.jsm \
modules/keyserver.jsm \
modules/keyserverUris.jsm \
modules/lazy.jsm \
modules/locale.jsm \
modules/log.jsm \
modules/mime.jsm \
modules/mimeDecrypt.jsm \
modules/mimeVerify.jsm \
modules/os.jsm \
modules/openpgp.jsm \
modules/passwordCheck.jsm \
modules/passwords.jsm \
modules/pEp.jsm \
modules/pEpAdapter.jsm \
modules/pEpDecrypt.jsm \
modules/pEpFilter.jsm \
modules/pEpListener.jsm \
modules/pEpKeySync.jsm \
modules/pipeConsole.jsm \
modules/prefs.jsm \
modules/rng.jsm \
modules/rules.jsm \
modules/send.jsm \
modules/socks5Proxy.jsm \
modules/stdlib/compose.jsm \
modules/stdlib/misc.jsm \
modules/stdlib/msgHdrUtils.jsm \
modules/stdlib/openpgp-lib.js \
modules/stdlib/openpgp.worker.min.js \
modules/stdlib.jsm \
modules/streams.jsm \
modules/subprocess.jsm \
modules/enigmailprocess_shared_unix.js \
modules/enigmailprocess_worker_common.js \
modules/enigmailprocess_common.jsm \
modules/enigmailprocess_shared_win.js \
modules/enigmailprocess_worker_unix.js \
modules/enigmailprocess_main.jsm \
modules/enigmailprocess_unix.jsm \
modules/enigmailprocess_worker_win.js \
modules/enigmailprocess_shared.js \
modules/enigmailprocess_win.jsm \
modules/system.jsm \
modules/time.jsm \
modules/timer.jsm \
modules/tor.jsm \
modules/trust.jsm \
modules/uris.jsm \
modules/verify.jsm \
modules/versioning.jsm \
modules/webKey.jsm \
modules/wkdLookup.jsm \
modules/windows.jsm \
modules/wksMimeHandler.jsm \
modules/zbase32.jsm \
wrappers/gpg-agent-wrapper \
chrome/${xpiModule}.jar \
chrome.manifest \
install.rdf