diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 05207e65dfcf5b4c0a3d6249dc3482bffebcd810..a0a764d47c9865b17e7729e9033fb5041b604580 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -20,7 +20,7 @@ "description": "Heading for Setting storage by plugin" }, "settings_store_settings_description": { - "message": "MetaGer search settings are typically stored in your browser with the help of cookies which leads to problems with settings being deleted when clearing the browser history. If you use this feature all settings will be stored in this extension instead and Cookies are not required anymore. Allow this extension to run in private browsing sessions to also apply your settings there.", + "message": "MetaGer search settings are typically stored in your browser with the help of cookies which leads to problems with settings being deleted when clearing the browser history. If you enable this feature all settings will be stored in this extension instead and Cookies are not required anymore. Allow this extension to run in private browsing sessions in order to also apply your settings there.", "description": "Heading for Setting storage by plugin" }, "settings_store_settings_sync": { diff --git a/build/js/Cookie.js b/build/js/Cookie.js new file mode 100644 index 0000000000000000000000000000000000000000..f0e9b023bcf1084311dedc91aa23fba8fe37e2a9 --- /dev/null +++ b/build/js/Cookie.js @@ -0,0 +1,46 @@ +export class Cookie { + key = null; + value = null; + expired = false; + constructor(cookieString) { + // Return an empty object if cookieString + // is empty + if (cookieString === "") + throw new Error("Cannot parse cookie string " + cookieString); + + // Get each individual key-value pairs + // from the cookie string + // This returns a new array + let pairs = cookieString.split(";"); + + // Separate keys from values in each pair string + // Returns a new array which looks like + // [[key1,value1], [key2,value2], ...] + let splittedPairs = pairs.map(cookie => cookie.split("=")); + + + let expiration = null; + for (const [index, pair] of splittedPairs.entries()) { + if (index == 0) { + this.key = pair[0].trim(); + this.value = pair[1].trim(); + } else { + let key = pair[0].trim(); + if (key.match(/^expires$/i) && this.expiration == null) { + expiration = new Date(pair[1].trim()); + } else if (key.match(/^max-age$/i)) { + let maxAge = parseInt(pair[1].trim()); + if (maxAge == 0) { + this.expired = true; + } else { + expiration = new Date((new Date()).getTime() + maxAge * 1000); + } + } + } + }; + + if (expiration != null && Date.now() > expiration.getTime()) { + this.expired = true; + } + } +} \ No newline at end of file diff --git a/build/js/SettingsManager.js b/build/js/SettingsManager.js new file mode 100644 index 0000000000000000000000000000000000000000..5bf522d76f27b91c020083fa6797ab5c6e9b9890 --- /dev/null +++ b/build/js/SettingsManager.js @@ -0,0 +1,132 @@ +import { Cookie } from "./Cookie"; + +export class SettingsManager { + + _responseListener = this._handleResponseHeaders.bind(this); + _requestListener = this._handleRequestHeaders.bind(this); + + settings = {}; + + constructor() { + // Register event handler for response headers on our domains + browser.storage.sync.get({ settings_store: false }).then(stored_settings => { + if (stored_settings.settings_store == true) { + this._toggleRequestListener(true); + this._toggleResponseListener(true); + } + }) + + // Listen for changes to the setting storage setting + browser.storage.onChanged.addListener((changes, areaName) => { + if (changes.hasOwnProperty("settings_store")) { + let new_value = changes.settings_store.newValue; + this._toggleResponseListener(new_value); + this._toggleRequestListener(new_value); + } + }); + + + } + + _toggleRequestListener(enabled = false) { + if (enabled) { + browser.webRequest.onBeforeSendHeaders.addListener( + this._requestListener, + { + urls: [ + "https://metager.org/*", + "https://metager.de/*" + ] + }, + ["blocking", "requestHeaders"], + ); + } else { + browser.webRequest.onBeforeSendHeaders.removeListener(this._requestListener); + } + } + + _toggleResponseListener(enabled = false) { + if (enabled) { + browser.webRequest.onHeadersReceived.addListener( + this._responseListener, + { + urls: [ + "https://metager.org/*", + "https://metager.de/*" + ] + }, + ["blocking", "responseHeaders"], + ); + } else { + browser.webRequest.onHeadersReceived.removeListener(this._responseListener); + } + } + + _handleRequestHeaders(details) { + let cookies = []; + + let headers = []; + // Include any possibly existing cookies + for (let header of details.requestHeaders) { + if (header.name.match(/cookie/i)) { + let existing_cookies = header.value.split(";"); + for (let cookie of existing_cookies) { + cookies.push(cookie.trim()); + } + } else { + headers.push(header); + } + } + + for (let key in this.settings) { + cookies.push(`${key}=${encodeURIComponent(this.settings[key])}`); + } + + if (cookies.length > 0) { + let cookie_header = { + name: "Cookie", + value: cookies.join("; ") + }; + headers.push(cookie_header); + + } + return { requestHeaders: headers } + } + + /** + * + * @param {*} details + */ + _handleResponseHeaders(details) { + if (details.hasOwnProperty("responseHeaders")) { + let new_headers = []; // Cookie Array without set-cookie + let settings_changed = false; + for (const header of details.responseHeaders) { + if (header.name.match(/set-cookie/i)) { + let cookie_responses = header.value.split("\n"); + let new_cookies = []; + for (let cookie of cookie_responses) { + let response_cookie = new Cookie(cookie); + if (response_cookie.expired) { + new_cookies.push(cookie); + delete this.settings[response_cookie.key]; + } else { + this.settings[response_cookie.key] = decodeURIComponent(response_cookie.value); + } + } + if (new_cookies.length > 0) { + header.value = new_cookies.join("\n"); + new_headers.push(header); + } + settings_changed = true; + } else { + new_headers.push(header); + } + } + if (settings_changed) { + browser.storage.sync.set({ settings_settings: this.settings }); + } + return { responseHeaders: new_headers }; + } + } +} \ No newline at end of file diff --git a/build/js/app.js b/build/js/app.js index f6213523cf8f8bfaa6a4ea31580d83bd3b8fadf6..a6c90605a8d5ffac57aca8e6f56ea0ac1e9786e0 100644 --- a/build/js/app.js +++ b/build/js/app.js @@ -1,14 +1,5 @@ +const { SettingsManager } = require("./SettingsManager"); + (() => { - /* - const permissionsToRequest = { - permissions: ["webRequest"], - origins: ["https://metager.de/*"] - }; - browser.permissions.request(permissionsToRequest).then(response => { - console.log(response); - browser.webRequest.onBeforeRequest.addListener(details => { - console.log(details.url); - }, { urls: ["https://metager.de/*"] }); - }) -*/ + let settingsManager = new SettingsManager(); })(); \ No newline at end of file diff --git a/build/js/contentupdater.js b/build/js/contentupdater.js deleted file mode 100644 index aa891efabdcfd46d2f25cbacf54e960ca7b359f0..0000000000000000000000000000000000000000 --- a/build/js/contentupdater.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Function to modify content of our webpages - * i.e. removes install plugin button as plugin already is installed - */ -export function updateContent() { - removePluginButton(); -} - -/** - * Remove the install plugin button from our webpages - */ -function removePluginButton() { - -} \ No newline at end of file diff --git a/manifest.json b/manifest.json index 5ade9ac5d7e7245d584ae3aa3c1e1eb77557d815..30784550d50cc1e616bd5a81fc8143d104863820 100644 --- a/manifest.json +++ b/manifest.json @@ -34,6 +34,7 @@ }, "permissions": [ "webRequest", + "webRequestBlocking", "storage" ], "content_scripts": [ @@ -52,7 +53,8 @@ "https://metager.de/*" ], "optional_permissions": [ - "webRequest" + "webRequest", + "webRequestBlocking" ], "background": { "scripts": [ diff --git a/package-lock.json b/package-lock.json index 3018cf0c5dce133411068a83f09df922885f66c0..0c44a63b83e6c7dcdf8d28be2c7edeac63bbcfc1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,6 +6,7 @@ "": { "dependencies": { "blind-signatures": "^1.0.7", + "cookie": "^0.6.0", "node-rsa": "^1.1.1", "uuid": "^9.0.0" }, @@ -1912,6 +1913,14 @@ "url": "https://github.com/yeoman/configstore?sponsor=1" } }, + "node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/core-js": { "version": "3.29.0", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.29.0.tgz", diff --git a/package.json b/package.json index 7e0d184614b448958cc4ac39a5e8d7863568a3a5..653d21558f06ae207f99f8966f0657b8a761658e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "dependencies": { "blind-signatures": "^1.0.7", + "cookie": "^0.6.0", "node-rsa": "^1.1.1", "uuid": "^9.0.0" }, @@ -12,4 +13,4 @@ "scripts": { "dev": "npm i --clean && webpack --mode=development" } -} \ No newline at end of file +} diff --git a/web/settings/index.js b/web/settings/index.js index 7be021157100f855e8cd545125495f20864cf17b..bd24bef3ade4966922f6fe032ad557cca7fb9b2d 100644 --- a/web/settings/index.js +++ b/web/settings/index.js @@ -13,7 +13,7 @@ async function verifyPermissions(request = false) { let permissionsToRequest = { - permissions: ["webRequest"], + permissions: ["webRequestBlocking"], origins: [ "https://metager.org/*", "https://metager.de/*",