File indexing completed on 2024-11-24 04:54:31
0001 /* This Source Code Form is subject to the terms of the Mozilla Public 0002 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 0003 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 0004 0005 const request = require('request') 0006 const { sanitizeABPInput } = require('./filtering') 0007 const fs = require('fs') 0008 const { AdBlockClient, adBlockLists } = require('..') 0009 0010 /** 0011 * Builds an adblock client, given one or more strings containing filter rules. 0012 * 0013 * @param filterRuleData -- either a string, describing adblock filter riles, 0014 * or an array of such strings. 0015 * @param options -- an optional object, describing parse options. 0016 * currently the only used rule is "keepRuleText", 0017 * which is a boolean flag determine whether to keep 0018 * the original filter rule text. 0019 */ 0020 const makeAdBlockClientFromString = (filterRuleData, options) => { 0021 const keepRuleText = !!(options && options.keepRuleText) 0022 return new Promise((resolve) => { 0023 const client = new AdBlockClient() 0024 if (filterRuleData.constructor === Array) { 0025 filterRuleData.forEach(filterRuleDataItem => client.parse(filterRuleDataItem, keepRuleText)) 0026 } else { 0027 client.parse(filterRuleData, keepRuleText) 0028 } 0029 resolve(client) 0030 }) 0031 } 0032 0033 /** 0034 * Creates an ABlock client from a DAT file 0035 * @param datFilePath - The file path to the DAT file 0036 */ 0037 const makeAdBlockClientFromDATFile = (datFilePath) => { 0038 return new Promise((resolve, reject) => { 0039 fs.readFile(datFilePath, (err, data) => { 0040 if (err) { 0041 reject(err) 0042 return 0043 } 0044 const client = new AdBlockClient() 0045 client.deserialize(data) 0046 resolve(client) 0047 }) 0048 }) 0049 } 0050 0051 /** 0052 * Creates an ABlock client from a list URL 0053 * @param listURL - The URL to check for obtaining the list. 0054 * @param filter - A filtering function that can be optionally specified. 0055 * @param options -- an optional object, describing parse options. 0056 * currently the only used rule is "keepRuleText", 0057 * which is a boolean flag determine whether to keep 0058 * the original filter rule text. 0059 * It will be called with the URL's body and it can filter and return a new string. 0060 */ 0061 const getSingleListDataFromSingleURL = (listURL, filter, options) => { 0062 return new Promise((resolve, reject) => { 0063 request.get(listURL, function (error, response, body) { 0064 if (error) { 0065 reject(new Error(`Request error: ${error}`)) 0066 return 0067 } 0068 if (response.statusCode !== 200) { 0069 reject(new Error(`Error status code ${response.statusCode} returned for URL: ${listURL}`)) 0070 return 0071 } 0072 if (filter) { 0073 body = filter(body) 0074 } 0075 body = sanitizeABPInput(body) 0076 resolve(body) 0077 }) 0078 }) 0079 } 0080 0081 /** 0082 * Creates an AdBlock client from a list URL 0083 * @param listURL -- The URL to check for obtaining the list. 0084 * @param filter -- A filtering function that can be optionally specified. 0085 * @param options -- an optional object, describing parse options. 0086 * currently the only used rule is "keepRuleText", 0087 * which is a boolean flag determine whether to keep 0088 * the original filter rule text. 0089 0090 * It will be called with the URL's body and it can filter and return a new string. 0091 */ 0092 const makeAdBlockClientFromListURL = (listURL, filter, options) => { 0093 return new Promise((resolve, reject) => { 0094 if (listURL.constructor === Array) { 0095 const requestPromises = listURL.map((currentURL) => { 0096 console.log(`${currentURL}...`) 0097 return getSingleListDataFromSingleURL(currentURL, filter) 0098 }) 0099 Promise.all(requestPromises).then((results) => { 0100 let body = results.join('\n') 0101 body = sanitizeABPInput(body) 0102 resolve(makeAdBlockClientFromString(body, options)) 0103 }).catch((error) => { 0104 reject(new Error(`getSingleListDataFromSingleURL error: ${error}`)) 0105 }) 0106 } else { 0107 console.log(`${listURL}...`) 0108 getSingleListDataFromSingleURL(listURL, filter).then((listData) => { 0109 const body = sanitizeABPInput(listData) 0110 resolve(makeAdBlockClientFromString(body, options)) 0111 }).catch((error) => { 0112 reject(new Error(`getSingleListDataFromSingleURL error: ${error}`)) 0113 }) 0114 } 0115 }) 0116 } 0117 0118 const getListFilterFunction = (uuid) => { 0119 if (uuid === 'FBB430E8-3910-4761-9373-840FC3B43FF2') { 0120 return (input) => input.split('\n').slice(4) 0121 .map((line) => `||${line}`).join('\n') 0122 } 0123 return undefined 0124 } 0125 0126 /** 0127 * Creates an AdBlock client with the rules given in a list described by a UUID. 0128 * 0129 * See lists/* for the definitions of these UUIDs. 0130 * 0131 * @param uuid -- a string, describing on of the UUIDs for a known filter 0132 * list. 0133 * @param options -- an optional object, describing parse options. 0134 * currently the only used rule is "keepRuleText", 0135 * which is a boolean flag determine whether to keep 0136 * the original filter rule text. 0137 */ 0138 const makeAdBlockClientFromListUUID = (uuid, options) => { 0139 let list = adBlockLists.default.find((l) => l.uuid === uuid) 0140 if (!list) { 0141 list = adBlockLists.regions.find((l) => l.uuid === uuid) 0142 } 0143 if (!list) { 0144 list = adBlockLists.malware.find((l) => l.uuid === uuid) 0145 } 0146 if (!list) { 0147 return Promise.reject(new Error(`No list found for UUID ${uuid}`)) 0148 } 0149 0150 const filterFn = getListFilterFunction(uuid) 0151 return makeAdBlockClientFromListURL(list.listURL, filterFn, options) 0152 } 0153 0154 /** 0155 * Builds an adblock client by reading one or more files filter rules off disk. 0156 * 0157 * @param filePath -- either a string, describing the path to a file of filter 0158 * rules on disk, or an array of the same. 0159 * @param options -- an optional object, describing parse options. 0160 * currently the only used rule is "keepRuleText", 0161 * which is a boolean flag determine whether to keep 0162 * the original filter rule text. 0163 */ 0164 const makeAdBlockClientFromFilePath = (filePath, options) => { 0165 return new Promise((resolve, reject) => { 0166 let filterRuleData 0167 if (filePath.constructor === Array) { 0168 filterRuleData = filePath.map((filePath) => fs.readFileSync(filePath, 'utf8')) 0169 } else { 0170 filterRuleData = fs.readFileSync(filePath, 'utf8') 0171 } 0172 resolve(makeAdBlockClientFromString(filterRuleData, options)) 0173 }) 0174 } 0175 0176 const getListBufferFromURL = (listURL, filter) => { 0177 return new Promise((resolve, reject) => { 0178 request.get(listURL, function (error, response, body) { 0179 if (error) { 0180 reject(new Error(`Request error: ${error}`)) 0181 return 0182 } 0183 if (response.statusCode !== 200) { 0184 reject(new Error(`Error status code ${response.statusCode} returned for URL: ${listURL}`)) 0185 return 0186 } 0187 if (filter) { 0188 body = filter(body) 0189 } 0190 body = sanitizeABPInput(body) 0191 resolve(body) 0192 }) 0193 }) 0194 } 0195 0196 /** 0197 * Reads a list of sites in the format of one site per newline 0198 * from a file path and returns an array with the sites. 0199 */ 0200 const readSiteList = (path) => 0201 fs.readFileSync(path, 'utf8').split('\n') 0202 0203 module.exports = { 0204 makeAdBlockClientFromString, 0205 makeAdBlockClientFromDATFile, 0206 makeAdBlockClientFromListURL, 0207 makeAdBlockClientFromFilePath, 0208 makeAdBlockClientFromListUUID, 0209 getListBufferFromURL, 0210 readSiteList, 0211 getListFilterFunction 0212 }