diff options
author | Ruben Rodriguez <ruben@gnu.org> | 2014-10-20 02:24:51 +0200 |
---|---|---|
committer | Ruben Rodriguez <ruben@gnu.org> | 2014-10-20 02:24:51 +0200 |
commit | 6e7918b6ccb69876d339a320091fdee811445395 (patch) | |
tree | 31cb88ee438d652fddefca1193f70289a8b3dcc8 /data/extensions/spyblock@gnu.org/lib/synchronizer.js | |
parent | 60e5b13c35d4d3ba21bb03b026750a0a414f6c77 (diff) |
Generalize data directory
Diffstat (limited to 'data/extensions/spyblock@gnu.org/lib/synchronizer.js')
-rw-r--r-- | data/extensions/spyblock@gnu.org/lib/synchronizer.js | 330 |
1 files changed, 330 insertions, 0 deletions
diff --git a/data/extensions/spyblock@gnu.org/lib/synchronizer.js b/data/extensions/spyblock@gnu.org/lib/synchronizer.js new file mode 100644 index 0000000..b9f9e29 --- /dev/null +++ b/data/extensions/spyblock@gnu.org/lib/synchronizer.js @@ -0,0 +1,330 @@ +/* + * This file is part of Adblock Plus <http://adblockplus.org/>, + * Copyright (C) 2006-2014 Eyeo GmbH + * + * Adblock Plus is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * Adblock Plus 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 Adblock Plus. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * @fileOverview Manages synchronization of filter subscriptions. + */ + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + +let {TimeLine} = require("timeline"); +let {Downloader, Downloadable, + MILLIS_IN_SECOND, MILLIS_IN_MINUTE, MILLIS_IN_HOUR, MILLIS_IN_DAY} = require("downloader"); +let {Filter, CommentFilter} = require("filterClasses"); +let {FilterStorage} = require("filterStorage"); +let {FilterNotifier} = require("filterNotifier"); +let {Prefs} = require("prefs"); +let {Subscription, DownloadableSubscription} = require("subscriptionClasses"); +let {Utils} = require("utils"); + +let INITIAL_DELAY = 6 * MILLIS_IN_MINUTE; +let CHECK_INTERVAL = 1 * MILLIS_IN_HOUR; +let DEFAULT_EXPIRATION_INTERVAL = 5 * MILLIS_IN_DAY; + +/** + * The object providing actual downloading functionality. + * @type Downloader + */ +let downloader = null; + +/** + * This object is responsible for downloading filter subscriptions whenever + * necessary. + * @class + */ +let Synchronizer = exports.Synchronizer = +{ + /** + * Called on module startup. + */ + init: function() + { + TimeLine.enter("Entered Synchronizer.init()"); + + downloader = new Downloader(this._getDownloadables.bind(this), INITIAL_DELAY, CHECK_INTERVAL); + onShutdown.add(function() + { + downloader.cancel(); + }); + + downloader.onExpirationChange = this._onExpirationChange.bind(this); + downloader.onDownloadStarted = this._onDownloadStarted.bind(this); + downloader.onDownloadSuccess = this._onDownloadSuccess.bind(this); + downloader.onDownloadError = this._onDownloadError.bind(this); + + TimeLine.leave("Synchronizer.init() done"); + }, + + /** + * Checks whether a subscription is currently being downloaded. + * @param {String} url URL of the subscription + * @return {Boolean} + */ + isExecuting: function(url) + { + return downloader.isDownloading(url); + }, + + /** + * Starts the download of a subscription. + * @param {DownloadableSubscription} subscription Subscription to be downloaded + * @param {Boolean} manual true for a manually started download (should not trigger fallback requests) + */ + execute: function(subscription, manual) + { + downloader.download(this._getDownloadable(subscription, manual)); + }, + + /** + * Yields Downloadable instances for all subscriptions that can be downloaded. + */ + _getDownloadables: function() + { + if (!Prefs.subscriptions_autoupdate) + return; + + for (let subscription of FilterStorage.subscriptions) + { + if (subscription instanceof DownloadableSubscription) + yield this._getDownloadable(subscription, false); + } + }, + + /** + * Creates a Downloadable instance for a subscription. + */ + _getDownloadable: function(/**Subscription*/ subscription, /**Boolean*/ manual) /**Downloadable*/ + { + let result = new Downloadable(subscription.url); + if (subscription.lastDownload != subscription.lastSuccess) + result.lastError = subscription.lastDownload * MILLIS_IN_SECOND; + result.lastCheck = subscription.lastCheck * MILLIS_IN_SECOND; + result.lastVersion = subscription.version; + result.softExpiration = subscription.softExpiration * MILLIS_IN_SECOND; + result.hardExpiration = subscription.expires * MILLIS_IN_SECOND; + result.manual = manual; + result.privateMode = subscription.privateMode; + return result; + }, + + _onExpirationChange: function(downloadable) + { + let subscription = Subscription.fromURL(downloadable.url); + subscription.lastCheck = Math.round(downloadable.lastCheck / MILLIS_IN_SECOND); + subscription.softExpiration = Math.round(downloadable.softExpiration / MILLIS_IN_SECOND); + subscription.expires = Math.round(downloadable.hardExpiration / MILLIS_IN_SECOND); + }, + + _onDownloadStarted: function(downloadable) + { + let subscription = Subscription.fromURL(downloadable.url); + FilterNotifier.triggerListeners("subscription.downloadStatus", subscription); + }, + + _onDownloadSuccess: function(downloadable, responseText, errorCallback, redirectCallback) + { + let lines = responseText.split(/[\r\n]+/); + let match = /\[Adblock(?:\s*Plus\s*([\d\.]+)?)?\]/i.exec(lines[0]); + if (!match) + return errorCallback("synchronize_invalid_data"); + let minVersion = match[1]; + + // Don't remove parameter comments immediately but add them to a list first, + // they need to be considered in the checksum calculation. + let remove = []; + let params = { + redirect: null, + homepage: null, + title: null, + version: null, + privatemode: null, + expires: null + }; + for (let i = 0; i < lines.length; i++) + { + let match = /^\s*!\s*(\w+)\s*:\s*(.*)/.exec(lines[i]); + if (match) + { + let keyword = match[1].toLowerCase(); + let value = match[2]; + if (keyword in params) + { + params[keyword] = value; + remove.push(i); + } + else if (keyword == "checksum") + { + lines.splice(i--, 1); + let checksum = Utils.generateChecksum(lines); + if (checksum && checksum != value.replace(/=+$/, "")) + return errorCallback("synchronize_checksum_mismatch"); + } + } + } + + if (params.redirect) + return redirectCallback(params.redirect); + + // Handle redirects + let subscription = Subscription.fromURL(downloadable.redirectURL || downloadable.url); + if (downloadable.redirectURL && downloadable.redirectURL != downloadable.url) + { + let oldSubscription = Subscription.fromURL(downloadable.url); + subscription.title = oldSubscription.title; + subscription.disabled = oldSubscription.disabled; + subscription.lastCheck = oldSubscription.lastCheck; + + let listed = (oldSubscription.url in FilterStorage.knownSubscriptions); + if (listed) + FilterStorage.removeSubscription(oldSubscription); + + delete Subscription.knownSubscriptions[oldSubscription.url]; + + if (listed) + FilterStorage.addSubscription(subscription); + } + + // The download actually succeeded + subscription.lastSuccess = subscription.lastDownload = Math.round(Date.now() / MILLIS_IN_SECOND); + subscription.downloadStatus = "synchronize_ok"; + subscription.errors = 0; + + // Remove lines containing parameters + for (let i = remove.length - 1; i >= 0; i--) + lines.splice(remove[i], 1); + + // Process parameters + if (params.homepage) + { + let uri = Utils.makeURI(params.homepage); + if (uri && (uri.scheme == "http" || uri.scheme == "https")) + subscription.homepage = uri.spec; + } + + if (params.privatemode) + { + subscription.privateMode = (params.privatemode == "true"); + } + + if (params.title) + { + subscription.title = params.title; + subscription.fixedTitle = true; + } + else + subscription.fixedTitle = false; + + subscription.version = (params.version ? parseInt(params.version, 10) : 0); + + let expirationInterval = DEFAULT_EXPIRATION_INTERVAL; + if (params.expires) + { + let match = /^(\d+)\s*(h)?/.exec(params.expires); + if (match) + { + let interval = parseInt(match[1], 10); + if (match[2]) + expirationInterval = interval * MILLIS_IN_HOUR; + else + expirationInterval = interval * MILLIS_IN_DAY; + } + } + + let [softExpiration, hardExpiration] = downloader.processExpirationInterval(expirationInterval); + subscription.softExpiration = Math.round(softExpiration / MILLIS_IN_SECOND); + subscription.expires = Math.round(hardExpiration / MILLIS_IN_SECOND); + + delete subscription.requiredVersion; + delete subscription.upgradeRequired; + if (minVersion) + { + let {addonVersion} = require("info"); + subscription.requiredVersion = minVersion; + if (Services.vc.compare(minVersion, addonVersion) > 0) + subscription.upgradeRequired = true; + } + + // Process filters + lines.shift(); + let filters = []; + for (let line of lines) + { + line = Filter.normalize(line); + if (line) + filters.push(Filter.fromText(line)); + } + + FilterStorage.updateSubscriptionFilters(subscription, filters); + + return undefined; + }, + + _onDownloadError: function(downloadable, downloadURL, error, channelStatus, responseStatus, redirectCallback) + { + let subscription = Subscription.fromURL(downloadable.url); + subscription.lastDownload = Math.round(Date.now() / MILLIS_IN_SECOND); + subscription.downloadStatus = error; + + // Request fallback URL if necessary - for automatic updates only + if (!downloadable.manual) + { + subscription.errors++; + + if (redirectCallback && subscription.errors >= Prefs.subscriptions_fallbackerrors && /^https?:\/\//i.test(subscription.url)) + { + subscription.errors = 0; + + let fallbackURL = Prefs.subscriptions_fallbackurl; + let {addonVersion} = require("info"); + fallbackURL = fallbackURL.replace(/%VERSION%/g, encodeURIComponent(addonVersion)); + fallbackURL = fallbackURL.replace(/%SUBSCRIPTION%/g, encodeURIComponent(subscription.url)); + fallbackURL = fallbackURL.replace(/%URL%/g, encodeURIComponent(downloadURL)); + fallbackURL = fallbackURL.replace(/%ERROR%/g, encodeURIComponent(error)); + fallbackURL = fallbackURL.replace(/%CHANNELSTATUS%/g, encodeURIComponent(channelStatus)); + fallbackURL = fallbackURL.replace(/%RESPONSESTATUS%/g, encodeURIComponent(responseStatus)); + + let request = new XMLHttpRequest(); + request.mozBackgroundRequest = true; + request.open("GET", fallbackURL); + request.overrideMimeType("text/plain"); + request.channel.loadFlags = request.channel.loadFlags | + request.channel.INHIBIT_CACHING | + request.channel.VALIDATE_ALWAYS; + request.addEventListener("load", function(ev) + { + if (onShutdown.done) + return; + + if (!(subscription.url in FilterStorage.knownSubscriptions)) + return; + + let match = /^(\d+)(?:\s+(\S+))?$/.exec(request.responseText); + if (match && match[1] == "301" && match[2] && /^https?:\/\//i.test(match[2])) // Moved permanently + redirectCallback(match[2]); + else if (match && match[1] == "410") // Gone + { + let data = "[Adblock]\n" + subscription.filters.map((f) => f.text).join("\n"); + redirectCallback("data:text/plain," + encodeURIComponent(data)); + } + }, false); + request.send(null); + } + } + }, +}; +Synchronizer.init(); |