2019-03-13 12:20:24 +00:00
/ *
2019-03-18 16:39:47 +00:00
* ClearURLs
2020-01-06 16:14:34 +00:00
* Copyright ( c ) 2017 - 2020 Kevin Röbert
2019-03-18 16:39:47 +00:00
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU Lesser 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 Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
* /
2019-03-13 12:20:24 +00:00
2019-02-13 17:02:08 +00:00
/*jshint esversion: 6 */
/ *
2019-03-18 16:39:47 +00:00
* This script is responsible for some tools .
2019-02-13 17:02:08 +00:00
* /
2020-06-05 18:13:21 +00:00
// Needed by the sha256 method
const enc = new TextEncoder ( ) ;
2020-06-05 19:50:03 +00:00
// Max amount of log entries to prevent performance issues
const logThreshold = 5000 ;
2019-03-27 17:31:37 +00:00
/ *
2019-06-12 18:17:49 +00:00
* To support Waterfox .
* /
2019-11-09 00:40:17 +00:00
Array . prototype . rmEmpty = function ( ) {
2019-03-27 17:31:37 +00:00
return this . filter ( v => v ) ;
} ;
/ *
2019-06-12 18:17:49 +00:00
* To support Waterfox .
* /
2019-11-09 00:40:17 +00:00
Array . prototype . flatten = function ( ) {
2019-03-27 17:31:37 +00:00
return this . reduce ( ( a , b ) => a . concat ( b ) , [ ] ) ;
} ;
2019-02-13 17:02:08 +00:00
/ * *
2019-11-09 00:40:17 +00:00
* Check if an object is empty .
* @ param { Object } obj
* @ return { Boolean }
* /
function isEmpty ( obj ) {
2019-02-13 17:02:08 +00:00
return ( Object . getOwnPropertyNames ( obj ) . length === 0 ) ;
}
/ * *
2019-11-09 00:40:17 +00:00
* Translate a string with the i18n API .
*
2020-03-20 00:50:22 +00:00
* @ param { string } string Name of the attribute used for localization
* @ param { string [ ] } placeholders Array of placeholders
2019-11-09 00:40:17 +00:00
* /
2020-03-20 00:50:22 +00:00
function translate ( string , ... placeholders )
{
return browser . i18n . getMessage ( string , placeholders ) ;
2019-02-13 17:02:08 +00:00
}
/ * *
2019-11-09 00:40:17 +00:00
* Reloads the extension .
* /
function reload ( ) {
2019-02-13 17:02:08 +00:00
browser . runtime . reload ( ) ;
}
/ * *
2019-11-09 00:40:17 +00:00
* Check if it is an android device .
* @ return bool
* /
async function checkOSAndroid ( ) {
if ( os === undefined || os === null || os === "" ) {
await chrome . runtime . getPlatformInfo ( function ( info ) {
2019-07-28 18:27:13 +00:00
os = info . os ;
} ) ;
}
2019-03-18 16:39:47 +00:00
2019-10-22 23:16:12 +00:00
return os === "android" ;
2019-02-13 17:02:08 +00:00
}
2019-09-11 16:08:41 +00:00
/ * *
2019-11-09 00:40:17 +00:00
* Extract the host without port from an url .
* @ param { String } url URL as String
* @ return { String } host as string
* /
2019-09-11 16:08:41 +00:00
function extractHost ( url ) {
let parsed _url = new URL ( url ) ;
return parsed _url . hostname ;
}
/ * *
2019-11-09 00:40:17 +00:00
* Returns true if the url has a local host .
* @ param { String } url URL as String
* @ return { boolean }
* /
2019-09-11 16:08:41 +00:00
function checkLocalURL ( url ) {
let host = extractHost ( url ) ;
2020-07-21 22:30:09 +00:00
if ( ! host . match ( /^\d/ ) && host !== 'localhost' ) {
return false ;
}
2019-09-11 16:08:41 +00:00
return ipRangeCheck ( host , [ "10.0.0.0/8" , "172.16.0.0/12" ,
2019-11-09 00:40:17 +00:00
"192.168.0.0/16" , "100.64.0.0/10" ,
"169.254.0.0/16" , "127.0.0.1" ] ) ||
host === 'localhost' ;
2019-09-11 16:08:41 +00:00
}
2019-02-13 17:02:08 +00:00
/ * *
2019-11-09 00:40:17 +00:00
* Return the number of parameters query strings .
* @ param { String } url URL as String
* @ return { int } Number of Parameters
* /
function countFields ( url ) {
2019-03-12 17:13:23 +00:00
return extractFileds ( url ) . length ;
}
2019-02-13 17:02:08 +00:00
2019-06-12 18:17:49 +00:00
/ * *
2019-11-09 00:40:17 +00:00
* Returns true if fields exists .
* @ param { String } url URL as String
* @ return { boolean }
* /
function existsFields ( url ) {
2019-10-22 23:16:12 +00:00
let matches = ( url . match ( /\?.+/i ) || [ ] ) ;
let count = matches . length ;
2019-06-12 18:17:49 +00:00
return ( count > 0 ) ;
}
2019-03-12 17:13:23 +00:00
/ * *
2019-11-09 00:40:17 +00:00
* Extract the fields from an url .
* @ param { String } url URL as String
* @ return { Array } Fields as array
* /
function extractFileds ( url ) {
if ( existsFields ( url ) ) {
2019-10-22 23:16:12 +00:00
let fields = url . replace ( new RegExp ( ".*?\\?" , "i" ) , "" ) ;
2019-11-09 00:40:17 +00:00
if ( existsFragments ( url ) ) {
2019-06-12 18:17:49 +00:00
fields = fields . replace ( new RegExp ( "#.*" , "i" ) , "" ) ;
}
2019-07-29 15:40:54 +00:00
return ( fields . match ( /[^\/|\?|&]+=?[^&]*/gi ) || [ ] ) ;
2019-06-12 18:17:49 +00:00
}
return [ ] ;
2019-02-13 17:02:08 +00:00
}
/ * *
2019-11-09 00:40:17 +00:00
* Return the number of fragments query strings .
* @ param { String } url URL as String
* @ return { int } Number of fragments
* /
function countFragments ( url ) {
2019-06-12 18:17:49 +00:00
return extractFragments ( url ) . length ;
}
/ * *
2019-11-09 00:40:17 +00:00
* Extract the fragments from an url .
* @ param { String } url URL as String
* @ return { Array } fragments as array
* /
function extractFragments ( url ) {
if ( existsFragments ( url ) ) {
2019-10-22 23:16:12 +00:00
let fragments = url . replace ( new RegExp ( ".*?#" , "i" ) , "" ) ;
2019-06-12 18:17:49 +00:00
return ( fragments . match ( /[^&]+=?[^&]*/gi ) || [ ] ) ;
}
return [ ] ;
}
/ * *
2019-11-09 00:40:17 +00:00
* Returns true if fragments exists .
* @ param { String } url URL as String
* @ return { boolean }
* /
function existsFragments ( url ) {
2019-10-22 23:16:12 +00:00
let matches = ( url . match ( /\#.+/i ) || [ ] ) ;
let count = matches . length ;
2019-02-13 17:02:08 +00:00
return ( count > 0 ) ;
}
/ * *
2019-11-09 00:40:17 +00:00
* Load local saved data , if the browser is offline or
* some other network trouble .
* /
function loadOldDataFromStore ( ) {
2019-02-13 17:02:08 +00:00
localDataHash = storage . dataHash ;
}
/ * *
2019-11-09 00:40:17 +00:00
* Increase by { number } the GlobalURLCounter
* @ param { int } number
* /
function increaseGlobalURLCounter ( number ) {
if ( storage . statisticsStatus ) {
2019-02-13 17:02:08 +00:00
storage . globalurlcounter += number ;
2019-09-12 16:44:14 +00:00
deferSaveOnDisk ( 'globalurlcounter' ) ;
2019-02-13 17:02:08 +00:00
}
}
/ * *
2019-11-09 00:40:17 +00:00
* Increase by one the URLCounter
* /
function increaseURLCounter ( ) {
if ( storage . statisticsStatus ) {
2019-02-13 17:02:08 +00:00
storage . globalCounter ++ ;
2019-09-12 16:44:14 +00:00
deferSaveOnDisk ( 'globalCounter' ) ;
2019-02-13 17:02:08 +00:00
}
}
/ * *
2019-11-09 00:40:17 +00:00
* Change the icon .
* /
function changeIcon ( ) {
2019-09-11 16:08:41 +00:00
checkOSAndroid ( ) . then ( ( res ) => {
2019-11-09 00:40:17 +00:00
if ( ! res ) {
if ( storage . globalStatus ) {
2020-02-20 13:12:06 +00:00
browser . browserAction . setIcon ( { path : "img/clearurls_128x128.png" } ) . catch ( handleError ) ;
2019-11-09 00:40:17 +00:00
} else {
2020-02-20 13:12:06 +00:00
browser . browserAction . setIcon ( { path : "img/clearurls_gray_128x128.png" } ) . catch ( handleError ) ;
2019-09-11 16:08:41 +00:00
}
2019-03-18 16:39:47 +00:00
}
2019-09-11 16:08:41 +00:00
} ) ;
2019-02-13 17:02:08 +00:00
}
/ * *
2019-11-09 00:40:17 +00:00
* Get the badged status from the browser storage and put the value
* into a local variable .
*
* /
function setBadgedStatus ( ) {
2019-09-11 16:08:41 +00:00
checkOSAndroid ( ) . then ( ( res ) => {
2019-11-09 00:40:17 +00:00
if ( ! res && storage . badgedStatus ) {
2020-01-04 00:14:03 +00:00
let color = storage . badged _color ;
2020-02-20 13:12:06 +00:00
if ( storage . badged _color . charAt ( 0 ) !== '#' )
2020-01-04 00:14:03 +00:00
color = '#' + storage . badged _color ;
2019-09-11 16:08:41 +00:00
browser . browserAction . setBadgeBackgroundColor ( {
2020-01-04 00:14:03 +00:00
'color' : color
2020-02-20 13:12:06 +00:00
} ) . catch ( handleError ) ;
2020-04-14 22:53:01 +00:00
// Works only in Firefox: https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/browserAction/setBadgeTextColor#Browser_compatibility
if ( getBrowser ( ) === "Firefox" ) {
browser . browserAction . setBadgeTextColor ( {
color : "#FFFFFF"
} ) . catch ( handleError ) ;
}
2019-09-11 16:08:41 +00:00
}
} ) ;
2019-02-13 17:02:08 +00:00
}
/ * *
2019-11-09 00:40:17 +00:00
* Returns the current URL .
* @ return { String } [ description ]
* /
function getCurrentURL ( ) {
2019-02-13 17:02:08 +00:00
return currentURL ;
}
2019-04-01 21:53:28 +00:00
/ * *
2019-11-09 00:40:17 +00:00
* Check for browser .
* /
2019-04-01 21:53:28 +00:00
function getBrowser ( ) {
2019-11-09 00:40:17 +00:00
if ( typeof InstallTrigger !== 'undefined' ) {
2019-04-01 21:53:28 +00:00
return "Firefox" ;
} else {
return "Chrome" ;
}
}
2019-10-22 23:16:12 +00:00
/ * *
* Decodes an URL , also one that is encoded multiple times .
2020-07-21 22:30:09 +00:00
*
* @ see https : //stackoverflow.com/a/38265168
*
2019-10-22 23:16:12 +00:00
* @ param url the url , that should be decoded
* /
function decodeURL ( url ) {
2020-07-21 22:30:09 +00:00
let rtn = decodeURIComponent ( url ) ;
while ( isEncodedURI ( rtn ) ) {
rtn = decodeURIComponent ( rtn ) ;
2019-10-22 23:16:12 +00:00
}
return rtn ;
2019-11-09 00:40:17 +00:00
}
2020-07-21 22:30:09 +00:00
/ * *
* Returns true , iff the given URI is encoded
* @ see https : //stackoverflow.com/a/38265168
* /
function isEncodedURI ( uri ) {
return uri !== decodeURIComponent ( uri || '' )
}
2020-01-06 15:22:37 +00:00
/ * *
2020-02-20 13:12:06 +00:00
* Gets the value of at ` key ` an object . If the resolved value is ` undefined ` , the ` defaultValue ` is returned in its place .
*
* @ param { string } key the key of the object
* @ param { object } defaultValue the default value
* /
2019-11-09 00:40:17 +00:00
Object . prototype . getOrDefault = function ( key , defaultValue ) {
return this [ key ] === undefined ? defaultValue : this [ key ] ;
2020-02-20 13:12:06 +00:00
} ;
function handleError ( error ) {
2020-06-05 18:13:21 +00:00
console . error ( "[ClearURLs ERROR]:" + error ) ;
2020-03-20 00:50:22 +00:00
}
/ * *
* Function to log all activities from ClearUrls .
* Only logging when activated .
*
* @ param beforeProcessing the url before the clear process
* @ param afterProcessing the url after the clear process
* @ param rule the rule that triggered the process
* /
function pushToLog ( beforeProcessing , afterProcessing , rule ) {
2020-06-07 19:32:24 +00:00
const limit = Math . max ( 0 , storage . logLimit ) ;
2020-06-05 19:50:03 +00:00
if ( storage . loggingStatus && limit !== 0 && ! isNaN ( limit ) ) {
while ( storage . log . log . length >= limit
|| storage . log . log . length >= logThreshold ) {
storage . log . log . shift ( ) ;
2020-03-20 00:50:22 +00:00
}
storage . log . log . push (
{
"before" : beforeProcessing ,
"after" : afterProcessing ,
"rule" : rule ,
"timestamp" : Date . now ( )
}
) ;
deferSaveOnDisk ( 'log' ) ;
}
}
/ * *
* Checks if the storage is available .
* /
function isStorageAvailable ( ) {
return storage . ClearURLsData . length !== 0 ;
2020-06-05 18:13:21 +00:00
}
/ * *
* This method calculates the SHA - 256 hash as HEX string of the given message .
* This method uses the native hashing implementations of the SubtleCrypto interface which is supported by all browsers
* that implement the Web Cryptography API specification and is based on :
* https : //developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest
*
* @ param message message for which the hash should be calculated
* @ returns { Promise < string > } SHA - 256 of the given message
* /
async function sha256 ( message ) {
const msgUint8 = enc . encode ( message ) ;
const hashBuffer = await crypto . subtle . digest ( 'SHA-256' , msgUint8 ) ;
const hashArray = Array . from ( new Uint8Array ( hashBuffer ) ) ;
return hashArray . map ( b => b . toString ( 16 ) . padStart ( 2 , '0' ) ) . join ( '' ) ;
2020-02-20 13:12:06 +00:00
}