Browse Source

disable for specific accounts, P4TB-73

1.1.101
francesco 3 years ago
parent
commit
a21fac6f42
20 changed files with 623 additions and 140 deletions
  1. +10
    -1
      Makefile
  2. +77
    -37
      chrome/bootstrap.js
  3. +109
    -3
      chrome/content/TbHelper.js
  4. +4
    -3
      chrome/content/TbMessageView.js
  5. +0
    -4
      chrome/content/dialogs/privacy_status.css
  6. +3
    -39
      chrome/content/dialogs/privacy_status.js
  7. +102
    -0
      chrome/content/modules/AccountOptions.js
  8. +72
    -1
      chrome/content/modules/utils.js
  9. +8
    -0
      chrome/content/options.css
  10. +48
    -2
      chrome/content/options.js
  11. +24
    -1
      chrome/content/options.xul
  12. +8
    -0
      chrome/content/pEp.css
  13. +3
    -2
      chrome/content/pEpMimeEncrypt.js
  14. +7
    -1
      chrome/content/pepmessengercompose.js
  15. +5
    -0
      chrome/content/pepmsghdrview.js
  16. +13
    -46
      chrome/content/prefsFactory.js
  17. +17
    -0
      lib.js
  18. +28
    -0
      tests/runtime/pepmessengercompose.js
  19. +35
    -0
      tests/runtime/pepmsghdrview.js
  20. +50
    -0
      tests/unit/accountOptions.js

+ 10
- 1
Makefile View File

@ -3,7 +3,16 @@ xpi:
cd chrome; zip -r p4t.xpi . ; cd ..
mv chrome/p4t.xpi build
runtime:
mkdir -p build
mkdir -p runtime-chrome
cp -R chrome/ runtime-chrome
cp tests/runtime/*.js runtime-chrome/content
cd runtime-chrome; zip -r runtime.xpi . ; cd ..
mv runtime-chrome/runtime.xpi build
rm -r runtime-chrome
clean:
rm build/p4t.xpi
rm build/*
all: xpi

+ 77
- 37
chrome/bootstrap.js View File

@ -17,26 +17,47 @@ function install() {
console.debug("bootstrap.js: Install");
}
function setUpPreferences() {
console.log("DEBUG: Setup dependencies");
let prefs = Services.prefs.getBranch('extensions.p4tb.');
try {
prefs.getBoolPref("initialised");
} catch(err) {
// If cannot access to initialised property, setup defaults
prefs.setBoolPref("initialised", true);
prefs.setBoolPref("storeAllSecurely", true);
prefs.setBoolPref("warnUnencrypted", false);
prefs.setBoolPref("unprotectedSubjects", false);
prefs.setBoolPref("storeProtectedOptions", true);
prefs.setBoolPref("pEpSync", false);
prefs.setStringPref("disclaimerMode", "disclaimer-none");
prefs.setStringPref("disclaimer", "Disclaimer text here");
function maybeSetDefaults (prefs) {
let defaults = {
Bool: {
"storeAllSecurely": true,
"warnUnencrypted": false,
"unprotectedSubjects": false,
"storeProtectedOptions": true,
"pEpSync": false
},
String: {
"disclaimerMode": "disclaimer-none",
"disclaimer": "Disclaimer text here",
"accountOptions": JSON.stringify({})
}
};
iterate(["Bool", "String"], defaults, check);
function iterate (types, defaults, fun) {
types.map(type => {
let options = defaults[type];
if (options) {
Object.entries(options).map(([option, default_]) => {
fun(type, option, default_);
});
} else {
console.log("no type", type, "in defaults", defaults);
}
});
}
function check (type, option, default_) {
let getter = "get"+type+"Pref";
let setter = "set"+type+"Pref";
try {
prefs[getter](option);
} catch(exc) {
if (exc.name == "NS_ERROR_UNEXPECTED") {
prefs[setter](option, default_);
} else {
throw exc;
}
}
}
}
function startup() {
@ -47,7 +68,8 @@ function startup() {
// We check the preferences on every startup instead of just on install
// in case something is wrong or those were deleted
setUpPreferences();
let prefs = Services.prefs.getBranch('extensions.p4tb.');
maybeSetDefaults(prefs);
}
function shutdown() {
@ -59,31 +81,49 @@ function uninstall() {
console.debug("bootstrap.js: Uninstall");
}
let windows = [
'mail:3pane',
'mail:messageWindow',
'msgcompose',
'mailnews:mailviewlist',
'msghdr'
];
function tryWindowApply (fun) {
function arrayFromEnumerator (enumerator) {
let array = [];
while (enumerator.hasMoreElements()) {
array.push(enumerator.getNext());
}
return array;
}
return (windowType) => {
function tryApply (window) {
try {
fun(window);
} catch (exc) {
try {
console.debug("exception", exc, "applying", fun, "to window", window, "of type", windowType);
} catch (exc2) {
console.debug("exception while showing the former exception", exc2);
}
}
}
let enumerator = Services.wm.getEnumerator(windowType);
arrayFromEnumerator(enumerator).map(tryApply);
}
}
let windowObserver = {
init: function() {
console.debug("bootstrap.js: 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);
windows.map(tryWindowApply(this.paint.bind(this)));
Services.ww.registerNotification(this);
},
destroy: function() {
this.enumerate('mail:3pane', this.unpaint);
this.enumerate('mail:messageWindow', this.unpaint);
this.enumerate('msgcompose', this.unpaint);
this.enumerate('mailnews:mailviewlist', this.unpaint);
this.enumerate('msghdr', this.unpaint);
windows.map(tryWindowApply(this.unpaint.bind(this)));
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);


+ 109
- 3
chrome/content/TbHelper.js View File

@ -1,3 +1,23 @@
// duplicate
function throughEntries(obj, fun) {
return Object.fromEntries(fun(Object.entries(obj)));
}
function arrayFromEnumerable(enumerable) {
let enumerator = enumerable.enumerate();
let array = [];
while (enumerator.hasMoreElements()) {
array.push(enumerator.getNext());
}
return array;
}
// duplicate
function filterEntries(obj, predicate) {
let filtering = entries => entries.filter(predicate);
return throughEntries(obj, filtering);
}
var TbHelper = {
getPane: function (window, panename) {
if (typeof window.GetMessagePaneFrame !== "undefined") return window.GetMessagePaneFrame();
@ -129,7 +149,7 @@ var TbHelper = {
try {
file.remove(false);
} catch (ex) {
console.debug("pepmsghdrview.js: Could not delete temp file\n");
console.debug("pepmsghdrgview.js: Could not delete temp file\n");
}
}
}
@ -194,9 +214,95 @@ var TbHelper = {
const r = '<pre wrap="">' + lines.join("\n") + (isSignature ? '</div>' : '') + '</pre>';
return r;
}
},
getAccounts() {
function prefetched(values, fun) {
return Object.fromEntries(values.map(v => [v, fun(v)]));
}
let branch = Services.prefs.getBranch('mail.accountmanager.');
let accountsString = branch.getStringPref('accounts');
let accounts = accountsString.split(',');
let manager = Cc["@mozilla.org/messenger/account-manager;1"].getService(Ci.nsIMsgAccountManager);
let allAccounts = prefetched(accounts, manager.getAccount.bind(manager));
// there is an extra account corresponding to the local
// folders. It does not have a default identity and we want to
// filter it out
function withIdentity([key, account]) {
return !!account.defaultIdentity;
}
return filterEntries(allAccounts, withIdentity);
},
hasIdentity(reference) {
return function(account) {
function sameIdentity(id) {
return reference.key === id.key;
}
let ids = arrayFromEnumerable(account.identities);
return ids.some(sameIdentity);
}
},
getSelectedServer(gFolderDisplay) {
let selectedMessage = gFolderDisplay.selectedMessage;
if (selectedMessage) {
return selectedMessage.folder.rootFolder.server;
} else {
return null;
}
},
getSelectedAccount(gFolderDisplay) {
let ser = this.getSelectedServer(gFolderDisplay);
if (ser) {
let accounts = this.getAccounts();
let matching = Object.values(accounts).filter((candidate) => {
return candidate.incomingServer.equals(ser);
});
// in theory we can have more accounts matching the same
// server. I set up two accounts with the same mail server
// and still got only one server object in the matching
// set
return matching.pop();
} else {
return null;
}
},
getAccountWithIdentity(identity) {
let accounts = this.getAccounts();
let matching = Object.values(accounts).filter(this.hasIdentity(identity));
// we expect only one account to have a matching identity
if (matching.length != 1) {
console.debug("unexpected number of matching accounts", accounts, "for identity", identity);
}
return matching.pop();
},
getAccountOptions () {
let AccountOptions = ChromeUtils.import("chrome://p4t/content/modules/AccountOptions.js").AccountOptions;
let Prefs = ChromeUtils.import("chrome://p4t/content/prefsFactory.js").prefsFactory();
let Helper = ChromeUtils.import("chrome://p4t/content/TbHelper.js").TbHelper;
let serialised = Prefs.getAccountOptions();
let accountOptions = AccountOptions.parse(serialised);
accountOptions.updateAccounts(Helper.getAccounts());
return accountOptions;
},
decryptForSelectedAccount (gFolderDisplay) {
let Helper = ChromeUtils.import("chrome://p4t/content/TbHelper.js").TbHelper;
let accountOptions = Helper.getAccountOptions();
let account = Helper.getSelectedAccount(gFolderDisplay);
return accountOptions.forAccount(account).decrypt();
},
disabledForAccount (gCurrentIdentity) {
let Helper = ChromeUtils.import("chrome://p4t/content/TbHelper.js").TbHelper;
let accountOptions = Helper.getAccountOptions();
let account = Helper.getAccountWithIdentity(gCurrentIdentity);
return accountOptions.forAccount(account).disabled();
}
};
const EXPORTED_SYMBOLS = ["TbHelper"];
const EXPORTED_SYMBOLS = ["TbHelper"];

+ 4
- 3
chrome/content/TbMessageView.js View File

@ -79,7 +79,8 @@ var TbMessageView = {
attachmentString += `${atob(attachment.value)}\n`;
return attachmentString;
}
};
},
}
const EXPORTED_SYMBOLS = ["TbMessageView"];
const EXPORTED_SYMBOLS = ["TbMessageView"];

+ 0
- 4
chrome/content/dialogs/privacy_status.css View File

@ -1,8 +1,4 @@
.template {
visibility: hidden;
}
.intro {
margin-bottom: 1em;
}


+ 3
- 39
chrome/content/dialogs/privacy_status.js View File

@ -1,5 +1,6 @@
let pEpController = ChromeUtils.import("chrome://p4t/content/p4tb.js").pEpController;
let Handshake = ChromeUtils.import("chrome://p4t/content/modules/handshake.js").Handshake;
let utils = ChromeUtils.import("chrome://p4t/content/modules/utils.js").pEpUtils;
let ownIdentity = window.arguments[0];
let allIdentities = window.arguments[1];
@ -7,11 +8,11 @@ let handshake = new Handshake(pEpController);
onLoad = function () {
let root = document.getElementById("root");
removeChildren(root);
utils.removeChildren(root);
allIdentities
.map(fetchData)
.map(([template, promise]) => {
let fragment = new Fragment(template);
let fragment = new utils.Fragment(template, document);
promise.then(({ data, handlers }) => {
fragment
.populate(data)
@ -66,40 +67,3 @@ function closeAfter(handler) {
}
}
function removeChildren(node) {
for (let i = 0; i < node.childNodes.length; i++) {
node.removeChild(node.childNodes[i]);
}
}
class Fragment {
constructor(id) {
// fetch the template
this.root = document
.querySelector(id + '.template')
.cloneNode(true);
this.root.removeAttribute('id');
this.root.classList.remove('template');
}
populate(data) {
Object.keys(data).map((key) => {
this.root
.querySelector("[data-field="+key+"]")
.textContent = data[key];
});
return this;
}
attach(eventName, handlers) {
Object.keys(handlers).map((key) => {
this.root
.querySelector("button#" + key)
.addEventListener(eventName, handlers[key]);
});
return this;
}
getRoot() {
return this.root;
}
}

+ 102
- 0
chrome/content/modules/AccountOptions.js View File

@ -0,0 +1,102 @@
// duplicate
function throughEntries (obj, fun) {
return Object.fromEntries(fun(Object.entries(obj)));
}
// duplicate
function mapEntries (obj, fun) {
function mapFun(entries) { return entries.map(fun); }
return throughEntries(obj, mapFun);
}
let defaults = {"disabled": false, "decrypt": true, "trusted": false};
class AccountOptions {
constructor (data) {
this.data = data;
}
serialise () {
return JSON.stringify(this.data);
}
getReaders () {
let accountOptions = this;
function reducer(accumulator, [account, fields]) {
let setters = Object.entries(fields).map(([key, _]) => {
function setter(val) {
accountOptions.data[account][key] = val;
return [account, key]; // just for debug and troubleshooting
}
return ["#"+account +"-"+ key, setter];
});
return accumulator.concat(setters);
}
function reducing(entries) {
return entries.reduce(reducer, []);
}
return throughEntries(this.data, reducing);
}
forAccount (account) {
function entryToGetter ([key, val]) {
return [key, () => val];
}
let fields = this.data[account.key];
if (fields) {
function mapping (entries) {
return entries.map(entryToGetter);
}
return throughEntries(fields, mapping);
} else {
return null
}
}
updateAccounts (newAccounts) {
let existingOrDefaults = (account) => {
let fields = this.data[account];
return [account, fields ? fields : defaults];
}
let entries = Object.keys(newAccounts).map(existingOrDefaults);
this.data = Object.fromEntries(entries);
}
getUpdates (accountOptions, accounts) {
function makeUpdates ([accountKey, accountOptions]) {
function optionToUpdater ([option, value]) {
let selector = ".account-"+option;
function updater (element) {
element.id = accountKey+"-"+option;
element.checked = value;
}
return [selector, updater];
}
let updates = mapEntries(accountOptions, optionToUpdater);
return [accountKey, updates];
}
function addDisplay (accounts) {
return ([accountKey, updates]) => {
let display = accounts[accountKey].defaultIdentity.fullAddress;
updates[".account-title"] = element => element.textContent = display;
return [accountKey, updates];
};
}
let updates = mapEntries(this.data, makeUpdates);
let withDisplay = mapEntries(updates, addDisplay(accounts));
return Object.values(withDisplay);
}
static equals (options1, options2) {
return options1.serialise() === options2.serialise();
}
static fromDefaults (keys) {
function keyToEntry (key) {
return [key, Object.assign({}, defaults)];
}
let entries = keys.map(keyToEntry);
let data = Object.fromEntries(entries);
return new AccountOptions(data);
}
static parse (string) {
let data = JSON.parse(string);
return new AccountOptions(data);
}
}
const EXPORTED_SYMBOLS = ["AccountOptions"];
if (typeof module !== 'undefined' && module.exports) {
module.exports = AccountOptions;
}

+ 72
- 1
chrome/content/modules/utils.js View File

@ -1,3 +1,8 @@
// duplicate
function throughEntries(obj, fun) {
return Object.fromEntries(fun(Object.entries(obj)));
}
const EXPORTED_SYMBOLS = ["pEpUtils"];
let pEpUtils = {
@ -78,7 +83,73 @@ let pEpUtils = {
}
return 0;
}};
},
Fragment: class {
constructor(id, document) {
// fetch the template
this.root = document
.querySelector(id + '.template')
.cloneNode(true);
this.root.removeAttribute('id');
this.root.classList.remove('template');
}
update(data) {
let applyUpdate = ([selector, updater]) => {
updater(this.root.querySelector(selector));
}
Object.entries(data).map(applyUpdate);
return this;
}
populate(data) {
function dataToUpdaters([fieldValue, content]) {
let selector = "[data-field="+fieldValue+"]";
function updater(element) {
element.textContent = content
};
return [selector, updater];
}
let updaters = throughEntries(data, dataToUpdaters);
this.update(updaters);
return this;
}
attach(eventName, handlers) {
function handlersToUpdaters([id, listener]) {
let selector = "button#" + id;
function updater(element) {
element.addEventListener(eventName, listener);
}
return [selector, updater];
}
let updaters = throughEntries(handlers);
this.update(updaters);
return this;
}
getRoot() {
return this.root;
}
},
removeChildren: (node) => {
function listToArray(list) {
let array = [];
for (let i = 0; i < list.length; i++) {
array.push((list[i]));
}
return array;
}
listToArray(node.childNodes).map(node.removeChild.bind(node));
},
throughEntries(obj, fun) {
return Object.fromEntries(fun(Object.entries(obj)));
},
mapEntries(obj, fun) {
function mapFun(entries) { return entries.map(fun); }
return this.throughEntries(obj, mapFun);
}
};
// export common.js module to allow one js file for browser and node.js
if (typeof module !== 'undefined' && module.exports) {


+ 8
- 0
chrome/content/options.css View File

@ -39,3 +39,11 @@ textarea#disclaimer {
border: 1px solid gray;
padding: 0.4em;
}
.full-width {
width: 100%;
}
groupbox {
width: 100%;
}

+ 48
- 2
chrome/content/options.js View File

@ -20,19 +20,64 @@ const Compat = ChromeUtils.import("chrome://p4t/content/compatFactory.js")
.compatFactory(window);
const Prefs = ChromeUtils.import("chrome://p4t/content/prefsFactory.js")
.prefsFactory(Compat);
let AccountOptions = ChromeUtils.import("chrome://p4t/content/modules/AccountOptions.js").AccountOptions;
let utils = ChromeUtils.import("chrome://p4t/content/modules/utils.js").pEpUtils;
let helper = ChromeUtils.import("chrome://p4t/content/TbHelper.js").TbHelper;
function update() {
let hidden = document.querySelector('#disclaimerMode').value === "disclaimer-none"
document.querySelector('#disclaimer').classList.toggle("hidden", hidden);
}
function loadAccounts (serialised) {
function makeFragment (updates) {
let fragment = new utils.Fragment('#account-options', document);
fragment.update(updates);
return fragment.getRoot();
}
function disableOnChange (root) {
// disable the "keep decrypting" checkbox if pep is enabled
let accountDisabled = root.querySelector(".account-disabled");
let accountDecrypt = root.querySelector(".account-decrypt");
let accountTrusted = root.querySelector(".account-trusted");
function updateCheckbox() {
accountDecrypt.disabled = !accountDisabled.checked;
}
// update when the fragment is created
updateCheckbox();
// update every time the input changes
accountDisabled.onclick = updateCheckbox;
// disabled until we connect this with the other trusted server option
accountTrusted.disabled = true;
return root;
}
let root = document.getElementById("root");
utils.removeChildren(root);
let accountOptions = AccountOptions.parse(serialised);
accountOptions.updateAccounts(helper.getAccounts());
accountOptions
.getUpdates(accountOptions, helper.getAccounts())
.map(makeFragment)
.map(disableOnChange)
.map(root.appendChild.bind(root));
return accountOptions;
}
function storeAccounts (accountOptions) {
var readers = accountOptions.getReaders();
Object.entries(readers).map(([selector, reader]) => {
let element = document.querySelector(selector);
return reader(element.checked);
});
return accountOptions.serialise();
}
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 accountOptions = loadAccounts(Prefs.getAccountOptions());
let acceptButton = document.querySelector('button#accept');
let cancelButton = document.querySelector('button#cancel');
@ -41,6 +86,7 @@ window.addEventListener('load', function () {
Prefs.setWarnUnencrypted(document.getElementById('warnUnencrypted').checked);
Prefs.setDisclaimerMode(document.getElementById('disclaimerMode').value);
Prefs.setDisclaimer(document.getElementById('disclaimer').value);
Prefs.setAccountOptions(storeAccounts(accountOptions));
window.close();
});
cancelButton.addEventListener('click', function () {


+ 24
- 1
chrome/content/options.xul View File

@ -16,10 +16,11 @@
<tabs>
<tab label="Account" />
<tab label="About" />
<tab label="Advanced" />
</tabs>
<tabpanels>
<tabpanel>
<vbox>
<vbox class="full-width">
<!--
<html:h3>Account Security</html:h3>
<groupbox>
@ -107,6 +108,28 @@
</html:div>
</html:div>
</tabpanel>
<tabpanel>
<html:div id="root">
<html:div class="spinner">
... fetching information about your accounts ...
</html:div>
</html:div>
<html:div class="template" id="account-options">
<groupbox>
<hbox class="groupbox-title">
<label class="header account-title">
account-display
</label>
</hbox>
<checkbox class="account-disabled"
label="disable p≡p for this account" />
<checkbox class="account-decrypt"
label="keep decrypting incoming messages with p≡p" />
<checkbox class="account-trusted"
label="store messages on this server without encryption" />
</groupbox>
</html:div>
</tabpanel>
</tabpanels>
</tabbox>
</window>

+ 8
- 0
chrome/content/pEp.css View File

@ -56,3 +56,11 @@ tabbox {
background-size: 16px;
background-position: center left 10px;
}
.template {
visibility: hidden;
}
.spinner {
text-align: center;
}

+ 3
- 2
chrome/content/pEpMimeEncrypt.js View File

@ -5,6 +5,7 @@
"use strict";
const jsmime = ChromeUtils.import("resource:///modules/jsmime.jsm").jsmime;
const helper = ChromeUtils.import("chrome://p4t/content/TbHelper.js").TbHelper;
const {classes: Cc, results: Cr, interfaces: Ci} = Components;
const maxBufferLen = 102400;
@ -155,9 +156,9 @@ PgpMimeEncrypt.prototype = {
},
// nsIMsgComposeSecure interface
requiresCryptoEncapsulation: function () {
requiresCryptoEncapsulation: function (identity) {
console.log('mimeEntcrypt.js: requiresCryptoEncapsusation');
return true;
return !helper.disabledForAccount(identity);
},
beginCryptoEncapsulation: function (outStream, recipientList, msgCompFields, msgIdentity, sendReport, isDraft) {


+ 7
- 1
chrome/content/pepmessengercompose.js View File

@ -146,7 +146,13 @@ var pEpComposer = {
let cc = (msgCompFields.cc) ? _parseAddress(msgCompFields.cc) : undefined;
let bcc = (msgCompFields.bcc) ? _parseAddress(msgCompFields.bcc) : undefined;
pEpController.getOngoingRating(from, to, cc, bcc).then((rating) => {
let ratingProm;
if (helper.disabledForAccount(gCurrentIdentity)) {
ratingProm = Promise.resolve(-2);
} else {
ratingProm = pEpController.getOngoingRating(from, to, cc, bcc);
}
ratingProm.then((rating) => {
console.log("ONGOING RATING: ", rating);
if (typeof rating === "undefined") rating = -2;


+ 5
- 0
chrome/content/pepmsghdrview.js View File

@ -170,6 +170,11 @@ var pEpHdrView = {
onLoadMsgPanelFrameListener: function (event) {
console.debug("pepmsghdrview.js: onLoadMsgPanelFrameListener()");
// do nothing if we are disabled for this account
if (!Helper.decryptForSelectedAccount(gFolderDisplay)) {
return;
}
let selectedMessage = gFolderDisplay.selectedMessage;
let msgHdr = selectedMessage.folder.GetMessageHeader(selectedMessage.messageKey);


+ 13
- 46
chrome/content/prefsFactory.js View File

@ -1,14 +1,6 @@
function prefsFactory(Compat) {
function newVersion () {
return Services.prefs.getBranch('extensions.p4tb.');
}
function oldVersion () {
return Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefService)
.getBranch("extensions.p4tb.");
}
const prefs = Compat.versionBranch(newVersion, oldVersion);
function prefsFactory() {
const prefs = Services.prefs.getBranch('extensions.p4tb.');
return {
isStoreAllSecurely: () => {
@ -35,43 +27,18 @@ function prefsFactory(Compat) {
getDisclaimerMode: () => {
return prefs.getStringPref('disclaimerMode');
},
maybeSetDefaults: () => {
console.log("DEBUG: 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
},
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);
}
setAccountOptions: (v) => {
return prefs.setStringPref('accountOptions', v);
},
getAccountOptions: () => {
return prefs.getStringPref('accountOptions');
},
directAccess: () => {
return prefs;
}
}
}
var EXPORTED_SYMBOLS = ['prefsFactory'];
if (typeof module !== 'undefined' && module.exports) {
module.exports = prefsFactory;
}

+ 17
- 0
lib.js View File

@ -0,0 +1,17 @@
/* at the moment we cannot reuse code everywhere, so we keep here some
* helpful functions to copy and paste around. They include the
* `duplicate` comment which can help us to track where are we using
* them */
// duplicate
function throughEntries(obj, fun) {
return Object.fromEntries(fun(Object.entries(obj)));
}
// duplicate
function mapEntries(obj, fun) {
function mapFun(entries) { return entries.map(fun); }
return throughEntries(obj, mapFun);
}

+ 28
- 0
tests/runtime/pepmessengercompose.js View File

@ -0,0 +1,28 @@
var pEpController = ChromeUtils.import("chrome://p4t/content/p4tb.js").pEpController;
var Handshake = ChromeUtils.import("chrome://p4t/content/modules/handshake.js").Handshake;
var helper = ChromeUtils.import("chrome://p4t/content/TbHelper.js").TbHelper;
var view = ChromeUtils.import("chrome://p4t/content/TbMessageView.js").TbMessageView;
var { compatFactory } = ChromeUtils.import("chrome://p4t/content/compatFactory.js");
var { prefsFactory } = ChromeUtils.import("chrome://p4t/content/prefsFactory.js");
function isObject(candidate) {
return typeof candidate == "object";
}
function t1() {
return isObject(helper.getAccounts());
}
function t2() {
return isObject(helper.getAccountWithIdentity(gCurrentIdentity));
}
function t3() {
return typeof helper.disabledForAccount(gCurrentIdentity) == 'boolean';
}
function test() {
return t1() && t2() && t3();
}
test();

+ 35
- 0
tests/runtime/pepmsghdrview.js View File

@ -0,0 +1,35 @@
var pEpController = ChromeUtils.import("chrome://p4t/content/p4tb.js").pEpController;
var Helper = ChromeUtils.import("chrome://p4t/content/TbHelper.js").TbHelper;
var utils = ChromeUtils.import("chrome://p4t/content/modules/utils.js").pEpUtils;
var view = ChromeUtils.import("chrome://p4t/content/TbMessageView.js").TbMessageView;
var Compat = ChromeUtils.import("chrome://p4t/content/compatFactory.js")
.compatFactory(window);
var Prefs = ChromeUtils.import("chrome://p4t/content/prefsFactory.js")
.prefsFactory(Compat);
function isObject(candidate) {
return typeof candidate == "object";
}
function t1() {
return isObject(Helper.getAccounts());
}
function t2() {
return isObject(Helper.getSelectedAccount(gFolderDisplay));
}
function t3() {
try {
return typeof Helper.decryptForSelectedAccount(gFolderDisplay) == "boolean";
} catch (e) {
return false;
}
}
function test() {
if (gFolderDisplay) {
return t1() && t2() && t3();
} else {
console.log("it's still too early for the tests");
return false;
}
}
test()

+ 50
- 0
tests/unit/accountOptions.js View File

@ -0,0 +1,50 @@
let {describe, it, before, beforeEach} = require('mocha');
let chai = require('chai').use(require('chai-as-promised'));
let expect = require('chai').expect
let AccountOptions = require('../../chrome/content/modules/AccountOptions');
describe("account preferences", () => {
let options;
before(() => {
options = AccountOptions.fromDefaults(["account1", "account2"]);
});
it("serialises, parses, compares", () => {
let o2 = AccountOptions.parse(options.serialise());
expect(AccountOptions.equals(o2, options));
});
it("generates updaters", () => {
let accountsMock = {
"account1": { defaultIdentity: { fullName: "name <address>" } },
"account2": { defaultIdentity: { fullName: "name <address>" } }
};
let updates = options.getUpdates(options, accountsMock);
expect(updates.length).to.equal(2);
});
it("generates readers", () => {
let readers = options.getReaders();
expect(readers["#account1-disabled"](true)).to.deep.equal(["account1", "disabled"]);
let identifiers = [
"#account1-disabled",
"#account1-decrypt",
"#account1-trusted",
"#account2-disabled",
"#account2-decrypt",
"#account2-trusted"
];
expect(Object.keys(readers)).to.deep.equal(identifiers);
});
it("generates option getters by account", () => {
expect(options.forAccount({ key: "account2" }).disabled()).to.be.false;
});
it("reacts to new or deleted accounts", () => {
let deleted = "a1";
let added = "a3";
let initial = [deleted, "a2"];
let eventual = {};
eventual["a2"] = {}; // we omit the account object
eventual[added] = {}; // we omit the account object
let options = AccountOptions.fromDefaults(initial);
options.updateAccounts(eventual);
expect(options.forAccount({ key: deleted })).to.be.null;
expect(options.forAccount({ key: added }).decrypt()).to.be.true;
});
});

Loading…
Cancel
Save