diff --git a/package/Makefile b/package/Makefile index 8b60db9c..c2d6e8b1 100644 --- a/package/Makefile +++ b/package/Makefile @@ -1,111 +1,110 @@ # 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 \ 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 \ passwordCheck.jsm \ passwords.jsm \ pEp.jsm \ pEpAdapter.jsm \ pEpDecrypt.jsm \ pEpFilter.jsm \ pEpListener.jsm \ pEpKeySync.jsm \ pEpMessageHist.jsm \ pipeConsole.jsm \ prefs.jsm \ - promise.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 \ 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/autocrypt.jsm b/package/autocrypt.jsm index 123e359e..d018badd 100644 --- a/package/autocrypt.jsm +++ b/package/autocrypt.jsm @@ -1,359 +1,359 @@ /*global Components: false*/ /* 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"; /** * Module for dealing with received Autocrypt headers, level 0 * See details at https://github.com/mailencrypt/autocrypt */ var EXPORTED_SYMBOLS = ["EnigmailAutocrypt"]; const Cc = Components.classes; const Ci = Components.interfaces; const Cr = Components.results; const Cu = Components.utils; Cu.import("resource://gre/modules/Sqlite.jsm"); /* global Sqlite: false */ Cu.import("resource://gre/modules/jsmime.jsm"); /*global jsmime: false*/ Cu.import("resource://enigmail/log.jsm"); /* global EnigmailLog: false*/ Cu.import("resource://enigmail/funcs.jsm"); /* global EnigmailFuncs: false*/ Cu.import("resource://enigmail/mime.jsm"); /* global EnigmailMime: false*/ -Cu.import("resource://enigmail/promise.jsm"); /*global Promise: false */ +Cu.import("resource://gre/modules/PromiseUtils.jsm"); /* global PromiseUtils: false */ Cu.import("resource://enigmail/timer.jsm"); /*global EnigmailTimer: false */ var EnigmailAutocrypt = { /** * Process the "Autocrypt:" header and if successful store the update in the database * * @param fromAddr: String - Address of sender (From: header) * @param headerDataArr: Array of String: all instances of the Autocrypt: header found in the message * @param dateSent: String - Date: field of the message */ processAutocryptHeader: function(fromAddr, headerDataArr, dateSent) { EnigmailLog.DEBUG("autocrypt.jsm: processAutocryptHeader: from=" + fromAddr + "\n"); // critical parameters: {param: mandatory} const CRITICAL = { to: true, key: true, type: false, "prefer-encrypted": false }; fromAddr = EnigmailFuncs.stripEmail(fromAddr).toLowerCase(); let foundTypes = {}; let paramArr = []; for (let hdrNum = 0; hdrNum < headerDataArr.length; hdrNum++) { paramArr = EnigmailMime.getAllParameters(headerDataArr[hdrNum]); for (let i in CRITICAL) { if (CRITICAL[i]) { // found mandatory parameter if (!(i in paramArr)) { EnigmailLog.DEBUG("autocrypt.jsm: processAutocryptHeader: cannot find param " + i + "\n"); return; // do nothing if not all mandatory parts are present } } } for (let i in paramArr) { if (i.substr(0, 1) !== "_") { if (!(i in CRITICAL)) { EnigmailLog.DEBUG("autocrypt.jsm: processAutocryptHeader: unknown critical param " + i + "\n"); return; // do nothing if an unknown critical parameter is found } } } if (fromAddr !== paramArr.to.toLowerCase()) { EnigmailLog.DEBUG("autocrypt.jsm: processAutocryptHeader: from Addr " + fromAddr + " != " + paramArr.to.toLowerCase() + "\n"); return; } if (!("type" in paramArr)) { paramArr.type = "p"; } else { paramArr.type = paramArr.type.toLowerCase(); if (paramArr.type !== "p") { EnigmailLog.DEBUG("autocrypt.jsm: processAutocryptHeader: unknown type " + paramArr.type + "\n"); return; // we currently only support p (=OpenPGP) } } try { let keyData = atob(paramArr.key); } catch (ex) { EnigmailLog.DEBUG("autocrypt.jsm: processAutocryptHeader: key is not base64-encoded\n"); return; } if (paramArr.type in foundTypes) { EnigmailLog.DEBUG("autocrypt.jsm: processAutocryptHeader: duplicate header for type=" + paramArr.type + "\n"); return; // do not process anything if more than one Autocrypt header for the same type is found } foundTypes[paramArr.type] = 1; } if (!("prefer-encrypted" in paramArr)) { paramArr["prefer-encrypted"] = "?"; } if ("_enigmail_artificial" in paramArr && paramArr.paramArr === "yes" && "_enigmail_fpr" in paramArr) { paramArr.fpr = paramArr._enigmail_fpr; paramArr.key = ""; } let lastDate = jsmime.headerparser.parseDateHeader(dateSent); let now = new Date(); if (lastDate > now) { lastDate = now; } paramArr.dateSent = lastDate; let conn; Sqlite.openConnection({ path: "enigmail.sqlite", sharedMemoryCache: false }).then( function onConnection(connection) { conn = connection; return checkDatabaseStructure(conn); }, function onError(error) { EnigmailLog.DEBUG("autocrypt.jsm: processAutocryptHeader: could not open database\n"); } ).then( function _f() { return findUserRecord(conn, fromAddr, paramArr.type); } ).then( function gotData(resultObj) { EnigmailLog.DEBUG("autocrypt.jsm: got " + resultObj.numRows + " rows\n"); if (resultObj.data === null) { return appendUser(conn, paramArr); } else { return updateUser(conn, paramArr, resultObj.data); } } ).then( function _done() { EnigmailLog.DEBUG("autocrypt.jsm: OK - closing connection\n"); conn.close(); } ).catch( function _err(reason) { EnigmailLog.DEBUG("autocrypt.jsm: error - closing connection: " + reason + "\n"); conn.close(); } ); } }; /** * Ensure that the database structure matches the latest version * (table is available) * * @param connection: Object - SQLite connection * * @return Promise */ function checkDatabaseStructure(connection) { EnigmailLog.DEBUG("autocrypt.jsm: checkDatabaseStructure\n"); - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); connection.tableExists("autocrypt_keys").then( function onSuccess(exists) { EnigmailLog.DEBUG("autocrypt.jsm: checkDatabaseStructure - success\n"); if (!exists) { createAutocryptTable(connection, deferred); } else { deferred.resolve(); } }, function onError(error) { EnigmailLog.DEBUG("autocrypt.jsm: checkDatabaseStructure - error\n"); deferred.reject(error); } ); return deferred.promise; } /** * Create the "autocrypt_keys" table and the corresponding index * * @param connection: Object - SQLite connection * @param deferred: Promise */ function createAutocryptTable(connection, deferred) { EnigmailLog.DEBUG("autocrypt.jsm: createAutocryptTable\n"); connection.execute("create table autocrypt_keys (" + "email text not null, " + // email address of correspondent "encryption_pref text not null, " + // encryption prefrence (yes / no / ?) "keydata text not null, " + // base64-encoded key as received "fpr text, " + // fingerprint of key (once key was imported in keyring) "type text not null, " + // key type (currently only OpenPGP) "last_changed text not null, " + // timestamp since when keydata and encryption_pref are unchanged "last_seen text not null);"). // timestamp of last mail received for the email/type combination then( function _ok() { EnigmailLog.DEBUG("autocrypt.jsm: createAutocryptTable - index\n"); connection.execute("create unique index autocrypt_keys_i1 on autocrypt_keys(email, type)"). then(function _f() { deferred.resolve(); }); } ); } /** * Find the database record for a given email address and type * * @param connection: Object - SQLite connection * @param email: String - Email address to search (in lowercase) * @param type: String - type to search (in lowercase) * * @return Promise */ function findUserRecord(connection, email, type) { EnigmailLog.DEBUG("autocrypt.jsm: findUserRecord\n"); - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); let data = null; let numRows = 0; connection.execute( "select * from autocrypt_keys where email = :email and type = :type", { email: email, type: type }, function _onRow(row) { EnigmailLog.DEBUG("autocrypt.jsm: findUserRecord - got row\n"); if (numRows === 0) { data = row; } else { data = null; } ++numRows; } ).then(function _f() { deferred.resolve({ data: data, numRows: numRows }); }); return deferred.promise; } /** * Create new database record for an Autorypt header * * @param connection: Object - SQLite connection * @param paramsArr: Object - the Autocrypt header parameters * * @return Promise */ function appendUser(connection, paramsArr) { EnigmailLog.DEBUG("autocrypt.jsm: appendUser\n"); - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); connection.executeTransaction(function _trx() { connection.execute("insert into autocrypt_keys (email, encryption_pref, keydata, fpr, type, last_changed, last_seen) values " + "(:email, :pref, :keyData, :fpr, :type, :lastChange, :lastSeen)", { email: paramsArr.to, pref: paramsArr["prefer-encrypted"], keyData: paramsArr.key, fpr: ("fpr" in paramsArr ? paramsArr.fpr : ""), type: paramsArr.type, lastChange: paramsArr.dateSent.toJSON(), lastSeen: paramsArr.dateSent.toJSON() }).then( function _ok() { deferred.resolve(); } ).catch(function _err() { deferred.reject("appendUser"); }); }); return deferred.promise; } /** * Update the record for an email address and type, if the email we got is newer * than the latest record we already stored * * @param connection: Object - SQLite connection * @param paramsArr: Object - the Autocrypt header parameters * @param currData: Object (mozIStorageRow) - current data stored in the database * * @return Promise */ function updateUser(connection, paramsArr, currData) { EnigmailLog.DEBUG("autocrypt.jsm: updateUser\n"); - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); let lastSeen = new Date(currData.getResultByName("last_seen")); let lastChanged = new Date(currData.getResultByName("last_changed")); if (lastSeen >= paramsArr.dateSent) { EnigmailLog.DEBUG("autocrypt.jsm: updateUser: not a new latest message\n"); EnigmailTimer.setTimeout(function _f() { deferred.resolve(); }, 0); return deferred.promise; } EnigmailLog.DEBUG("autocrypt.jsm: updateUser: updating latest message\n"); let pref = currData.getResultByName("encryption_pref"); if (pref !== "?" && paramsArr["prefer-encrypted"] === "?") { paramsArr["prefer-encrypted"] = pref; } if (paramsArr["prefer-encrypted"] !== pref || currData.getResultByName("keydata") !== paramsArr.key) { lastChanged = paramsArr.dateSent; } connection.executeTransaction(function _trx() { connection.execute("update autocrypt_keys set encryption_pref = :pref, keydata = :keyData, last_changed = :lastChanged, " + "fpr = :fpr, last_seen = :lastSeen where email = :email and type = :type", { email: paramsArr.to, pref: paramsArr["prefer-encrypted"], keyData: paramsArr.key, fpr: ("fpr" in paramsArr ? paramsArr.fpr : ""), type: paramsArr.type, lastChanged: lastChanged.toJSON(), lastSeen: paramsArr.dateSent.toJSON() }).then( function _ok() { deferred.resolve(); } ).catch(function _err() { deferred.reject("update failed"); }); }); return deferred.promise; } diff --git a/package/decryptPermanently.jsm b/package/decryptPermanently.jsm index 5ee1e5d7..52680fbc 100644 --- a/package/decryptPermanently.jsm +++ b/package/decryptPermanently.jsm @@ -1,1255 +1,1254 @@ /*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://enigmail/promise.jsm"); /*global Promise: false */ Cu.import("resource:///modules/MailUtils.js"); /*global MailUtils: false */ Cu.import("resource://enigmail/core.jsm"); /*global EnigmailCore: false */ Cu.import("resource://enigmail/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://gre/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 = { 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); } }, 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/fixExchangeMsg.jsm b/package/fixExchangeMsg.jsm index e273ec06..7d49380f 100644 --- a/package/fixExchangeMsg.jsm +++ b/package/fixExchangeMsg.jsm @@ -1,379 +1,378 @@ /*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 = ["EnigmailFixExchangeMsg"]; const Cu = Components.utils; Cu.import("resource:///modules/MailUtils.js"); /*global MailUtils: false */ Cu.import("resource://enigmail/core.jsm"); /*global EnigmailCore: false */ Cu.import("resource://enigmail/funcs.jsm"); /*global EnigmailFuncs: false */ Cu.import("resource://enigmail/log.jsm"); /*global EnigmailLog: false */ -Cu.import("resource://enigmail/promise.jsm"); /*global Promise: false */ Cu.import("resource://enigmail/streams.jsm"); /*global EnigmailStreams: false */ const EC = EnigmailCore; const Cc = Components.classes; const Ci = Components.interfaces; const nsIEnigmail = Components.interfaces.nsIEnigmail; const IOSERVICE_CONTRACTID = "@mozilla.org/network/io-service;1"; /* * Fix a broken message from MS-Exchange and replace it with the original message * * @param nsIMsgDBHdr hdr Header of the message to fix (= pointer to message) * @param String brokenByApp Type of app that created the message. Currently one of * exchange, iPGMail * @param String destFolderUri optional destination Folder URI * * @return Promise; upon success, the promise returns the messageKey */ const EnigmailFixExchangeMsg = { fixExchangeMessage: function(hdr, brokenByApp, destFolderUri) { var self = this; return new Promise( function fixExchangeMessage_p(resolve, reject) { let msgUriSpec = hdr.folder.getUriForMsg(hdr); EnigmailLog.DEBUG("fixExchangeMsg.jsm: fixExchangeMessage: msgUriSpec: " + msgUriSpec + "\n"); self.hdr = hdr; self.destFolder = hdr.folder; self.resolve = resolve; self.reject = reject; self.brokenByApp = brokenByApp; if (destFolderUri) { self.destFolder = MailUtils.getFolderForURI(destFolderUri, false); } let messenger = Cc["@mozilla.org/messenger;1"].createInstance(Ci.nsIMessenger); self.msgSvc = messenger.messageServiceFromURI(msgUriSpec); let p = self.getMessageBody(); p.then( function resolved(fixedMsgData) { EnigmailLog.DEBUG("fixExchangeMsg.jsm: fixExchangeMessage: got fixedMsgData\n"); self.copyToTargetFolder(fixedMsgData); }); p.catch( function rejected(reason) { EnigmailLog.DEBUG("fixExchangeMsg.jsm: fixExchangeMessage: caught rejection: " + reason + "\n"); reject(); return; }); } ); }, getMessageBody: function() { EnigmailLog.DEBUG("fixExchangeMsg.jsm: getMessageBody:\n"); var self = this; return new Promise( function(resolve, reject) { let u = {}; self.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("fixExchangeMsg.jsm: getting data from URL " + url + "\n"); let s = EnigmailStreams.newStringStreamListener( function analyzeData(data) { EnigmailLog.DEBUG("fixExchangeMsg.jsm: analyzeDecryptedData: got " + data.length + " bytes\n"); if (EnigmailLog.getLogLevel() > 5) { EnigmailLog.DEBUG("*** start data ***\n'" + data + "'\n***end data***\n"); } let hdrEnd = data.search(/\r?\n\r?\n/); if (hdrEnd <= 0) { // cannot find end of header data reject(0); return; } let hdrLines = data.substr(0, hdrEnd).split(/\r?\n/); let hdrObj = self.getFixedHeaderData(hdrLines); if (hdrObj.headers.length === 0 || hdrObj.boundary.length === 0) { reject(1); return; } let boundary = hdrObj.boundary; let body; switch (self.brokenByApp) { case "exchange": body = self.getCorrectedExchangeBodyData(data.substr(hdrEnd + 2), boundary); break; case "iPGMail": body = self.getCorrectediPGMailBodyData(data.substr(hdrEnd + 2), boundary); break; default: EnigmailLog.ERROR("fixExchangeMsg.jsm: getMessageBody: unknown appType " + self.brokenByApp + "\n"); reject(99); return; } if (body) { resolve(hdrObj.headers + "\r\n" + body); return; } else { reject(2); return; } } ); var ioServ = Components.classes[IOSERVICE_CONTRACTID].getService(Components.interfaces.nsIIOService); try { let channel = EnigmailStreams.createChannel(url); channel.asyncOpen(s, null); } catch (e) { EnigmailLog.DEBUG("fixExchangeMsg.jsm: getMessageBody: exception " + e + "\n"); } } ); }, /** * repair header data, such that they are working for PGP/MIME * * @return: object: { * headers: String - all headers ready for appending to message * boundary: String - MIME part boundary (incl. surrounding "" or '') * } */ getFixedHeaderData: function(hdrLines) { EnigmailLog.DEBUG("fixExchangeMsg.jsm: getFixedHeaderData: hdrLines[]:'" + hdrLines.length + "'\n"); let r = { headers: "", boundary: "" }; for (let i = 0; i < hdrLines.length; i++) { if (hdrLines[i].search(/^content-type:/i) >= 0) { // Join the rest of the content type lines together. // See RFC 2425, section 5.8.1 let contentTypeLine = hdrLines[i]; i++; while (i < hdrLines.length) { // Does the line start with a space or a tab, followed by something else? if (hdrLines[i].search(/^[ \t]+?/) === 0) { contentTypeLine += hdrLines[i]; i++; } else { // we got the complete content-type header contentTypeLine = contentTypeLine.replace(/[\r\n]/g, ""); let h = EnigmailFuncs.getHeaderData(contentTypeLine); r.boundary = h.boundary || ""; break; } } } else { r.headers += hdrLines[i] + "\r\n"; } } r.boundary = r.boundary.replace(/^(['"])(.*)(['"])/, "$2"); r.headers += 'Content-Type: multipart/encrypted;\r\n' + ' protocol="application/pgp-encrypted";\r\n' + ' boundary="' + r.boundary + '"\r\n' + 'X-Enigmail-Info: Fixed broken PGP/MIME message\r\n'; return r; }, /** * Get corrected body for MS-Exchange messages */ getCorrectedExchangeBodyData: function(bodyData, boundary) { EnigmailLog.DEBUG("fixExchangeMsg.jsm: getCorrectedExchangeBodyData: boundary='" + boundary + "'\n"); let boundRx = new RegExp("^--" + boundary, "gm"); let match = boundRx.exec(bodyData); if (match.index < 0) { EnigmailLog.DEBUG("fixExchangeMsg.jsm: getCorrectedExchangeBodyData: did not find index of mime type to skip\n"); return null; } let skipStart = match.index; // found first instance -- that's the message part to ignore match = boundRx.exec(bodyData); if (match.index <= 0) { EnigmailLog.DEBUG("fixExchangeMsg.jsm: getCorrectedExchangeBodyData: did not find boundary of PGP/MIME version identification\n"); return null; } let versionIdent = match.index; if (bodyData.substring(skipStart, versionIdent).search(/^content-type:[ \t]*text\/plain/mi) < 0) { EnigmailLog.DEBUG("fixExchangeMsg.jsm: getCorrectedExchangeBodyData: first MIME part is not content-type text/plain\n"); return null; } match = boundRx.exec(bodyData); if (match.index < 0) { EnigmailLog.DEBUG("fixExchangeMsg.jsm: getCorrectedExchangeBodyData: did not find boundary of PGP/MIME encrypted data\n"); return null; } let encData = match.index; let mimeHdr = Cc["@mozilla.org/messenger/mimeheaders;1"].createInstance(Ci.nsIMimeHeaders); mimeHdr.initialize(bodyData.substring(versionIdent, encData)); let ct = mimeHdr.extractHeader("content-type", false); if (!ct || ct.search(/application\/pgp-encrypted/i) < 0) { EnigmailLog.DEBUG("fixExchangeMsg.jsm: getCorrectedExchangeBodyData: wrong content-type of version-identification\n"); EnigmailLog.DEBUG(" ct = '" + ct + "'\n"); return null; } mimeHdr.initialize(bodyData.substr(encData, 5000)); ct = mimeHdr.extractHeader("content-type", false); if (!ct || ct.search(/application\/octet-stream/i) < 0) { EnigmailLog.DEBUG("fixExchangeMsg.jsm: getCorrectedExchangeBodyData: wrong content-type of PGP/MIME data\n"); EnigmailLog.DEBUG(" ct = '" + ct + "'\n"); return null; } return bodyData.substr(versionIdent); }, /** * Get corrected body for iPGMail messages */ getCorrectediPGMailBodyData: function(bodyData, boundary) { EnigmailLog.DEBUG("fixExchangeMsg.jsm: getCorrectediPGMailBodyData: boundary='" + boundary + "'\n"); let boundRx = new RegExp("^--" + boundary, "gm"); let match = boundRx.exec(bodyData); if (match.index < 0) { EnigmailLog.DEBUG("fixExchangeMsg.jsm: getCorrectediPGMailBodyData: did not find index of mime type to skip\n"); return null; } let skipStart = match.index; // found first instance -- that's the message part to ignore match = boundRx.exec(bodyData); if (match.index <= 0) { EnigmailLog.DEBUG("fixExchangeMsg.jsm: getCorrectediPGMailBodyData: did not find boundary of text/plain msg part\n"); return null; } let encData = match.index; match = boundRx.exec(bodyData); if (match.index < 0) { EnigmailLog.DEBUG("fixExchangeMsg.jsm: getCorrectediPGMailBodyData: did not find end boundary of PGP/MIME encrypted data\n"); return null; } let mimeHdr = Cc["@mozilla.org/messenger/mimeheaders;1"].createInstance(Ci.nsIMimeHeaders); mimeHdr.initialize(bodyData.substr(encData, 5000)); let ct = mimeHdr.extractHeader("content-type", false); if (!ct || ct.search(/application\/pgp-encrypted/i) < 0) { EnigmailLog.DEBUG("fixExchangeMsg.jsm: getCorrectediPGMailBodyData: wrong content-type of PGP/MIME data\n"); EnigmailLog.DEBUG(" ct = '" + ct + "'\n"); return null; } return "--" + boundary + "\r\n" + "Content-Type: application/pgp-encrypted\r\n" + "Content-Description: PGP/MIME version identification\r\n\r\n" + "Version: 1\r\n\r\n" + bodyData.substring(encData, match.index). replace(/^Content-Type: +application\/pgp-encrypted/im, "Content-Type: application/octet-stream") + "--" + boundary + "--\r\n"; }, copyToTargetFolder: function(msgData) { var self = 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); // octal 0600 - since octal is deprected in JS // 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(msgData, msgData.length); foStream.close(); extAppLauncher.deleteTemporaryFileOnExit(tempFile); // note: 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); var copyListener = { QueryInterface: function(iid) { if (iid.equals(Ci.nsIMsgCopyServiceListener) || iid.equals(Ci.nsISupports)) { return this; } throw Components.results.NS_NOINTERFACE; }, msgKey: null, GetMessageId: function(messageId) {}, OnProgress: function(progress, progressMax) {}, OnStartCopy: function() {}, SetMessageKey: function(key) { this.msgKey = key; }, OnStopCopy: function(statusCode) { if (statusCode !== 0) { EnigmailLog.DEBUG("fixExchangeMsg.jsm: error copying message: " + statusCode + "\n"); tempFile.remove(false); self.reject(3); return; } EnigmailLog.DEBUG("fixExchangeMsg.jsm: copy complete\n"); EnigmailLog.DEBUG("fixExchangeMsg.jsm: deleting message key=" + self.hdr.messageKey + "\n"); let msgArray = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray); msgArray.appendElement(self.hdr, false); self.hdr.folder.deleteMessages(msgArray, null, true, false, null, false); EnigmailLog.DEBUG("fixExchangeMsg.jsm: deleted original message\n"); tempFile.remove(false); self.resolve(this.msgKey); return; } }; let copySvc = Cc["@mozilla.org/messenger/messagecopyservice;1"].getService(Ci.nsIMsgCopyService); copySvc.CopyFileMessage(fileSpec, this.destFolder, null, false, this.hdr.flags, null, copyListener, null); } }; diff --git a/package/installGnuPG.jsm b/package/installGnuPG.jsm index e934515c..344c480a 100644 --- a/package/installGnuPG.jsm +++ b/package/installGnuPG.jsm @@ -1,665 +1,665 @@ /*global Components: false, escape: false, unescape: false, Uint8Array: false */ /* eslint no-invalid-this: 0 */ /* * 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 = ["InstallGnuPG"]; /* Usage: InstallGnuPG.start(progressListener). progressListener needs to implement the following methods: void onError (errorMessage) boolean onWarning (message) void onProgress (event) void onLoaded (event) void onDownloaded () void onStart (requestObj) requestObj: abort(): cancel download onWarning can return true if the warning should be ignored, false otherwise */ var Cu = Components.utils; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); /*global XPCOMUtils: false */ Cu.import("resource://enigmail/subprocess.jsm"); /*global subprocess: false */ Cu.import("resource://enigmail/log.jsm"); /*global EnigmailLog: false */ Cu.import("resource://enigmail/os.jsm"); /*global EnigmailOS: false */ Cu.import("resource://enigmail/app.jsm"); /*global EnigmailApp: false */ -Cu.import("resource://enigmail/promise.jsm"); /*global Promise: false */ +Cu.import("resource://gre/modules/PromiseUtils.jsm"); /* global PromiseUtils: false */ Cu.import("resource://enigmail/files.jsm"); /*global EnigmailFiles: false */ const Cc = Components.classes; const Ci = Components.interfaces; const EXEC_FILE_PERMS = 0x1C0; // 0700 const NS_LOCALFILEOUTPUTSTREAM_CONTRACTID = "@mozilla.org/network/file-output-stream;1"; const DIR_SERV_CONTRACTID = "@mozilla.org/file/directory_service;1"; const NS_LOCAL_FILE_CONTRACTID = "@mozilla.org/file/local;1"; const XPCOM_APPINFO = "@mozilla.org/xre/app-info;1"; const queryUrl = "https://www.enigmail.net/service/getGnupdDownload.svc"; function toHexString(charCode) { return ("0" + charCode.toString(16)).slice(-2); } function sanitizeFileName(str) { // remove shell escape, #, ! and / from string return str.replace(/[`\/\#\!]/g, ""); } function sanitizeHash(str) { return str.replace(/[^a-hA-H0-9]/g, ""); } // Adapted from the patch for mozTCPSocket error reporting (bug 861196). function createTCPErrorFromFailedXHR(xhr) { let status = xhr.channel.QueryInterface(Ci.nsIRequest).status; let errType; let errName; if ((status & 0xff0000) === 0x5a0000) { // Security module const nsINSSErrorsService = Ci.nsINSSErrorsService; let nssErrorsService = Cc['@mozilla.org/nss_errors_service;1'].getService(nsINSSErrorsService); let errorClass; // getErrorClass will throw a generic NS_ERROR_FAILURE if the error code is // somehow not in the set of covered errors. try { errorClass = nssErrorsService.getErrorClass(status); } catch (ex) { errorClass = 'SecurityProtocol'; } if (errorClass == nsINSSErrorsService.ERROR_CLASS_BAD_CERT) { errType = 'SecurityCertificate'; } else { errType = 'SecurityProtocol'; } // NSS_SEC errors (happen below the base value because of negative vals) if ((status & 0xffff) < Math.abs(nsINSSErrorsService.NSS_SEC_ERROR_BASE)) { // The bases are actually negative, so in our positive numeric space, we // need to subtract the base off our value. let nssErr = Math.abs(nsINSSErrorsService.NSS_SEC_ERROR_BASE) - (status & 0xffff); switch (nssErr) { case 11: // SEC_ERROR_EXPIRED_CERTIFICATE, sec(11) errName = 'SecurityExpiredCertificateError'; break; case 12: // SEC_ERROR_REVOKED_CERTIFICATE, sec(12) errName = 'SecurityRevokedCertificateError'; break; // per bsmith, we will be unable to tell these errors apart very soon, // so it makes sense to just folder them all together already. case 13: // SEC_ERROR_UNKNOWN_ISSUER, sec(13) case 20: // SEC_ERROR_UNTRUSTED_ISSUER, sec(20) case 21: // SEC_ERROR_UNTRUSTED_CERT, sec(21) case 36: // SEC_ERROR_CA_CERT_INVALID, sec(36) errName = 'SecurityUntrustedCertificateIssuerError'; break; case 90: // SEC_ERROR_INADEQUATE_KEY_USAGE, sec(90) errName = 'SecurityInadequateKeyUsageError'; break; case 176: // SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED, sec(176) errName = 'SecurityCertificateSignatureAlgorithmDisabledError'; break; default: errName = 'SecurityError'; break; } } else { let sslErr = Math.abs(nsINSSErrorsService.NSS_SSL_ERROR_BASE) - (status & 0xffff); switch (sslErr) { case 3: // SSL_ERROR_NO_CERTIFICATE, ssl(3) errName = 'SecurityNoCertificateError'; break; case 4: // SSL_ERROR_BAD_CERTIFICATE, ssl(4) errName = 'SecurityBadCertificateError'; break; case 8: // SSL_ERROR_UNSUPPORTED_CERTIFICATE_TYPE, ssl(8) errName = 'SecurityUnsupportedCertificateTypeError'; break; case 9: // SSL_ERROR_UNSUPPORTED_VERSION, ssl(9) errName = 'SecurityUnsupportedTLSVersionError'; break; case 12: // SSL_ERROR_BAD_CERT_DOMAIN, ssl(12) errName = 'SecurityCertificateDomainMismatchError'; break; default: errName = 'SecurityError'; break; } } } else { errType = 'Network'; switch (status) { // connect to host:port failed case 0x804B000C: // NS_ERROR_CONNECTION_REFUSED, network(13) errName = 'ConnectionRefusedError'; break; // network timeout error case 0x804B000E: // NS_ERROR_NET_TIMEOUT, network(14) errName = 'NetworkTimeoutError'; break; // hostname lookup failed case 0x804B001E: // NS_ERROR_UNKNOWN_HOST, network(30) errName = 'DomainNotFoundError'; break; case 0x804B0047: // NS_ERROR_NET_INTERRUPT, network(71) errName = 'NetworkInterruptError'; break; default: errName = 'NetworkError'; break; } } return { name: errName, type: errType }; } function Installer(progressListener) { this.progressListener = progressListener; } Installer.prototype = { installMacOs: function(deferred) { EnigmailLog.DEBUG("installGnuPG.jsm: installMacOs\n"); var exitCode = -1; var mountPath = Cc[NS_LOCAL_FILE_CONTRACTID].createInstance(Ci.nsIFile); mountPath.initWithPath("/Volumes/" + this.mount); if (mountPath.exists()) { let p = mountPath.path + " "; let i = 1; mountPath.initWithPath(p + i); while (mountPath.exists() && i < 10) { ++i; mountPath.initWithPath(p + i); } if (mountPath.exists()) { throw "Error - cannot mount package"; } } this.mountPath = mountPath; EnigmailLog.DEBUG("installGnuPG.jsm: installMacOs - mount Package\n"); var cmd = Cc[NS_LOCAL_FILE_CONTRACTID].createInstance(Ci.nsIFile); cmd.initWithPath("/usr/bin/open"); var args = ["-W", this.installerFile.path]; var proc = { command: cmd, arguments: args, charset: null, done: function(result) { exitCode = result.exitCode; } }; try { subprocess.call(proc).wait(); if (exitCode) throw "Installer failed with exit code " + exitCode; } catch (ex) { EnigmailLog.ERROR("installGnuPG.jsm: installMacOs: subprocess.call failed with '" + ex.toString() + "'\n"); throw ex; } EnigmailLog.DEBUG("installGnuPG.jsm: installMacOs - run installer\n"); args = ["-W", this.mountPath.path + "/" + this.command]; proc = { command: cmd, arguments: args, charset: null, done: function(result) { if (result.exitCode !== 0) { deferred.reject("Installer failed with exit code " + result.exitCode); } else deferred.resolve(); } }; try { subprocess.call(proc); } catch (ex) { EnigmailLog.ERROR("installGnuPG.jsm: installMacOs: subprocess.call failed with '" + ex.toString() + "'\n"); throw ex; } }, cleanupMacOs: function() { EnigmailLog.DEBUG("installGnuPG.jsm.cleanupMacOs: unmount package\n"); var cmd = Cc[NS_LOCAL_FILE_CONTRACTID].createInstance(Ci.nsIFile); cmd.initWithPath("/usr/sbin/diskutil"); var args = ["eject", this.mountPath.path]; var proc = { command: cmd, arguments: args, charset: null, done: function(result) { if (result.exitCode) EnigmailLog.ERROR("Installer failed with exit code " + result.exitCode); } }; try { subprocess.call(proc).wait(); } catch (ex) { EnigmailLog.ERROR("installGnuPG.jsm.cleanupMacOs: subprocess.call failed with '" + ex.toString() + "'\n"); } EnigmailLog.DEBUG("installGnuPG.jsm: cleanupMacOs - remove package\n"); this.installerFile.remove(false); }, /** * Create the gpg4win installer config file * @return nsIFile - config object file */ createGpg4WinCfgFile: function() { EnigmailLog.DEBUG("installGnuPG.jsm: createGpg4WinCfgFile\n"); let tmpFile = EnigmailFiles.getTempDirObj().clone(); tmpFile.append("gpg4win.ini"); tmpFile.createUnique(tmpFile.NORMAL_FILE_TYPE, EXEC_FILE_PERMS); let dataStr = "[gpg4win]\r\n"; let cfgKeys = [ "inst_gpgol", "inst_gpgex", "inst_kleopatra", "inst_gpa", "inst_claws_mail", "inst_compendium", "inst_desktop", "inst_quick_launch_bar" ]; // disable optional components by default for (let i of cfgKeys) { dataStr += " " + i + " = false\r\n"; } dataStr += " inst_start_menu = true\r\n"; if (EnigmailFiles.writeFileContents(tmpFile, dataStr)) { return tmpFile; } return null; }, installWindows: function(deferred) { EnigmailLog.DEBUG("installGnuPG.jsm: installWindows\n"); try { // use runwAsync in order to get UAC approval on Windows 7 / 8 if required var obs = { QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupports]), observe: function(proc, aTopic, aData) { EnigmailLog.DEBUG("installGnuPG.jsm: installWindows.observe: topic='" + aTopic + "' \n"); if (aTopic == "process-finished") { EnigmailLog.DEBUG("installGnuPG.jsm: installWindows finished\n"); deferred.resolve(); } else if (aTopic == "process-failed") { deferred.reject("Installer could not be started"); } } }; this.gpg4WinCfgFile = this.createGpg4WinCfgFile(); let cfgFile = EnigmailFiles.getFilePath(this.gpg4WinCfgFile); let params = []; if (cfgFile) { if (cfgFile.indexOf('"') >= 0) cfgFile = '"' + cfgFile + '"'; params.push('/C=' + cfgFile); } EnigmailLog.DEBUG("installGnuPG.jsm: installWindows: executing " + this.installerFile.path + " " + params.join(" ") + "\n"); var proc = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess); proc.init(this.installerFile); proc.runwAsync(params, params.length, obs, false); } catch (ex) { deferred.reject("Installer could not be started"); } }, cleanupWindows: function() { EnigmailLog.DEBUG("installGnuPG.jsm: cleanupWindows - remove package\n"); this.installerFile.remove(false); if (this.gpg4WinCfgFile) this.gpg4WinCfgFile.remove(false); }, installUnix: function() {}, checkHashSum: function() { EnigmailLog.DEBUG("installGnuPG.jsm: checkHashSum\n"); var istream = Components.classes["@mozilla.org/network/file-input-stream;1"] .createInstance(Components.interfaces.nsIFileInputStream); // open for reading istream.init(this.installerFile, 0x01, 292, 0); // octal 0444 - octal literals are deprecated var ch = Components.classes["@mozilla.org/security/hash;1"] .createInstance(Components.interfaces.nsICryptoHash); ch.init(ch.SHA1); const PR_UINT32_MAX = 0xffffffff; // read entire file ch.updateFromStream(istream, PR_UINT32_MAX); var gotHash = ch.finish(false); // convert the binary hash data to a hex string. var hashStr = ""; for (let i in gotHash) { hashStr += toHexString(gotHash.charCodeAt(i)); } if (this.hash != hashStr) { EnigmailLog.DEBUG("installGnuPG.jsm: checkHashSum - hash sums don't match: " + hashStr + "\n"); } else EnigmailLog.DEBUG("installGnuPG.jsm: checkHashSum - hash sum OK\n"); return this.hash == hashStr; }, getDownloadUrl: function(on) { - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); function reqListener() { // "this" is set by the calling XMLHttpRequest if (typeof(this.responseXML) == "object") { EnigmailLog.DEBUG("installGnuPG.jsm: getDownloadUrl.reqListener: got: " + this.responseText + "\n"); if (!this.responseXML) { onError({ type: "Network" }); return; } let doc = this.responseXML.firstChild; self.url = unescape(doc.getAttribute("url")); self.hash = sanitizeHash(doc.getAttribute("hash")); self.command = sanitizeFileName(doc.getAttribute("command")); self.mount = sanitizeFileName(doc.getAttribute("mount")); deferred.resolve(); } } function onError(error) { deferred.reject("error"); if (self.progressListener) { return self.progressListener.onError(error); } return false; } EnigmailLog.DEBUG("installGnuPG.jsm: getDownloadUrl: start request\n"); var self = this; try { var xulRuntime = Cc[XPCOM_APPINFO].getService(Ci.nsIXULRuntime); var platform = xulRuntime.XPCOMABI.toLowerCase(); var os = EnigmailOS.getOS().toLowerCase(); // create a XMLHttpRequest object var oReq = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(); oReq.onload = reqListener; oReq.addEventListener("error", function(e) { var error = createTCPErrorFromFailedXHR(oReq); onError(error); }, false); oReq.open("get", queryUrl + "?vEnigmail=" + escape(EnigmailApp.getVersion()) + "&os=" + escape(os) + "&platform=" + escape(platform), true); oReq.send(); } catch (ex) { deferred.reject(ex); EnigmailLog.writeException("installGnuPG.jsm", ex); if (self.progressListener) self.progressListener.onError("installGnuPG.downloadFailed"); } return deferred.promise; }, performDownload: function() { EnigmailLog.DEBUG("installGnuPG.jsm: performDownload: " + this.url + "\n"); var self = this; - var deferred = Promise.defer(); + var deferred = PromiseUtils.defer(); function onProgress(event) { if (event.lengthComputable) { var percentComplete = event.loaded / event.total; EnigmailLog.DEBUG("installGnuPG.jsm: performDownload: " + percentComplete * 100 + "% loaded\n"); } else { EnigmailLog.DEBUG("installGnuPG.jsm: performDownload: got " + event.loaded + "bytes\n"); } if (self.progressListener) self.progressListener.onProgress(event); } function onError(error) { deferred.reject("error"); if (self.progressListener) self.progressListener.onError(error); } function onLoaded(event) { EnigmailLog.DEBUG("installGnuPG.jsm: performDownload: downloaded " + event.loaded + "bytes\n"); if (self.progressListener) self.progressListener.onDownloaded(); try { // "this" is set by the calling XMLHttpRequest performInstall(this.response).then(function _f() { performCleanup(); }); } catch (ex) { EnigmailLog.writeException("installGnuPG.jsm", ex); if (self.progressListener) self.progressListener.onError("installGnuPG.installFailed"); } } function performInstall(response) { var arraybuffer = response; // not responseText EnigmailLog.DEBUG("installGnuPG.jsm: performDownload: bytes " + arraybuffer.byteLength + "\n"); try { var flags = 0x02 | 0x08 | 0x20; var fileOutStream = Cc[NS_LOCALFILEOUTPUTSTREAM_CONTRACTID].createInstance(Ci.nsIFileOutputStream); self.installerFile = EnigmailFiles.getTempDirObj().clone(); switch (EnigmailOS.getOS()) { case "Darwin": self.installerFile.append("GnuPG-Installer.dmg"); self.performCleanup = self.cleanupMacOs; break; case "WINNT": self.installerFile.append("gpg4win.exe"); self.performCleanup = self.cleanupWindows; break; default: self.installerFile.append("gpg-installer.bin"); self.performCleanup = null; } self.installerFile.createUnique(self.installerFile.NORMAL_FILE_TYPE, EXEC_FILE_PERMS); EnigmailLog.DEBUG("installGnuPG.jsm: performDownload: writing file to " + self.installerFile.path + "\n"); fileOutStream.init(self.installerFile, flags, EXEC_FILE_PERMS, 0); var binStr = Cc["@mozilla.org/binaryoutputstream;1"].createInstance(Ci.nsIBinaryOutputStream); binStr.setOutputStream(fileOutStream.QueryInterface(Ci.nsIOutputStream)); var buf = new Uint8Array(arraybuffer); binStr.writeByteArray(buf, buf.length); binStr.flush(); binStr.close(); fileOutStream.close(); if (!self.checkHashSum()) { var cont = true; if (self.progressListener) { cont = self.progressListener.onWarning("hashSumMismatch"); } if (!cont) { deferred.reject("Aborted due to hash sum error"); return null; } } switch (EnigmailOS.getOS()) { case "Darwin": self.installMacOs(deferred); break; case "WINNT": self.installWindows(deferred); break; default: self.installUnix(deferred); } } catch (ex) { deferred.reject(ex); EnigmailLog.writeException("installGnuPG.jsm", ex); if (self.progressListener) self.progressListener.onError("installGnuPG.installFailed"); } return deferred.promise; } function performCleanup() { EnigmailLog.DEBUG("installGnuPG.jsm: performCleanup:\n"); try { if (self.performCleanup) self.performCleanup(); } catch (ex) {} if (self.progressListener) { EnigmailLog.DEBUG("installGnuPG.jsm: performCleanup - onLoaded()\n"); self.progressListener.onLoaded(); } } try { // create a XMLHttpRequest object var oReq = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(); oReq.addEventListener("load", onLoaded, false); oReq.addEventListener("error", function(e) { var error = createTCPErrorFromFailedXHR(oReq); onError(error); }, false); oReq.addEventListener("progress", onProgress, false); oReq.open("get", this.url, true); oReq.responseType = "arraybuffer"; if (self.progressListener) self.progressListener.onStart({ abort: function() { oReq.abort(); } }); oReq.send(); } catch (ex) { deferred.reject(ex); EnigmailLog.writeException("installGnuPG.jsm", ex); if (self.progressListener) self.progressListener.onError("installGnuPG.downloadFailed"); } } }; var InstallGnuPG = { // check if there is a downloadable item for the given platform // returns true if item available checkAvailability: function() { switch (EnigmailOS.getOS()) { case "Darwin": case "WINNT": return true; } return false; }, startInstaller: function(progressListener) { var i = new Installer(progressListener); i.getDownloadUrl(i). then(function _dl() { i.performDownload(); }); return i; } }; diff --git a/package/installPep.jsm b/package/installPep.jsm index 89f4fc39..27472a42 100644 --- a/package/installPep.jsm +++ b/package/installPep.jsm @@ -1,595 +1,595 @@ /*global Components: false, escape: false, unescape: false, Uint8Array: false */ /* eslint no-invalid-this: 0 */ /* * 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 = ["EnigmailInstallPep"]; const Cu = Components.utils; const Cc = Components.classes; const Ci = Components.interfaces; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); /*global XPCOMUtils: false */ Cu.import("resource://enigmail/subprocess.jsm"); /*global subprocess: false */ Cu.import("resource://enigmail/log.jsm"); /*global EnigmailLog: false */ Cu.import("resource://enigmail/os.jsm"); /*global EnigmailOS: false */ Cu.import("resource://enigmail/app.jsm"); /*global EnigmailApp: false */ -Cu.import("resource://enigmail/promise.jsm"); /*global Promise: false */ +Cu.import("resource://gre/modules/PromiseUtils.jsm"); /* global PromiseUtils: false */ Cu.import("resource://enigmail/files.jsm"); /*global EnigmailFiles: false */ const EXEC_FILE_PERMS = 0x1C0; // 0700 const NS_LOCALFILEOUTPUTSTREAM_CONTRACTID = "@mozilla.org/network/file-output-stream;1"; const DIR_SERV_CONTRACTID = "@mozilla.org/file/directory_service;1"; const NS_LOCAL_FILE_CONTRACTID = "@mozilla.org/file/local;1"; const XPCOM_APPINFO = "@mozilla.org/xre/app-info;1"; const queryUrl = "https://www.enigmail.net/service/getPepDownload.svc"; function toHexString(charCode) { return ("0" + charCode.toString(16)).slice(-2); } function sanitizeFileName(str) { // remove shell escape, #, ! and / from string return str.replace(/[`\/\#\!]/g, ""); } function sanitizeHash(str) { return str.replace(/[^a-hA-H0-9]/g, ""); } // Adapted from the patch for mozTCPSocket error reporting (bug 861196). function createTCPErrorFromFailedXHR(xhr) { let status = xhr.channel.QueryInterface(Ci.nsIRequest).status; let errType; let errName; if ((status & 0xff0000) === 0x5a0000) { // Security module const nsINSSErrorsService = Ci.nsINSSErrorsService; let nssErrorsService = Cc['@mozilla.org/nss_errors_service;1'].getService(nsINSSErrorsService); let errorClass; // getErrorClass will throw a generic NS_ERROR_FAILURE if the error code is // somehow not in the set of covered errors. try { errorClass = nssErrorsService.getErrorClass(status); } catch (ex) { errorClass = 'SecurityProtocol'; } if (errorClass == nsINSSErrorsService.ERROR_CLASS_BAD_CERT) { errType = 'SecurityCertificate'; } else { errType = 'SecurityProtocol'; } // NSS_SEC errors (happen below the base value because of negative vals) if ((status & 0xffff) < Math.abs(nsINSSErrorsService.NSS_SEC_ERROR_BASE)) { // The bases are actually negative, so in our positive numeric space, we // need to subtract the base off our value. let nssErr = Math.abs(nsINSSErrorsService.NSS_SEC_ERROR_BASE) - (status & 0xffff); switch (nssErr) { case 11: // SEC_ERROR_EXPIRED_CERTIFICATE, sec(11) errName = 'SecurityExpiredCertificateError'; break; case 12: // SEC_ERROR_REVOKED_CERTIFICATE, sec(12) errName = 'SecurityRevokedCertificateError'; break; // per bsmith, we will be unable to tell these errors apart very soon, // so it makes sense to just folder them all together already. case 13: // SEC_ERROR_UNKNOWN_ISSUER, sec(13) case 20: // SEC_ERROR_UNTRUSTED_ISSUER, sec(20) case 21: // SEC_ERROR_UNTRUSTED_CERT, sec(21) case 36: // SEC_ERROR_CA_CERT_INVALID, sec(36) errName = 'SecurityUntrustedCertificateIssuerError'; break; case 90: // SEC_ERROR_INADEQUATE_KEY_USAGE, sec(90) errName = 'SecurityInadequateKeyUsageError'; break; case 176: // SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED, sec(176) errName = 'SecurityCertificateSignatureAlgorithmDisabledError'; break; default: errName = 'SecurityError'; break; } } else { let sslErr = Math.abs(nsINSSErrorsService.NSS_SSL_ERROR_BASE) - (status & 0xffff); switch (sslErr) { case 3: // SSL_ERROR_NO_CERTIFICATE, ssl(3) errName = 'SecurityNoCertificateError'; break; case 4: // SSL_ERROR_BAD_CERTIFICATE, ssl(4) errName = 'SecurityBadCertificateError'; break; case 8: // SSL_ERROR_UNSUPPORTED_CERTIFICATE_TYPE, ssl(8) errName = 'SecurityUnsupportedCertificateTypeError'; break; case 9: // SSL_ERROR_UNSUPPORTED_VERSION, ssl(9) errName = 'SecurityUnsupportedTLSVersionError'; break; case 12: // SSL_ERROR_BAD_CERT_DOMAIN, ssl(12) errName = 'SecurityCertificateDomainMismatchError'; break; default: errName = 'SecurityError'; break; } } } else { errType = 'Network'; switch (status) { // connect to host:port failed case 0x804B000C: // NS_ERROR_CONNECTION_REFUSED, network(13) errName = 'ConnectionRefusedError'; break; // network timeout error case 0x804B000E: // NS_ERROR_NET_TIMEOUT, network(14) errName = 'NetworkTimeoutError'; break; // hostname lookup failed case 0x804B001E: // NS_ERROR_UNKNOWN_HOST, network(30) errName = 'DomainNotFoundError'; break; case 0x804B0047: // NS_ERROR_NET_INTERRUPT, network(71) errName = 'NetworkInterruptError'; break; default: errName = 'NetworkError'; break; } } return { name: errName, type: errType }; } function Installer(progressListener) { this.progressListener = progressListener; } Installer.prototype = { installOnOs: function(deferred) { EnigmailLog.DEBUG("installPep.jsm: installOnOs\n"); let exitCode = -1; let execFile = this.installerFile.path; if (this.mount && this.mount.length > 0) { // mount image if needed (macOS) let mountPath = Cc[NS_LOCAL_FILE_CONTRACTID].createInstance(Ci.nsIFile); mountPath.initWithPath("/Volumes/" + this.mount); if (mountPath.exists()) { let p = mountPath.path + " "; let i = 1; mountPath.initWithPath(p + i); while (mountPath.exists() && i < 10) { ++i; mountPath.initWithPath(p + i); } if (mountPath.exists()) { throw "Error - cannot mount package"; } } this.mountPath = mountPath; EnigmailLog.DEBUG("installPep.jsm: installOnOs - mount Package\n"); let cmd = Cc[NS_LOCAL_FILE_CONTRACTID].createInstance(Ci.nsIFile); cmd.initWithPath("/usr/bin/open"); let args = ["-W", this.installerFile.path]; let proc = { command: cmd, arguments: args, charset: null, done: function(result) { exitCode = result.exitCode; } }; try { subprocess.call(proc).wait(); if (exitCode) throw "Installer failed with exit code " + exitCode; } catch (ex) { EnigmailLog.ERROR("installPep.jsm: installOnOs: subprocess.call failed with '" + ex.toString() + "'\n"); throw ex; } execFile = this.mountPath.path + "/" + this.command; } EnigmailLog.DEBUG("installPep.jsm: installOnOs - run installer\n"); let proc = { command: execFile, arguments: [], charset: null, done: function(result) { if (result.exitCode !== 0) { deferred.reject("Installer failed with exit code " + result.exitCode); } else deferred.resolve(); } }; try { subprocess.call(proc); } catch (ex) { EnigmailLog.ERROR("installPep.jsm: installOnOs: subprocess.call failed with '" + ex.toString() + "'\n"); throw ex; } }, cleanupOnOs: function() { EnigmailLog.DEBUG("installPep.jsm.cleanupOnOs():\n"); if (this.mountPath && this.mountPath.lenth > 0) { EnigmailLog.DEBUG("installPep.jsm.cleanupOnOs: unmounting\n"); var cmd = Cc[NS_LOCAL_FILE_CONTRACTID].createInstance(Ci.nsIFile); cmd.initWithPath("/usr/sbin/diskutil"); var args = ["eject", this.mountPath.path]; var proc = { command: cmd, arguments: args, charset: null, done: function(result) { if (result.exitCode) EnigmailLog.ERROR("Installer failed with exit code " + result.exitCode); } }; try { subprocess.call(proc).wait(); } catch (ex) { EnigmailLog.ERROR("installPep.jsm.cleanupOnOs: subprocess.call failed with '" + ex.toString() + "'\n"); } } EnigmailLog.DEBUG("installPep.jsm: cleanupOnOs - remove package\n"); this.installerFile.remove(false); if (this.progressListener) { this.progressListener.onInstalled(); } }, checkHashSum: function() { EnigmailLog.DEBUG("installPep.jsm: checkHashSum\n"); var istream = Cc["@mozilla.org/network/file-input-stream;1"] .createInstance(Ci.nsIFileInputStream); // open for reading istream.init(this.installerFile, 0x01, 292, 0); // octal 0444 - octal literals are deprecated var ch = Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash); ch.init(ch.SHA256); const PR_UINT32_MAX = 0xffffffff; // read entire file ch.updateFromStream(istream, PR_UINT32_MAX); var gotHash = ch.finish(false); // convert the binary hash data to a hex string. var hashStr = ""; for (let i in gotHash) { hashStr += toHexString(gotHash.charCodeAt(i)); } if (this.hash != hashStr) { EnigmailLog.DEBUG("installPep.jsm: checkHashSum - hash sums don't match: " + hashStr + "\n"); } else EnigmailLog.DEBUG("installPep.jsm: checkHashSum - hash sum OK\n"); return this.hash == hashStr; }, getUrlObj: function() { let o = { url: this.url, hash: this.hash, comamnd: this.command, mount: this.mount }; return o; }, getDownloadUrl: function(on) { - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); function reqListener() { // "this" is set by the calling XMLHttpRequest if (!this.responseText) { onError({ type: "Network" }); return; } if (typeof(this.responseText) == "string") { EnigmailLog.DEBUG("installPep.jsm: getDownloadUrl.reqListener: got: " + this.responseText + "\n"); try { let doc = JSON.parse(this.responseText); self.url = doc.url; self.hash = sanitizeHash(doc.hash); self.command = doc.command; self.mount = sanitizeFileName(doc.mountPath); deferred.resolve(); } catch (ex) { EnigmailLog.DEBUG("installPep.jsm: getDownloadUrl.reqListener: exception: " + ex.toString() + "\n"); onError({ type: "JSON" }); } } } function onError(error) { deferred.reject("error"); if (self.progressListener) { return self.progressListener.onError(error); } return false; } EnigmailLog.DEBUG("installPep.jsm: getDownloadUrl: start request\n"); var self = this; try { var xulRuntime = Cc[XPCOM_APPINFO].getService(Ci.nsIXULRuntime); var platform = xulRuntime.XPCOMABI.toLowerCase(); var os = EnigmailOS.getOS().toLowerCase(); // create a XMLHttpRequest object var oReq = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(); oReq.onload = reqListener; oReq.addEventListener("error", function(e) { var error = createTCPErrorFromFailedXHR(oReq); onError(error); }, false); oReq.open("get", queryUrl + "?vEnigmail=" + escape(EnigmailApp.getVersion()) + "&os=" + escape(os) + "&platform=" + escape(platform), true); oReq.send(); } catch (ex) { deferred.reject(ex); EnigmailLog.writeException("installPep.jsm", ex); if (self.progressListener) self.progressListener.onError({ type: "installPep.downloadFailed" }); } return deferred.promise; }, performDownload: function() { EnigmailLog.DEBUG("installPep.jsm: performDownload: " + this.url + "\n"); var self = this; - var deferred = Promise.defer(); + var deferred = PromiseUtils.defer(); function onProgress(event) { if (event.lengthComputable) { var percentComplete = event.loaded / event.total; EnigmailLog.DEBUG("installPep.jsm: performDownload: " + percentComplete * 100 + "% loaded\n"); } else { EnigmailLog.DEBUG("installPep.jsm: performDownload: got " + event.loaded + "bytes\n"); } } function onError(error) { deferred.reject("error"); if (self.progressListener) self.progressListener.onError(error); } function onLoaded(event) { EnigmailLog.DEBUG("installPep.jsm: performDownload: downloaded " + event.loaded + "bytes\n"); try { // "this" is set by the calling XMLHttpRequest performInstall(this.response).then(function _f() { performCleanup(); }); } catch (ex) { EnigmailLog.writeException("installPep.jsm", ex); if (self.progressListener) self.progressListener.onError({ type: "installPep.installFailed" }); } } function performInstall(response) { var arraybuffer = response; // not responseText EnigmailLog.DEBUG("installPep.jsm: performInstall: bytes " + arraybuffer.byteLength + "\n"); try { var flags = 0x02 | 0x08 | 0x20; var fileOutStream = Cc[NS_LOCALFILEOUTPUTSTREAM_CONTRACTID].createInstance(Ci.nsIFileOutputStream); self.installerFile = EnigmailFiles.getTempDirObj().clone(); self.performCleanup = self.cleanupOnOs; switch (EnigmailOS.getOS()) { case "WINNT": self.installerFile.append("installPep.exe"); break; default: self.installerFile.append("installPep"); break; } self.installerFile.createUnique(self.installerFile.NORMAL_FILE_TYPE, EXEC_FILE_PERMS); EnigmailLog.DEBUG("installPep.jsm: performInstall: writing file to " + self.installerFile.path + "\n"); fileOutStream.init(self.installerFile, flags, EXEC_FILE_PERMS, 0); var binStr = Cc["@mozilla.org/binaryoutputstream;1"].createInstance(Ci.nsIBinaryOutputStream); binStr.setOutputStream(fileOutStream.QueryInterface(Ci.nsIOutputStream)); var buf = new Uint8Array(arraybuffer); binStr.writeByteArray(buf, buf.length); binStr.flush(); binStr.close(); fileOutStream.close(); if (!self.checkHashSum()) { EnigmailLog.ERROR("installPep.jsm: performInstall: HASH sum mismatch!\n"); deferred.reject("Aborted due to hash sum error"); return null; } self.installOnOs(deferred); } catch (ex) { deferred.reject(ex); EnigmailLog.writeException("installPep.jsm", ex); if (self.progressListener) self.progressListener.onError({ type: "installPep.installFailed" }); } return deferred.promise; } function performCleanup() { EnigmailLog.DEBUG("installPep.jsm: performCleanup:\n"); try { if (self.performCleanup) self.performCleanup(); } catch (ex) {} } try { // "main" part of performDownload if (!this.url || this.url.length === 0) { onError({ type: "downloadPep.noURL" }); return; } // create a XMLHttpRequest object var oReq = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(); oReq.addEventListener("load", onLoaded, false); oReq.addEventListener("error", function(e) { var error = createTCPErrorFromFailedXHR(oReq); onError(error); }, false); oReq.addEventListener("progress", onProgress, false); oReq.open("get", this.url, true); oReq.responseType = "arraybuffer"; oReq.send(); } catch (ex) { deferred.reject(ex); EnigmailLog.writeException("installPep.jsm", ex); if (self.progressListener) self.progressListener.onError({ type: "installPep.downloadFailed" }); } } }; var EnigmailInstallPep = { /** * Start downloading and installing pEp * * @param progressListener: Object (optional) * progressListener needs to implement the following methods: * void onError ({type: errorType, name: errorName}) * void onInstalled () * * @return Installer object */ startInstaller: function(progressListener) { EnigmailLog.DEBUG("installPep.jsm: startInstaller()\n"); let i = new Installer(progressListener); i.getDownloadUrl(i). then(function _gotUrl() { i.performDownload(); }); return i; }, isPepInstallerAvailable: function() { EnigmailLog.DEBUG("installPep.jsm: isPepInstallerAvailable()\n"); let inspector = Cc["@mozilla.org/jsinspector;1"].createInstance(Ci.nsIJSInspector); let urlObj = null; let i = new Installer(null); i.getDownloadUrl(i). then(function _gotUrl() { urlObj = i.getUrlObj(); inspector.exitNestedEventLoop(); }). catch(function _err(data) { inspector.exitNestedEventLoop(); }); inspector.enterNestedEventLoop(0); return (urlObj ? urlObj.url !== null && urlObj.url !== "" : false); } }; diff --git a/package/pEp.jsm b/package/pEp.jsm index 03585848..29162dfc 100644 --- a/package/pEp.jsm +++ b/package/pEp.jsm @@ -1,1123 +1,1123 @@ /*global Components: false */ /* * 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/. */ /* eslint no-invalid-this: 0 */ /** * This module serves to integrate pEp into Enigmail * * The module is still a prototype - not ready for daily use! */ "use strict"; var EXPORTED_SYMBOLS = ["EnigmailpEp"]; const FT_CALL_FUNCTION = "callFunction"; const FT_CREATE_SESSION = "createSession"; var gPepServerPath = null; var gLogFunction = null; var gShuttingDown = false; const pepSecurityInfo = "/pEp-json-token-"; const Cu = Components.utils; const Cc = Components.classes; const Ci = Components.interfaces; Cu.import("resource://enigmail/subprocess.jsm"); /*global subprocess: false */ -Cu.import("resource://enigmail/promise.jsm"); /*global Promise: false */ +Cu.import("resource://gre/modules/PromiseUtils.jsm"); /* global PromiseUtils: false */ Cu.import("resource://enigmail/timer.jsm"); /*global EnigmailTimer: false */ Cu.import("resource://enigmail/files.jsm"); /*global EnigmailFiles: false */ Cu.import("resource://enigmail/core.jsm"); /*global EnigmailCore: false */ var gRequestId = 1; var gConnectionInfo = null; var gRetryCount = 0; var gTbListener = null; var gRequestArr = []; var EnigmailpEp = { /** * In case of failures, an error object with the following structure is returned to the * catch() method of the promise: * code : String, one of: PEP-ERROR, PEP-unavailable * exception: JavaScript exception object (may be null) * message : an error message describing the failure */ /* * pEpPerson: * - user_id * - username * - address */ /** * get the pEp version number * * @return: Promise. * then: String - version identifier * catch: Error object (see above) */ getPepVersion: function() { DEBUG_LOG("getPepVersion()"); let onLoad = function(responseObj) { let version = null; if ("result" in responseObj) { version = responseObj.result.return; } return version; }; // TODO: switch back to "version" once fixed in adapter return this._callPepFunction(FT_CALL_FUNCTION, "version", [], onLoad); }, /** * Provide the pEp Connection info. If necessary, load the data from file * * @return String - URL to connecto to pEp JSON Server */ getConnectionInfo: function() { DEBUG_LOG("getConnectionInfo()"); if (!gConnectionInfo) { let env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment); let tmpDir = env.get("TEMP"); let userName = env.get("USER"); let fileName = (tmpDir !== "" ? tmpDir : "/tmp") + pepSecurityInfo + (userName !== "" ? userName : "XXX"); let fileHandle = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); EnigmailFiles.initPath(fileHandle, fileName); let jsonData = EnigmailFiles.readBinaryFile(fileHandle); if (jsonData.length > 0) { try { // we cannot use parseJSON here, it won't work before TB has finished initialization gConnectionInfo = this.parseJSON(jsonData); if (gConnectionInfo.address === "0.0.0.0") { gConnectionInfo.address = "127.0.0.1"; } } catch (ex) {} } } let o = gConnectionInfo; if (!gConnectionInfo) { o = { // provide a default (that should fail) address: "127.0.0.1", port: 1234, path: "/none/", security_token: "" }; } return "http://" + o.address + ":" + o.port + o.path; }, /** * get the path to the GnuPG executable, and the env. variables GNUPGHOME and GPG_AGENT_INFO * * @return: Promise. * then: String - Full path to gpg executable * catch: Error object (see above) */ getGpgEnv: function() { DEBUG_LOG("getGpgEnv()"); let onLoad = function(responseObj) { if ("result" in responseObj) { return responseObj.result.return; } return responseObj; }; return this._callPepFunction(FT_CALL_FUNCTION, "getGpgEnvironment", [], onLoad); }, /** * Register a debugging log function. * The log function must have the form f(logDataStr) * * @param logFunction: function - the function to register */ registerLogHandler: function(logFunction) { gLogFunction = logFunction; DEBUG_LOG("registered log handler"); }, /** * encrypt a message using the pEp server * * @param fromAddr : String - sender Email address * @param toAddrList: Array of String - array with all recipients * @param subject : String - the message subject * @param message : String - the message to encrypt * @param pEpMode : optional Number - the PEP encryption mode: * 0: none - message is not encrypted * 1: inline PGP + PGP extensions * 2: S/MIME (RFC5751) * 3: PGP/MIME (RFC3156) * 4: pEp encryption format * * @return: Promise. * then: returned result (message Object) * catch: Error object (see above) */ encryptMessage: function(fromAddr, toAddrList, subject, messageObj, pEpMode) { DEBUG_LOG("encryptMessage (" + fromAddr + ")"); if (pEpMode === null) pEpMode = 4; if (!toAddrList) toAddrList = []; if (typeof(toAddrList) === "string") toAddrList = [toAddrList]; messageObj.from = { "user_id": "", "username": "anonymous", "address": fromAddr }; messageObj.to = toAddrList.reduce(function _f(p, addr) { p.push({ "user_id": "", "username": "anonymous", "address": addr }); return p; }, []); let msgId = "enigmail-" + String(gRequestId++); messageObj.shortmsg = subject; messageObj.id = msgId; messageObj.dir = 1; try { let params = [ messageObj, // pep messge object [], // extra ["OP"], // dest pEpMode, // encryption_format 0 // encryption flags ]; return this._callPepFunction(FT_CALL_FUNCTION, "encrypt_message", params); } catch (ex) { - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); deferred.reject(makeError("PEP-ERROR", ex)); return deferred.promise; } }, /** * encrypt a message using the pEp server * * @param mimeStr : String - complete MIME message * @param pEpMode : optional Number - the PEP encryption mode: * 0: none - message is not encrypted * 1: inline PGP + PGP extensions * 2: S/MIME (RFC5751) * 3: PGP/MIME (RFC3156) * 4: pEp encryption format * @param encryptFlags: optional Number - bitmap for encryption modes * 0x0: default * 0x1: force encryption * 0x2: force unsigned message * 0x4: do not attach own key * * @return: Promise. * then: returned result (message Object) * catch: Error object (see above) */ encryptMimeString: function(mimeStr, pEpMode, encryptFlags = 0) { DEBUG_LOG("encryptMimeString()"); if (pEpMode === null) pEpMode = 4; try { let params = [ mimeStr, // mimetext mimeStr.length, // size [], // extra ["OP"], // resulting data pEpMode, // encryption_format encryptFlags // encryption flags ]; return this._callPepFunction(FT_CALL_FUNCTION, "MIME_encrypt_message", params); } catch (ex) { - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); deferred.reject(makeError("PEP-ERROR", ex)); return deferred.promise; } }, /** * decrypt a message using the pEp server * * @param message : String - the message to decrypt * @param sender : pEpPerson - sender information * @param to : Array of pEpPerson - recipients (To) information * @param sender : Array of pEpPerson - recipients (Cc) information * @param replyTo : Array of pEpPerson - Reply-to information * * @return: Promise. * then: returned result * catch: Error object (see above) */ decryptMessage: function(message, sender, to, cc, replyTo) { DEBUG_LOG("decryptMessage()"); if (!sender) sender = "*"; let msgId = "enigmail-" + String(gRequestId++); if (typeof(message) === "object") { message.shortmsg = "pEp"; message.longmsg = "RFC 3156 message"; message.msgId = msgId; message.dir = 0; } else { message = { // src message "shortmsg": "pEp", "longmsg": message, msgId: msgId, dir: 0 }; } message.from = sender; message.to = to; if (cc) { message.cc = cc; } if (replyTo) { message.reply_to = replyTo; } try { let params = [ message, // pEp Message Obj ["OP"], // msg Output ["OP"], // StringList Output ["OP"], // pep rating Output ["OP"] // flags ]; return this._callPepFunction(FT_CALL_FUNCTION, "decrypt_message", params); } catch (ex) { - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); deferred.reject(makeError("PEP-ERROR", ex)); return deferred.promise; } }, /** * decrypt a complete mime string using the pEp server * * @param mimeStr : String - complete MIME message * * @return: Promise. * then: returned result (message Object) * catch: Error object (see above) */ decryptMimeString: function(mimeStr) { DEBUG_LOG("decryptMimeString()"); try { let params = [ mimeStr, // mimetext mimeStr.length, // size ["OP"], // extra ["OP"], // resulting data ["OP"], // rating ["OP"] // decryption flags ]; return this._callPepFunction(FT_CALL_FUNCTION, "MIME_decrypt_message", params); } catch (ex) { - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); deferred.reject(makeError("PEP-ERROR", ex)); return deferred.promise; } }, /** * determine the rating (=trust level) of a pEp Identity * * @param userId : Object - pEp Identity to check * * @return: Promise. * then: returned result * catch: Error object (see above) */ getIdentityRating: function(userId) { DEBUG_LOG("getIdentityRating()"); try { let params = [ userId, [""] // rating ]; return this._callPepFunction(FT_CALL_FUNCTION, "identity_rating", params); } catch (ex) { - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); deferred.reject(makeError("PEP-ERROR", ex)); return deferred.promise; } }, /** * tell pEp our own user identity * * @param idObject - Object: * - address: email Address * - user_id: user ID (usually TOFU_email@address) * - username: name of person (Firstname Lastname), * * @return: Promise. * then: returned result * catch: Error object (see above) */ setMyself: function(idObject) { DEBUG_LOG("setMyself()"); try { let params = [idObject]; return this._callPepFunction(FT_CALL_FUNCTION, "myself", params); } catch (ex) { - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); deferred.reject(makeError("PEP-ERROR", ex)); return deferred.promise; } }, /** * get a user identity from pEp * * @param emailAddress: String - the email address * @param userId : String - unique C string to identify person that identity is refering to * * @return: Promise. * then: returned result * catch: Error object (see above) */ getIdentity: function(emailAddress, userId) { DEBUG_LOG("getIdentity()"); if (!userId) userId = ""; if (!emailAddress) emailAddress = ""; try { let params = [ emailAddress, userId, ["OP"] ]; return this._callPepFunction(FT_CALL_FUNCTION, "get_identity", params); } catch (ex) { - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); deferred.reject(makeError("PEP-ERROR", ex)); return deferred.promise; } }, /** * get all own identities from pEp * * @return: Promise. * then: returned result * catch: Error object (see above) */ getOwnIdentities: function() { DEBUG_LOG("getOwnIdentities()"); try { let params = [ ["OP"] ]; return this._callPepFunction(FT_CALL_FUNCTION, "own_identities_retrieve", params); } catch (ex) { - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); deferred.reject(makeError("PEP-ERROR", ex)); return deferred.promise; } }, /** * check the trustwords for a pair of keys * * @param id1: Object - the 1st pEp ID to check * @param id2: Object - the 2nd pEp ID to check * @param language: String - language (2-letter ISOCODE) * @param longList: Boolean - if true, return complete list of trustWords, otherwise * return short list (default = false) * * @return: Promise. * then: returned result * catch: Error object (see above) */ getTrustWords: function(id1, id2, language, longList = false) { DEBUG_LOG("getTrustWords()"); try { let params = [ id1, id2, language.toUpperCase(), ["OP"], // words ["OP"], // words_size longList ]; return this._callPepFunction(FT_CALL_FUNCTION, "get_trustwords", params); } catch (ex) { - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); deferred.reject(makeError("PEP-ERROR", ex)); return deferred.promise; } }, /** * set the trust level of a user identity in pEp to "trusted" * * @param idObject: Object - pEp Identity object * * @return: Promise. * then: returned result * catch: Error object (see above) */ trustIdentity: function(idObject) { DEBUG_LOG("trustIdentity()"); try { if ("comm_type" in idObject) { delete idObject.comm_type; } let params = [idObject]; return this._callPepFunction(FT_CALL_FUNCTION, "trust_personal_key", params); } catch (ex) { - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); deferred.reject(makeError("PEP-ERROR", ex)); return deferred.promise; } }, /** * reset the trust level of a user identity in pEp * * @param idObject: Object - pEp Identity object * * @return: Promise. * then: returned result * catch: Error object (see above) */ resetIdentityTrust: function(idObject) { DEBUG_LOG("resetIdentityTrust()"); try { if ("comm_type" in idObject) { delete idObject.comm_type; } let params = [idObject]; return this._callPepFunction(FT_CALL_FUNCTION, "key_reset_trust", params); } catch (ex) { - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); deferred.reject(makeError("PEP-ERROR", ex)); return deferred.promise; } }, /** * reset the trust level of a user identity in pEp * * @param idObject: Object - pEp Identity object * * @return: Promise. * then: returned result * catch: Error object (see above) */ mistrustIdentity: function(idObject) { DEBUG_LOG("mistrustIdentity()"); try { let params = [idObject]; return this._callPepFunction(FT_CALL_FUNCTION, "key_mistrusted", params); } catch (ex) { - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); deferred.reject(makeError("PEP-ERROR", ex)); return deferred.promise; } }, /** * reset the trust level of a user identity in pEp * * @param partnerId: Object - pEp Identity object of partner * @param resultValue: Number - Handshake result value (from pEp sync.h): * SYNC_HANDSHAKE_CANCEL = -1 * SYNC_HANDSHAKE_ACCEPTED = 0 * SYNC_HANDSHAKE_REJECTED = 1 * * @return: Promise. * then: returned result * catch: Error object (see above) */ deliverHandshakeResult: function(partnerId, resultValue) { DEBUG_LOG("deliverHandshakeResult()"); try { let params = [partnerId, resultValue]; return this._callPepFunction(FT_CALL_FUNCTION, "deliverHandshakeResult", params); } catch (ex) { - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); deferred.reject(makeError("PEP-ERROR", ex)); return deferred.promise; } }, /** * get list of languaes for which pEp trustwords are available * * @return: Promise. * then: returned result * catch: Error object (see above) */ getLanguageList: function() { DEBUG_LOG("getLanguageList()"); try { let params = [ ["OP"] // list of languages ]; return this._callPepFunction(FT_CALL_FUNCTION, "get_languagelist", params); } catch (ex) { - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); deferred.reject(makeError("PEP-ERROR", ex)); return deferred.promise; } }, /** * Process the output from pEp for the language list and return an array of languages * * @param languageStr - String: string of pEp output * * @return Array of Object: * - short: 2-Letter ISO-Codes * - long: Language name in the language * - desc: Describing sentence in the language */ processLanguageList: function(languageStr) { if ((typeof(languageStr) === "object") && ("result" in languageStr)) { let inArr = languageStr.result.outParams[0].split(/\n/); let outArr = inArr.reduce(function _f(p, langLine) { let y = langLine.split(/","/); if (langLine.length > 0) p.push({ short: y[0].replace(/^"/, ""), long: y[1], desc: y[2].replace(/"$/, "") }); return p; }, []); return outArr; } return []; }, /** * determine the trust rating that an outgoing message would receive * * @param from: Object (pEpPerson) - sender * @param to: Array of Object (pEpPerson) - array with all recipients * @param message: String - the message to encrypt * * @return: Promise. * then: returned result * catch: Error object (see above) */ outgoingMessageRating: function(from, to, message) { DEBUG_LOG("outgoingMessageRating()"); if (!to) to = []; try { let msgId = "enigmail-" + String(gRequestId++); let params = [{ // src message "id": msgId, "dir": 1, "longmsg": message, "from": from, "to": to }, "O" ]; return this._callPepFunction(FT_CALL_FUNCTION, "outgoing_message_rating", params); } catch (ex) { - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); deferred.reject(makeError("PEP-ERROR", ex)); return deferred.promise; } }, /** * Get list of all blaclisted keys (fpr) */ blacklistGetKeyList: function() { DEBUG_LOG("blacklistGetKeyList()"); try { let params = [ "O" ]; return this._callPepFunction(FT_CALL_FUNCTION, "blacklist_retrieve", params); } catch (ex) { - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); deferred.reject(makeError("PEP-ERROR", ex)); return deferred.promise; } }, blacklistAddKey: function(fpr) { DEBUG_LOG("blacklistAddKey()"); try { let params = [ fpr ]; return this._callPepFunction(FT_CALL_FUNCTION, "blacklist_add", params); } catch (ex) { - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); deferred.reject(makeError("PEP-ERROR", ex)); return deferred.promise; } }, blacklistDeleteKey: function(fpr) { DEBUG_LOG("blacklistDeleteKey()"); try { let params = [ fpr ]; return this._callPepFunction(FT_CALL_FUNCTION, "blacklist_delete", params); } catch (ex) { - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); deferred.reject(makeError("PEP-ERROR", ex)); return deferred.promise; } }, /** * Enable or disable the passive mode for pEp. * Passive mode means that no key is attached to a message * * @param isPassive: Boolean - true: enable passive mode / false: disable passive mode */ setPassiveMode: function(isPassive) { DEBUG_LOG("setPassiveMode()"); try { let params = [ isPassive ]; return this._callPepFunction(FT_CALL_FUNCTION, "config_passive_mode", params); } catch (ex) { - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); deferred.reject(makeError("PEP-ERROR", ex)); return deferred.promise; } }, registerTbListener: function(port, securityToken) { gTbListener = { port: port, securityToken: securityToken }; return this.registerListener(); }, registerListener: function() { DEBUG_LOG("registerListener()"); try { if (gTbListener) { let params = [ "127.0.0.1", gTbListener.port, gTbListener.securityToken ]; return this._callPepFunction(FT_CALL_FUNCTION, "registerEventListener", params); } else { - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); deferred.resolve(0); return deferred.promise; } } catch (ex) { - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); deferred.reject(makeError("PEP-ERROR", ex)); return deferred.promise; } }, unregisterListener: function(port, securityToken) { DEBUG_LOG("unregisterListener()"); gShuttingDown = true; try { if (gTbListener) { let params = [ "127.0.0.1", gTbListener.port, gTbListener.securityToken ]; return this._callPepFunction(FT_CALL_FUNCTION, "unregisterEventListener", params); } else { - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); deferred.resolve(0); return deferred.promise; } } catch (ex) { - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); deferred.reject(makeError("PEP-ERROR", ex)); return deferred.promise; } }, /** * parse a JSON string. Ensure that character codes < 32 are correctly escaped first * * @param str - String: string in JSON notation * * @return whatever JSON.parse returns */ parseJSON: function(str) { for (let i = 0; i < str.length; i++) { if (str.charCodeAt(i) < 32) { let c = str.charCodeAt(i); if (!(c == 13 || c == 10)) { str = str.substr(0, i) + "\\u" + c.toLocaleString("en-US", { useGrouping: false, minimumIntegerDigits: 4 }) + str.substr(i + 1); } } } try { return JSON.parse(str); } catch (x) { return null; } }, setServerPath: function(pathName) { DEBUG_LOG("setServerPath()"); gPepServerPath = pathName; }, /******************* internal (private) methods *********************/ /** * Asynchronously call a pEp function * * @param funcType - String: one of FT_CALL_FUNCTION and FT_CREATE_SESSION * @param functionName - String: the pEp function name * @param paramsArr - Array : parameter array for pEp function * @param onLoadListener - function: if the call is successful, callback function of the form: * funcName(responseObj) * @param onErrorListene - function: if the call fails, callback function of the form: * funcName(responseText) - * @param deferred - object: optional Promise.defer() object + * @param deferred - object: optional PromiseUtils.defer() object * * @return Object - a Promise */ _callPepFunction: function(funcType, functionName, paramsArr, onLoadListener, onErrorListener, deferred) { DEBUG_LOG("_callPepFunction(" + funcType + ", " + functionName + ")"); - if (!deferred) deferred = Promise.defer(); + if (!deferred) deferred = PromiseUtils.defer(); let self = this; let conn = this.getConnectionInfo(); let functionCall = { "security_token": (gConnectionInfo ? gConnectionInfo.security_token : ""), "method": functionName, "params": paramsArr, "id": gRequestId++, "jsonrpc": "2.0" }; let oReq = getXmlRequest(); if (!onLoadListener) { onLoadListener = function(obj) { return obj; }; } oReq.addEventListener("load", function _f() { try { let parsedObj = self.parseJSON(this.responseText); if ((typeof(parsedObj) === "object") && ("error" in parsedObj)) { if (parsedObj.error.code === -32600) { // wrong security token gConnectionInfo = null; self.getConnectionInfo(); ++gRetryCount; if (gRetryCount < 2) { self.registerListener() .then(function _f() { self._callPepFunction(funcType, functionName, paramsArr, onLoadListener, onErrorListener, deferred); }); return; } } } else { gRetryCount = 0; } let r = onLoadListener(parsedObj); deferred.resolve(r); } catch (ex) { deferred.reject(makeError("PEP-ERROR", ex, this.responseText)); } }); if (!onErrorListener) { onErrorListener = function(txt) { return txt; }; } oReq.addEventListener("error", function(e) { DEBUG_LOG("XMLHttpRequest: got error: " + e); dropXmlRequest(this); if (!gShuttingDown) { self._startPepServer(funcType, deferred, functionCall, onLoadListener, function _f() { let r = onErrorListener(this.responseText); deferred.resolve(r); }); } else { deferred.resolve({ result: -1 }); } }, false); oReq.open("POST", conn + funcType); oReq.send(JSON.stringify(functionCall)); return deferred.promise; }, /** * internal function to start pEp server if not available */ _startPepServer: function(funcType, deferred, functionCall, onLoadListener) { DEBUG_LOG("_startPepServer:(" + funcType + ")"); let self = this; if (!gPepServerPath) { DEBUG_LOG("_startPepServer: cannot find executable"); deferred.reject(makeError("PEP-unavailable", null, "Cannot find JSON-PEP executable")); } let exec = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); try { exec.initWithPath(gPepServerPath); if ((!exec.exists()) || (!exec.isExecutable())) { DEBUG_LOG("_startPepServer: executable not available"); deferred.reject(makeError("PEP-unavailable", null, "Cannot find JSON-PEP executable")); return; } EnigmailCore.getService(null, true); let process = subprocess.call({ command: exec, charset: null, environment: EnigmailCore.getEnvList(), mergeStderr: false, stdin: function(stdin) { // do nothing }, stdout: function(data) { // do nothing }, stderr: function(data) { // do nothing } }); process.wait(); DEBUG_LOG("_startPepServer: JSON server started"); gConnectionInfo = null; // wait trying to access the pEp server for 1 second such that it can open the connection // and write the connection info file EnigmailTimer.setTimeout(function _f() { self.getConnectionInfo(); functionCall.security_token = (gConnectionInfo ? gConnectionInfo.security_token : ""); let oReq = getXmlRequest(); oReq.addEventListener("load", function _onload() { try { let parsedObj = self.parseJSON(this.responseText); let r = onLoadListener(parsedObj); deferred.resolve(r); } catch (ex) { deferred.reject(makeError("PEP-ERROR", ex, this.responseText)); } }); oReq.addEventListener("error", function(e) { DEBUG_LOG("XMLHttpRequest: got error: " + e); dropXmlRequest(this); let status = oReq.channel.QueryInterface(Ci.nsIRequest).status; deferred.reject(makeError("PEP-unavailable", null, "Cannot establish connection to PEP service")); }, false); oReq.open("POST", self.getConnectionInfo() + funcType); oReq.send(JSON.stringify(functionCall)); }, 1000); } catch (ex) { deferred.reject(makeError("PEP-unavailable", ex, "Cannot start PEP service")); } } }; function DEBUG_LOG(logStr) { if (gLogFunction) gLogFunction("pEp.jsm: " + logStr + "\n"); } function makeError(str, ex, msg) { let o = { code: str, exception: ex, message: (msg ? msg : (ex ? ex.toString() : "")) }; return o; } function getXmlRequest() { return Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(); // if (gRequestArr.length === 0) { // // create a XMLHttpRequest() in Mozilla priviledged environment // // gRequestArr[0] = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(); // } // // return gRequestArr[0]; } function dropXmlRequest(oReq) { //gRequestArr = []; } diff --git a/package/pEpAdapter.jsm b/package/pEpAdapter.jsm index 540709d0..e3f0a15a 100644 --- a/package/pEpAdapter.jsm +++ b/package/pEpAdapter.jsm @@ -1,942 +1,942 @@ /*global Components: false */ /* 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"; /** * Module for interfacing to pEp (Enigmail-specific functions) */ const Cu = Components.utils; const Cc = Components.classes; const Ci = Components.interfaces; Cu.import("resource://enigmail/core.jsm"); /*global EnigmailCore: false */ Cu.import("resource://enigmail/pEp.jsm"); /*global EnigmailpEp: false */ Cu.import("resource://enigmail/pEpListener.jsm"); /*global EnigmailpEpListener: false */ Cu.import("resource://enigmail/prefs.jsm"); /*global EnigmailPrefs: false */ Cu.import("resource://enigmail/log.jsm"); /*global EnigmailLog: false */ Cu.import("resource://enigmail/mime.jsm"); /*global EnigmailMime: false */ -Cu.import("resource://enigmail/promise.jsm"); /*global Promise: false */ +Cu.import("resource://gre/modules/PromiseUtils.jsm"); /* global PromiseUtils: false */ Cu.import("resource://enigmail/rng.jsm"); /*global EnigmailRNG: false */ Cu.import("resource://enigmail/lazy.jsm"); /*global EnigmailLazy: false */ Cu.import("resource://enigmail/streams.jsm"); /*global EnigmailStreams: false */ Cu.import("resource://enigmail/pEpMessageHist.jsm"); /*global EnigmailPEPMessageHist: false */ Cu.import("resource://enigmail/addrbook.jsm"); /*global EnigmailAddrbook: false */ Cu.import("resource://enigmail/locale.jsm"); /*global EnigmailLocale: false */ Cu.import("resource://enigmail/funcs.jsm"); /*global EnigmailFuncs: false */ Cu.import("resource://enigmail/pEpFilter.jsm"); /*global EnigmailPEPFilter: false */ Cu.import("resource://enigmail/subprocess.jsm"); /*global subprocess: false */ Cu.import("resource://enigmail/installPep.jsm"); /*global EnigmailInstallPep: false */ Cu.import("resource://gre/modules/jsmime.jsm"); /*global jsmime: false*/ Cu.import("resource://enigmail/pEpKeySync.jsm"); /*global EnigmailPEPKeySync: false */ Cu.import("resource://enigmail/timer.jsm"); /*global EnigmailTimer: false */ Cu.import("resource://enigmail/filters.jsm"); /*global EnigmailFilters: false */ const getFiles = EnigmailLazy.loader("enigmail/files.jsm", "EnigmailFiles"); // pEp JSON Server executable name const PEP_SERVER_EXECUTABLE = "pep-json-server"; var gPepVersion = null; var gSecurityToken = null; var gPepAvailable = null; var gPepListenerPort = -1; var gOwnIdentities = []; var EXPORTED_SYMBOLS = ["EnigmailPEPAdapter"]; function pepCallback(dataObj) { EnigmailLog.DEBUG("pEpAdapter.jsm: pepCallback()\n"); if ("method" in dataObj) { switch (dataObj.method) { case "messageToSend": EnigmailLog.DEBUG("pEpAdapter.jsm: pepCallback: messageToSend\n"); EnigmailPEPKeySync.sendMessage(dataObj.params[0]); return 0; case "notifyHandshake": EnigmailLog.DEBUG("pEpAdapter.jsm: pepCallback: notifyHandshake\n"); EnigmailPEPKeySync.notifyHandshake(dataObj.params); return 0; } } return 1; } function startListener() { EnigmailLog.DEBUG("pEpAdapter.jsm: startListener():\n"); gSecurityToken = EnigmailRNG.generateRandomString(40); gPepListenerPort = EnigmailpEpListener.createListener(pepCallback, gSecurityToken); if (gPepListenerPort < 0) { EnigmailLog.DEBUG("pEpAdapter.jsm: startListener: could not open socket\n"); return; } EnigmailpEp.registerTbListener(gPepListenerPort, gSecurityToken).then(function _ok(data) { EnigmailLog.DEBUG("pEpAdapter.jsm: startListener: registration with pEp OK\n"); }).catch(function _fail(data) { EnigmailLog.DEBUG("pEpAdapter.jsm: startListener: registration with pEp failed\n"); }); } var EnigmailPEPAdapter = { pep: EnigmailpEp, filter: EnigmailPEPFilter, /** * Get the pEp JSON server version number. * * @return String: * - null if the module is not initialized * - a non-empty string if pEp is available * - "" in case pEp is not available */ getPepVersion: function() { return gPepVersion; }, /** * Determine if pEp is available * * @return: Boolean: true - pEp is available / false - pEp is not usable */ usingPep: function() { if (!this.getPepJuniorMode()) return false; if ((typeof(gPepVersion) === "string") && gPepVersion.length > 0) { return true; } return false; }, /** * Determine if the pEp JSON adapter is available at all * * @param attemptInstall: Boolean - try to install pEp if possible * * @return Boolean - true if pEp is available / false otherwise */ isPepAvailable: function(attemptInstall = true) { EnigmailLog.DEBUG("pEpAdapter.jsm: isPepAvailable()\n"); if (gPepAvailable === null) { gPepAvailable = false; let execFile = getFiles().resolvePathWithEnv(PEP_SERVER_EXECUTABLE); if (execFile && execFile.exists() && execFile.isExecutable()) { EnigmailCore.getService(null, true); let pepVersionStr = ""; let process = subprocess.call({ command: execFile, arguments: ["--version"], charset: null, environment: EnigmailCore.getEnvList(), mergeStderr: false, stdin: function(stdin) { // do nothing }, stdout: function(data) { pepVersionStr += data; }, stderr: function(data) { // do nothing } }); process.wait(); EnigmailLog.DEBUG("pEpAdapter.jsm: isPepAvailable: got version '" + pepVersionStr + "'\n"); if (pepVersionStr.search(/pEp JSON/i) >= 0) { gPepAvailable = true; } } else if (attemptInstall) { this.installPep(); } } return gPepAvailable; }, /** * try to download and install pEp (runs asynchronously!) */ installPep: function() { EnigmailLog.DEBUG("pEpAdapter.jsm: installPep()\n"); let progressListener = { onError: function(err) { EnigmailLog.DEBUG("pEpAdapter.jsm: installPep: got error " + err.type + "\n"); }, onInstalled: function() { EnigmailLog.DEBUG("pEpAdapter.jsm: installPep: installation completed\n"); gPepAvailable = null; } }; EnigmailInstallPep.startInstaller(progressListener); }, /** * Determine if pEp should be used or Enigmail * * @return: Boolean: true - use pEp / false - use Enigmail */ getPepJuniorMode: function() { let mode = EnigmailPrefs.getPref("juniorMode"); if (mode === 0) return false; // manual pEp or automatic mode if (mode === 2 || (!this.isAccountCryptEnabled())) { return this.isPepAvailable(true); } return false; }, /** * Determine if any account is enabled for crypto (S/MIME or Enigmail) * * @return: Boolean: true if at least one account is enabled for S/MIME or Enigmail, * false otherwise */ isAccountCryptEnabled: function() { // automatic mode: go through all identities let amService = Components.classes["@mozilla.org/messenger/account-manager;1"].getService(Ci.nsIMsgAccountManager); amService.LoadAccounts(); let ids = amService.allIdentities; for (let i = 0; i < ids.length; i++) { let msgId = ids.queryElementAt(i, Ci.nsIMsgIdentity); if ((msgId.getUnicharAttribute("signing_cert_name") !== "") || (msgId.getUnicharAttribute("encryption_cert_name") !== "") || msgId.getBoolAttribute("enablePgp")) { return true; } } return false; }, /** * Thunderbird shutdown callback (called from enigmail.js) */ onShutdown: function() { EnigmailLog.DEBUG("pEpAdapter.jsm: onShutdown()\n"); if (gPepListenerPort > 0) { let inspector = Cc["@mozilla.org/jsinspector;1"].createInstance(Ci.nsIJSInspector); EnigmailTimer.setTimeout(function _f() { // wait at most 1 second to continue shutdown if (gPepListenerPort > 0) { inspector.exitNestedEventLoop(); } }, 1000); EnigmailpEp.unregisterListener().then(function _ok(data) { EnigmailLog.DEBUG("pEpAdapter.jsm: onShutdown: de-registring from pEp OK\n"); gPepListenerPort = -1; inspector.exitNestedEventLoop(); }).catch(function _fail(data) { EnigmailLog.DEBUG("pEpAdapter.jsm: onShutdown: de-registring from pEp failed\n"); gPepListenerPort = -1; inspector.exitNestedEventLoop(); }); // onShutdown should be synchronus in order for Thunderbird to wait // with shutting down until we're completed inspector.enterNestedEventLoop(0); EnigmailpEp.registerLogHandler(null); } }, /** * Initialize the pEpAdapter (should be called during startup of application) * * no input and no retrun values */ initialize: function() { EnigmailLog.DEBUG("pEpAdapter.jsm: initialize:\n"); let self = this; let pEpMode = EnigmailPrefs.getPref("juniorMode"); // force using Enigmail (do not use pEp) if (pEpMode === 0) return; EnigmailpEp.registerLogHandler(EnigmailLog.DEBUG); // automatic mode, with Crypto enabled (do not use pEp) if (this.isAccountCryptEnabled() && pEpMode !== 2) return; let execFile = getFiles().resolvePathWithEnv(PEP_SERVER_EXECUTABLE); if (execFile) EnigmailpEp.setServerPath(execFile.path); try { EnigmailpEp.getPepVersion().then(function _success(data) { EnigmailLog.DEBUG("pEpAdapter.jsm: initialize: success '" + JSON.stringify(data) + "'\n"); if (typeof(data) === "string") { gPepVersion = data; startListener(); self.setupIncomingFilter(); } return EnigmailpEp.getGpgEnv(); }). then(function _gotGpgEnv(gpgEnv) { EnigmailLog.DEBUG("pEpAdapter.jsm: initialize: got GnuPG env '" + JSON.stringify(gpgEnv) + "'\n"); let envStr = ""; if (gpgEnv && typeof gpgEnv === "object" && "gnupg_path" in gpgEnv) { EnigmailLog.DEBUG("pEpAdapter.jsm: initialize: got GnuPG path '" + gpgEnv.gnupg_path + "'\n"); if (typeof(gpgEnv.gpg_agent_info) === "string" && gpgEnv.gpg_agent_info.length > 0) { envStr += "GPG_AGENT_INFO=" + gpgEnv.gpg_agent_info + "\n"; } if (typeof(gpgEnv.gnupg_home) === "string" && gpgEnv.gnupg_home.length > 0) { envStr += "GNUPGHOME=" + gpgEnv.gnupg_home + "\n"; } let enigmailSvc = Cc["@mozdev.org/enigmail/enigmail;1"].getService(Ci.nsIEnigmail); enigmailSvc.perferGpgPath(gpgEnv.gnupg_path); enigmailSvc.overwriteEnvVar(envStr); if (enigmailSvc.initialized) { enigmailSvc.reinitialize(); } else { enigmailSvc.initialize(null, false); } } self.setOwnIdentities(0); }). catch(function failed(err) { EnigmailLog.DEBUG("pEpAdapter.jsm: initialize: error during pEp init:\n"); EnigmailLog.DEBUG(" " + err.code + ": " + ("exception" in err && err.exception ? err.exception.toString() : err.message) + "\n"); gPepVersion = ""; }); } catch (ex) {} }, setOwnIdentities: function(accountNum) { let self = this; let accountManager = Cc["@mozilla.org/messenger/account-manager;1"].getService(Ci.nsIMsgAccountManager); let id; // pEp currently only supports 1 identity per account, we therefore only set the 1st id of each accunt if (accountManager.accounts.length > accountNum) { let ac = accountManager.accounts.queryElementAt(accountNum, Ci.nsIMsgAccount); try { id = ac.identities.queryElementAt(0, Ci.nsIMsgIdentity); } catch (ex) { id = null; } if (!id) { self.setOwnIdentities(accountNum + 1); return; } let pepId = { address: id.email.toLowerCase(), user_id: "", username: id.fullName }; EnigmailLog.DEBUG("pEpAdapter.jsm: setOwnIdentities: " + id.identityName + "\n"); self.pep.setMyself(pepId).then( function _ok(data) { if (data) { self.processOwnIdentity(data); } self.setOwnIdentities(accountNum + 1); }).catch( function _err(data) { EnigmailLog.DEBUG("pEpAdapter.jsm: setOwnIdentities: ERROR: '" + JSON.stringify(data) + "'\n"); }); } else { EnigmailLog.DEBUG("pEpAdapter.jsm: setOwnIdentities: done.\n"); } }, processOwnIdentity: function(identityData) { EnigmailLog.DEBUG("pEpAdapter.jsm: processOwnIdentity()\n"); if ("result" in identityData) { let id = identityData.result.outParams[0]; gOwnIdentities[id.address.toLowerCase()] = id; } }, /** * get the pEp Identity of own emails (i.e. for those what we should have a secret key) * for a given email address. * * @param emailAddress: String - my own email address * * @return Object: pEp Identity or null (if not found) */ getOwnIdentityForEmail: function(emailAddress) { emailAddress = emailAddress.toLowerCase(); if (emailAddress in gOwnIdentities) { return gOwnIdentities[emailAddress]; } return null; }, /** * Get a MIME tree as String from the pEp-internal message object * * @param resObj: Object - result object from encryption * * @return String - a MIME string, or "" if no message extracted */ stripMsgHeadersFromEncryption: function(resObj) { let mimeStr = ""; if (Array.isArray(resObj) && typeof(resObj[0]) === "string") { mimeStr = resObj[0]; } let startPos = mimeStr.search(/\r?\n\r?\n/); if (startPos < 0) return ""; let headers = Cc["@mozilla.org/messenger/mimeheaders;1"].createInstance(Ci.nsIMimeHeaders); headers.initialize(mimeStr.substring(0, startPos)); let n = headers.headerNames; let printHdr = ""; while (n.hasMore()) { let hdr = n.getNext(); if (hdr.search(/^(from|to|mime-version)$/i) < 0) { printHdr += hdr + ": " + EnigmailMime.formatHeaderData(headers.extractHeader(hdr, true)) + "\r\n"; } } return printHdr + "\r\n" + mimeStr.substr(startPos); }, /** * Get the encryption quality rating for a list of recipients * * @param sender: - Object msgIAddressObject message sender * @param recipients: - Array of Object msgIAddressObject message recipients * * @return Number: quality of encryption (-3 ... 9) */ getOutgoingMessageRating: function(sender, recipients) { let resultObj = null; let inspector = Cc["@mozilla.org/jsinspector;1"].createInstance(Ci.nsIJSInspector); let from = this.emailToPepPerson(sender); let to = []; if (recipients.length === 0) { return 0; } for (let i of recipients) { to.push(EnigmailPEPAdapter.emailToPepPerson(i)); } EnigmailPEPAdapter.pep.outgoingMessageRating(from, to, "test").then(function _step2(res) { EnigmailLog.DEBUG("pEpAdapter.jsm: outgoingMessageRating: SUCCESS\n"); if ((typeof(res) === "object") && ("result" in res)) { resultObj = res.result.outParams; } else EnigmailLog.DEBUG("pEpAdapter.jsm: outgoingMessageRating: typeof res=" + typeof(res) + "\n"); if (inspector && inspector.eventLoopNestLevel > 0) { // unblock the waiting lock in finishCryptoEncapsulation inspector.exitNestedEventLoop(); } }).catch(function _error(err) { EnigmailLog.DEBUG("pEpAdapter.jsm: outgoingMessageRating: ERROR\n"); EnigmailLog.DEBUG(err.code + ": " + ("exception" in err ? err.exception.toString() : err.message) + "\n"); if (inspector && inspector.eventLoopNestLevel > 0) { // unblock the waiting lock in finishCryptoEncapsulation inspector.exitNestedEventLoop(); } }); // wait here for PEP to terminate inspector.enterNestedEventLoop(0); if (resultObj && Array.isArray(resultObj) && "rating" in resultObj[0]) { return resultObj[0].rating; } return 3; // unencrypted }, /** * Obtain a list of supported languages for trustwords * * @return Promise, delivering Array of Object: * - short: 2-Letter ISO-Codes * - long: Language name in the language * - desc: Describing sentence in the language */ getSupportedLanguages: function() { - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); EnigmailpEp.getLanguageList().then(function _success(res) { let outArr = EnigmailpEp.processLanguageList(res); deferred.resolve(outArr); }).catch(function _err(err) { deferred.resolve([]); }); return deferred.promise; }, getIdentityForEmail: function(emailAddress) { - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); EnigmailpEp.getIdentity(emailAddress, "TOFU_" + emailAddress).then(function _ok(data) { if (("result" in data) && typeof data.result === "object" && typeof data.result.outParams[0] === "object") { if ("username" in data.result.outParams[0] && data.result.outParams[0].username) { let u = jsmime.headerparser.parseAddressingHeader(data.result.outParams[0].username, true); if (Array.isArray(u) && u.length > 0) { data.result.outParams[0].username = u[0].name; } } } deferred.resolve(data); }).catch(function _err(data) { deferred.reject(data); }); return deferred.promise; }, /** * Convert an msgIAddressObject object into a pEpPerson object * If no name given, the name is looked up in the address book * * @param emailObj - Object msgIAddressObject * * @return pEpPerson object */ emailToPepPerson: function(emailObj) { let p = { user_id: "", username: "unknown", address: "" }; if (!emailObj) return p; if ("email" in emailObj) { p.address = emailObj.email; } if ("name" in emailObj && emailObj.name.length > 0) { p.username = emailObj.name; } else { let addr = EnigmailAddrbook.lookupEmailAddress(p.address); if (addr) { if (addr.card.displayName.length > 0) { p.username = addr.card.displayName; } else { p.username = (addr.card.firstName + " " + addr.card.lastName).trim(); } } } if (p.username.length === 0 || p.username === "unknown") { p.username = p.address.replace(/@.*$/, ""); } return p; }, /** * Update the last sent date for PGP/MIME messages. We only do this such that * we don't unnecessarily process earlier inline-PGP messages */ processPGPMIME: function(headerData) { EnigmailLog.DEBUG("pEpAdapter.jsm: processPGPMIME\n"); if (!(("from" in headerData) && ("date" in headerData))) return; EnigmailPEPMessageHist.isLatestMessage(headerData.from.headerValue, headerData.date.headerValue). then(function _result(latestMessage) { EnigmailLog.DEBUG("pEpAdapter.jsm: processPGPMIME: " + latestMessage + "\n"); }).catch(function _fail() { EnigmailLog.DEBUG("pEpAdapter.jsm: processPGPMIME: error\n"); }); }, /** * Update the last sent date for inline-PGP messages. We do this to make sure * that pEp can potentially derive information from the message (such as extracting an * attached key). */ processInlinePGP: function(msgUri, headerData) { EnigmailLog.DEBUG("pEpAdapter.jsm: processInlinePGP: " + msgUri + "\n"); if (!("from" in headerData) && ("date" in headerData)) return; let stream = EnigmailStreams.newStringStreamListener( function analyzeData(data) { EnigmailLog.DEBUG("pEpAdapter.jsm: processInlinePGP: got " + data.length + " bytes\n"); if (data.indexOf("From -") === 0) { // remove 1st line from Mails stored in msgbox format data = data.replace(/^From .*\r?\n/, ""); } EnigmailpEp.decryptMimeString(data). then(function _ignore() {}). catch(function _ignore() {}); } ); EnigmailPEPMessageHist.isLatestMessage(headerData.from.headerValue, headerData.date.headerValue). then(function _result(latestMessage) { EnigmailLog.DEBUG("pEpAdapter.jsm: processInlinePGP: " + latestMessage + "\n"); try { if (latestMessage) { var channel = EnigmailStreams.createChannel(msgUri.spec); channel.asyncOpen(stream, null); } } catch (e) { EnigmailLog.DEBUG("pEpAdapter.jsm: processInlinePGP: exception " + e.toString() + "\n"); } }).catch(function _fail() { EnigmailLog.DEBUG("pEpAdapter.jsm: processInlinePGP: error\n"); }); }, /** * prepare the relevant data for the Trustwords dialog * * @param emailAddress: String - the email address of the peer to verify * @param headerData: either: Object - nsIMsgHdr object for the message * (to identify the ideal own identity) * or: String - email address of own identity * @return Promise(object) */ prepareTrustWordsDlg: function(emailAddress, headerData) { - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); let emailId = null; let useOwnId = null; let emailIdRating = null; let useLocale = "en"; let ownIds = []; let supportedLocale = []; let uiLocale = EnigmailLocale.getUILocale().substr(0, 2).toLowerCase(); emailAddress = emailAddress.toLowerCase(); let allEmails = ""; if (typeof(headerData) === "string") { allEmails = headerData; } else { if ("from" in headerData) { allEmails += headerData.from.headerValue + ","; } if ("to" in headerData) { allEmails += headerData.to.headerValue + ","; } if ("cc" in headerData) { allEmails += headerData.cc.headerValue + ","; } } let emailsInMessage = EnigmailFuncs.stripEmail(allEmails.toLowerCase()).split(/,/); EnigmailPEPAdapter.pep.getOwnIdentities().then(function _gotOwnIds(data) { if (("result" in data) && typeof data.result.outParams[0] === "object" && Array.isArray(data.result.outParams[0])) { ownIds = data.result.outParams[0]; } for (let i = 0; i < ownIds.length; i++) { if (ownIds[i].address.toLowerCase() === emailAddress) { deferred.reject("cannotVerifyOwnId"); } useOwnId = ownIds[0]; for (let j = 0; j < emailsInMessage.length; j++) { if (ownIds[i].address.toLowerCase() === emailsInMessage[j]) { useOwnId = ownIds[i]; break; } } } return EnigmailPEPAdapter.getIdentityForEmail(emailAddress); }).then(function _gotIdentityForEmail(data) { if (("result" in data) && typeof data.result === "object" && typeof data.result.outParams[0] === "object") { emailId = data.result.outParams[0]; } else { deferred.reject("cannotFindKey"); } return EnigmailPEPAdapter.pep.getIdentityRating(emailId); }).then(function _gotIdentityRating(data) { if ("result" in data && Array.isArray(data.result.outParams) && typeof(data.result.outParams[0]) === "object" && "rating" in data.result.outParams[0]) { emailIdRating = data.result.outParams[0]; } return EnigmailPEPAdapter.getSupportedLanguages(); }).then(function _gotLocale(localeList) { supportedLocale = localeList; for (let i = 0; i < localeList.length; i++) { if (localeList[i].short === uiLocale) { useLocale = localeList[i].short; } } return EnigmailPEPAdapter.getTrustWordsForLocale(useOwnId, emailId, useLocale, false); }).then(function _gotTrustWords(data) { if (("result" in data) && typeof data.result === "object" && typeof data.result.outParams[1] === "string") { let trustWords = data.result.outParams[1]; deferred.resolve({ ownId: useOwnId, otherId: emailId, userRating: emailIdRating, locale: useLocale, supportedLocale: supportedLocale, trustWords: trustWords, dialogMode: 0 }); } else { deferred.reject("generalFailure"); } }).catch(function _err(errorMsg) { deferred.reject(errorMsg); }); return deferred.promise; }, /** * Get the trustwords for a pair of pEpPerson's and a given language * * @param ownId: Object - pEpPerson object of own id * @param otherId: Object - pEpPerson object of other person's identity * * @return Promise(data) */ getTrustWordsForLocale: function(ownId, otherId, language, longWords) { return EnigmailPEPAdapter.pep.getTrustWords(ownId, otherId, language, longWords); }, resetTrustForEmail: function(emailAddr) { - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); EnigmailPEPAdapter.getIdentityForEmail(emailAddr). then(function _gotIdentityForEmail(data) { if (("result" in data) && typeof data.result === "object" && typeof data.result.outParams[0] === "object") { let emailId = data.result.outParams[0]; EnigmailPEPAdapter.pep.resetIdentityTrust(emailId).then( function _ok() { deferred.resolve(); } ).catch(function _err() { deferred.resolve(); }); } }); return deferred.promise; }, getRatingsForEmails: function(emailArr) { EnigmailLog.DEBUG("pEpAdapter.getRatingsForEmails(" + emailArr.length + ")\n"); - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); let identities = []; function getNextIdentity(emailNum) { if (emailNum >= emailArr.length) { EnigmailLog.DEBUG("pEpAdapter.getRatingsForEmails: done\n"); deferred.resolve(identities); return; } if (emailArr[emailNum].indexOf("@") < 0) { // skip if not an email address getNextIdentity(emailNum + 1); return; } let identity = null; let rating = 3; // default rating: no key available EnigmailPEPAdapter.getIdentityForEmail(emailArr[emailNum]).then( function _gotIdentity(data) { if (data && ("result" in data) && typeof data.result === "object" && typeof data.result.outParams[0] === "object") { identity = data.result.outParams[0]; return EnigmailPEPAdapter.pep.getIdentityRating(identity); } else { - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); deferred.resolve({ status: 0 }); return deferred.promise; } }).then( function _gotRating(data) { if ("result" in data && Array.isArray(data.result.outParams) && typeof(data.result.outParams[0]) === "object" && "rating" in data.result.outParams[0]) { rating = data.result.outParams[0].rating; } identities.push({ email: emailArr[emailNum], user_id: identity, rating: rating }); getNextIdentity(emailNum + 1); }).catch( function _err(data) { EnigmailLog.DEBUG("pEpAdapter.getIdentitiesForEmails: ERROR: " + JSON.stringify(data) + "\n"); deferred.reject(data); }); } getNextIdentity(0); return deferred.promise; }, calculateColorFromRating: function(rating) { let color = "grey"; if (rating === -2 || rating === 2) { color = "grey"; } else if (rating < 0) { color = "red"; } else if (rating < 6) { color = "grey"; } else if (rating >= 7) { color = "green"; } else { color = "yellow"; } return color; }, /** * Get CSS class for pEp rating */ getRatingClass: function(rating) { let setClass = ""; let color = this.calculateColorFromRating(rating); switch (color) { case "grey": setClass = "enigmailPepIdentityUnknown"; break; case "red": setClass = "enigmailPepIdentityMistrust"; break; case "yellow": setClass = "enigmailPepIdentityReliable"; break; case "green": setClass = "enigmailPepIdentityTrusted"; } return setClass; }, getRatingLabel: function(ratingNum) { let ratingDesc = "Undefined"; switch (ratingNum) { case 1: ratingDesc = "CannotDecrypt"; break; case 2: ratingDesc = "HaveNoKey"; break; case 3: ratingDesc = "Unencrypted"; break; case 4: ratingDesc = "UnencryptedForSome"; break; case 5: ratingDesc = "Unreliable"; break; case 6: ratingDesc = "Reliable"; break; case 7: case 8: case 9: ratingDesc = "Trusted"; break; case -2: ratingDesc = "Broken"; break; case -1: ratingDesc = "Mistrust"; break; case -3: ratingDesc = "UnderAttack"; break; } return ratingDesc; }, setupIncomingFilter: function() { EnigmailFilters.addNewMailConsumer({ headersOnly: false, incomingMailOnly: true, unreadOnly: false, selfSentOnly: true, consumeMessage: EnigmailPEPFilter.newMailConsumer.bind(EnigmailPEPFilter) }); } }; diff --git a/package/pEpKeySync.jsm b/package/pEpKeySync.jsm index 7cddc77f..344228c4 100644 --- a/package/pEpKeySync.jsm +++ b/package/pEpKeySync.jsm @@ -1,308 +1,308 @@ /*global Components: false*/ /* 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 = ["EnigmailPEPKeySync"]; const Cc = Components.classes; const Ci = Components.interfaces; const Cu = Components.utils; Cu.import("resource://enigmail/rng.jsm"); /*global EnigmailRNG: false */ Cu.import("resource://enigmail/mime.jsm"); /*global EnigmailMime: false */ Cu.import("resource://gre/modules/jsmime.jsm"); /*global jsmime: false*/ Cu.import("resource://enigmail/data.jsm"); /*global EnigmailData: false */ Cu.import("resource://enigmail/files.jsm"); /*global EnigmailFiles: false */ Cu.import("resource://enigmail/log.jsm"); /*global EnigmailLog: false */ Cu.import("resource://enigmail/send.jsm"); /*global EnigmailSend: false */ Cu.import("resource://enigmail/locale.jsm"); /*global EnigmailLocale: false */ Cu.import("resource://enigmail/pEp.jsm"); /*global EnigmailpEp: false */ Cu.import("resource://enigmail/lazy.jsm"); /*global EnigmailLazy: false */ -Cu.import("resource://enigmail/promise.jsm"); /*global Promise: false */ +Cu.import("resource://gre/modules/PromiseUtils.jsm"); /* global PromiseUtils: false */ const getWindows = EnigmailLazy.loader("enigmail/windows.jsm", "EnigmailWindows"); const testMessage = { "dir": 1, "shortmsg": "pEp", "longmsg": "this message was encrypted with p≡p https://pEp-project.org", "attachments": [{ "value": "VmVyc2lvbjogMQ==", "size": 10, "mime_type": "application/pgp-encrypted" }, { "value": "LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpoUUlNQThNZGNTR0taTkJxQVEvL1NBUzZUTVlOOHZtUDFJZnoreTk4QUU1Wjc3QXZOck8vdHo5MFlNN21leldyCndhM0lYdjVXMTBrMnJ2dmZDSkpEQWlqMHE3Y3EzaUJlZ3kweGZHMVlkUzlPT3BoYTg4MjBRRy91Z3kwRGxjWk0KeCtZR3dPS2lpbUF2dWUxR2NNeUc4V3BTZ05aV25zeVBib2tUQXFVMlVKY09ReTdhRnd3cmhQZ0JJOTQzSWlqRgpyandYKzZVQnNwakpTODV5ZXVwbTVqaTR6OUg5VnBSRW5ZMkVmN0dEVHJudGVyRDZlTkhMbXh2djhhdVIyZytPCmZPVmgycytqTTVsOFZDTDBpOGhuVUZYYjZNUXZ0encrM3dwVUNDSTdoeTNhMEpocXdJWi9tWDc1VTVnS1VPVTYKZlVSWktJVWNZRS84OTZPRzh4Q0x4ZThqWW1ldnoyLzdZbEREbEd4ZHZvbUFuSkVJUThPTWY2VjBBc0ZZTHkwWQpIVSszWk9Vb1k1VTFEZE14Mlp4a3cxY1YxZFp0eE9wS1VsaTQ1WlNzOXM5bUpvVHNtaEtZSVhER1BYNXpKZHpYCm5sM05lbVJZNUZlR2ZuNktCOCtaV0RlZ3RtamtQaTI4b1VNTk5WUmd5bmVaRTRqemRiSTNobGNJa0RjZjhHbGYKSGg0S1laK294bVBYMWh6RWhHK0VSOXRIRTBsVWNmWGMvNWQ0a3pESnliZks2amt1SzZiUWh3ay95eVgyWk1tcApLQUJ4QUg2V2NiS2lDTEVlTWpJTlJCMk9ndkI1ZlJiSHVTU0s4Mk8zQWlSeW9vKzdQZWJIekwyMG04YmlPUlJ0CmhYQmFVUGVWUmRkai9XTTA5MlJXeVZ1SFNYcHBZNkJBSHFwUGNIS0o2YWtCV2RCanUrZ3lYelhhM1lmeitycUYKQWd3REs5Y2F4Uld5NFRZQkQvMFFJeW5KZnQwQjN3TTNxUXBnRnNWdFIvZ2ZDR2k2TGp1Y1NTMGRzQjZ2RVRETwp5ZGgyV3dXMzl4bTcxUHV2ck9GVk9oOTlRNUFaY2VIOURMVkxxdGc3Z0dGZ1pOWmQ2bVEyck5lWVdLNVluaW1NClEzLzRicTBIS2YvN3liNlJ1dm55UWlQdVNEUHdjUmNlaWhNN3V1blFZNzVhZ1pQVlNGWVplUk9OMHhrWHV5dVkKTk9NQWFnM3RXTCticEdScU1aTE9PRHhRZXArc0FNbENrbUdkZEtETFpkUnZndWRWaWw4dEI3ejVSUk44NVJjUAo0aWs5SkZ0SzU5YkkvdTdqbmdaaUhOSmkvcVNsd0lKMHRVZzlmMTd5OFFFNjRVc256aDhld2JFcThjcDVJd05BCm5FNFo3cjNDeEFVMTd3eXI0UytqTFd4N2tjempiNFcvUEEwVnBuTGg4bFEzRzAxR2Q5bnB6R2NSMVpPVWZkUG4KN0dSZ2dtaXFOb1Y4YUJvZ2RDaDdwOFF3MFRySFpKVzR4dytyajFaQ2poZWVSUEtMUExoVlBEMjVHVktIeDRCMAowMHF6MW4xM1YrSzRTNTF3VUNGT3Bra1oyK0J6MXF4TFNUemlvSGJSTk1aK0FRZmttM3o1WEhKM0kzMHlTMzR4CjlUTTM4MWpaK0ZhU09ybGc1eE1TRUttb1U3aFI1Nm5tU2RsL2FvaWpwMkZvZmdDM2RTZm0zN2pjWGMrTEhnd0oKaU9QejhacDVEQXZtWkdxL0Znb004eEZXSDc2TW5GK2RiUklOajJQYTdzQXZwTlNUMVM5dUl3V2JKNVhqVVdKbQo1V051Uk9Mc0VnNXFPVWdKaGdKU2RIUHBueVNka2ZieVV6c0hlK2VpM1oxeTB5L2hhbkpQbnpTMUhwVW9HdExzCkFSVjVlSUZRbnRmbWZMaTlFcVBycWZYMEMvSGxRYVp4U0lrd3o4dUMvL29acXJOZFMxRnhGUVpZcjEwdFNKalEKck8wVHl4SHlhUG5pMUd3Nkh5OThneXhsdU0wK1FnR0k4OFR6UTk5ZmU4ZmRUQW9ZLzVqcmZaRDk3V1BzQVFVNAp5eWRmQWtzOS90aUswd29ZSDJKdXgzakFKZ1VKNFBUZEQ5WE9OeWtnSXpNZWVxbHE4Zms0dExlVEVSeWRkZUpOClNwRVVwcUN4cVc2eUJzZEJla3ZQSnhpcWc4QjBJZ0dLNG01LzJ5UUY0QjZBMHp6SkRzU3JFN0V4OVY2YlBqOHEKK3NIbTZJUzZqdi9nYzlMNkhFOUhwOTBvMlNoT2ttcTVrV3VJVXU3Mzl5cmR5ejFVNDRPeUQwajJWS0VVOWZZNQp6Y2dwWkpnbHdXazU4WnU5dmlTanpuNUFLMW50MWRhTlRLYjZaUUJNUmMrWmowUU4yRW82RTBSNGRDV0Y0RUN1CmVFQXZHRGtVM3ZZdWFwa3E3cVM1ZUwwUXRXelZkRW1aUEV3UHdaWFFKUzlYSUhxU2hPcjMxSi91SXZoZ3A5VHQKRWZrWUdWclJDS3FsVkFzekE0dU1PR0NROWhxYlQyRERvbFNYSllyZVErSGZxalREMmhCR21RWFdVTnd6dEVNMQo2clFJOHJ2cW1rTkdwNDhsWnhLdVNkNnhGc1FQU2l2d09vU0NiaTZJakwvM3RPS0ZyUDNtM2tXdjVUT2JLN1l5Cmx0UFQwL013dW1LMmpiSVhVNy9PcTB4RWVNRnZPZTJPcmZRRkFCVVlwRDJwcnZpeWFUd09NemEwMW9PcUNVQlUKTUgzdVQ4OWtFMTRBaTYwYWRJaC9RUCtDSzlBSkM1WEFhakY4QmFkTEZJaGZoR0hJeTBTVGhlM3NOTnFKUEs3VApkbU9PRjFSTkIzbzVpNTFhN2xGejlkRzZTeThPWm43anBkTU9PMVd6SU04ODBKVit4RUxFY3dSUW1UczFjZGVSCjJSMm1WL0c0R09yQXJISmhBcE5STkl5dUd0NGpoT1FhUFM2bSsvWDJMSHNUTmFjc0ZWSlRaT296WldscG1XZngKK3RaUExDaC85QzJLc3pJT25PNnArRms3a2xCRkpsRE4vaVE5TlhZQnVWRThUTWdzNUxManhieGxYVk9TSnI5Rgpmd2VLeFJaa2FydlRMSXZzTmlCcnlkb1VlLzRLOTc0ekJYT1lLUDNlOXRINC95TjJWQUM4RGlMZFdTY291eHlzCmVpK2p1UU5KVVZ3M2tFUmJuc3BOdDMzdSt2cHc5R3NxZWl1dnFtUWw0NVZvUng3LzRFOFJQK2l0S3pxK3kzWSsKVHgwNDFZSUxhcVBtaHo1VzdXeEFwbjNMWUpuMHoxem01TGExOHh0RmIzT1NlT3RabTdpZTRXSUgvRERKNC9sZwpvRE5OaFZ1cTZ4Q3BFaTJydFZURk94S3JGRWZHakxPYmZkcjVRZmRZNmovUzkwWkpvTnVGNDJtOFlJbyt0Ny9tCnhNdGQ4YTM5WHFQMXp6Q25uK3RhUlo4MG5uSThhMXpDSk1lZzFPbmE3eGJDL2dhR29ialFBSlYvSVh5cTY1UlAKNVdKQVZLbkFScEl4c3IyVWZkWTZ4bGUwNWhRSnN0UUx5M01Nc2IreGFScjFxNnBnaHJLSlpWbnF6WDNJL1VBSwpmUjZkS3l0QzRuWkxoQjRGeUdRdHYwSWlEMnNOUDIwQU9Ea1lDVFphR2lsKzZQZk9YWEc5dmF0cXZvTWd0Q0R4CmdyRzZ4M21GOVVtYlJnSG5qa3BYVVIzQVo3empLNGVJU2xLVDFVR2tEQWlJdWFMMU5qcEsvWWpxbDZPUWNLUFoKR21vQmpObm5JbGJKNFhRRVdPenZyUUtVTm1ER0NZUWtLUE9JdnN6TnpjQWkvVFZ6RkNVYkFRRG95L2JEZWVjUgpGejhMdzR1STJkek15bkQ0ZUU4NDRtbVg4RTdSamduc1psTERmOHRjZVBnaHpydzI0TE1ScXpSRU9OTE5CVlFkCmw1MDJyV1hMR2ljL2pVQVFyY2toVTA1Qk1leDNreDJtb0doVzQ4OVFZWTJTU1dsaEh5RTB5N1A4eVloRTF2ZHMKZVk3SWJEbXZrQjVhWDFyeHBPQkQ5NHlKQnpUdE5NVnMzaTl0Zi9BQ2xQbGpDSCtBZEw4V2tYRzZYMVVFWWV2dAp2cHBHMG1NR0N0WEZnL2pEZkxuZGZQY3A2SlNjK0RBcGFVVndLR0kvQnd5QnBHdG5pU1NOalBVK3hhRVhvZWNmCkJSdEd3dmltcDBPS1pERndwWWgzdkpQV01GUE8rTlVWRVVxcGQ2ZWNsZng1SHhKMU1TRXVIWjEyQlpMNEdhREwKdnFDV0dFOTFoOEcxeEgxb1ZkdUZXWitzdXJIMEZjZHE2bW9nNG4vL09pVlUzWm5pb0dHWEpFWmcyUVplQ01BMgpPR3B5Uk9XVzJUYzVYZWYwL3BoN3lSVXdiQlB3VjhIbVpHVlBXUHB1M1FIbWhPR2t2RHE0SHJhWEorZ3Y4bFpQCkg3SlNISWxDRXAzNnJVanJ6UXdNd3VVQ2JOMWViMW8vQ3BRWEhEbThnV1RFMllIODYzZjV1UzhHNjdIYTQzWWQKK2dHV3lwVjA1VjUvVVVQTDlmQi9hVWk3N2VxUG1qaTJRekFRMWNIZENRYzJ0Y2dXc0tlbU85dWZCcWFmSXlpbAoyM2NFM0p4dlJTRlVPWjJnRkRpYVNaVWgzL2tLQU1aZERIWUZ3U1BOMkdhY1c5ZEI2SEhKWDZQS2YrWXJPcUJ6CjN1RDNoN0crZ2tUS1BjY29wcXBtdW5FR0hZeGZWZjBlN1RRRWVURk1FS2c3VUFMamlLVnZNdU1aV21ML3VieUIKZ0dQWW9yMDgyekhSUE9PajJ6RUNiUERZam5URUxEWUNzakVzMVlFLzMwdTBVRFd3dnBQajd0UDRDeGhYTjRoVwpSYlRDaUJKUjB0ZVZOWGFRemJwMmhSSXJ6YkdhNWZVMC91TG1PTUxhTlpZaEhrQkp6SVRHYU1YdU1tM2hJeHpuClB1anhtazlNaEhJY2t0RDVTNFVpbklyYUZUdVlrUTZOOUlUL0pmeGQ0bHU4SldjUUlsaTZiZjI4SmtYSzZ3TFMKY3kweDdDVWJkbUtsYTUzVnpNeFhQcUsray9xL2MxV3NibU91K1pRTlBHTnBKQ2IzQUlPYVM4V1NHYmljNkY1TAphVmhKb3RveUxtelY5b0J3bzIyQVA2eENlVkVUcERhaFJJTDdHTGczRUFCbTV6VDhqalc2eFZFS0FyalJEb3I3CkR1OFFPWmdhY0JBNWVORVAzY0d1ZUZrc2plSW5aUmVIbEY5M2FDUGhkNTRUTHFmTXNrQ2xKN3FuaVI5UlBKNnkKZUNBSFRGS0ZRQjFCM0lEWElmVnlkZ0UrMWVHYU1yQU5uTG1oc2F4TjFXTUtsN0U5dmRkVkxPczN4UTRrMWxsNwozZWYweTllWnJ6TGl2Y2tSRmdseVI5SWRiRXlVOXdHVktXWlVRWTRtRnZFZ1hpandQN2JsazUrRDgyMHpCemN3Ci8yakw3TGp6dzkwdkV3TFo5My91RVA4cmZQTXVZOXYvQWJIc3JCem9lMFpmNXEyalJUMG81djRxeXVJS0pIakcKZi9xcUs5bTZydmgyY0IzSVJ1QWFmSUlLZDNMYlR3UkdXWlNseFdEY2kvR1NRY3VmNWRFVVNpMFRqdGhXdEx3bApVRGErcGlIQVZmVm5td3hEbUd5QmJjNFJNbWtPa09razB4K2RMVGllb2c4RCt0eExsWWhWNU1ZbjNqU0JTQU9OCitZRTRCaFVTWG1yU0FtUXdmMS83dHl3M2JMb1VvbjRYbmhVVG1uOWtXTUo3Q1VMY0I5RWxoQzRiWG1aTytYTk8KQzVVb0o1ZUxySDRYTW9PU21WbDZ6S2lURVZRUGc1VGhDWUxpZ1V0MFhaRzJ0cUpqd0ZkNHlybW5XcSsxaisyRgpHbEVYejM0aGNiVW5xOFN5elFKVnNVL3JFMml0cDlTQWR4QUgvWVpRQmVJSlFFQ1huMjhEZTJOLzF4N29tZlBZCkcvMDNyemtXcjNxRHJkV1B1cTZoYWgxa1M1NHVDTEFCMnhUYmpyTW1mVEd2cTN4YytPTlpDUVRyU214Y25Pa3oKZStjQUFLc0JoSU1VZyttSkpLd2orUDZKalAxVnBzNmxOOThXbUdxWFB5UTgyUnR6TkZQUlRidVczSjZXd2dkdApLaW5xKy9xOHdKMVpHSWRCWW1vUlM1SUZRYkl1VnNFak9hZGhNY0JaQTExaFAzVUFUS0FlWVdrRXVaQktwSnN1CnAxVTAyTmJvbmlZSG5oRnFHYjFBQWcrNDlHVkNyYmpwVnBvNGx1NjVxYy9Ua0hjYVlIZExnY2dtK0RFbzNQSEwKMnpJclRjQ0IzcEUxU1FzR3Y2M0M5djEwT0xpNW1qNGVwbGNnQ0h0VjF2NTNrK2VXYmlpYmhRVlQ0c0JkZjFqRgpPdDhmVm51Ni9pbTRLMG1QZEJON2tjS3B5MlNRUytYRnlEaHlRK0FieEVWeHF4azIwZ3hmY0lheUNYQjErVE5FCmovMDQ3NXU4YWdwTWIzdk56aTRtR2U0K0d3aUt4ampnZjBTWXp5Tk45Z09EVjVhTVRtTkJYMFpvemZKS3hPM3IKNi9CZ0lYZmx1aFVhVUc0MnAvSUhNOEh6OFpwa2p0WmZJV2tHUi82elZPb25iVUZLa1lLYzJuTjdHaUlnY3RrNgpWSHN3N0NnNG9QbzNqYk1vamp4QUpSQVg0SlBFWEEyZFYvMTBSZkhZNkQ2K0Z2Z3VrRmVFdmdlQXEyMjhHcXNkCmh6S0hYdkc2THNiTkVPY3p2ZnlURFZwVVoxa2Z1SjVuWjlLOGJTeE1yZjhxd0d0c2UwdldzeDd6SDJIZTV2a3AKaGhuWkYzSVZnNWRQMTJ0T1U3cEtIbUZqNHBrVVRSd0JMMzBkUzBjSE1EWGx3eUQweVBmY1U3M25tMVRjN0FRSwo2b21JUWo5OUhDc2dFV3JYUjc4WmFuSTIvOFErSFlPWlcxaEdRZVBtNExtaElsK0hCeUt3dU9RN0hyZVNONDMwCnU4NmxhbWpZei9IeGV3STJ1VTdXYXVIOGs1a09yTkVCYVJkSTh4NVhGL3M5elJLc0NKdlZ2K0g1d0VVdXFSMnkKbzVLNk85dE85anloSTFublp5SXJoZHE4UTFWUTVaQk5DeVFYaE1td2N6cFFwVDJGMHAxSHBHQVFOSHg5cU9DUwpFbTIzaDFpQUROWGRBeXpoZkg0cHlIQmNSWGRrR3BBaVhWUHIzTi9iMGtJS2c3VXkyVkFjYUhMV1cvTWE1cmhwCk5RVUJJRGtUQUdLSVdGdERSbGNWR1RBcUFlaWlFYjFFckpBb0xVc29nTHJzWEZyei9lSDNNQm0rcGkxSnZUcmkKQWVXM3pLZSsxcW9qMWlkanRiems4TjZYTG1ZRjhEMlZZYlliYW5JalNLeHhTRDkvMkFXNWhtNU9pTUZCNkw3YQphQ2QzNzgzZUNKM0NPY3N3TzJtSUFLU2lmK2NtMDN5UWJieU9XMG96RXRYUC9kLzdudW5WcEVIUkpjeXhyb2svCkxnZ09WWlhuR2swOS95LzhIdFZvOUR0MmttWFFtWkIzRjdUeThpRzBhR3ZhaEhqQ0NZU2hTWmxMVGMvalBvN3YKRlp4UmVqVEtxYTZoazF4Sm9rR1IzVjgwRWRhZGhNRVA1ekJvYWl2VmF6VGI5NEtiaHhldzRUME1HaWZ2NngzUgpuSktjc09LYVdSOERyR014OWZDVkF4a1JIR2hSTEQwWm5MSm9DTXc4YjBqWUpuZTFjNXI4Q1dtRytLVk1jK3g1ClFkd3JjTnZ4VkZsbmdFdDQ3UGdMNGkzcEwwZ2o4OXQwa2lGV0c2SmdlTzJobEpCS2xuZ3hYVE52ZW5Dd1JKcUQKWDdNeTZVQllvdU1JV01EZ3BHeHFxMEtaQ2ZPOG5rMm1UU2tpbXNFdFRlKzlMYlh6eVk0QUVJRjVFZGk5NWJkVQpYL091NFhLa1FTY1NBWFpFT2NlbU9RaTRjT1hXMUNjOFd3T0JtTzlmQVBXWElPTjVhM3lucXdlcWZCZWxtVTUxCmxEczlITC8vRHVzMTYrWUR1NTM0MmJmVFpiYzF4K3lzYklzTUIwYW5hUDUvTnNiVE5yUTV5YXR6MWFMSGhiU3AKSFVXYmNXbWdoLzdrMnI0bXZUNld0WCsvLzBubnNIWlNUZ0ExblZNSW9Bei9Sb1FTU2dxNVNpZGE2T3ZrMXBscAo1c3VUdmRhNC8rSGxBME1WYWVHQW1aUFFPTS9VNnpJN25Sb2xyVGkxcm0yV1RoQ1UxalorYXB5RTcyOEhYRTVSCnd5bHVDblU0K3NDbVd2ekQ0bWF6VExIQ0g3cElWbHAySTB5akVmQWFmbWZCQTRCVXlMTUlVRmJtN05BVk5ueU4KMmZXSlJYK3JwRS81WWtrMkR5RVU4blVlcE9xbjBvaS95dTdBV0szOVN6R0R5TVRNSmR5YnpPK0Rneno2MURUYQp5ZGVvSStibzUvZklwWkNMVytKS1orai8wS0dHMENmZkNacWtVdk9wNjVrYjZmRWtBYnZENFNXdThOTWVSWHBBCms0Y0dRbW00cUducWcwYzFhL0h0d0c3VUN4NFRzU0V4SUdRMU5rRllwa3pOWUxJZUFQZ1FJaE12TkRSczROVzAKRk5BTUZEU25TcmJKMTJ5WDlEazZoZEI3c3FQQU55bTZwNXFjYmtvUHhUczZnUlV6aTk1clR3MnJ4UlhCQ3lSdQpScTI0MDBqWlRZQ2tFZWpBUDFNYjdyY2J0ZnJUbTBJTUs2TGZIaXUvZHZheUdUcUMrTkhXYkhQeERidzM3bkJsCldnblNsY0VRckhRdjUvSVpncFBJUDZacFFiWnZmNW1ZdVJTSC9rWDJjbmRPVHAzOEMvM3lJclZuRXNiN3o0VngKSGl6T2dlakttRjdMWU9HN3RsY29NT0NGODVsMyt3cWxWL2xJRlhGTGZTaHMvMzU3UmhvajJRUDVBSkJhZmVyOApiTUFtdDM2dDlmVGkzall4ZXlZSnhCSHF2blJRcU1ESTh5dVJ4emRmQlQ0WEs3L0JyckVmOUNEUGhkTk9tNS9PCldZZ25NY0x2RnJvTkFPK3dIR1JXenhsM29MV3dUVmRvblVaaENEcGJtN0hQY0xISVF6ZkRlakNlUUtZVytwcGYKQ0JhY0p6UEpSUURQQnBBZ2s4MXA4K3A4ZTJTS1QzVHUyYUt1U3BRUHhPTlN6RTk5c2Zla0J3blNFZGt0NElDbQpQK2VIV1lsczJDR0NGS2dEZ1ZOUWFiU21UWTJmalR3SklidGZNdGdLNjJBRjZDTUl4amJBVXV5c1h2V1U1VHpOCjIvdlhaWWN0aTVFSWRhTGdOOVB1ZWNYc0VUVEZqRlp2OW94UkJLVCtLQUFZS2VJVUQyZk1BcnppdDd0VWZGQXIKbzVmM2FPS2RoRVZQT3pjR1AyNnh6YTZySE5BTGYxdldPVktrcmZ4b0w2VGxMUGh6dlZCTStYYVZ3aTNvTlNkUApKd25XNFBUb1dnak94bGFuTkV1MjNrczVCYVFJUXE3MlBJTWVhRDBLTC82UFdyeUdPQlU2ZEFBb0FMZmJWbWdkCnplNFF3OEd6ZWdRQlpqemxlSWY5SFN6emFwRWFNdG1Pc0tndkxuN1RhMlhaWk45Nmt3VTdoTHR5QStGMlBlSkcKUDhUMnR0V2I5VUg5a0ZxZ0lnY1FTWUxWSjdXV0pnT3Jaa2pyaVNpU1NvSW1XbzNWSlZnLzZ5andWNVp3VmNsTAp4Uks1eE1hSHlVdUhJUHp0NjY2SUF6L3JRZ2NETHl1M2ZDTzhqRVBrckRrS1k5M05BSnFWWjJpYThZeFFCVFVYCnlXOElLdlZGbmlyc0NaYUNpWUFFY1FXZkViUURlVHJhNlprSUJlWHNtRWRWNWhza2VaUjhNUzNIeHZDYWxSdFQKa3NHZ09mU3p5cEg5U1lOMCtudWtVcVdGMFBSY2tiakcwaE0xVWdRYlVWNWlDa3hwSmlKWkpRVjBxZk9KaDFLZQpsRkdIdU5YMjd3VzYvQWY1N3ZrdDB0UnVYaGpYdnlvQWE0eVFTVDV2RmpTaHFKK29DTnhadC8ydTFaRDlKZVhvCm5ONEQ5cDJKS3J6YTlWWWREaW00NjZlbEtDS1dGeTdULzhEdERreGFWbmRTRlhoQ2VCZmFNSG5oRURpa1pWYkYKNUlYL2JDZS95SVZJWDc5aGFlYlI1TnVSMTNFUkE1WVM0QS9CSlRQbUl1Y1p0YmxXdUlSNlF5eHBMdUFZY24zUwpTNVgwZUViTXZ2QTdCUEJFQTV2OWZIRndvMzhlQXBzSVNxM2JMUzRiQXA0d05jOHpnWGIyK2FGY1U4SUc1VmkzCmM5YXBpWEF1UWg2bmxVZXJJUmNqdysvNjE1NnVEbG16NjUxZ3ZRQTlsb1hzdWlBL0xMZG9HaFNHRTZiTlFCN0cKMFBJUFlpZWorenduenppaHlYbWpMWDdFUUpWckQwcE15MEM4WXZ6RTZyOEg1UHdwQzRFcUZxSWdBZTcvaHRldQp0bTJ5Nkk5QXBrMDBiWUYzeU9JZzFkNnFORTUrVEJuMWIyRThGcFZEd1FHU1VjZy9LdE1Cc2pJcEY0UWdNMzd3CittQmtMY1c5L25vVXJPS0sya3ZyaFEraUlrZ3ltemJBdmVPZGgvY2xMUGU3TjBiQ1cwSzRNN01XblBnK3p4V2IKM3B1QWx6bGdHY2RRaVFPaDlFekd1dS9xeXd5YlNac09TdVRVbHk3cEpGT0VqM09LdDlQdTY4UTRmeGhBa3FGMQpvUWozZmxnaTNkRGhpeU82SnNPY0h1d3ExYldoaTNrbnI2cHFKbTgwMDFWZ3FhTVFQaU1MbXlwWmF3SUl6YXd3CkxoTVoxaHZaZUxxMlFDV0xuZzRVUjk4cisxUHZjYkJTQUhHQlZXRVY3cHpCSklYdmpEWlg4RG9raDZhQlFlSGIKUVduUm1jNFhqcWFoMkRFRGc5aUdRTk9JSFY0NzFmVDFEck9FRnNXMXZUTEk2VC9lenZxYWRlbkMwaGUxY2g3eApEbTBxVk9maS9NbGhoY0w1VVF5dDBkK0lsNXdDS1h2Rk0yaDNGZTFwM01sTUFwMlJxbHZzK2xUcm1GUkowMDRhCkpLOTd1Qlk2ZmVUL1NHYjVjZUIvR3lCck1mc1NBWWhTaURQY0RwemkvdFMyWksrYzhHczFBK011dXhmcWxQdkcKVzdLZk9WQUxBamNyaWZ2QlErcjEzdTRBSUVDWU5rMVJuYXNHdWU1dGd6dVlLaGpmTGJJTFRkYVBDazFoS2taSgpreVpteThndUtVRFpOMVUxdjhIWEpxdU9aNUpFYnR1SHdLNmU0Zk13Y0NQWmZMcllHUkxRY1dzRjg0akdsQ2c1CmZYdG1XT2huN084emxTS1JJdXhSdTk1bFEwakJoRXh1OFhZMkgxbktZQnlFWjBGem5NVDBsWkJLaytRRkVlVEsKUTFGY2pka1Z2NllaWTFJaU1kamo2eDJseUV5Q3F5RVFQUEYrYlZVVDY3b2RKTUpOTUlPcFRicGRCaHo2K0srMwovTmhzc01SWkhzWnlFYy9MS2JtUVhaYXhTVGpNbnc1c1JjMkVzZjZCdXkvNTRpcWYzZXRaYW9GQXhsVXc1UGZrCnk4cTVvUXFTcGRCZjdUUkpmZDU2dlhNckFqTjJQYUJPdU56cm9heG1FQXNhV3pjVDdRZ1pkTEFDbC9xNFZGN0MKL0l4Y2NkZ2N0cmhSWk1hRmsyQTlvMThJc093QlBMMkhnZUI5K3NhSWY3ZG1KWGpObHNQQjNrRytLUDFxT2VOYwpKbVdLeGtqSWRXWkRGazZIMzFYb3dScCt0ZDhzMG1SQ0QrdEpQMmsrblJVWnBjeXEvNERQaGNzaVFqdzMzKy9yCkR0Q3UwUXJ6MkRMeVVsaDdCUFpVeXluc0lZREh5NG1QakQ4enVFaFU1N25jell1eGdkelFNUHhiMHpROExuRUYKOFN5dnNnPT0KPVdvQWYKLS0tLS1FTkQgUEdQIE1FU1NBR0UtLS0tLQo=", "size": 9170, "mime_type": "application/octet-stream", "filename": "msg.asc" }], "from": { "address": "enigtest@brunschwig.net", "fpr": "35639B7D397A4737F11C6160CA45EF9C1C17AB90", "user_id": "pEp_own_userId", "username": "anonymous", "comm_type": 255, "me": true, "flags": 1 }, "to": [{ "address": "enigtest@brunschwig.net", "fpr": "F871CDB8990483FD6B305B8F319B7AE82E21E970", "user_id": "f827eb96-2f4f-11e7-8fa4-4be0b4159ac2", "username": "Patrick Brunschwig", "comm_type": 56, "lang": "en", "me": false, "flags": 0 }], "opt_fields": [{ "key": "pEp-auto-consume", "value": "yes" }, { "key": "X-pEp-Version", "value": "1.0" }], "enc_format": 3 }; const keySyncMsg = { "jsonrpc": "2.0", "id": 2002, "security_token": "mmhqlMwmlFBwd5NO3UK7jD18FRs7wW0rm5KetnSe", "method": "notifyHandshake", "params": [{ "address": "enigtest@brunschwig.net", "fpr": "7A0D51844B9C06849E3C313F9B299A39D1CCD0BD", "user_id": "pEp_own_userId", "username": "anonymous", "comm_type": 255, "me": false, "flags": 0 }, { "address": "enigtest@brunschwig.net", "fpr": "F871CDB8990483FD6B305B8F319B7AE82E21E970", "user_id": "fcb950d2-3931-11e7-a0bf-97c07b2bb40e", "username": "anonymous", "comm_type": 255, "lang": "en", "me": false, "flags": 0 }, { "sync_handshake_signal": 1 }] }; const CRLF = "\r\n"; var EnigmailPEPKeySync = { notifyHandshake: function(pepParams) { EnigmailLog.DEBUG("pEpMessage.notifyHandshake()\n"); let myId = pepParams[0]; let partnerId = pepParams[1]; let uiLocale = EnigmailLocale.getUILocale().substr(0, 2).toLowerCase(); let useLocale = uiLocale; let supportedLocale = []; EnigmailpEp.getLanguageList().then(function _success(res) { - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); EnigmailLog.DEBUG("pEpMessage.notifyHandshake: got language list 1: " + JSON.stringify(res) + "\n"); let localeList = EnigmailpEp.processLanguageList(res); supportedLocale = localeList; for (let i = 0; i < localeList.length; i++) { if (localeList[i].short === uiLocale) { useLocale = localeList[i].short; } } return EnigmailpEp.getTrustWords(myId, partnerId, useLocale, false); }).then(function _displayDialog(data) { // open trustwords dialog if (("result" in data) && typeof data.result === "object" && typeof data.result.outParams[1] === "string") { try { let trustWords = data.result.outParams[1]; let win = getWindows().getBestParentWin(); let inputObj = { supportedLocale: supportedLocale, locale: useLocale, trustWords: trustWords, dialogMode: 1, ownId: myId, otherId: partnerId }; win.openDialog("chrome://enigmail/content/pepTrustWords.xul", "", "dialog,modal,centerscreen", inputObj); } catch (ex) { EnigmailLog.DEBUG("pEpMessage.notifyHandshake: caught exception: " + ex.toString() + "\n"); } } }).catch(function _err(err) { EnigmailLog.DEBUG("pEpMessage.notifyHandshake: caught error: " + JSON.stringify(err) + "\n"); }); }, /** * Convert a pEp message into a regular MIME string * * @param pepMessage: Object - pEp message object * * @return Object: * - data: String - message string * - compFields: nsIMsgCompFields object */ mimeStringFromMessage: function(pepMessage) { EnigmailLog.DEBUG("pEpMessage.mimeStringFromMessage()\n"); let boundary = EnigmailMime.createBoundary(); let msgFormat = "plain"; let now = new Date(); let composeFields = Cc["@mozilla.org/messengercompose/composefields;1"].createInstance(Ci.nsIMsgCompFields); composeFields.characterSet = "UTF-8"; composeFields.messageId = EnigmailRNG.generateRandomString(27) + "-enigmail"; let mimeStr = "Message-Id: " + composeFields.messageId + CRLF; mimeStr += "Date: " + now.toUTCString() + CRLF; if ("enc_format" in pepMessage && pepMessage.enc_format === 3) { msgFormat = "pgpmime"; } if ("from" in pepMessage) { let m = this.createAddress(pepMessage.from); let addr = jsmime.headerparser.parseAddressingHeader(m, false); mimeStr += jsmime.headeremitter.emitStructuredHeader("From", addr, {}); composeFields.from = pepMessage.from.address; } if ("to" in pepMessage) { let m = ""; for (let i of pepMessage.to) { if (m.length > 0) { m += ", "; } m += this.createAddress(i); } let addr = jsmime.headerparser.parseAddressingHeader(m, false); mimeStr += jsmime.headeremitter.emitStructuredHeader("To", addr, {}); composeFields.to = m; } if ("cc" in pepMessage) { let m = ""; for (let i of pepMessage.to) { if (m.length > 0) { m += ", "; } m += this.createAddress(i); } let addr = jsmime.headerparser.parseAddressingHeader(m, false); mimeStr += jsmime.headeremitter.emitStructuredHeader("Cc", addr, {}); composeFields.cc = m; } if ("shortmsg" in pepMessage) { mimeStr += jsmime.headeremitter.emitStructuredHeader("Subject", pepMessage.shortmsg, {}); } if ("opt_fields" in pepMessage) { for (let i of pepMessage.opt_fields) { mimeStr += i.key + ": " + i.value + CRLF; } } if (msgFormat === "pgpmime") { mimeStr += 'Content-Type: multipart/encrypted;' + CRLF + ' protocol="application/pgp-encrypted";' + CRLF + ' boundary="' + boundary + '"' + CRLF; } else if ("attachments" in pepMessage) { msgFormat = "multipart"; mimeStr += 'Content-Type: multipart/mixed;' + CRLF + ' boundary="' + boundary + '"' + CRLF; } else { mimeStr += 'Content-Type: text/plain; charset="UTF-8"' + CRLF; } mimeStr += CRLF; if ("longmsg" in pepMessage && msgFormat !== "multipart") { mimeStr += EnigmailData.convertFromUnicode(pepMessage.longmsg, "utf-8") + CRLF; } if (msgFormat !== "plain") { if ("longmsg" in pepMessage && msgFormat === "multipart") { mimeStr += "--" + boundary + CRLF; mimeStr += 'Content-Type: text/plain; charset="UTF-8"' + CRLF + CRLF; mimeStr += EnigmailData.convertFromUnicode(pepMessage.longmsg, "utf-8") + CRLF; } if ("attachments" in pepMessage) { for (let att of pepMessage.attachments) { mimeStr += "--" + boundary + CRLF; mimeStr += "Content-Type: " + att.mime_type; if ("filename" in att) { mimeStr += '; name="' + att.filename + '"'; } mimeStr += CRLF + "Content-Transfer-Encoding: base64" + CRLF + CRLF; mimeStr += att.value.replace(/(.{72})/g, "$1\r\n") + CRLF; } } mimeStr += "--" + boundary + "--" + CRLF; } return { data: mimeStr, compFields: composeFields }; }, createAddress: function(pepUserId) { let m = ""; if ("username" in pepUserId) { m = pepUserId.username + " "; } m += "<" + pepUserId.address + ">"; return m; }, sendMessage: function(pepMessage, listener = null) { EnigmailLog.DEBUG("pEpMessage.sendMessage()\n"); let msg = this.mimeStringFromMessage(pepMessage); return EnigmailSend.sendMessage(msg.data, msg.compFields, listener); }, getTestMessage: function() { return testMessage; }, getTestKeySync: function() { return keySyncMsg; } }; diff --git a/package/pEpMessageHist.jsm b/package/pEpMessageHist.jsm index 6b2aebfb..753def26 100644 --- a/package/pEpMessageHist.jsm +++ b/package/pEpMessageHist.jsm @@ -1,261 +1,261 @@ /*global Components: false*/ /* 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 = ["EnigmailPEPMessageHist"]; const Cc = Components.classes; const Ci = Components.interfaces; const Cr = Components.results; const Cu = Components.utils; Cu.import("resource://gre/modules/Sqlite.jsm"); /* global Sqlite: false */ Cu.import("resource://gre/modules/jsmime.jsm"); /*global jsmime: false*/ Cu.import("resource://enigmail/log.jsm"); /* global EnigmailLog: false*/ Cu.import("resource://enigmail/funcs.jsm"); /* global EnigmailFuncs: false*/ Cu.import("resource://enigmail/mime.jsm"); /* global EnigmailMime: false*/ -Cu.import("resource://enigmail/promise.jsm"); /*global Promise: false */ +Cu.import("resource://gre/modules/PromiseUtils.jsm"); /* global PromiseUtils: false */ Cu.import("resource://enigmail/timer.jsm"); /*global EnigmailTimer: false */ var EnigmailPEPMessageHist = { /** * Determine if message is latest messager from sender * * @param fromAddr: String - Address of sender (From: header) * @param dateSent: String - Date: field of the message * * @return Promise (Boolean). If true, message is latest message */ isLatestMessage: function(fromAddr, dateSent) { EnigmailLog.DEBUG("pEpMessageHist.jsm: isLatestMessage: from=" + fromAddr + "\n"); - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); fromAddr = EnigmailFuncs.stripEmail(fromAddr).toLowerCase(); let lastDate = jsmime.headerparser.parseDateHeader(dateSent); let now = new Date(); if (lastDate > now) { lastDate = now; } let paramArr = { dateSent: lastDate, fromAddr: fromAddr }; let conn; Sqlite.openConnection({ path: "enigmail.sqlite", sharedMemoryCache: false }).then( function onConnection(connection) { conn = connection; return checkDatabaseStructure(conn); }, function onError(error) { EnigmailLog.DEBUG("pEpMessageHist.jsm: isLatestMessage: could not open database\n"); } ).then( function _f() { return findUserRecord(conn, fromAddr); } ).then( function gotData(resultObj) { EnigmailLog.DEBUG("pEpMessageHist.jsm: got " + resultObj.numRows + " rows\n"); if (resultObj.data === null) { return appendUser(conn, paramArr); } else { return updateUser(conn, paramArr, resultObj.data); } } ).then( function _done(isNewestMessage) { EnigmailLog.DEBUG("pEpMessageHist.jsm: OK - closing connection: " + isNewestMessage + "\n"); deferred.resolve(true); // TODO: revert to isNewestMessage conn.close(); } ).catch( function _err(reason) { EnigmailLog.DEBUG("pEpMessageHist.jsm: error - closing connection: " + reason + "\n"); deferred.resolve(false); conn.close(); } ); return deferred.promise; } }; /** * Ensure that the database structure matches the latest version * (table is available) * * @param connection: Object - SQLite connection * * @return Promise */ function checkDatabaseStructure(connection) { EnigmailLog.DEBUG("pEpMessageHist.jsm: checkDatabaseStructure\n"); - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); connection.tableExists("pep_senderlist").then( function onSuccess(exists) { EnigmailLog.DEBUG("pEpMessageHist.jsm: checkDatabaseStructure - success\n"); if (!exists) { createSenderListTable(connection, deferred); } else { deferred.resolve(); } }, function onError(error) { EnigmailLog.DEBUG("pEpMessageHist.jsm: checkDatabaseStructure - error\n"); deferred.reject(error); } ); return deferred.promise; } /** * Create the "pep_senderlist" table and the corresponding index * * @param connection: Object - SQLite connection * @param deferred: Promise */ function createSenderListTable(connection, deferred) { EnigmailLog.DEBUG("pEpMessageHist.jsm: createSenderListTable\n"); connection.execute("create table pep_senderlist (" + "email text not null, " + // email address of correspondent "last_date text not null);"). // timestamp of last mail received for the email combination then( function _ok() { EnigmailLog.DEBUG("pEpMessageHist.jsm: createSenderListTable - index\n"); connection.execute("create unique index pep_senderlist_i1 on pep_senderlist(email)"). then(function _f() { deferred.resolve(); }); } ); } /** * Find the database record for a given email address and type * * @param connection: Object - SQLite connection * @param email: String - Email address to search (in lowercase) * * @return Promise */ function findUserRecord(connection, email) { EnigmailLog.DEBUG("pEpMessageHist.jsm: findUserRecord\n"); - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); let data = null; let numRows = 0; connection.execute( "select * from pep_senderlist where email = :email", { email: email }, function _onRow(row) { EnigmailLog.DEBUG("pEpMessageHist.jsm: findUserRecord - got row\n"); if (numRows === 0) { data = row; } else { data = null; } ++numRows; } ).then(function _f() { deferred.resolve({ data: data, numRows: numRows }); }); return deferred.promise; } /** * Create new database record for an Autorypt header * * @param connection: Object - SQLite connection * @param paramsArr: Object - the message headers * * @return Promise */ function appendUser(connection, paramsArr) { EnigmailLog.DEBUG("pEpMessageHist.jsm: appendUser\n"); - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); connection.executeTransaction(function _trx() { connection.execute("insert into pep_senderlist (email, last_date) values " + "(:email, :lastDate)", { email: paramsArr.fromAddr, lastDate: paramsArr.dateSent.toJSON() }).then( function _ok() { deferred.resolve(true); } ).catch(function _err() { deferred.reject("appendUser"); }); }); return deferred.promise; } /** * Update the record for an email address and type, if the email we got is newer * than the latest record we already stored * * @param connection: Object - SQLite connection * @param paramsArr: Object - the message headers * @param currData: Object (mozIStorageRow) - current data stored in the database * * @return Promise */ function updateUser(connection, paramsArr, currData) { EnigmailLog.DEBUG("pEpMessageHist.jsm: updateUser\n"); - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); let lastDate = new Date(currData.getResultByName("last_date")); if (lastDate >= paramsArr.dateSent) { EnigmailLog.DEBUG("pEpMessageHist.jsm: updateUser: not a new latest message\n"); EnigmailTimer.setTimeout(function _f() { deferred.resolve(false); }, 0); return deferred.promise; } EnigmailLog.DEBUG("pEpMessageHist.jsm: updateUser: updating latest message\n"); connection.executeTransaction(function _trx() { connection.execute("update pep_senderlist set last_date = :dateSent where email = :email", { email: paramsArr.fromAddr, dateSent: paramsArr.dateSent.toJSON() }).then( function _ok() { deferred.resolve(true); } ).catch(function _err() { deferred.reject("update failed"); }); }); return deferred.promise; } diff --git a/package/promise.jsm b/package/promise.jsm deleted file mode 100644 index 43a748ce..00000000 --- a/package/promise.jsm +++ /dev/null @@ -1,33 +0,0 @@ -/*global Components: false */ -/* - * 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"; - -/* - This module is a shim module to make it easier to load - Promise from the various potential sources -*/ - -var EXPORTED_SYMBOLS = ["Promise"]; - -const Cu = Components.utils; - -var scope = {}; - -try { - Cu.import("resource://gre/modules/Promise.jsm", scope); // Gecko >= 25 -} -catch (ex) { - try { - Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", scope); // Gecko 21 to 24} - } - catch (ex2) { - Cu.import("resource://gre/modules/commonjs/promise/core.js", scope); // Gecko 17 to 20 - } -} - -var Promise = scope.Promise; diff --git a/package/windows.jsm b/package/windows.jsm index cb1d7ac7..b5922392 100644 --- a/package/windows.jsm +++ b/package/windows.jsm @@ -1,608 +1,608 @@ /*global Components: false, escape: 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 = ["EnigmailWindows"]; const Cc = Components.classes; const Ci = Components.interfaces; const Cu = Components.utils; Cu.import("resource://enigmail/log.jsm"); /*global EnigmailLog: false */ Cu.import("resource://enigmail/core.jsm"); /*global EnigmailCore: false */ Cu.import("resource://enigmail/locale.jsm"); /*global EnigmailLocale: false */ Cu.import("resource://enigmail/keyRing.jsm"); /*global EnigmailKeyRing: false */ Cu.import("resource://enigmail/rules.jsm"); /*global EnigmailRules: false */ Cu.import("resource://enigmail/pEpAdapter.jsm"); /*global EnigmailPEPAdapter: false */ -Cu.import("resource://enigmail/promise.jsm"); /*global Promise: false */ +Cu.import("resource://gre/modules/PromiseUtils.jsm"); /* global PromiseUtils: false */ const APPSHELL_MEDIATOR_CONTRACTID = "@mozilla.org/appshell/window-mediator;1"; const APPSHSVC_CONTRACTID = "@mozilla.org/appshell/appShellService;1"; const LOCAL_FILE_CONTRACTID = "@mozilla.org/file/local;1"; const IOSERVICE_CONTRACTID = "@mozilla.org/network/io-service;1"; const EnigmailWindows = { /** * Display the OpenPGP setup wizard window * * win : nsIWindow - the parent window * skipIntro: Boolean - optional, if true, skip the introduction page * * no return value */ openSetupWizard: function(win, skipIntro) { EnigmailLog.DEBUG("windows.jsm: openSetupWizard()\n"); let param = ""; if (skipIntro) { param = "?skipIntro=true"; } win.open("chrome://enigmail/content/enigmailSetupWizard.xul" + param, "", "chrome,centerscreen,resizable"); }, /** * Open a window, or focus it if it is already open * * @winName : String - name of the window; used to identify if it is already open * @spec : String - window URL (e.g. chrome://enigmail/content/test.xul) * @winOptions: String - window options as defined in nsIWindow.open * @optObj : any - an Object, Array, String, etc. that is passed as parameter * to the window */ openWin: function(winName, spec, winOptions, optObj) { var windowManager = Cc[APPSHELL_MEDIATOR_CONTRACTID].getService(Ci.nsIWindowMediator); var winEnum = windowManager.getEnumerator(null); var recentWin = null; while (winEnum.hasMoreElements() && !recentWin) { var thisWin = winEnum.getNext(); if (thisWin.location.href == spec) { recentWin = thisWin; break; } if (winName && thisWin.name && thisWin.name == winName) { thisWin.focus(); break; } } if (recentWin) { recentWin.focus(); } else { var appShellSvc = Cc[APPSHSVC_CONTRACTID].getService(Ci.nsIAppShellService); var domWin = appShellSvc.hiddenDOMWindow; try { domWin.open(spec, winName, "chrome," + winOptions, optObj); } catch (ex) { domWin = windowManager.getMostRecentWindow(null); domWin.open(spec, winName, "chrome," + winOptions, optObj); } } }, /** * Determine the best possible window to serve as parent window for dialogs. * * @return: nsIWindow object */ getBestParentWin: function() { var windowManager = Cc[APPSHELL_MEDIATOR_CONTRACTID].getService(Ci.nsIWindowMediator); var bestFit = null; var winEnum = windowManager.getEnumerator(null); while (winEnum.hasMoreElements()) { var thisWin = winEnum.getNext(); if (thisWin.location.href.search(/\/messenger.xul$/) > 0) { bestFit = thisWin; } if (!bestFit && thisWin.location.href.search(/\/messengercompose.xul$/) > 0) { bestFit = thisWin; } } if (!bestFit) { winEnum = windowManager.getEnumerator(null); bestFit = winEnum.getNext(); } return bestFit; }, /** * Iterate through the frames of a window and return the first frame with a * matching name. * * @win: nsIWindow - XUL window to search * @frameName: String - name of the frame to seach * * @return: the frame object or null if not found */ getFrame: function(win, frameName) { EnigmailLog.DEBUG("windows.jsm: getFrame: name=" + frameName + "\n"); for (var j = 0; j < win.frames.length; j++) { if (win.frames[j].name == frameName) { return win.frames[j]; } } return null; }, getMostRecentWindow: function() { var windowManager = Cc[APPSHELL_MEDIATOR_CONTRACTID].getService(Ci.nsIWindowMediator); return windowManager.getMostRecentWindow(null); }, /** * Display the key help window * * @source - |string| containing the name of the file to display * * no return value */ openHelpWindow: function(source) { EnigmailWindows.openWin("enigmail:help", "chrome://enigmail/content/enigmailHelp.xul?src=" + source, "centerscreen,resizable"); }, /** * Display the "About Enigmail" window * * no return value */ openAboutWindow: function() { EnigmailWindows.openWin("about:enigmail", "chrome://enigmail/content/enigmailAbout.xul", "resizable,centerscreen"); }, /** * Display the Per-Recipient Rules editor window * * no return value */ openRulesEditor: function() { EnigmailWindows.openWin("enigmail:rulesEditor", "chrome://enigmail/content/enigmailRulesEditor.xul", "dialog,centerscreen,resizable"); }, /** * Display the OpenPGP key manager window * * no return value */ openKeyManager: function(win) { EnigmailCore.getService(win); EnigmailWindows.openWin("enigmail:KeyManager", "chrome://enigmail/content/enigmailKeyManager.xul", "resizable"); }, /** * If the Key Manager is open, dispatch an event to tell the key * manager to refresh the displayed keys */ keyManReloadKeys: function() { let windowManager = Cc[APPSHELL_MEDIATOR_CONTRACTID].getService(Ci.nsIWindowMediator); const winName = "enigmail:KeyManager"; const spec = "chrome://enigmail/content/enigmailKeygen.xul"; let winEnum = windowManager.getEnumerator(null); let recentWin = null; while (winEnum.hasMoreElements() && !recentWin) { let thisWin = winEnum.getNext(); if (thisWin.location.href == spec) { recentWin = thisWin; break; } if (thisWin.name && thisWin.name == winName) { let evt = new thisWin.Event("reload-keycache", { "bubbles": true, "cancelable": false }); thisWin.dispatchEvent(evt); break; } } }, /** * Display the key creation window * * no return value */ openKeyGen: function() { EnigmailWindows.openWin("enigmail:generateKey", "chrome://enigmail/content/enigmailKeygen.xul", "chrome,resizable=yes"); }, /** * Display the card details window * * no return value */ openCardDetails: function() { EnigmailWindows.openWin("enigmail:cardDetails", "chrome://enigmail/content/enigmailCardDetails.xul", "centerscreen"); }, /** * Display the console log window * * @win - |object| holding the parent window for the dialog * * no return value */ openConsoleWindow: function() { EnigmailWindows.openWin("enigmail:console", "chrome://enigmail/content/enigmailConsole.xul", "resizable,centerscreen"); }, /** * Display the window for the debug log file * * @win - |object| holding the parent window for the dialog * * no return value */ openDebugLog: function(win) { EnigmailWindows.openWin("enigmail:logFile", "chrome://enigmail/content/enigmailViewFile.xul?viewLog=1&title=" + escape(EnigmailLocale.getString("debugLog.title")), "resizable,centerscreen"); }, /** * Display the preferences dialog * * @win - |object| holding the parent window for the dialog * @showBasic - |boolean| true if only the 1st page of the preferences window * should be displayed / false otherwise * @selectTab - |string| ID of the tab element (in XUL) to display when opening * * no return value */ openPrefWindow: function(win, showBasic, selectTab) { EnigmailLog.DEBUG("windows.js: openPrefWindow\n"); EnigmailCore.getService(win, true); // true: starting preferences dialog let url; if (EnigmailPEPAdapter.usingPep()) { url = "chrome://enigmail/content/pref-pep.xul"; } else { url = "chrome://enigmail/content/pref-enigmail.xul"; } win.openDialog(url, "_blank", "chrome,resizable=yes", { 'showBasic': showBasic, 'clientType': 'thunderbird', 'selectTab': selectTab }); }, /** * Display the dialog for creating a new per-recipient rule * * @win - |object| holding the parent window for the dialog * @emailAddress - |string| containing the email address for the rule * * @return - always true */ createNewRule: function(win, emailAddress) { // make sure the rules database is loaded const enigmailSvc = EnigmailCore.getService(win); if (!enigmailSvc) { return false; } // open rule dialog EnigmailRules.getRulesData({}); const inputObj = { toAddress: "{" + emailAddress + "}", options: "", command: "add" }; win.openDialog("chrome://enigmail/content/enigmailSingleRcptSettings.xul", "", "dialog,modal,centerscreen,resizable", inputObj, {}); return true; }, /** * Display the dialog for changing the expiry date of one or several keys * * @win - |object| holding the parent window for the dialog * @userIdArr - |array| of |strings| containing the User IDs * @keyIdArr - |array| of |strings| containing the key IDs (eg. "0x12345678") to change * * @return Boolean - true if expiry date was changed; false otherwise */ editKeyExpiry: function(win, userIdArr, keyIdArr) { const inputObj = { keyId: keyIdArr, userId: userIdArr }; const resultObj = { refresh: false }; win.openDialog("chrome://enigmail/content/enigmailEditKeyExpiryDlg.xul", "", "dialog,modal,centerscreen,resizable", inputObj, resultObj); return resultObj.refresh; }, /** * Display the dialog for changing key trust of one or several keys * * @win - |object| holding the parent window for the dialog * @userIdArr - |array| of |strings| containing the User IDs * @keyIdArr - |array| of |strings| containing the key IDs (eg. "0x12345678") to change * * @return Boolean - true if key trust was changed; false otherwise */ editKeyTrust: function(win, userIdArr, keyIdArr) { const inputObj = { keyId: keyIdArr, userId: userIdArr }; const resultObj = { refresh: false }; win.openDialog("chrome://enigmail/content/enigmailEditKeyTrustDlg.xul", "", "dialog,modal,centerscreen,resizable", inputObj, resultObj); return resultObj.refresh; }, /** * Display the dialog for signing a key * * @win - |object| holding the parent window for the dialog * @userId - |string| containing the User ID (for displaing in the dialog only) * @keyId - |string| containing the key ID (eg. "0x12345678") * * @return Boolean - true if key was signed; false otherwise */ signKey: function(win, userId, keyId) { const inputObj = { keyId: keyId, userId: userId }; const resultObj = { refresh: false }; win.openDialog("chrome://enigmail/content/enigmailSignKeyDlg.xul", "", "dialog,modal,centerscreen,resizable", inputObj, resultObj); return resultObj.refresh; }, /** * Display the photo ID associated with a key * * @win - |object| holding the parent window for the dialog * @keyId - |string| containing the key ID (eg. "0x12345678") * @userId - |string| containing the User ID (for displaing in the dialog only) * @photoNumber - |number| UAT entry in the squence of appearance in the key listing, starting with 0 * no return value */ showPhoto: function(win, keyId, userId, photoNumber) { const enigmailSvc = EnigmailCore.getService(win); if (enigmailSvc) { if (!photoNumber) photoNumber = 0; if (keyId.search(/^0x/) < 0) { keyId = "0x" + keyId; } let exitCodeObj = {}; let photoFile = EnigmailKeyRing.getPhotoFile(keyId, photoNumber, exitCodeObj, {}); if (photoFile && exitCodeObj.value === 0) { if (!(photoFile.isFile() && photoFile.isReadable())) { EnigmailWindows.alert(win, EnigmailLocale.getString("error.photoPathNotReadable", photoFile.path)); } else { const photoUri = Cc[IOSERVICE_CONTRACTID].getService(Ci.nsIIOService). newFileURI(photoFile).spec; const argsObj = { photoUri: photoUri, userId: userId, keyId: keyId }; win.openDialog("chrome://enigmail/content/enigmailDispPhoto.xul", photoUri, "chrome,modal,resizable,dialog,centerscreen", argsObj); try { // delete the photo file photoFile.remove(false); } catch (ex) {} } } else { EnigmailWindows.alert(win, EnigmailLocale.getString("noPhotoAvailable")); } } }, /** * Display the OpenPGP Key Details window * * @win - |object| holding the parent window for the dialog * @keyId - |string| containing the key ID (eg. "0x12345678") * @refresh - |boolean| if true, cache is cleared and the key data is loaded from GnuPG * * @return Boolean - true: keylist needs to be refreshed * - false: no need to refresh keylist */ openKeyDetails: function(win, keyId, refresh) { const keyListObj = {}; keyId = keyId.replace(/^0x/, ""); if (refresh) { EnigmailKeyRing.clearCache(); } const inputObj = { keyId: keyId }; const resultObj = { refresh: false }; win.openDialog("chrome://enigmail/content/keyDetailsDlg.xul", "", "dialog,modal,centerscreen,resizable", inputObj, resultObj); if (resultObj.refresh) { EnigmailKeyRing.clearCache(); } return resultObj.refresh; }, /** * Display the dialog to search and/or download key(s) from a keyserver * * @win - |object| holding the parent window for the dialog * @inputObj - |object| with member searchList (|string| containing the keys to search) * @resultObj - |object| with member importedKeys (|number| containing the number of imporeted keys) * * no return value */ downloadKeys: function(win, inputObj, resultObj) { EnigmailLog.DEBUG("windows.jsm: downloadKeys: searchList=" + inputObj.searchList + "\n"); resultObj.importedKeys = 0; const ioService = Cc[IOSERVICE_CONTRACTID].getService(Ci.nsIIOService); if (ioService && ioService.offline) { EnigmailWindows.alert(win, EnigmailLocale.getString("needOnline")); return; } let valueObj = {}; if (inputObj.searchList) { valueObj = { keyId: "<" + inputObj.searchList.join("> <") + ">" }; } const keysrvObj = {}; if (inputObj.searchList && inputObj.autoKeyServer) { keysrvObj.value = inputObj.autoKeyServer; } else { win.openDialog("chrome://enigmail/content/enigmailKeyserverDlg.xul", "", "dialog,modal,centerscreen", valueObj, keysrvObj); } if (!keysrvObj.value) { return; } inputObj.keyserver = keysrvObj.value; if (!inputObj.searchList) { const searchval = keysrvObj.email. replace(/^(\s*)(.*)/, "$2"). replace(/\s+$/, ""); // trim spaces // special handling to convert fingerprints with spaces into fingerprint without spaces if (searchval.length == 49 && searchval.match(/^[0-9a-fA-F ]*$/) && searchval[4] == ' ' && searchval[9] == ' ' && searchval[14] == ' ' && searchval[19] == ' ' && searchval[24] == ' ' && searchval[29] == ' ' && searchval[34] == ' ' && searchval[39] == ' ' && searchval[44] == ' ') { inputObj.searchList = ["0x" + searchval.replace(/ /g, "")]; } else if (searchval.length == 40 && searchval.match(/^[0-9a-fA-F ]*$/)) { inputObj.searchList = ["0x" + searchval]; } else if (searchval.length == 8 && searchval.match(/^[0-9a-fA-F]*$/)) { // special handling to add the required leading 0x when searching for keys inputObj.searchList = ["0x" + searchval]; } else if (searchval.length == 16 && searchval.match(/^[0-9a-fA-F]*$/)) { inputObj.searchList = ["0x" + searchval]; } else { inputObj.searchList = searchval.split(/[,; ]+/); } } win.openDialog("chrome://enigmail/content/enigmailSearchKey.xul", "", "dialog,modal,centerscreen", inputObj, resultObj); }, /** * Open the Trustwords dialog for a specific pair of keys * * @param win: Object - nsIWindow * @param emailAddress: String - Email address of peer to verify * @param headerData: either: Object - nsIMsgHdr object for the message (to identify the ideal own identity) * or: String - own email address to compare with * * @return: Promise (resolve() case of success; rejection otherwise). */ verifyPepTrustWords: function(win, emailAddress, headerData) { - let deferred = Promise.defer(); + let deferred = PromiseUtils.defer(); EnigmailPEPAdapter.prepareTrustWordsDlg(emailAddress, headerData). then(function _ok(inputObj) { win.openDialog("chrome://enigmail/content/pepTrustWords.xul", "", "dialog,modal,centerscreen", inputObj); deferred.resolve(); }). catch(function _err(errorMsg) { switch (errorMsg) { case "cannotVerifyOwnId": EnigmailWindows.alert(window, EnigmailLocale.getString("pepTrustWords.cannotVerifyOwnId")); break; case "cannotFindKey": EnigmailWindows.alert(window, EnigmailLocale.getString("pepTrustWords.cannotFindKey", emailAddress)); break; default: EnigmailWindows.alert(window, EnigmailLocale.getString("pepTrustWords.generalFailure", emailAddress)); break; } deferred.reject(); }); return deferred.promise; }, pepHandshake: function(window, direction, myself, peers) { let inputObj = { myself: myself, peers: peers, direction: direction }; window.openDialog("chrome://enigmail/content/pepHandshake.xul", "", "dialog,modal,centerscreen", inputObj); } }; diff --git a/ui/content/enigRetrieveProgress.js b/ui/content/enigRetrieveProgress.js index 4be7fe2d..1ef77a02 100644 --- a/ui/content/enigRetrieveProgress.js +++ b/ui/content/enigRetrieveProgress.js @@ -1,315 +1,314 @@ /*global Components: false*/ /* * 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/. */ /* eslint no-invalid-this: 0, no-loop-func: 0 */ "use strict"; const Cu = Components.utils; const Ci = Components.interfaces; const Cc = Components.classes; const nsIEnigmail = Ci.nsIEnigmail; Cu.import("resource://enigmail/core.jsm"); /*global EnigmailCore: false */ Cu.import("resource://enigmail/log.jsm"); /*global EnigmailLog: false */ Cu.import("resource://enigmail/locale.jsm"); /*global EnigmailLocale: false */ Cu.import("resource://enigmail/keyserver.jsm"); /*global EnigmailKeyServer: false */ Cu.import("resource://enigmail/errorHandling.jsm"); /*global EnigmailErrorHandling: false */ Cu.import("resource://enigmail/webKey.jsm"); /*global EnigmailWks: false */ Cu.import("resource://enigmail/data.jsm"); /*global EnigmailData: false */ Cu.import("resource://enigmail/dialog.jsm"); /*global EnigmailDialog: false */ -Cu.import("resource://enigmail/promise.jsm"); /*global Promise: false */ // dialog is just an array we'll use to store various properties from the dialog document... var dialog; // the msgProgress is a nsIMsgProgress object var msgProgress = null; // random global variables... var targetFile; var itsASaveOperation = false; var gProcess = null; var gEnigCallbackFunc = null; var gErrorData = ''; // all progress notifications are done through the nsIWebProgressListener implementation... var progressListener = { onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus) { if (aStateFlags & Ci.nsIWebProgressListener.STATE_START) { // dialog.progress.setAttribute( "value", 0 ); // Put progress meter in undetermined mode. dialog.progress.setAttribute("mode", "undetermined"); } if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) { // we are done transmitting // Indicate completion in status area. // Put progress meter at 100%. dialog.progress.setAttribute("value", 100); dialog.progress.setAttribute("mode", "normal"); if (msgProgress.processCanceledByUser) enigSendKeyCancel(); window.close(); } }, onProgressChange: function(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress) {}, onLocationChange: function(aWebProgress, aRequest, aLocation) { // we can ignore this notification }, onStatusChange: function(aWebProgress, aRequest, aStatus, aMessage) { // we can ignore this notification }, onSecurityChange: function(aWebProgress, aRequest, state) { // we can ignore this notification }, QueryInterface: function(iid) { if (iid.equals(Ci.nsIWebProgressListener) || iid.equals(Ci.nsISupportsWeakReference) || iid.equals(Ci.nsISupports)) return this; throw Components.results.NS_NOINTERFACE; } }; function onLoad() { // Set global variables. EnigmailLog.DEBUG("enigRetrieveProgress: onLoad\n"); var inArg = window.arguments[0]; window.arguments[1].result = false; dialog = {}; dialog.strings = []; dialog.progress = document.getElementById("dialog.progress"); var enigmailSvc = EnigmailCore.getService(window); if (!enigmailSvc) return; gEnigCallbackFunc = inArg.cbFunc; msgProgress = Cc["@mozilla.org/messenger/progress;1"].createInstance(Ci.nsIMsgProgress); if (inArg.accessType == nsIEnigmail.UPLOAD_WKD) { onLoadWkd(inArg); } else { onLoadGpg(inArg); } } function onLoadWkd(inArg) { try { var statTxt = document.getElementById("dialog.status2"); statTxt.value = EnigmailLocale.getString("keyserverTitle.uploading"); document.getElementById("progressWindow").setAttribute("title", EnigmailLocale.getString("keyserverTitle.uploading")); var progressDlg = document.getElementById("dialog.progress"); progressDlg.setAttribute("mode", "undetermined"); let uploads = []; // For each key fpr/sender identity pair, check whenever WKS is supported // Result is an array of booleans for (let i = 0; i < inArg.senderIdentities.length; i++) { let keyFpr = inArg.fprList[i]; let senderIdent = inArg.senderIdentities[i]; let was_uploaded = new Promise(function(resolve, reject) { EnigmailLog.DEBUG("enigRetrieveProgress: onLoadWkd: ident=" + senderIdent.email + ", key=" + keyFpr + "\n"); EnigmailWks.isWksSupportedAsync(senderIdent.email, window, function(is_supported) { if (msgProgress && msgProgress.processCanceledByUser) { reject("canceled"); } resolve(is_supported); }); }).then(function(is_supported) { if (is_supported) { let keyFpr = inArg.fprList[i]; let senderIdent = inArg.senderIdentities[i]; return new Promise(function(resolve, reject) { EnigmailWks.submitKey(senderIdent, { 'fpr': keyFpr }, window, function(success) { if (success) { resolve(senderIdent); } else { reject(); } }); }); } else { return new Promise.resolve(null); } }); uploads.push(was_uploaded); } Promise.all(uploads).catch(function(reason) { let errorMsg = EnigmailLocale.getString("keyserverProgress.wksUploadFailed"); window.close(); gEnigCallbackFunc(-1, errorMsg, true); }).then(function(senders) { let uploaded_uids = []; senders.forEach(function(val) { if (val !== null) { uploaded_uids.push(val.email); } }); progressDlg.setAttribute("value", 100); progressDlg.setAttribute("mode", "normal"); EnigmailDialog.info(window, EnigmailLocale.getString("keyserverProgress.wksUploadCompleted")); window.close(); }); } catch (ex) { EnigmailLog.DEBUG(ex); } } function onLoadGpg(inArg) { EnigmailLog.DEBUG("enigRetrieveProgress: onLoadGpg\n"); var subject; var statTxt = document.getElementById("dialog.status2"); if (inArg.accessType == nsIEnigmail.UPLOAD_KEY) { statTxt.value = EnigmailLocale.getString("keyserverProgress.uploading"); subject = EnigmailLocale.getString("keyserverTitle.uploading"); } else { statTxt.value = EnigmailLocale.getString("keyserverProgress.refreshing"); subject = EnigmailLocale.getString("keyserverTitle.refreshing"); } var procListener = { done: function(exitCode) { EnigmailLog.DEBUG("enigRetrieveProgress: subprocess terminated with " + exitCode + "\n"); processEnd(msgProgress, exitCode); }, stdout: function(data) { EnigmailLog.DEBUG("enigRetrieveProgress: got data on stdout: '" + data + "'\n"); }, stderr: function(data) { EnigmailLog.DEBUG("enigRetrieveProgress: got data on stderr: '" + data + "'\n"); gErrorData += data; } }; msgProgress.registerListener(progressListener); msgProgress.onStateChange(null, null, Ci.nsIWebProgressListener.STATE_START, 0); var errorMsgObj = {}; gProcess = EnigmailKeyServer.access(inArg.accessType, inArg.keyServer, inArg.keyList, procListener, errorMsgObj); if (!gProcess) { EnigmailDialog.alert(window, EnigmailLocale.getString("sendKeysFailed") + "\n" + EnigmailData.convertGpgToUnicode(errorMsgObj.value)); } document.getElementById("progressWindow").setAttribute("title", subject); } function onUnload() { if (msgProgress) { try { msgProgress.unregisterListener(progressListener); msgProgress = null; } catch (exception) {} } } // If the user presses cancel, tell the app launcher and close the dialog... function onCancel() { try { msgProgress.processCanceledByUser = true; } catch (ex) { return true; } // don't Close up dialog by returning false, the backend will close the dialog when everything will be aborted. return false; } function processEnd(progressBar, exitCode) { EnigmailLog.DEBUG("enigmailRetrieveProgress.js: processEnd\n"); var errorMsg; if (gProcess) { gProcess = null; EnigmailLog.DEBUG("enigmailRetrieveProgress.js: processEnd: exitCode = " + exitCode + "\n"); var statusText = gEnigCallbackFunc(exitCode, "", false); errorMsg = ""; try { if (gErrorData.length > 0) { var statusFlagsObj = {}; var statusMsgObj = {}; errorMsg = EnigmailErrorHandling.parseErrorOutput(gErrorData, statusFlagsObj, statusMsgObj); } } catch (ex) {} EnigmailLog.DEBUG("enigmailRetrieveProgress.js: processEnd: errorMsg=" + errorMsg); if (errorMsg.search(/ec=\d+/i) >= 0) { exitCode = -1; } let j = errorMsg.search(/^\[GNUPG:\] IMPORT_RES/m); if (j >= 0) { let m = errorMsg.substr(j, 35).match(/^(\[GNUPG:\] IMPORT_RES +)([0-9]+)/); if (m && m.length > 2) { if (m[2] == "0") { // no keys imported exitCode = -2; } else { exitCode = 0; } } } statusText = gEnigCallbackFunc(exitCode, "", false); if (exitCode === 0) { window.arguments[1].result = true; } } if (progressBar) { try { progressBar.onStateChange(null, null, Ci.nsIWebProgressListener.STATE_STOP, 0); } catch (ex) {} } gEnigCallbackFunc(exitCode, errorMsg, true); } function enigSendKeyCancel() { if (gProcess) { var p = gProcess; gEnigCallbackFunc = null; gProcess = null; p.kill(false); } } diff --git a/ui/content/enigmailCommon.js b/ui/content/enigmailCommon.js index 611f2a75..f8b04237 100644 --- a/ui/content/enigmailCommon.js +++ b/ui/content/enigmailCommon.js @@ -1,820 +1,819 @@ /*global Components: false, EnigmailFiles: false, EnigmailCore: false, EnigmailApp: false, EnigmailDialog: false, EnigmailWindows: false, EnigmailTime: false */ /* * 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/. */ /** * PLEASE NOTE: this module is legacy and must not be used for newe code - it will be removed! */ "use strict"; // enigmailCommon.js: shared JS functions for Enigmail // WARNING: This module functions must not be loaded in overlays to standard functionality! // Many of these components are not used in this file, but are instead used in other files that are loaded together with EnigmailCommon Components.utils.import("resource://enigmail/core.jsm"); /*global EnigmailCore: false */ Components.utils.import("resource://enigmail/funcs.jsm"); /*global EnigmailFuncs: false */ Components.utils.import("resource://enigmail/keyEditor.jsm"); /*global EnigmailKeyEditor: false */ Components.utils.import("resource://enigmail/key.jsm"); /*global EnigmailKey: false */ Components.utils.import("resource://enigmail/log.jsm"); /*global EnigmailLog: false */ Components.utils.import("resource://enigmail/prefs.jsm"); /*global EnigmailPrefs: false */ Components.utils.import("resource://enigmail/os.jsm"); /*global EnigmailOS: false */ Components.utils.import("resource://enigmail/locale.jsm"); /*global EnigmailLocale: false */ Components.utils.import("resource://enigmail/data.jsm"); /*global EnigmailData: false */ Components.utils.import("resource://enigmail/files.jsm"); /*global EnigmailFiles: false */ Components.utils.import("resource://enigmail/app.jsm"); /*global EnigmailApp: false */ Components.utils.import("resource://enigmail/dialog.jsm"); /*global EnigmailDialog: false */ Components.utils.import("resource://enigmail/windows.jsm"); /*global EnigmailWindows: false */ Components.utils.import("resource://enigmail/time.jsm"); /*global EnigmailTime: false */ Components.utils.import("resource://enigmail/timer.jsm"); /*global EnigmailTimer: false */ Components.utils.import("resource://enigmail/keyRing.jsm"); /*global EnigmailKeyRing: false */ Components.utils.import("resource://enigmail/trust.jsm"); /*global EnigmailTrust: false */ Components.utils.import("resource://enigmail/constants.jsm"); /*global EnigmailConstants: false */ Components.utils.import("resource://enigmail/errorHandling.jsm"); /*global EnigmailErrorHandling: false */ Components.utils.import("resource://enigmail/keyserver.jsm"); /*global EnigmailKeyServer: false */ Components.utils.import("resource://enigmail/events.jsm"); /*global EnigmailEvents: false */ Components.utils.import("resource://enigmail/gpg.jsm"); /*global EnigmailGpg: false */ -Components.utils.import("resource://enigmail/promise.jsm"); /*global Promise: false */ Components.utils.import("resource://enigmail/gpgAgent.jsm"); /*global EnigmailGpgAgent: false */ Components.utils.import("resource://enigmail/streams.jsm"); /*global EnigmailStreams: false */ // The compatible Enigmime version var gEnigmailSvc; var gEnigPromptSvc; // Maximum size of message directly processed by Enigmail const ENIG_PROCESSINFO_CONTRACTID = "@mozilla.org/xpcom/process-info;1"; const ENIG_ENIGMAIL_CONTRACTID = "@mozdev.org/enigmail/enigmail;1"; const ENIG_STRINGBUNDLE_CONTRACTID = "@mozilla.org/intl/stringbundle;1"; const ENIG_LOCAL_FILE_CONTRACTID = "@mozilla.org/file/local;1"; const ENIG_DIRSERVICE_CONTRACTID = "@mozilla.org/file/directory_service;1"; const ENIG_MIME_CONTRACTID = "@mozilla.org/mime;1"; const ENIG_WMEDIATOR_CONTRACTID = "@mozilla.org/rdf/datasource;1?name=window-mediator"; const ENIG_ASS_CONTRACTID = "@mozilla.org/appshell/appShellService;1"; const ENIG_LOCALE_SVC_CONTRACTID = "@mozilla.org/intl/nslocaleservice;1"; const ENIG_DATE_FORMAT_CONTRACTID = "@mozilla.org/intl/scriptabledateformat;1"; const ENIG_ACCOUNT_MANAGER_CONTRACTID = "@mozilla.org/messenger/account-manager;1"; const ENIG_THREAD_MANAGER_CID = "@mozilla.org/thread-manager;1"; const ENIG_SIMPLEURI_CONTRACTID = "@mozilla.org/network/simple-uri;1"; const ENIG_SEAMONKEY_ID = "{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}"; const ENIG_STANDARD_URL_CONTRACTID = "@mozilla.org/network/standard-url;1"; const ENIG_SCRIPTABLEINPUTSTREAM_CONTRACTID = "@mozilla.org/scriptableinputstream;1"; const ENIG_BINARYINPUTSTREAM_CONTRACTID = "@mozilla.org/binaryinputstream;1"; const ENIG_SAVEASCHARSET_CONTRACTID = "@mozilla.org/intl/saveascharset;1"; const ENIG_STREAMCONVERTERSERVICE_CID_STR = "{892FFEB0-3F80-11d3-A16C-0050041CAF44}"; const ENIG_ISCRIPTABLEUNICODECONVERTER_CONTRACTID = "@mozilla.org/intl/scriptableunicodeconverter"; const ENIG_IOSERVICE_CONTRACTID = "@mozilla.org/network/io-service;1"; const ENIG_C = Components.classes; const ENIG_I = Components.interfaces; // Key algorithms const ENIG_KEYTYPE_DSA = 1; const ENIG_KEYTYPE_RSA = 2; // field ID's of key list (as described in the doc/DETAILS file in the GnuPG distribution) const ENIG_KEY_TRUST = 1; const ENIG_KEY_ID = 4; const ENIG_CREATED = 5; const ENIG_EXPIRY = 6; const ENIG_UID_ID = 7; const ENIG_OWNERTRUST = 8; const ENIG_USER_ID = 9; const ENIG_SIG_TYPE = 10; const ENIG_KEY_USE_FOR = 11; const ENIG_KEY_EXPIRED = "e"; const ENIG_KEY_REVOKED = "r"; const ENIG_KEY_INVALID = "i"; const ENIG_KEY_DISABLED = "d"; const ENIG_KEY_NOT_VALID = ENIG_KEY_EXPIRED + ENIG_KEY_REVOKED + ENIG_KEY_INVALID + ENIG_KEY_DISABLED; // GUI List: The corresponding image to set the "active" flag / checkbox const ENIG_IMG_NOT_SELECTED = "chrome://enigmail/content/check0.png"; const ENIG_IMG_SELECTED = "chrome://enigmail/content/check1.png"; const ENIG_IMG_DISABLED = "chrome://enigmail/content/check2.png"; // Interfaces const nsIEnigmail = ENIG_I.nsIEnigmail; // Encryption flags if (nsIEnigmail) { const ENIG_SIGN = nsIEnigmail.SEND_SIGNED; const ENIG_ENCRYPT = nsIEnigmail.SEND_ENCRYPTED; const ENIG_ENCRYPT_OR_SIGN = ENIG_ENCRYPT | ENIG_SIGN; } // UsePGPMimeOption values const PGP_MIME_NEVER = 0; const PGP_MIME_POSSIBLE = 1; const PGP_MIME_ALWAYS = 2; const ENIG_POSSIBLE_PGPMIME = EnigmailConstants.POSSIBLE_PGPMIME; const ENIG_PGP_DESKTOP_ATT = -2082; var gUsePGPMimeOptionList = ["usePGPMimeNever", "usePGPMimePossible", "usePGPMimeAlways" ]; // sending options: var gEnigEncryptionModel = ["encryptionModelConvenient", "encryptionModelManually" ]; var gEnigAcceptedKeys = ["acceptedKeysValid", "acceptedKeysAll" ]; var gEnigAutoSendEncrypted = ["autoSendEncryptedNever", "autoSendEncryptedIfKeys" ]; var gEnigConfirmBeforeSending = ["confirmBeforeSendingNever", "confirmBeforeSendingAlways", "confirmBeforeSendingIfEncrypted", "confirmBeforeSendingIfNotEncrypted", "confirmBeforeSendingIfRules" ]; const ENIG_BUTTON_POS_0 = 1; const ENIG_BUTTON_POS_1 = 1 << 8; const ENIG_BUTTON_POS_2 = 1 << 16; const ENIG_BUTTON_TITLE_IS_STRING = 127; const ENIG_HEADERMODE_KEYID = 0x01; const ENIG_HEADERMODE_URL = 0x10; function EnigGetFrame(win, frameName) { return EnigmailWindows.getFrame(win, frameName); } // Initializes enigmailCommon function EnigInitCommon(id) { EnigmailLog.DEBUG("enigmailCommon.js: EnigInitCommon: id=" + id + "\n"); gEnigPromptSvc = enigGetService("@mozilla.org/embedcomp/prompt-service;1", "nsIPromptService"); } function GetEnigmailSvc() { if (!gEnigmailSvc) gEnigmailSvc = EnigmailCore.getService(window); return gEnigmailSvc; } // maxBytes == -1 => read everything function EnigReadURLContents(url, maxBytes) { EnigmailLog.DEBUG("enigmailCommon.js: EnigReadURLContents: url=" + url + ", " + maxBytes + "\n"); var ioServ = enigGetService(ENIG_IOSERVICE_CONTRACTID, "nsIIOService"); if (!ioServ) throw Components.results.NS_ERROR_FAILURE; var fileChannel = EnigmailStreams.createChannel(url); var rawInStream = fileChannel.open(); var inStream = ENIG_C[ENIG_BINARYINPUTSTREAM_CONTRACTID].createInstance(ENIG_I.nsIBinaryInputStream); inStream.setInputStream(rawInStream); var available = inStream.available(); if ((maxBytes < 0) || (maxBytes > available)) maxBytes = available; var data = inStream.readBytes(maxBytes); inStream.close(); return data; } // maxBytes == -1 => read whole file function EnigReadFileContents(localFile, maxBytes) { EnigmailLog.DEBUG("enigmailCommon.js: EnigReadFileContents: file=" + localFile.leafName + ", " + maxBytes + "\n"); if (!localFile.exists() || !localFile.isReadable()) throw Components.results.NS_ERROR_FAILURE; var ioServ = enigGetService(ENIG_IOSERVICE_CONTRACTID, "nsIIOService"); if (!ioServ) throw Components.results.NS_ERROR_FAILURE; var fileURI = ioServ.newFileURI(localFile); return EnigReadURLContents(fileURI.asciiSpec, maxBytes); } /////////////////////////////////////////////////////////////////////////////// // write exception information function EnigWriteException(referenceInfo, ex) { EnigmailLog.writeException(referenceInfo, ex); } /////////////////////////////////////////////////////////////////////////////// function EnigAlert(mesg) { return EnigmailDialog.alert(window, mesg); } /** * Displays an alert dialog with 3-4 optional buttons. * checkBoxLabel: if not null, display checkbox with text; the checkbox state is returned in checkedObj * button-Labels: use "&" to indicate access key * use "buttonType:label" or ":buttonType" to indicate special button types * (buttonType is one of cancel, help, extra1, extra2) * return: 0-2: button Number pressed * -1: ESC or close window button pressed * */ function EnigLongAlert(mesg, checkBoxLabel, okLabel, labelButton2, labelButton3, checkedObj) { return EnigmailDialog.longAlert(window, mesg, checkBoxLabel, okLabel, labelButton2, labelButton3, checkedObj); } function EnigAlertPref(mesg, prefText) { return EnigmailDialog.alertPref(window, mesg, prefText); } // Confirmation dialog with OK / Cancel buttons (both customizable) function EnigConfirm(mesg, okLabel, cancelLabel) { return EnigmailDialog.confirmDlg(window, mesg, okLabel, cancelLabel); } function EnigError(mesg) { return gEnigPromptSvc.alert(window, EnigGetString("enigError"), mesg); } function EnigHelpWindow(source) { EnigmailWindows.openHelpWindow(source); } function EnigDisplayRadioPref(prefName, prefValue, optionElementIds) { EnigmailLog.DEBUG("enigmailCommon.js: EnigDisplayRadioPref: " + prefName + ", " + prefValue + "\n"); if (prefValue >= optionElementIds.length) return; var groupElement = document.getElementById("enigmail_" + prefName); var optionElement = document.getElementById(optionElementIds[prefValue]); if (groupElement && optionElement) { groupElement.selectedItem = optionElement; groupElement.value = prefValue; } } function EnigSetRadioPref(prefName, optionElementIds) { EnigmailLog.DEBUG("enigmailCommon.js: EnigSetRadioPref: " + prefName + "\n"); try { var groupElement = document.getElementById("enigmail_" + prefName); if (groupElement) { var optionElement = groupElement.selectedItem; var prefValue = optionElement.value; if (prefValue < optionElementIds.length) { EnigSetPref(prefName, prefValue); groupElement.value = prefValue; } } } catch (ex) {} } function EnigSavePrefs() { return EnigmailPrefs.savePrefs(); } function EnigGetPref(prefName) { return EnigmailPrefs.getPref(prefName); } function EnigGetDefaultPref(prefName) { EnigmailLog.DEBUG("enigmailCommon.js: EnigGetDefaultPref: prefName=" + prefName + "\n"); var prefValue = null; try { EnigmailPrefs.getPrefBranch().lockPref(prefName); prefValue = EnigGetPref(prefName); EnigmailPrefs.getPrefBranch().unlockPref(prefName); } catch (ex) {} return prefValue; } function EnigSetPref(prefName, value) { return EnigmailPrefs.setPref(prefName, value); } function EnigConvertFromUnicode(text, charset) { EnigmailLog.DEBUG("enigmailCommon.js: EnigConvertFromUnicode: " + charset + "\n"); if (!text) return ""; if (!charset) charset = "utf-8"; // Encode plaintext try { var unicodeConv = ENIG_C[ENIG_ISCRIPTABLEUNICODECONVERTER_CONTRACTID].getService(ENIG_I.nsIScriptableUnicodeConverter); unicodeConv.charset = charset; return unicodeConv.ConvertFromUnicode(text); } catch (ex) { EnigmailLog.DEBUG("enigmailCommon.js: EnigConvertFromUnicode: caught an exception\n"); return text; } } function EnigConvertToUnicode(text, charset) { // EnigmailLog.DEBUG("enigmailCommon.js: EnigConvertToUnicode: "+charset+"\n"); if (!text || !charset /*|| (charset.toLowerCase() == "iso-8859-1")*/ ) return text; // Encode plaintext try { var unicodeConv = ENIG_C[ENIG_ISCRIPTABLEUNICODECONVERTER_CONTRACTID].getService(ENIG_I.nsIScriptableUnicodeConverter); unicodeConv.charset = charset; return unicodeConv.ConvertToUnicode(text); } catch (ex) { EnigmailLog.DEBUG("enigmailCommon.js: EnigConvertToUnicode: caught an exception while converting'" + text + "' to " + charset + "\n"); return text; } } function EnigConvertGpgToUnicode(text) { return EnigmailData.convertGpgToUnicode(text); } function EnigFormatFpr(fingerprint) { return EnigmailKey.formatFpr(fingerprint); } ///////////////////////// // Console stuff ///////////////////////// // return the options passed to a window function EnigGetWindowOptions() { var winOptions = []; if (window.location.search) { var optList = window.location.search.substr(1).split(/\&/); for (var i = 0; i < optList.length; i++) { var anOption = optList[i].split(/\=/); winOptions[anOption[0]] = unescape(anOption[1]); } } return winOptions; } function EnigRulesEditor() { EnigmailWindows.openRulesEditor(); } function EngmailCardDetails() { EnigmailWindows.openCardDetails(); } function EnigKeygen() { EnigmailWindows.openKeyGen(); } // retrieves a localized string from the enigmail.properties stringbundle function EnigGetString(aStr) { var argList = []; // unfortunately arguments.shift() doesn't work, so we use a workaround if (arguments.length > 1) for (var i = 1; i < arguments.length; i++) { argList.push(arguments[i]); } return EnigmailLocale.getString(aStr, (arguments.length > 1 ? argList : null)); } //get path for temporary directory (e.g. /tmp, C:\TEMP) function EnigGetTempDir() { return EnigmailFiles.getTempDir(); } // get the OS platform function EnigGetOS() { return EnigmailOS.getOS(); } function EnigGetVersion() { return EnigmailApp.getVersion(); } function EnigFilePicker(title, displayDir, save, defaultExtension, defaultName, filterPairs) { return EnigmailDialog.filePicker(window, title, displayDir, save, defaultExtension, defaultName, filterPairs); } // get keys from keyserver function EnigDownloadKeys(inputObj, resultObj) { return EnigmailWindows.downloadKeys(window, inputObj, resultObj); } // create new PGP Rule function EnigNewRule(emailAddress) { return EnigmailWindows.createNewRule(window, emailAddress); } function EnigGetTrustCode(keyObj) { return EnigmailTrust.getTrustCode(keyObj); } function EnigEditKeyTrust(userIdArr, keyIdArr) { return EnigmailWindows.editKeyTrust(window, userIdArr, keyIdArr); } function EnigEditKeyExpiry(userIdArr, keyIdArr) { return EnigmailWindows.editKeyExpiry(window, userIdArr, keyIdArr); } function EnigDisplayKeyDetails(keyId, refresh) { return EnigmailWindows.openKeyDetails(window, keyId, refresh); } function EnigSignKey(userId, keyId) { return EnigmailWindows.signKey(window, userId, keyId); } function EnigChangeKeyPwd(keyId, userId) { // gpg-agent used: gpg-agent will handle everything EnigmailKeyEditor.changePassphrase(window, "0x" + keyId, "", "", function _changePwdCb(exitCode, errorMsg) { if (exitCode !== 0) { EnigAlert(EnigGetString("changePassFailed") + "\n\n" + errorMsg); } }); } function EnigRevokeKey(keyId, userId, callbackFunc) { var enigmailSvc = GetEnigmailSvc(); if (!enigmailSvc) return false; var userDesc = "0x" + keyId + " - " + userId; if (!EnigConfirm(EnigGetString("revokeKeyQuestion", userDesc), EnigGetString("keyMan.button.revokeKey"))) return false; var tmpDir = EnigGetTempDir(); var revFile; try { revFile = ENIG_C[ENIG_LOCAL_FILE_CONTRACTID].createInstance(EnigGetLocalFileApi()); revFile.initWithPath(tmpDir); if (!(revFile.isDirectory() && revFile.isWritable())) { EnigAlert(EnigGetString("noTempDir")); return false; } } catch (ex) {} revFile.append("revkey.asc"); EnigmailKeyEditor.genRevokeCert(window, "0x" + keyId, revFile, "0", "", function _revokeCertCb(exitCode, errorMsg) { if (exitCode !== 0) { revFile.remove(false); EnigAlert(EnigGetString("revokeKeyFailed") + "\n\n" + errorMsg); return; } var errorMsgObj = {}; var keyList = {}; var r = EnigmailKeyRing.importKeyFromFile(revFile, errorMsgObj, keyList); revFile.remove(false); if (r !== 0) { EnigAlert(EnigGetString("revokeKeyFailed") + "\n\n" + EnigConvertGpgToUnicode(errorMsgObj.value)); } else { EnigAlert(EnigGetString("revokeKeyOk")); } if (callbackFunc) { callbackFunc(r === 0); } }); return true; } function EnigGetLocalFileApi() { return Components.interfaces.nsIFile; } function EnigShowPhoto(keyId, userId, photoNumber) { EnigmailWindows.showPhoto(window, keyId, userId, photoNumber); } function EnigGetFilePath(nsFileObj) { return EnigmailFiles.getFilePath(nsFileObj); } function EnigCreateRevokeCert(keyId, userId, callbackFunc) { var defaultFileName = userId.replace(/[<\>]/g, ""); defaultFileName += " (0x" + keyId + ") rev.asc"; var outFile = EnigFilePicker(EnigGetString("saveRevokeCertAs"), "", true, "*.asc", defaultFileName, [EnigGetString("asciiArmorFile"), "*.asc"]); if (!outFile) return -1; var enigmailSvc = GetEnigmailSvc(); if (!enigmailSvc) return -1; EnigmailKeyEditor.genRevokeCert(window, "0x" + keyId, outFile, "1", "", function _revokeCertCb(exitCode, errorMsg) { if (exitCode !== 0) { EnigAlert(EnigGetString("revokeCertFailed") + "\n\n" + errorMsg); } else { EnigAlert(EnigGetString("revokeCertOK")); } if (callbackFunc) callbackFunc(exitCode === 0); }); return 0; } // return the label of trust for a given trust code function EnigGetTrustLabel(trustCode) { return EnigmailTrust.getTrustLabel(trustCode); } function EnigGetDateTime(dateNum, withDate, withTime) { return EnigmailTime.getDateTime(dateNum, withDate, withTime); } function enigCreateInstance(aURL, aInterface) { return ENIG_C[aURL].createInstance(ENIG_I[aInterface]); } function enigGetService(aURL, aInterface) { // determine how 'aInterface' is passed and handle accordingly switch (typeof(aInterface)) { case "object": return ENIG_C[aURL].getService(aInterface); case "string": return ENIG_C[aURL].getService(ENIG_I[aInterface]); default: return ENIG_C[aURL].getService(); } } function EnigCollapseAdvanced(obj, attribute, dummy) { return EnigmailFuncs.collapseAdvanced(obj, attribute, dummy); } /** * EnigOpenUrlExternally * * forces a uri to be loaded in an external browser * * @uri nsIUri object */ function EnigOpenUrlExternally(uri) { let eps = ENIG_C["@mozilla.org/uriloader/external-protocol-service;1"]. getService(ENIG_I.nsIExternalProtocolService); eps.loadUrl(uri, null); } function EnigOpenURL(event, hrefObj) { var xulAppinfo = ENIG_C["@mozilla.org/xre/app-info;1"].getService(ENIG_I.nsIXULAppInfo); if (xulAppinfo.ID == ENIG_SEAMONKEY_ID) return; try { var ioservice = ENIG_C["@mozilla.org/network/io-service;1"]. getService(ENIG_I.nsIIOService); var iUri = ioservice.newURI(hrefObj.href, null, null); EnigOpenUrlExternally(iUri); event.preventDefault(); event.stopPropagation(); } catch (ex) {} } function EnigGetHttpUri(aEvent) { function hRefForClickEvent(aEvent, aDontCheckInputElement) { var href; var isKeyCommand = (aEvent.type == "command"); var target = isKeyCommand ? document.commandDispatcher.focusedElement : aEvent.target; if (target instanceof HTMLAnchorElement || target instanceof HTMLAreaElement || target instanceof HTMLLinkElement) { if (target.hasAttribute("href")) href = target.href; } else if (!aDontCheckInputElement && target instanceof HTMLInputElement) { if (target.form && target.form.action) href = target.form.action; } else { // we may be nested inside of a link node var linkNode = aEvent.originalTarget; while (linkNode && !(linkNode instanceof HTMLAnchorElement)) linkNode = linkNode.parentNode; if (linkNode) href = linkNode.href; } return href; } // getHttpUri main function let href = hRefForClickEvent(aEvent); if (!href) return null; EnigmailLog.DEBUG("enigmailAbout.js: interpretHtmlClick: href='" + href + "'\n"); var ioServ = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService); var uri = ioServ.newURI(href, null, null); if (Components.classes["@mozilla.org/uriloader/external-protocol-service;1"] .getService(Components.interfaces.nsIExternalProtocolService) .isExposedProtocol(uri.scheme) && (uri.schemeIs("http") || uri.schemeIs("https"))) return uri; return null; } /** * GUI List: Set the "active" flag and the corresponding image */ function EnigSetActive(element, status) { if (status >= 0) { element.setAttribute("active", status.toString()); } switch (status) { case 0: element.setAttribute("src", ENIG_IMG_NOT_SELECTED); break; case 1: element.setAttribute("src", ENIG_IMG_SELECTED); break; case 2: element.setAttribute("src", ENIG_IMG_DISABLED); break; default: element.setAttribute("active", -1); } } /** * Receive a GUI List and remove all entries * * @param XML-DOM (it will be changed!) */ function EnigCleanGuiList(guiList) { while (guiList.firstChild) { guiList.removeChild(guiList.firstChild); } } /** * create a new treecell element * * @param String label of the cell * * @return treecell node */ function createCell(label) { var cell = document.createElement("treecell"); cell.setAttribute("label", label); return cell; } /** * Process the output of GPG and return the key details * * @param String Values separated by colons and linebreaks * * @return Object with the following keys: * gUserId: Main user ID * calcTrust, * ownerTrust, * fingerprint, * showPhoto, * uidList: List of Pseudonyms and E-Mail-Addresses, * subkeyList: List of Subkeys */ function EnigGetKeyDetails(sigListStr) { var gUserId; var calcTrust; var ownerTrust; var fingerprint; var creationDate; var expiryDate; var uidList = []; var subkeyList = []; var showPhoto = false; var sigList = sigListStr.split(/[\n\r]+/); for (var i = 0; i < sigList.length; i++) { var aLine = sigList[i].split(/:/); switch (aLine[0]) { case "pub": gUserId = EnigConvertGpgToUnicode(aLine[9]); calcTrust = aLine[1]; if (aLine[11].indexOf("D") >= 0) { calcTrust = "d"; } ownerTrust = aLine[8]; creationDate = EnigmailTime.getDateTime(aLine[5], true, false); expiryDate = EnigmailTime.getDateTime(aLine[6], true, false); subkeyList.push(aLine); if (!gUserId) { gUserId = EnigConvertGpgToUnicode(aLine[9]); } else if (uidList !== false) { uidList.push(aLine); } break; case "uid": if (!gUserId) { gUserId = EnigConvertGpgToUnicode(aLine[9]); } else if (uidList !== false) { uidList.push(aLine); } break; case "uat": // @TODO document what that means if (aLine[9].search("1 ") === 0) { showPhoto = true; } break; case "sub": subkeyList.push(aLine); break; case "fpr": if (!fingerprint) { fingerprint = aLine[9]; } break; } } var keyDetails = { gUserId: gUserId, calcTrust: calcTrust, ownerTrust: ownerTrust, fingerprint: fingerprint, showPhoto: showPhoto, uidList: uidList, creationDate: creationDate, expiryDate: expiryDate, subkeyList: subkeyList }; return keyDetails; } diff --git a/util/genxpi b/util/genxpi index 7097bcec..876a7935 100755 --- a/util/genxpi +++ b/util/genxpi @@ -1,165 +1,164 @@ #!/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/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/passwordCheck.jsm \ modules/passwords.jsm \ modules/pEp.jsm \ modules/pEpAdapter.jsm \ modules/pEpDecrypt.jsm \ modules/pEpFilter.jsm \ modules/pEpListener.jsm \ modules/pEpKeySync.jsm \ modules/pEpMessageHist.jsm \ modules/pipeConsole.jsm \ modules/prefs.jsm \ - modules/promise.jsm \ modules/rng.jsm \ modules/rules.jsm \ modules/send.jsm \ modules/socks5Proxy.jsm \ modules/stdlib/send.jsm \ modules/stdlib/compose.jsm \ modules/stdlib/misc.jsm \ modules/stdlib/msgHdrUtils.jsm \ 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/windows.jsm \ modules/wksMimeHandler.jsm \ modules/zbase32.jsm \ wrappers/gpg-agent-wrapper \ chrome/${xpiModule}.jar \ chrome.manifest \ install.rdf