diff --git a/Dockerfile b/Dockerfile index 8b39af3..b63caef 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,8 @@ FROM alpine:latest -RUN apk add zip +RUN apk add --update zip +RUN apk add --update make WORKDIR /usr/src/app -COPY chrome . +COPY . . -CMD ["zip", "-r", "build/p4t.xpi", "."] \ No newline at end of file +CMD ["make", "clean", "xpi"] \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..60eb4ea --- /dev/null +++ b/Makefile @@ -0,0 +1,6 @@ +xpi: + cd chrome; zip -r p4t.xpi . ; cd .. + mv chrome/p4t.xpi build +clean: + rm build/p4t.xpi +all:xpi \ No newline at end of file diff --git a/chrome/bootstrap.js b/chrome/bootstrap.js index a264662..fc5005e 100644 --- a/chrome/bootstrap.js +++ b/chrome/bootstrap.js @@ -1,16 +1,85 @@ //TODO: Implement for restartless app https://developer.mozilla.org/en-US/docs/Archive/Add-ons/How_to_convert_an_overlay_extension_to_restartless#Step_9_bootstrap.js +/* globals Components, Services */ +/* exported install, uninstall, startup, shutdown */ +Components.utils.import('resource://gre/modules/Services.jsm'); + function install() { console.log("bootstrap.js: Install"); } function startup() { console.log("bootstrap.js: Startup"); + windowObserver.init(); } function shutdown() { console.log("bootstrap.js: Shutdown"); + windowObserver.destroy(); } function uninstall() { console.log("bootstrap.js: Uninstall"); -} \ No newline at end of file +} + +let windowObserver = { + init: function() { + console.log("INIT"); + this.enumerate('mail:3pane', this.paint); + this.enumerate('mail:messageWindow', this.paint); + this.enumerate('msgcompose', this.paint); + this.enumerate('mailnews:mailviewlist', this.paint); + this.enumerate('msghdr', this.paint); + Services.ww.registerNotification(this); + }, + destroy: function() { + this.enumerate('mail:3pane', this.unpaint); + this.enumerate('msgcompose', this.unpaint); + Services.ww.unregisterNotification(this); + }, + enumerate: function(windowType, callback) { + let windowEnum = Services.wm.getEnumerator(windowType); + while (windowEnum.hasMoreElements()) { + callback.call(this, windowEnum.getNext()); + } + }, + observe: function(subject) { + subject.addEventListener('load', function() { + windowObserver.paint(subject); + }, false); + }, + paint: function(win) { + console.log("PAINT", win.location.href); + let script; + switch (win.location.href) { + case 'chrome://messenger/content/messenger.xul': + script = 'chrome://p4t/content/pepmessenger.js'; + break; + + case 'chrome://messenger/content/messengercompose/messengercompose.xul': + script = 'chrome://p4t/content/pepmessengercompose.js'; + break; + + case 'chrome://messenger/content/messengercompose/msgHdrViewOverlay.xul': + script = 'chrome://p4t/content/pepmsghdrview.js'; + break; + + default: + return; + } + Services.scriptloader.loadSubScript(script, win); + }, + unpaint: function(win) { + console.log("UN PAINT"); + switch (win.location.href) { + case 'chrome://messenger/content/messenger.xul': + win.pEpMessenger.destroy(); + break; + case 'chrome://messenger/content/messengercompose/messengercompose.xul': + + break; + case 'chrome://messenger/content/messengercompose/msgHdrViewOverlay.xul': + win.pEpMsgHdrView.destroy(); + break; + } + } +}; diff --git a/chrome/content/modules/pEp.js b/chrome/content/modules/pEp.js index 94e2116..647d9ed 100644 --- a/chrome/content/modules/pEp.js +++ b/chrome/content/modules/pEp.js @@ -90,12 +90,24 @@ class pEp { return result; } - checkPrivacy() { + async emailRatings(email) { + return await this.getIdentityRating(email); + } + async getIdentityRating(email) { + let identity = new pEp.Identity(email, "", "", ""); + return await this.adapter.getIdentityRating(identity); + } + + async getOngoingRating(from, to = []) { + let msgId = PEP_PREFIX + String(this.requestId++); + let message = new pEp.Message(msgId, "test", "test", from, to); + + return await this.adapter.getOngoingRating(message); } async getPrivacyColorFromRating(rating) { - return await this.adapter.color_from_rating(rating); + return await this.adapter.colorFromRating(rating); } showTrustwords() { @@ -143,7 +155,6 @@ class pEp { pEp.Identity = class { constructor(address, id = "", name = "anonymous", fingerprint) { - if (typeof address === "string") { this.user_id = id; this.username = name; @@ -189,11 +200,22 @@ pEp.Message = class { this.shortmsg = short; this.longmsg = long; - this.from = new pEp.Identity(from); - this.to = to.reduce((addresses, addr) => { - addresses.push(new pEp.Identity(addr)); - return addresses; - }, []); + if(pEp.Identity.prototype.isPrototypeOf(from)) { + this.from = from; + } + else { + this.from = new pEp.Identity(from); + } + + if(pEp.Identity.prototype.isPrototypeOf(to[0])) { + this.to = to; + } + else { + this.to = to.reduce((addresses, addr) => { + addresses.push(new pEp.Identity(addr)); + return addresses; + }, []); + } this.dir = dir; this.enc_format = enc_format; diff --git a/chrome/content/modules/pEpAdapter.js b/chrome/content/modules/pEpAdapter.js index 11e7d1f..d0c045a 100644 --- a/chrome/content/modules/pEpAdapter.js +++ b/chrome/content/modules/pEpAdapter.js @@ -4,6 +4,9 @@ const SERVER_TYPE_CALL_FUNC = "callFunction"; const API_METHOD_SERVER_VERSION = "serverVersion"; const API_METHOD_DECRYPT = "decrypt_message"; const API_METHOD_ENCRYPT = "encrypt_message"; +const API_METHOD_GET_OUTGOING_RATING = "outgoing_message_rating"; +const API_METHOD_IDENTITY_RATING = "identity_rating"; + const DIR_INCOMING = 0; const DIR_OUTGOING = 1; const ENC_FORMAT_PIECES = 1; @@ -33,6 +36,7 @@ class pEpAdapter { this.log = log; this.server = server; this.requestId = 0; + this.ratings = []; } async serverVersion() { @@ -49,6 +53,20 @@ class pEpAdapter { }); } + async getOngoingRating(message) { + this.log("pEpAdapter.js: getOngoingRating()"); + let params = [message, "0"]; + return await this.server.callPepAdapter( + SERVER_TYPE_CALL_FUNC, + API_METHOD_GET_OUTGOING_RATING, + params + ).then((response) => { + return response.result.outParams[0].rating; + }).catch((err) => { + this.log(err); + }); + } + async decrypt_message(message) { this.log("pEpAdapter.js: decrypt_message()", message); let params = [ @@ -96,11 +114,19 @@ class pEpAdapter { case(PEP_RESPONSE_OK): case(PEP_RESPONSE_DECRYPTED): decryptedMessage = response.result.outParams[3]; + + if (decryptedMessage === null) { + decryptedMessage = response.result.outParams[1]; + } else { + decryptedMessage.rating = response.result.outParams[1].rating; + } + this.log("callPepAdapter: 'decrypt' returned with success: ", response); break; case(PEP_UNENCRYPTED): decryptedMessage = message; + decryptedMessage.rating = RATING_COLOR_NO_COLOR; this.log("callPepAdapter: 'decrypt' returned with UNENCRYPTED status: ", response); break; @@ -114,23 +140,6 @@ class pEpAdapter { }); } - async decrypt(subject, body, sender = "*", to, cc, replyTo, enc_format = ENC_FORMAT_PEP) { - this.log("pEpAdapter.js: decrypt()"); - - let message = new pEp.Message(null, subject, body, sender, to, DIR_INCOMING, enc_format); - - if (cc) { - message.cc = cc; - } - if (replyTo) { - message.reply_to = replyTo; - } - - message.addAttachment(btoa(body), body.length); - - this.decrypt_message(message); - } - async encrypt_message(message) { let params = [ message, @@ -192,34 +201,57 @@ class pEpAdapter { }); } - async encrypt(subject, body, from, to = [], stringlist = [], encodingFormat = ENC_FORMAT_PEP) { + async colorFromRating(rating) { - let msgId = PEP_PREFIX + String(this.requestId); - let message = new pEp.Message(msgId, subject, body, from, to, DIR_OUTGOING, encodingFormat); - return this.encrypt_message(message); - - } - - async color_from_rating(rating) { - - let color = RATING_COLOR_NO_COLOR; if (rating === -2 || rating === 2) { - color = RATING_COLOR_NO_COLOR; + return RATING_COLOR_NO_COLOR; } - else if (rating < 0) { - color = RATING_COLOR_RED; + + if (rating < 0) { + return RATING_COLOR_RED; } - else if (rating < 6) { - color = RATING_COLOR_NO_COLOR; + + if (rating < 6) { + return RATING_COLOR_NO_COLOR; } - else if (rating >= 7) { - color = RATING_COLOR_GREEN; + + if (rating >= 7) { + return RATING_COLOR_GREEN; } - else { - color = RATING_COLOR_YELLOW; + + return RATING_COLOR_YELLOW; + } + + async getIdentityRating(identity) { + this.log("pEpAdapter.js: getIdentityRating()"); + // new Proxy(this.server.callPepAdapter, { + // apply(target, thisArg, args) { + // if(this.ratings[email]) { + // return this.ratings[email]; + // } + // let rating = Reflect.apply(target, thisArg, args); + // this.ratings[email] = rating; + // return rating; + // } + // } + // ); + + let cachedMail = this.ratings[identity.address]; + if(cachedMail) { + return cachedMail; } - return color; + let params = [ identity, ["OP"] ]; + return await this.server.callPepAdapter( + SERVER_TYPE_CALL_FUNC, + API_METHOD_IDENTITY_RATING, + params + ).then((response) => { + return response.result.return; + }).catch((err) => { + this.log(err); + }); + } } diff --git a/chrome/content/pEp.css b/chrome/content/pEp.css new file mode 100644 index 0000000..8394b92 --- /dev/null +++ b/chrome/content/pEp.css @@ -0,0 +1,31 @@ +.green { + background-color: #59b753; + border-color: #468f41; +} + +.yellow { + background-color: #e3d02e; + border-color: #c7b729; + color: black; +} + +.red { + background-color: #fc625d; + border-color: #c35855; +} + +.no-color { + background-color: #dddddd; + border-color: #959595; + color: black; +} + +.privacy-bar { + height: 25px; + color: white; + position: fixed; + bottom:15px; + width: 100%; + margin: auto; + padding-left: 10px; +} \ No newline at end of file diff --git a/chrome/content/pepmessenger.js b/chrome/content/pepmessenger.js index 1c28900..ae50979 100644 --- a/chrome/content/pepmessenger.js +++ b/chrome/content/pepmessenger.js @@ -1,14 +1,202 @@ -const Cu = Components.utils; -Cu.import("chrome://p4t/content/modules/pEp.js"); +Cu.import("chrome://p4t/content/p4tb.js"); -window.addEventListener("load", function(e) { - let privacyPanel = document.getElementById("pEp-statusbar-privacy-status"); +let pEpMessenger = { + init: () => { + console.log("pEpMessenger: init()"); + }, + destroy: () => { + console.log("pEpMessenger: destroy()"); + }, - pEpController.getVersion().then(value => { - privacyPanel.value = `p≡p running (v${value})`; - }). - catch(e => { - privacyPanel.value = `p≡p not running`; - }); + getCurrentMessageURI: () => { + if (gFolderDisplay.selectedMessages.length === 1) { + return gFolderDisplay.selectedMessageUris[0]; + } + return null; + }, -}, false); + readMailFromUri: (msgUri) => { + let msgWindow = Cc["@mozilla.org/messenger/msgwindow;1"].createInstance(); + msgWindow = msgWindow.QueryInterface(Ci.nsIMsgWindow); + + let msgStream = Cc["@mozilla.org/network/sync-stream-listener;1"].createInstance(); + msgStream = msgStream.QueryInterface(Ci.nsIInputStream); + + let messenger = Cc["@mozilla.org/messenger;1"].getService(Ci.nsIMessenger); + let msgService = messenger.messageServiceFromURI(msgUri); + + let scriptInputStream = Cc["@mozilla.org/scriptableinputstream;1"].createInstance(); + scriptInputStream = scriptInputStream.QueryInterface(Ci.nsIScriptableInputStream); + + scriptInputStream.init(msgStream); + + try { + msgService.streamMessage(msgUri, // uri of message to stream + msgStream, // a stream listener listening to the message + msgWindow, // a nsIMsgWindow for progress and status feedback + null, // a nsIUrlListener that is notified when url starts and stops + false, // it will create a stream converter from message rfc2822 to + null // Header added to the URI. e.g., header=filter + ); + } catch (ex) { + console.debug("Error while reading mail from server", ex); + } + + // Get only the PGP message + let keyFound = false; + let content = ""; + let str = ""; + + const PGP_MESSAGE_HEADER = "-----BEGIN PGP MESSAGE-----"; + const PGP_MESSAGE_FOOTER = "-----END PGP MESSAGE-----"; + const INPUTSTREAM_READING_SIZE = 512; + + while (scriptInputStream.available()) { + str = scriptInputStream.read(INPUTSTREAM_READING_SIZE); + + let pgpStartIndex = str.indexOf(PGP_MESSAGE_HEADER); + let pgpEndIndex = str.indexOf(PGP_MESSAGE_FOOTER); + + if (pgpStartIndex >= 0) { + keyFound = true; + str = str.substring(pgpStartIndex); + } else if (pgpEndIndex >= 0) { + content += str.substring(0, pgpEndIndex + PGP_MESSAGE_FOOTER.length); + break; + } + + if (keyFound) { + content += str; + } + } + + return content; + }, + + _parseAddress: (address) => { + let hdr = Cc["@mozilla.org/messenger/headerparser;1"].createInstance(Ci.nsIMsgHeaderParser); + let mails = hdr.parseEncodedHeader(address, "utf-8"); + return mails.map((mail) => { + return { + address: mail.email, + username: mail.name + }; + }) + }, + onLoadListener: (event) => { + console.log("pepmessenger.js: onLoadListener()"); + + let onStartHeadersMessageListener = () => { + let messagePanelFrame = window.GetMessagePaneFrame(); + console.log("pepmessenger.js: onLoadMsgPanelFrameListener(), frame: ", messagePanelFrame); + if (messagePanelFrame != null) { + console.log("pepmessenger.js: onLoadListener() added listener"); + messagePanelFrame.addEventListener("load", onLoadMsgPanelFrameListener, false); + } + }; + let onEndHeadersMessageListener = () => { + }; + + gMessageListeners.push({ + onStartHeaders: onStartHeadersMessageListener, + onEndHeaders: onEndHeadersMessageListener + }); + + onStartHeadersMessageListener(); + onEndHeadersMessageListener(); + }, + onLoadMsgPanelFrameListener: (event) => { + + /* global currentHeaderData: false, gViewAllHeaders: false, gExpandedHeaderList: false, goDoCommand: false, HandleSelectedAttachments: false */ + function updateSubject(newSubject) { + let subjectBox = document.getElementById("expandedsubjectBox"); + subjectBox.headerValue = newSubject; + } + + function updateBody(newBody) { + //TODO Not implemented yet + let panel = window.GetMessagePaneFrame(); + let bodyElement = panel.document.getElementsByTagName("body")[0]; + let node = bodyElement.firstChild; + while (node) { + if (node.nodeName === "DIV" || node.nodeName === "PRE") { + if (node.textContent.indexOf("-----BEGIN PGP")) { + node.innerHTML = newBody; + return; + } + } + node = node.nextSibling; + } + } + + function updatePrivacy(privacyStatus) { + let privacyPanel = document.getElementById("pEp-msghdr-privacy-label"); + + switch (privacyStatus) { + case "red": + case "green": + case "yellow": + break; + default: + privacyStatus = "no-color"; + break; + } + privacyPanel.value = privacyStatus; + privacyPanel.class = privacyStatus; + } + + + let messageString; + console.log("pepmessenger.js: onLoadMsgPanelFrameListener()", currentHeaderData); + + // Clean previous privacy labels + updatePrivacy("no-color"); + + // read email from tb + let currentUri = this.getCurrentMessageURI(); + + if (currentUri !== null) { + + try { + messageString = this.readMailFromUri(currentUri); + console.log("==============="); + console.log(messageString); + console.log("==============="); + } + catch (e) { + console.debug("Errors while reading message from uri", e); + } + + if (messageString === "") { + console.debug("Nothing to decrypt"); + return; + } + + let sender = this._parseAddress(currentHeaderData.from.headerValue)[0]; + let to = this._parseAddress(currentHeaderData.to.headerValue); + + pEpController.decryptMail(messageString, sender, to, currentHeaderData["message-id"].headerValue).then((message) => { + if (message === null) { + console.log("Message not decrypted"); + return; + } + // replace encrypted mail with decrypted from pEpAdapter + updateSubject(message.shortmsg); + updateBody(message.longmsg); + + pEpController.getPrivacyColorFromRating(message.rating).then( + (rating) => { + updatePrivacy(rating); + } + ); + }).catch((err) => { + console.log(err) + + }); + + } + + } +}; + +pEpMessenger.init(); \ No newline at end of file diff --git a/chrome/content/pepmessenger.xul b/chrome/content/pepmessenger.xul index 49038fb..36e910e 100644 --- a/chrome/content/pepmessenger.xul +++ b/chrome/content/pepmessenger.xul @@ -5,19 +5,11 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. --> - + + - -