first draft toward fixing #30
parent
bf4f3e71d3
commit
2cf4a57bf4
@ -0,0 +1,217 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
uMatrix - a Chromium browser extension to black/white list requests.
|
||||||
|
Copyright (C) 2018 Raymond Hill
|
||||||
|
|
||||||
|
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, see {http://www.gnu.org/licenses/}.
|
||||||
|
|
||||||
|
Home: https://github.com/gorhill/uMatrix
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* global punycode */
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
µMatrix.recipeManager = (function() {
|
||||||
|
let rawRecipes = [];
|
||||||
|
let recipeIdGenerator = 1;
|
||||||
|
let recipeBook = new Map();
|
||||||
|
let reValidRecipeFile = /^! uMatrix: Ruleset recipes [0-9.]+\n/;
|
||||||
|
let reNoUnicode = /^[\x00-\x7F]$/;
|
||||||
|
|
||||||
|
var authorFromHeader = function(header) {
|
||||||
|
let match = /^! +maintainer: +([^\n]+)/im.exec(header);
|
||||||
|
return match !== null ? match[1].trim() : '';
|
||||||
|
};
|
||||||
|
|
||||||
|
var conditionMatch = function(condition, srcHostname, desHostnames) {
|
||||||
|
let i = condition.indexOf(' ');
|
||||||
|
if ( i === -1 ) { return false; }
|
||||||
|
let hn = condition.slice(0, i).trim();
|
||||||
|
if ( hn !== '*' && srcHostname.endsWith(hn) === false ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
hn = condition.slice(i + 1).trim();
|
||||||
|
if ( hn === '*' ) { return true; }
|
||||||
|
for ( let desHostname of desHostnames ) {
|
||||||
|
if ( desHostname.endsWith(hn) ) { return true; }
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
var toASCII = function(rule) {
|
||||||
|
if ( reNoUnicode.test(rule) ) { return rule; }
|
||||||
|
let parts = rule.split(/\s+/);
|
||||||
|
for ( let i = 0; i < parts.length; i++ ) {
|
||||||
|
parts[i] = punycode.toASCII(parts[i]);
|
||||||
|
}
|
||||||
|
return parts.join(' ');
|
||||||
|
};
|
||||||
|
|
||||||
|
var compareLength = function(a, b) {
|
||||||
|
return b.length - a.length;
|
||||||
|
};
|
||||||
|
|
||||||
|
var getTokens = function(s) {
|
||||||
|
let tokens = s.match(/[a-z0-9]+/gi);
|
||||||
|
if ( tokens === null ) { return []; }
|
||||||
|
return tokens;
|
||||||
|
};
|
||||||
|
|
||||||
|
var addRecipe = function(recipe) {
|
||||||
|
let tokens = getTokens(recipe.condition);
|
||||||
|
tokens.sort(compareLength);
|
||||||
|
let token = tokens[0];
|
||||||
|
let recipes = recipeBook.get(token);
|
||||||
|
if ( recipes === undefined ) {
|
||||||
|
recipeBook.set(token, recipes = []);
|
||||||
|
}
|
||||||
|
recipes.push(recipe);
|
||||||
|
};
|
||||||
|
|
||||||
|
var fromString = function(raw) {
|
||||||
|
var recipeName,
|
||||||
|
recipeCondition,
|
||||||
|
recipeRuleset;
|
||||||
|
let rawHeader = raw.slice(0, 1024);
|
||||||
|
if ( reValidRecipeFile.test(rawHeader) === false ) { return; }
|
||||||
|
let maintainer = authorFromHeader(rawHeader);
|
||||||
|
let lineIter = new µMatrix.LineIterator(raw);
|
||||||
|
for (;;) {
|
||||||
|
let line = lineIter.next().trim();
|
||||||
|
if ( line.length === 0 ) {
|
||||||
|
if (
|
||||||
|
recipeName !== undefined &&
|
||||||
|
recipeCondition !== undefined &&
|
||||||
|
recipeRuleset.length !== 0
|
||||||
|
) {
|
||||||
|
addRecipe({
|
||||||
|
id: recipeIdGenerator++,
|
||||||
|
name: recipeName,
|
||||||
|
maintainer: maintainer,
|
||||||
|
condition: recipeCondition,
|
||||||
|
ruleset: recipeRuleset
|
||||||
|
});
|
||||||
|
}
|
||||||
|
recipeName = undefined;
|
||||||
|
}
|
||||||
|
if ( lineIter.eot() && recipeName === undefined ) { break; }
|
||||||
|
if ( line.length === 0 ) { continue; }
|
||||||
|
let c = line.charCodeAt(0);
|
||||||
|
if ( c === 0x23 /* '#' */ || c === 0x21 /* '!' */ ) { continue; }
|
||||||
|
if ( recipeName === undefined ) {
|
||||||
|
recipeName = line;
|
||||||
|
recipeCondition = undefined;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( recipeCondition === undefined ) {
|
||||||
|
recipeCondition = toASCII(line);
|
||||||
|
recipeRuleset = '';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( recipeRuleset.length !== 0 ) {
|
||||||
|
recipeRuleset += '\n';
|
||||||
|
}
|
||||||
|
recipeRuleset += toASCII(line);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var fromPendingStrings = function() {
|
||||||
|
if ( rawRecipes.length === 0 ) { return; }
|
||||||
|
for ( var raw of rawRecipes ) {
|
||||||
|
fromString(raw);
|
||||||
|
}
|
||||||
|
rawRecipes = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
apply: function(details) {
|
||||||
|
let µm = µMatrix;
|
||||||
|
let tMatrix = µm.tMatrix;
|
||||||
|
let pMatrix = µm.pMatrix;
|
||||||
|
let mustPersist = false;
|
||||||
|
for ( let rule of details.ruleset.split('\n') ) {
|
||||||
|
let parts = rule.split(/\s+/);
|
||||||
|
let action = tMatrix.evaluateCellZXY(parts[0], parts[1], parts[2]);
|
||||||
|
if ( action === 1 ) {
|
||||||
|
tMatrix.whitelistCell(parts[0], parts[1], parts[2]);
|
||||||
|
}
|
||||||
|
if ( details.commit !== true ) { continue; }
|
||||||
|
action = pMatrix.evaluateCellZXY(parts[0], parts[1], parts[2]);
|
||||||
|
if ( action === 1 ) {
|
||||||
|
pMatrix.whitelistCell(parts[0], parts[1], parts[2]);
|
||||||
|
mustPersist = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( mustPersist ) {
|
||||||
|
µm.saveMatrix();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fetch: function(srcHostname, desHostnames, callback) {
|
||||||
|
fromPendingStrings();
|
||||||
|
let out = [];
|
||||||
|
let fetched = new Set();
|
||||||
|
let tokens = getTokens(srcHostname + ' ' + desHostnames.join(' '));
|
||||||
|
for ( let token of tokens ) {
|
||||||
|
let recipes = recipeBook.get(token);
|
||||||
|
if ( recipes === undefined ) { continue; }
|
||||||
|
for ( let recipe of recipes ) {
|
||||||
|
if ( fetched.has(recipe.id) ) { continue; }
|
||||||
|
if (
|
||||||
|
conditionMatch(
|
||||||
|
recipe.condition,
|
||||||
|
srcHostname,
|
||||||
|
desHostnames
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
out.push(recipe);
|
||||||
|
fetched.add(recipe.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callback(out);
|
||||||
|
},
|
||||||
|
commitStatuses: function(details) {
|
||||||
|
let matrix = µMatrix.pMatrix;
|
||||||
|
for ( let recipe of details.recipes ) {
|
||||||
|
let ruleIter = new µMatrix.LineIterator(recipe.ruleset);
|
||||||
|
while ( ruleIter.eot() === false ) {
|
||||||
|
let parts = ruleIter.next().split(/\s+/);
|
||||||
|
if (
|
||||||
|
matrix.evaluateCellZXY(
|
||||||
|
details.scope,
|
||||||
|
parts[1],
|
||||||
|
parts[2]
|
||||||
|
) === 1
|
||||||
|
) {
|
||||||
|
recipe.mustCommit = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return details;
|
||||||
|
},
|
||||||
|
fromString: function(raw) {
|
||||||
|
rawRecipes.push(raw);
|
||||||
|
},
|
||||||
|
reset: function() {
|
||||||
|
rawRecipes.length = 0;
|
||||||
|
recipeBook.clear();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
Loading…
Reference in New Issue