diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md
index 5aacd7a..f53ee7d 100644
--- a/DEVELOPMENT.md
+++ b/DEVELOPMENT.md
@@ -16,7 +16,7 @@ When we want to change the plugin preferences we can do so by
modifying three areas in the code:
- `options.xul` the markup, here you define the identifier for the option that will be used also elsewhere
-- `prefsFactory.js` update getter and setter functions corresponding to the option
+- `prefsFactory.js` update getter and setter functions corresponding to the option and the default value
- `options.js` update `addAll`, the window load handler, the dialogAccept handler
## Compat and Prefs
diff --git a/chrome/content/modules/pEp.js b/chrome/content/modules/pEp.js
index 668f5de..a0a0f62 100644
--- a/chrome/content/modules/pEp.js
+++ b/chrome/content/modules/pEp.js
@@ -29,6 +29,10 @@ if (typeof btoa === "undefined" && typeof require == "function") {
var btoa = require('btoa');
}
+function deepCopy(obj) {
+ return obj && JSON.parse(JSON.stringify(obj));
+}
+
class pEp {
constructor(env, logger, adapter, files, os) {
if (pEp.exists) return pEp.instance;
@@ -228,9 +232,6 @@ class pEp {
}
static cloneMessage(source) {
- function deepCopy(obj) {
- return obj && JSON.parse(JSON.stringify(obj));
- }
let clone = new pEp.Message(source.id,
source.shortmsg,
source.longmsg,
@@ -244,6 +245,40 @@ class pEp {
source.bcc && clone.setBcc(source.bcc);
return clone;
}
+
+ setPrefs(prefs) {
+ this.prefs = prefs;
+ }
+
+ disclaimerStep(message) {
+ let mode = this.prefs.getDisclaimerMode();
+ let disclaimer = this.prefs.getDisclaimer();
+ function withDisclaimer(message) {
+ let modified = deepCopy(message);
+ modified.longmsg += "\r\n" + disclaimer;
+ return modified;
+ }
+ if (mode === "disclaimer-encrypted") {
+ return this
+ .getOngoingRating(
+ message.from,
+ message.to,
+ message.cc,
+ message.bcc)
+ .then((rating) => {
+ if (rating > 5) {
+ return withDisclaimer(message);
+ } else {
+ return message;
+ }
+ })
+ .catch(() => message );
+ } else if (mode === "disclaimer-all") {
+ return Promise.resolve(withDisclaimer(message));
+ } else {
+ return Promise.resolve(message);
+ }
+ }
}
pEp.Identity = class {
diff --git a/chrome/content/options.css b/chrome/content/options.css
index d21f68a..aad014c 100644
--- a/chrome/content/options.css
+++ b/chrome/content/options.css
@@ -6,6 +6,9 @@ tabpanel {
h3 {
padding-left: 8px; /* to match any groupbox below */
}
+select, textarea {
+ margin: 0.5em 0;
+}
.section {
margin-top: 0.1em;
margin-bottom: 0.1em;
@@ -26,3 +29,6 @@ h3 {
padding: 0;
margin: 0;
}
+.hidden {
+ display: none;
+}
diff --git a/chrome/content/options.js b/chrome/content/options.js
index ce167f9..4ccf47f 100644
--- a/chrome/content/options.js
+++ b/chrome/content/options.js
@@ -1,10 +1,16 @@
// see developer.thunderbird.net/add-ons/updates/tb68
+// i got an exception when trying to access preferences before this call
Preferences.addAll([
{ id: "extensions.p4tb.storeAllSecurely", type: "bool" },
{ id: "extensions.p4tb.warnUnencrypted", type: "bool" },
{ id: "extensions.p4tb.unprotectedSubjects", type: "bool" },
{ id: "extensions.p4tb.storeProtectedOptions", type: "bool" },
{ id: "extensions.p4tb.pEpSync", type: "bool" },
+ { id: "extensions.p4tb.disclaimerMode", type: "string" },
+ { id: "extensions.p4tb.disclaimer", type: "string" },
+ // `pristine` is used only by us in order to provide defaults, see
+ // `options.js`
+ { id: "extensions.p4tb.initialised", type: "bool" },
// examples with different types
//{ id: "extensions.nameOfAddon.pref2", type: "string" },
//{ id: "extensions.nameOfAddon.pref3", type: "int" },
@@ -15,17 +21,31 @@ const Compat = ChromeUtils.import("chrome://p4t/content/compatFactory.js")
const Prefs = ChromeUtils.import("chrome://p4t/content/prefsFactory.js")
.prefsFactory(Compat);
+
+function update() {
+ let hidden = document.querySelector('#disclaimerMode').value === "disclaimer-none"
+ document.querySelector('#disclaimer').classList.toggle("hidden", hidden);
+}
+
window.addEventListener('load', function () {
+ Prefs.maybeSetDefaults();
document.getElementById('storeAllSecurely').checked = Prefs.isStoreAllSecurely();
document.getElementById('warnUnencrypted').checked = Prefs.isWarnUnencrypted();
+ document.getElementById('disclaimerMode').value = Prefs.getDisclaimerMode();
+ document.getElementById('disclaimer').value = Prefs.getDisclaimer();
+
let acceptButton = document.querySelector('button#accept');
let cancelButton = document.querySelector('button#cancel');
acceptButton.addEventListener('click', function () {
Prefs.setIsStoreAllSecurely(document.getElementById('storeAllSecurely').checked);
Prefs.setWarnUnencrypted(document.getElementById('warnUnencrypted').checked);
+ Prefs.setDisclaimerMode(document.getElementById('disclaimerMode').value);
+ Prefs.setDisclaimer(document.getElementById('disclaimer').value);
window.close();
});
cancelButton.addEventListener('click', function () {
window.close();
});
+ update();
+ document.addEventListener('change', update);
});
diff --git a/chrome/content/options.xul b/chrome/content/options.xul
index fcf83ef..0a6fb70 100644
--- a/chrome/content/options.xul
+++ b/chrome/content/options.xul
@@ -38,6 +38,27 @@
+
+
+
+
+
+
+ None
+
+
+ All mail
+
+
+ Only encrypted mail
+
+
+
+ Disclaimer text here
+
+
Cancel
diff --git a/chrome/content/pEpMimeEncrypt.js b/chrome/content/pEpMimeEncrypt.js
index 1b37d85..9eaad30 100644
--- a/chrome/content/pEpMimeEncrypt.js
+++ b/chrome/content/pEpMimeEncrypt.js
@@ -343,6 +343,8 @@ PgpMimeEncrypt.prototype = {
console.log(pEpMessage);
console.log("==============");
+ return this.pEpController.disclaimerStep(pEpMessage);
+ }).then((pEpMessage) => {
this.pEpController.encryptMailWithMessage(pEpMessage).then((result) => {
resultObj = result;
diff --git a/chrome/content/pepmessengercompose.js b/chrome/content/pepmessengercompose.js
index 7b43c0b..c375605 100644
--- a/chrome/content/pepmessengercompose.js
+++ b/chrome/content/pepmessengercompose.js
@@ -99,12 +99,13 @@ var pEpComposer = {
// resolved. pEpComposer init is run when the compose window
// is open and privacy will be checked when the message is
// sent. A race condition is unlikely yet possible.
+ pEpController.setPrefs(this.prefs);
TbAbstraction.getCurrentIdentity().setIntAttribute("encryptionpolicy", 2);
},
initPrivacyWarning() {
let id = this.window.arguments[0].originalMsgURI;
- if (typeof id == 'undefined') {
+ if (id === '') {
// these windows have no original, users are starting a
// new conversation
return Promise.reject();
diff --git a/chrome/content/pepmsghdrview.js b/chrome/content/pepmsghdrview.js
index 0f7047b..0761968 100644
--- a/chrome/content/pepmsghdrview.js
+++ b/chrome/content/pepmsghdrview.js
@@ -1,6 +1,10 @@
var pEpController = ChromeUtils.import("chrome://p4t/content/p4tb.js").pEpController;
var Helper = ChromeUtils.import("chrome://p4t/content/TbHelper.js").TbHelper;
var MessageView = ChromeUtils.import("chrome://p4t/content/TbMessageView.js").TbMessageView;
+const Compat = ChromeUtils.import("chrome://p4t/content/compatFactory.js")
+ .compatFactory(window);
+const Prefs = ChromeUtils.import("chrome://p4t/content/prefsFactory.js")
+ .prefsFactory(Compat);
var PEP_COLUMN_NAME = "pEpStatusCol";
diff --git a/chrome/content/prefsFactory.js b/chrome/content/prefsFactory.js
index 5290c99..54cbe4f 100644
--- a/chrome/content/prefsFactory.js
+++ b/chrome/content/prefsFactory.js
@@ -1,3 +1,4 @@
+
function prefsFactory(Compat) {
function newVersion () {
return Services.prefs.getBranch('extensions.p4tb.');
@@ -5,9 +6,10 @@ function prefsFactory(Compat) {
function oldVersion () {
return Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefService)
- .getBranch("extensions.stockwatcher2.");
+ .getBranch("extensions.p4tb.");
}
const prefs = Compat.versionBranch(newVersion, oldVersion);
+
return {
isStoreAllSecurely: () => {
return prefs.getBoolPref('storeAllSecurely');
@@ -20,6 +22,55 @@ function prefsFactory(Compat) {
},
setWarnUnencrypted: (v) => {
return prefs.setBoolPref('warnUnencrypted', v);
+ },
+ setDisclaimer: (v) => {
+ return prefs.setStringPref('disclaimer', v);
+ },
+ getDisclaimer: () => {
+ return prefs.getStringPref('disclaimer');
+ },
+ setDisclaimerMode: (v) => {
+ return prefs.setStringPref('disclaimerMode', v);
+ },
+ getDisclaimerMode: () => {
+ return prefs.getStringPref('disclaimerMode');
+ },
+ maybeSetDefaults: () => {
+ // set up default values to avoid exceptions when we try to access a
+ // preference for the first time
+ if (prefs.getBoolPref("initialised")) {
+ return;
+ } else {
+ let defaults = {
+ bool: {
+ "storeAllSecurely": true,
+ "warnUnencrypted": false,
+ "unprotectedSubjects": false,
+ "storeProtectedOptions": true,
+ "pEpSync": false,
+ "pristine": false // this is just for us to set up defaults
+ },
+ string: {
+ "disclaimerMode": "disclaimer-none",
+ "disclaimer": "Disclaimer text here"
+ }
+ }
+ function ObjectEntries(object) {
+ return Object
+ .keys(object)
+ .map((key) => {
+ return [key, object[key]];
+ });
+ }
+ function setter(type) {
+ return function([key, value]) {
+ prefs["set"+type+"Pref"](key, value);
+ }
+ }
+ ObjectEntries(defaults.bool).map(setter("Bool"));
+ ObjectEntries(defaults.string).map(setter("String"));
+ prefs.setBoolPref("initialised", true);
+ }
}
}
}
diff --git a/manual-and-test.md b/manual-and-test.md
index 0646448..0673962 100644
--- a/manual-and-test.md
+++ b/manual-and-test.md
@@ -30,3 +30,14 @@ your mail server
When you reply or forward a private message to untrusted receivers,
the conversation will continue without encryption. In this case you
will see a warning when this option is set
+
+## Disclaimer
+
+This option is designed to work with the disclaimer functionality
+provided by your mail server. You can choose "Only encrypted mail" in
+order for p≡p to add the disclaimer to encrypted messages that cannot
+be modified by your mail server.
+
+Alternatively you can disable the disclaimer on your server and select
+"All mail" here to use only p≡p's disclaimer.
+
diff --git a/tests/unit/controller.js b/tests/unit/controller.js
index a82f40f..c309a67 100644
--- a/tests/unit/controller.js
+++ b/tests/unit/controller.js
@@ -1,6 +1,7 @@
/* eslint-disable no-console */
let {describe, it, before, beforeEach} = require('mocha');
let chai = require('chai').use(require('chai-as-promised'));
+let sinon = require('sinon');
let getQueue = require('../mock').getQueue;
let getController = require('../boilerplate').getController;
let expect = require('chai').expect
@@ -174,4 +175,50 @@ describe('controller calls', () => {
return result.should.become([]);
});
});
+ describe('appends the disclaimer, P4TB-131', () => {
+ it('to unencrypted messages', () => {
+ controller.setPrefs({
+ getDisclaimer: () => "disclaimer",
+ getDisclaimerMode: () => "disclaimer-all"
+ });
+ let prom = controller.disclaimerStep(messages.unencrypted.simple);
+ return expect(prom).to.eventually.have.property("longmsg", "body\r\ndisclaimer");
+ });
+ it('to encrypted messages', () => {
+ queue.respondWith({"jsonrpc":"2.0","id":21,"result":{"outParams":[{"rating": 6}],"return":{"status":0,"hex":"0 \"PEP_STATUS_OK\""},"errorstack":["(1 elements cleared)"]}});
+ controller.setPrefs({
+ getDisclaimer: () => "disclaimer",
+ getDisclaimerMode: () => "disclaimer-encrypted"
+ });
+ let prom = controller.disclaimerStep(messages.unencrypted.simple);
+ return expect(prom).to.eventually.have.property("longmsg", "body\r\ndisclaimer");
+ });
+ it('encrypted mode with unencrypted message', () => {
+ queue.respondWith({"jsonrpc":"2.0","id":21,"result":{"outParams":[{"rating":3}],"return":{"status":0,"hex":"0 \"PEP_STATUS_OK\""},"errorstack":["(1 elements cleared)"]}});
+ controller.setPrefs({
+ getDisclaimer: () => "disclaimer",
+ getDisclaimerMode: () => "disclaimer-encrypted"
+ });
+ let prom = controller.disclaimerStep(messages.unencrypted.simple);
+ return expect(prom).to.eventually.have.property("longmsg", "body");
+ });
+ it('none mode', () => {
+ controller.setPrefs({
+ getDisclaimer: () => "disclaimer",
+ getDisclaimerMode: () => "disclaimer-none"
+ });
+ let prom = controller.disclaimerStep(messages.unencrypted.simple);
+ return expect(prom).to.eventually.have.property("longmsg", "body");
+ });
+ it('leaves the message untouched when the call to the engine fails', () => {
+ controller.setPrefs({
+ getDisclaimer: () => "disclaimer",
+ getDisclaimerMode: () => "disclaimer-encrypted"
+ });
+ let rating = sinon.stub(controller, "getOngoingRating");
+ rating.returns(Promise.reject());
+ let prom = controller.disclaimerStep(messages.unencrypted.simple);
+ return expect(prom).to.eventually.have.property("longmsg", "body");
+ });
+ });
});