// // Copyright (c) 2012 Stefan Bolte // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // /* * Block requests from thirdparty domains * * Extension that blocks requests from thirdparty domains with whitelisting * support, either permanently or just for the session. * It is also possible to block requests from certain domains on all sites, they * have highest precedence and will also be blocked on sites where * thirdparty requests are allowed in general. * * * To use this extension load it with a userscript in * $HOME/.config/dwb/userscripts/, e.g. * * ------------------------------------------------------------------------------ * |#!javascript | * | | * |extensions.load("requestpolicy"); | * ------------------------------------------------------------------------------ * * * Configuration options: * * shortcut : Shortcut to block / allow requests, default "erp" * * unblockCurrent : Shortcut to unblock always blocked requests, shows only * domains from the current site, default "erC" * * unblockAll : Shortcut to unblock always blocked requests, shows all * blocked domains, default "erA" * * whiteList : A path to the whitelisting file, default is * $XDG_CONFIG_HOME/dwb//requestpolicy.json * * autoreload : Whether to automatically reload the website after the * persistentList has changed, default false * * notify : Whether to notify about blocked request, default false * * * Example (loading config with extensions.load()) * * ------------------------------------------------------------------------------ * |extensions.load("requestpolicy", { | * | whiteList : system.getEnv("HOME") + "/.dwb_request_policy", | * | autoreload : true | * |}); | * ------------------------------------------------------------------------------ * * Example extensionrc: * * ------------------------------------------------------------------------------ * |return { | * | foo : { ... }, | * | | * | requestpolicy : { | * | autoreload : true | * | shortcut : "rp", | * | notify : false | * | }, | * | bar : { ... } | * |} | * ------------------------------------------------------------------------------ * * */ var me = "requestpolicy"; var defaultConfig = { //DEFAULT_CONFIG }; var config = {}; var sigs = { resource : -1, navigation : -1, loadFinished : -1 }; var persistentList = null; var tmpList = {}; var getPrivate = (function() { var priv = "_requestPolicy" + parseInt(Math.random() * 9999999999, 10); return function (wv) { var o = wv[priv]; if (o === undefined) { o = { domains : [], blocked : 0 }; Object.defineProperty(wv, priv, { value : o, writable : true }); } return o; }; })(); function listAdd(o, key, value, doWrite) { if (!o[key]) o[key] = []; if (o[key].fastIndexOf(value) == -1) o[key].push(value); if (doWrite) io.write(config.whiteList, "w", JSON.stringify(persistentList)); } function listRemove(o, firstParty, domain, doWrite) { var idx; if (o[firstParty] && (idx = o[firstParty].fastIndexOf(domain)) != -1) { o[firstParty].splice(idx, 1); if (o[firstParty].length === 0) delete o[firstParty]; if (doWrite) io.write(config.whiteList, "w", JSON.stringify(persistentList)); return true; } return false; } // MENU {{{ function showMenu() { var tmpWhiteListed, whiteListed; var isWhiteListed = false; var dom, i, l, domains, labels, currentDomain; var domain = tabs.current.mainFrame.domain; if (domain === null) return; domains = getPrivate(tabs.current).domains; labels = []; currentDomain = tabs.current.mainFrame.domain; for (i=0, l=domains.length; i 0) { tabComplete("Unblock:", labels, function(response) { listRemove(persistentList, "_alwaysBlock", response, true); if (config.autoreload) tabs.current.reload(); }, true); } else { io.notify("No domains to unblock"); } } function unblockAll() { if (!persistentList._alwaysBlock) { io.notify("No domains to unblock"); return; } var i, l, labels = []; var domains = persistentList._alwaysBlock; for (i=0, l=domains.length; i 0) { tabComplete("Unblock:", labels, function(response) { listRemove(persistentList, "_alwaysBlock", response, true); if (config.autoreload) tabs.current.reload(); }, true); } } function blockRequest(wv, request, priv, domain) { request.uri = "about:blank"; priv.blocked++; if (config.notify && wv == tabs.current) io.notify("RP: blocked " + domain); return true; } // SIGNALS {{{ var resourceCB = (function () { var regexEmpty = /^\s*$/; return function resourceCB(wv, frame, request, response) { var o, message, domain, firstParty; if (regexEmpty.test(request.uri)) return false; message = request.message; if (!message) return false; firstParty = util.domainFromHost(message.firstParty.host); domain = util.domainFromHost(message.uri.host); if (firstParty == domain) return false; o = getPrivate(wv); if (o.domains.fastIndexOf(domain) == -1) { o.domains.push(domain); } // Check for requests that are always blocked if (persistentList._alwaysBlock && persistentList._alwaysBlock.fastIndexOf(domain) != -1) return blockRequest(wv, request, o, domain); // Check if domain is always allowed if ((persistentList._all && persistentList._all.fastIndexOf(firstParty) != -1) || (tmpList._all && tmpList._all.fastIndexOf(firstParty) != -1)) return false; // Check request is always allowed if (persistentList._always && persistentList._always.fastIndexOf(domain) != -1) return false; // Check if request is whitelisted if ( (!persistentList[firstParty] || persistentList[firstParty].fastIndexOf(domain) == -1) && (!tmpList[firstParty] || tmpList[firstParty].fastIndexOf(domain) == -1)) return blockRequest(wv, request, o, domain); }; })(); function navigationCB(wv, frame) { if (frame == wv.mainFrame) { var o = getPrivate(wv); o.domains = []; o.blocked = 0; } } function loadFinishedCB(wv) { if (wv != tabs.current) return; var blocked = getPrivate(wv).blocked; if (blocked > 0) io.notify("RP: blocked " + blocked + " requests"); } function connect() { sigs.resource = signals.connect("resource", resourceCB); sigs.navigation = signals.connect("navigation", navigationCB); if (config.notify) sigs.loadFinished = signals.connect("loadFinished", loadFinishedCB); } function disconnect() { sigs.forEach(function (key, value) { if (value != -1) { signals.disconnect(value); sigs[key] = -1; } }); }//}}} return { init : function(c) { config = extensions.getConfig(c, defaultConfig); if (system.fileTest(config.whiteList, FileTest.regular | FileTest.symlink)) { var rawWhiteList = io.read(config.whiteList); try { persistentList = JSON.parse(rawWhiteList); } catch (e) { extensions.debug(me, e, "Error parsing persistentList"); } } persistentList = persistentList || {}; connect(); bind(config.shortcut, showMenu, "requestpolicy"); bind(config.unblockCurrent, unblockCurrent, "requestpolicyUnblockCurrent"); bind(config.unblockAll, unblockAll, "requestpolicyUnblockAll"); return true; }, end : function () { disconnect(); unbind("requestpolicy"); unbind("requestpolicyUnblockCurrent"); unbind("requestpolicyUnblockAll"); } } // vim: set ft=javascript: