|
@ -1,7 +1,7 @@ |
|
|
/******************************************************************************* |
|
|
/******************************************************************************* |
|
|
|
|
|
|
|
|
uMatrix - a Chromium browser extension to black/white list requests. |
|
|
uMatrix - a Chromium browser extension to black/white list requests. |
|
|
Copyright (C) 2014-2017 Raymond Hill |
|
|
|
|
|
|
|
|
Copyright (C) 2014-2018 Raymond Hill |
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify |
|
|
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 |
|
|
it under the terms of the GNU General Public License as published by |
|
@ -20,7 +20,6 @@ |
|
|
*/ |
|
|
*/ |
|
|
|
|
|
|
|
|
/* global punycode */ |
|
|
/* global punycode */ |
|
|
/* jshint bitwise: false */ |
|
|
|
|
|
|
|
|
|
|
|
'use strict'; |
|
|
'use strict'; |
|
|
|
|
|
|
|
@ -31,13 +30,11 @@ |
|
|
/******************************************************************************/ |
|
|
/******************************************************************************/ |
|
|
|
|
|
|
|
|
var µm = µMatrix; |
|
|
var µm = µMatrix; |
|
|
var magicId = 'axyorpwxtmnf'; |
|
|
|
|
|
var uniqueIdGenerator = 1; |
|
|
|
|
|
|
|
|
var selfieVersion = 1; |
|
|
|
|
|
|
|
|
/******************************************************************************/ |
|
|
/******************************************************************************/ |
|
|
|
|
|
|
|
|
var Matrix = function() { |
|
|
var Matrix = function() { |
|
|
this.id = uniqueIdGenerator++; |
|
|
|
|
|
this.reset(); |
|
|
this.reset(); |
|
|
this.sourceRegister = ''; |
|
|
this.sourceRegister = ''; |
|
|
this.decomposedSourceRegister = ['']; |
|
|
this.decomposedSourceRegister = ['']; |
|
@ -103,10 +100,10 @@ var switchStateToNameMap = new Map([ |
|
|
[ 2, 'false' ] |
|
|
[ 2, 'false' ] |
|
|
]); |
|
|
]); |
|
|
|
|
|
|
|
|
var nameToSwitchStateMap = { |
|
|
|
|
|
'true': 1, |
|
|
|
|
|
'false': 2 |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
var nameToSwitchStateMap = new Map([ |
|
|
|
|
|
[ 'true', 1 ], |
|
|
|
|
|
[ 'false', 2 ] |
|
|
|
|
|
]); |
|
|
|
|
|
|
|
|
/******************************************************************************/ |
|
|
/******************************************************************************/ |
|
|
|
|
|
|
|
@ -594,15 +591,13 @@ Matrix.prototype.extractAllSourceHostnames = (function() { |
|
|
|
|
|
|
|
|
/******************************************************************************/ |
|
|
/******************************************************************************/ |
|
|
|
|
|
|
|
|
Matrix.prototype.toString = function() { |
|
|
|
|
|
var out = []; |
|
|
|
|
|
var rule, type, switchName, val; |
|
|
|
|
|
var srcHostname, desHostname; |
|
|
|
|
|
for ( rule of this.rules.keys() ) { |
|
|
|
|
|
srcHostname = this.srcHostnameFromRule(rule); |
|
|
|
|
|
desHostname = this.desHostnameFromRule(rule); |
|
|
|
|
|
for ( type of typeBitOffsets.keys() ) { |
|
|
|
|
|
val = this.evaluateCell(srcHostname, desHostname, type); |
|
|
|
|
|
|
|
|
Matrix.prototype.toArray = function() { |
|
|
|
|
|
let out = []; |
|
|
|
|
|
for ( let rule of this.rules.keys() ) { |
|
|
|
|
|
let srcHostname = this.srcHostnameFromRule(rule); |
|
|
|
|
|
let desHostname = this.desHostnameFromRule(rule); |
|
|
|
|
|
for ( let type of typeBitOffsets.keys() ) { |
|
|
|
|
|
let val = this.evaluateCell(srcHostname, desHostname, type); |
|
|
if ( val === 0 ) { continue; } |
|
|
if ( val === 0 ) { continue; } |
|
|
out.push( |
|
|
out.push( |
|
|
punycode.toUnicode(srcHostname) + ' ' + |
|
|
punycode.toUnicode(srcHostname) + ' ' + |
|
@ -612,176 +607,140 @@ Matrix.prototype.toString = function() { |
|
|
); |
|
|
); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
for ( srcHostname of this.switches.keys() ) { |
|
|
|
|
|
for ( switchName of switchBitOffsets.keys() ) { |
|
|
|
|
|
val = this.evaluateSwitch(switchName, srcHostname); |
|
|
|
|
|
|
|
|
for ( let srcHostname of this.switches.keys() ) { |
|
|
|
|
|
for ( let switchName of switchBitOffsets.keys() ) { |
|
|
|
|
|
let val = this.evaluateSwitch(switchName, srcHostname); |
|
|
if ( val === 0 ) { continue; } |
|
|
if ( val === 0 ) { continue; } |
|
|
out.push(switchName + ': ' + srcHostname + ' ' + switchStateToNameMap.get(val)); |
|
|
|
|
|
|
|
|
out.push( |
|
|
|
|
|
switchName + ': ' + |
|
|
|
|
|
srcHostname + ' ' + |
|
|
|
|
|
switchStateToNameMap.get(val) |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
return out; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************************/ |
|
|
|
|
|
|
|
|
|
|
|
Matrix.prototype.fromArray = function(lines, append) { |
|
|
|
|
|
let matrix = append === true ? this : new Matrix(); |
|
|
|
|
|
for ( let line of lines ) { |
|
|
|
|
|
matrix.fromLine(line); |
|
|
} |
|
|
} |
|
|
return out.sort().join('\n'); |
|
|
|
|
|
|
|
|
if ( append !== true ) { |
|
|
|
|
|
this.assign(matrix); |
|
|
|
|
|
} |
|
|
|
|
|
this.modifiedTime = Date.now(); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************************/ |
|
|
|
|
|
|
|
|
|
|
|
Matrix.prototype.toString = function() { |
|
|
|
|
|
return this.toArray().join('\n'); |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
/******************************************************************************/ |
|
|
/******************************************************************************/ |
|
|
|
|
|
|
|
|
Matrix.prototype.fromString = function(text, append) { |
|
|
Matrix.prototype.fromString = function(text, append) { |
|
|
var matrix = append ? this : new Matrix(); |
|
|
|
|
|
var textEnd = text.length; |
|
|
|
|
|
var lineBeg = 0, lineEnd; |
|
|
|
|
|
var line, pos; |
|
|
|
|
|
var fields, fieldVal; |
|
|
|
|
|
var switchName; |
|
|
|
|
|
var srcHostname = ''; |
|
|
|
|
|
var desHostname = ''; |
|
|
|
|
|
var type, state; |
|
|
|
|
|
|
|
|
let matrix = append === true ? this : new Matrix(); |
|
|
|
|
|
let textEnd = text.length; |
|
|
|
|
|
let lineBeg = 0; |
|
|
|
|
|
|
|
|
while ( lineBeg < textEnd ) { |
|
|
while ( lineBeg < textEnd ) { |
|
|
lineEnd = text.indexOf('\n', lineBeg); |
|
|
|
|
|
if ( lineEnd < 0 ) { |
|
|
|
|
|
|
|
|
let lineEnd = text.indexOf('\n', lineBeg); |
|
|
|
|
|
if ( lineEnd === -1 ) { |
|
|
lineEnd = text.indexOf('\r', lineBeg); |
|
|
lineEnd = text.indexOf('\r', lineBeg); |
|
|
if ( lineEnd < 0 ) { |
|
|
|
|
|
|
|
|
if ( lineEnd === -1 ) { |
|
|
lineEnd = textEnd; |
|
|
lineEnd = textEnd; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
line = text.slice(lineBeg, lineEnd).trim(); |
|
|
|
|
|
|
|
|
let line = text.slice(lineBeg, lineEnd).trim(); |
|
|
lineBeg = lineEnd + 1; |
|
|
lineBeg = lineEnd + 1; |
|
|
|
|
|
|
|
|
pos = line.indexOf('# '); |
|
|
|
|
|
|
|
|
let pos = line.indexOf('# '); |
|
|
if ( pos !== -1 ) { |
|
|
if ( pos !== -1 ) { |
|
|
line = line.slice(0, pos).trim(); |
|
|
line = line.slice(0, pos).trim(); |
|
|
} |
|
|
} |
|
|
if ( line === '' ) { |
|
|
|
|
|
continue; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fields = line.split(/\s+/); |
|
|
|
|
|
|
|
|
if ( line === '' ) { continue; } |
|
|
|
|
|
|
|
|
// Less than 2 fields makes no sense
|
|
|
|
|
|
if ( fields.length < 2 ) { |
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
matrix.fromLine(line); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
fieldVal = fields[0]; |
|
|
|
|
|
|
|
|
|
|
|
// Special directives:
|
|
|
|
|
|
|
|
|
|
|
|
// title
|
|
|
|
|
|
pos = fieldVal.indexOf('title:'); |
|
|
|
|
|
if ( pos !== -1 ) { |
|
|
|
|
|
// TODO
|
|
|
|
|
|
continue; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Name
|
|
|
|
|
|
pos = fieldVal.indexOf('name:'); |
|
|
|
|
|
if ( pos !== -1 ) { |
|
|
|
|
|
// TODO
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
if ( append !== true ) { |
|
|
|
|
|
this.assign(matrix); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Switch on/off
|
|
|
|
|
|
|
|
|
this.modifiedTime = Date.now(); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
// `switch:` srcHostname state
|
|
|
|
|
|
// state = [`true`, `false`]
|
|
|
|
|
|
switchName = ''; |
|
|
|
|
|
if ( fieldVal === 'switch:' || fieldVal === 'matrix:' ) { |
|
|
|
|
|
fieldVal = 'matrix-off:'; |
|
|
|
|
|
} |
|
|
|
|
|
pos = fieldVal.indexOf(':'); |
|
|
|
|
|
if ( pos !== -1 ) { |
|
|
|
|
|
switchName = fieldVal.slice(0, pos); |
|
|
|
|
|
} |
|
|
|
|
|
if ( switchBitOffsets.has(switchName) ) { |
|
|
|
|
|
srcHostname = punycode.toASCII(fields[1]); |
|
|
|
|
|
|
|
|
/******************************************************************************/ |
|
|
|
|
|
|
|
|
// No state field: reject
|
|
|
|
|
|
fieldVal = fields[2]; |
|
|
|
|
|
if ( fieldVal === null ) { |
|
|
|
|
|
continue; |
|
|
|
|
|
} |
|
|
|
|
|
// Unknown state: reject
|
|
|
|
|
|
if ( nameToSwitchStateMap.hasOwnProperty(fieldVal) === false ) { |
|
|
|
|
|
continue; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// https://github.com/gorhill/uMatrix/issues/759
|
|
|
|
|
|
// Backward compatibility: 'plugin' => 'media'
|
|
|
|
|
|
|
|
|
// Backward compatibility:
|
|
|
|
|
|
// `chromium-behind-the-scene` is now `behind-the-scene`
|
|
|
|
|
|
if ( srcHostname === 'chromium-behind-the-scene' ) { |
|
|
|
|
|
srcHostname = 'behind-the-scene'; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
Matrix.prototype.fromLine = function(line) { |
|
|
|
|
|
let fields = line.split(/\s+/); |
|
|
|
|
|
if ( fields.length < 3 ) { return false; } |
|
|
|
|
|
let field0 = fields[0]; |
|
|
|
|
|
|
|
|
matrix.setSwitch(switchName, srcHostname, nameToSwitchStateMap[fieldVal]); |
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
// Switches
|
|
|
|
|
|
let pos = field0.indexOf(':'); |
|
|
|
|
|
if ( pos !== -1 ) { |
|
|
|
|
|
let switchName = field0.slice(0, pos); |
|
|
|
|
|
let srcHostname = punycode.toASCII(fields[1]); |
|
|
|
|
|
let state = fields[2]; |
|
|
|
|
|
if ( |
|
|
|
|
|
switchBitOffsets.has(switchName) === false || |
|
|
|
|
|
nameToSwitchStateMap.has(state) === false |
|
|
|
|
|
) { |
|
|
|
|
|
return false; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Unknown directive
|
|
|
|
|
|
if ( fieldVal.endsWith(':') ) { |
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
this.setSwitch( |
|
|
|
|
|
switchName, |
|
|
|
|
|
srcHostname, |
|
|
|
|
|
nameToSwitchStateMap.get(state) |
|
|
|
|
|
); |
|
|
|
|
|
return true; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Valid rule syntax:
|
|
|
|
|
|
|
|
|
// Rules
|
|
|
|
|
|
if ( fields.length < 4 ) { return false; } |
|
|
|
|
|
|
|
|
// srcHostname desHostname [type [state]]
|
|
|
|
|
|
// type = a valid request type
|
|
|
|
|
|
// state = [`block`, `allow`, `inherit`]
|
|
|
|
|
|
|
|
|
let srcHostname = punycode.toASCII(fields[0]); |
|
|
|
|
|
let desHostname = punycode.toASCII(fields[1]); |
|
|
|
|
|
let type = fields[2]; |
|
|
|
|
|
|
|
|
// srcHostname desHostname type
|
|
|
|
|
|
// type = a valid request type
|
|
|
|
|
|
// state = `allow`
|
|
|
|
|
|
|
|
|
|
|
|
// srcHostname desHostname
|
|
|
|
|
|
// type = `*`
|
|
|
|
|
|
// state = `allow`
|
|
|
|
|
|
|
|
|
|
|
|
// Lines with invalid syntax silently ignored
|
|
|
|
|
|
|
|
|
|
|
|
srcHostname = punycode.toASCII(fields[0]); |
|
|
|
|
|
desHostname = punycode.toASCII(fields[1]); |
|
|
|
|
|
|
|
|
|
|
|
fieldVal = fields[2]; |
|
|
|
|
|
|
|
|
|
|
|
if ( fieldVal !== undefined ) { |
|
|
|
|
|
type = fieldVal; |
|
|
|
|
|
// https://github.com/gorhill/uMatrix/issues/759
|
|
|
|
|
|
// Backward compatibility.
|
|
|
|
|
|
|
|
|
if ( type !== undefined ) { |
|
|
if ( type === 'plugin' ) { |
|
|
if ( type === 'plugin' ) { |
|
|
type = 'media'; |
|
|
type = 'media'; |
|
|
} |
|
|
|
|
|
// Unknown type: reject
|
|
|
|
|
|
if ( typeBitOffsets.has(type) === false ) { |
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
} else if ( typeBitOffsets.has(type) === false ) { |
|
|
|
|
|
return false; |
|
|
} |
|
|
} |
|
|
} else { |
|
|
} else { |
|
|
type = '*'; |
|
|
type = '*'; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
fieldVal = fields[3]; |
|
|
|
|
|
|
|
|
let state = fields[3]; |
|
|
|
|
|
|
|
|
if ( fieldVal !== undefined ) { |
|
|
|
|
|
// Unknown state: reject
|
|
|
|
|
|
if ( nameToStateMap.hasOwnProperty(fieldVal) === false ) { |
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
if ( state !== undefined ) { |
|
|
|
|
|
if ( nameToStateMap.hasOwnProperty(state) === false ) { |
|
|
|
|
|
return false; |
|
|
} |
|
|
} |
|
|
state = nameToStateMap[fieldVal]; |
|
|
|
|
|
|
|
|
state = nameToStateMap[state]; |
|
|
} else { |
|
|
} else { |
|
|
state = 2; |
|
|
state = 2; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
matrix.setCell(srcHostname, desHostname, type, state); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if ( !append ) { |
|
|
|
|
|
this.assign(matrix); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
this.modifiedTime = Date.now(); |
|
|
|
|
|
|
|
|
this.setCell(srcHostname, desHostname, type, state); |
|
|
|
|
|
return true; |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
/******************************************************************************/ |
|
|
/******************************************************************************/ |
|
|
|
|
|
|
|
|
Matrix.prototype.toSelfie = function() { |
|
|
Matrix.prototype.toSelfie = function() { |
|
|
return { |
|
|
return { |
|
|
magicId: magicId, |
|
|
|
|
|
|
|
|
version: selfieVersion, |
|
|
switches: Array.from(this.switches), |
|
|
switches: Array.from(this.switches), |
|
|
rules: Array.from(this.rules) |
|
|
rules: Array.from(this.rules) |
|
|
}; |
|
|
}; |
|
@ -790,7 +749,7 @@ Matrix.prototype.toSelfie = function() { |
|
|
/******************************************************************************/ |
|
|
/******************************************************************************/ |
|
|
|
|
|
|
|
|
Matrix.prototype.fromSelfie = function(selfie) { |
|
|
Matrix.prototype.fromSelfie = function(selfie) { |
|
|
if ( selfie.magicId !== magicId ) { return false; } |
|
|
|
|
|
|
|
|
if ( selfie.version !== selfieVersion ) { return false; } |
|
|
this.switches = new Map(selfie.switches); |
|
|
this.switches = new Map(selfie.switches); |
|
|
this.rules = new Map(selfie.rules); |
|
|
this.rules = new Map(selfie.rules); |
|
|
this.modifiedTime = Date.now(); |
|
|
this.modifiedTime = Date.now(); |
|
|