1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/mozapps/extensions/nsBlocklistService.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1334 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 + 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +"use strict"; 1.11 + 1.12 +const Cc = Components.classes; 1.13 +const Ci = Components.interfaces; 1.14 +const Cr = Components.results; 1.15 + 1.16 +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); 1.17 +Components.utils.import("resource://gre/modules/AddonManager.jsm"); 1.18 +Components.utils.import("resource://gre/modules/Services.jsm"); 1.19 + 1.20 +XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", 1.21 + "resource://gre/modules/FileUtils.jsm"); 1.22 +XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel", 1.23 + "resource://gre/modules/UpdateChannel.jsm"); 1.24 +XPCOMUtils.defineLazyModuleGetter(this, "OS", 1.25 + "resource://gre/modules/osfile.jsm"); 1.26 +XPCOMUtils.defineLazyModuleGetter(this, "Task", 1.27 + "resource://gre/modules/Task.jsm"); 1.28 + 1.29 +const TOOLKIT_ID = "toolkit@mozilla.org" 1.30 +const KEY_PROFILEDIR = "ProfD"; 1.31 +const KEY_APPDIR = "XCurProcD"; 1.32 +const FILE_BLOCKLIST = "blocklist.xml"; 1.33 +const PREF_BLOCKLIST_LASTUPDATETIME = "app.update.lastUpdateTime.blocklist-background-update-timer"; 1.34 +const PREF_BLOCKLIST_URL = "extensions.blocklist.url"; 1.35 +const PREF_BLOCKLIST_ITEM_URL = "extensions.blocklist.itemURL"; 1.36 +const PREF_BLOCKLIST_ENABLED = "extensions.blocklist.enabled"; 1.37 +const PREF_BLOCKLIST_INTERVAL = "extensions.blocklist.interval"; 1.38 +const PREF_BLOCKLIST_LEVEL = "extensions.blocklist.level"; 1.39 +const PREF_BLOCKLIST_PINGCOUNTTOTAL = "extensions.blocklist.pingCountTotal"; 1.40 +const PREF_BLOCKLIST_PINGCOUNTVERSION = "extensions.blocklist.pingCountVersion"; 1.41 +const PREF_BLOCKLIST_SUPPRESSUI = "extensions.blocklist.suppressUI"; 1.42 +const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser"; 1.43 +const PREF_GENERAL_USERAGENT_LOCALE = "general.useragent.locale"; 1.44 +const PREF_APP_DISTRIBUTION = "distribution.id"; 1.45 +const PREF_APP_DISTRIBUTION_VERSION = "distribution.version"; 1.46 +const PREF_EM_LOGGING_ENABLED = "extensions.logging.enabled"; 1.47 +const XMLURI_BLOCKLIST = "http://www.mozilla.org/2006/addons-blocklist"; 1.48 +const XMLURI_PARSE_ERROR = "http://www.mozilla.org/newlayout/xml/parsererror.xml" 1.49 +const UNKNOWN_XPCOM_ABI = "unknownABI"; 1.50 +const URI_BLOCKLIST_DIALOG = "chrome://mozapps/content/extensions/blocklist.xul" 1.51 +const DEFAULT_SEVERITY = 3; 1.52 +const DEFAULT_LEVEL = 2; 1.53 +const MAX_BLOCK_LEVEL = 3; 1.54 +const SEVERITY_OUTDATED = 0; 1.55 +const VULNERABILITYSTATUS_NONE = 0; 1.56 +const VULNERABILITYSTATUS_UPDATE_AVAILABLE = 1; 1.57 +const VULNERABILITYSTATUS_NO_UPDATE = 2; 1.58 + 1.59 +const EXTENSION_BLOCK_FILTERS = ["id", "name", "creator", "homepageURL", "updateURL"]; 1.60 + 1.61 +var gLoggingEnabled = null; 1.62 +var gBlocklistEnabled = true; 1.63 +var gBlocklistLevel = DEFAULT_LEVEL; 1.64 + 1.65 +XPCOMUtils.defineLazyServiceGetter(this, "gConsole", 1.66 + "@mozilla.org/consoleservice;1", 1.67 + "nsIConsoleService"); 1.68 + 1.69 +XPCOMUtils.defineLazyServiceGetter(this, "gVersionChecker", 1.70 + "@mozilla.org/xpcom/version-comparator;1", 1.71 + "nsIVersionComparator"); 1.72 + 1.73 +XPCOMUtils.defineLazyGetter(this, "gPref", function bls_gPref() { 1.74 + return Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService). 1.75 + QueryInterface(Ci.nsIPrefBranch); 1.76 +}); 1.77 + 1.78 +XPCOMUtils.defineLazyGetter(this, "gApp", function bls_gApp() { 1.79 + return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo). 1.80 + QueryInterface(Ci.nsIXULRuntime); 1.81 +}); 1.82 + 1.83 +XPCOMUtils.defineLazyGetter(this, "gABI", function bls_gABI() { 1.84 + let abi = null; 1.85 + try { 1.86 + abi = gApp.XPCOMABI; 1.87 + } 1.88 + catch (e) { 1.89 + LOG("BlockList Global gABI: XPCOM ABI unknown."); 1.90 + } 1.91 +#ifdef XP_MACOSX 1.92 + // Mac universal build should report a different ABI than either macppc 1.93 + // or mactel. 1.94 + let macutils = Cc["@mozilla.org/xpcom/mac-utils;1"]. 1.95 + getService(Ci.nsIMacUtils); 1.96 + 1.97 + if (macutils.isUniversalBinary) 1.98 + abi += "-u-" + macutils.architecturesInBinary; 1.99 +#endif 1.100 + return abi; 1.101 +}); 1.102 + 1.103 +XPCOMUtils.defineLazyGetter(this, "gOSVersion", function bls_gOSVersion() { 1.104 + let osVersion; 1.105 + let sysInfo = Cc["@mozilla.org/system-info;1"]. 1.106 + getService(Ci.nsIPropertyBag2); 1.107 + try { 1.108 + osVersion = sysInfo.getProperty("name") + " " + sysInfo.getProperty("version"); 1.109 + } 1.110 + catch (e) { 1.111 + LOG("BlockList Global gOSVersion: OS Version unknown."); 1.112 + } 1.113 + 1.114 + if (osVersion) { 1.115 + try { 1.116 + osVersion += " (" + sysInfo.getProperty("secondaryLibrary") + ")"; 1.117 + } 1.118 + catch (e) { 1.119 + // Not all platforms have a secondary widget library, so an error is nothing to worry about. 1.120 + } 1.121 + osVersion = encodeURIComponent(osVersion); 1.122 + } 1.123 + return osVersion; 1.124 +}); 1.125 + 1.126 +// shared code for suppressing bad cert dialogs 1.127 +XPCOMUtils.defineLazyGetter(this, "gCertUtils", function bls_gCertUtils() { 1.128 + let temp = { }; 1.129 + Components.utils.import("resource://gre/modules/CertUtils.jsm", temp); 1.130 + return temp; 1.131 +}); 1.132 + 1.133 +function getObserverService() { 1.134 + return Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService); 1.135 +} 1.136 + 1.137 +/** 1.138 + * Logs a string to the error console. 1.139 + * @param string 1.140 + * The string to write to the error console.. 1.141 + */ 1.142 +function LOG(string) { 1.143 + if (gLoggingEnabled) { 1.144 + dump("*** " + string + "\n"); 1.145 + gConsole.logStringMessage(string); 1.146 + } 1.147 +} 1.148 + 1.149 +/** 1.150 + * Gets a preference value, handling the case where there is no default. 1.151 + * @param func 1.152 + * The name of the preference function to call, on nsIPrefBranch 1.153 + * @param preference 1.154 + * The name of the preference 1.155 + * @param defaultValue 1.156 + * The default value to return in the event the preference has 1.157 + * no setting 1.158 + * @returns The value of the preference, or undefined if there was no 1.159 + * user or default value. 1.160 + */ 1.161 +function getPref(func, preference, defaultValue) { 1.162 + try { 1.163 + return gPref[func](preference); 1.164 + } 1.165 + catch (e) { 1.166 + } 1.167 + return defaultValue; 1.168 +} 1.169 + 1.170 +/** 1.171 + * Constructs a URI to a spec. 1.172 + * @param spec 1.173 + * The spec to construct a URI to 1.174 + * @returns The nsIURI constructed. 1.175 + */ 1.176 +function newURI(spec) { 1.177 + var ioServ = Cc["@mozilla.org/network/io-service;1"]. 1.178 + getService(Ci.nsIIOService); 1.179 + return ioServ.newURI(spec, null, null); 1.180 +} 1.181 + 1.182 +// Restarts the application checking in with observers first 1.183 +function restartApp() { 1.184 + // Notify all windows that an application quit has been requested. 1.185 + var os = Cc["@mozilla.org/observer-service;1"]. 1.186 + getService(Ci.nsIObserverService); 1.187 + var cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]. 1.188 + createInstance(Ci.nsISupportsPRBool); 1.189 + os.notifyObservers(cancelQuit, "quit-application-requested", null); 1.190 + 1.191 + // Something aborted the quit process. 1.192 + if (cancelQuit.data) 1.193 + return; 1.194 + 1.195 + var as = Cc["@mozilla.org/toolkit/app-startup;1"]. 1.196 + getService(Ci.nsIAppStartup); 1.197 + as.quit(Ci.nsIAppStartup.eRestart | Ci.nsIAppStartup.eAttemptQuit); 1.198 +} 1.199 + 1.200 +/** 1.201 + * Checks whether this blocklist element is valid for the current OS and ABI. 1.202 + * If the element has an "os" attribute then the current OS must appear in 1.203 + * its comma separated list for the element to be valid. Similarly for the 1.204 + * xpcomabi attribute. 1.205 + */ 1.206 +function matchesOSABI(blocklistElement) { 1.207 + if (blocklistElement.hasAttribute("os")) { 1.208 + var choices = blocklistElement.getAttribute("os").split(","); 1.209 + if (choices.length > 0 && choices.indexOf(gApp.OS) < 0) 1.210 + return false; 1.211 + } 1.212 + 1.213 + if (blocklistElement.hasAttribute("xpcomabi")) { 1.214 + choices = blocklistElement.getAttribute("xpcomabi").split(","); 1.215 + if (choices.length > 0 && choices.indexOf(gApp.XPCOMABI) < 0) 1.216 + return false; 1.217 + } 1.218 + 1.219 + return true; 1.220 +} 1.221 + 1.222 +/** 1.223 + * Gets the current value of the locale. It's possible for this preference to 1.224 + * be localized, so we have to do a little extra work here. Similar code 1.225 + * exists in nsHttpHandler.cpp when building the UA string. 1.226 + */ 1.227 +function getLocale() { 1.228 + try { 1.229 + // Get the default branch 1.230 + var defaultPrefs = gPref.getDefaultBranch(null); 1.231 + return defaultPrefs.getComplexValue(PREF_GENERAL_USERAGENT_LOCALE, 1.232 + Ci.nsIPrefLocalizedString).data; 1.233 + } catch (e) {} 1.234 + 1.235 + return gPref.getCharPref(PREF_GENERAL_USERAGENT_LOCALE); 1.236 +} 1.237 + 1.238 +/* Get the distribution pref values, from defaults only */ 1.239 +function getDistributionPrefValue(aPrefName) { 1.240 + var prefValue = "default"; 1.241 + 1.242 + var defaults = gPref.getDefaultBranch(null); 1.243 + try { 1.244 + prefValue = defaults.getCharPref(aPrefName); 1.245 + } catch (e) { 1.246 + // use default when pref not found 1.247 + } 1.248 + 1.249 + return prefValue; 1.250 +} 1.251 + 1.252 +/** 1.253 + * Parse a string representation of a regular expression. Needed because we 1.254 + * use the /pattern/flags form (because it's detectable), which is only 1.255 + * supported as a literal in JS. 1.256 + * 1.257 + * @param aStr 1.258 + * String representation of regexp 1.259 + * @return RegExp instance 1.260 + */ 1.261 +function parseRegExp(aStr) { 1.262 + let lastSlash = aStr.lastIndexOf("/"); 1.263 + let pattern = aStr.slice(1, lastSlash); 1.264 + let flags = aStr.slice(lastSlash + 1); 1.265 + return new RegExp(pattern, flags); 1.266 +} 1.267 + 1.268 +/** 1.269 + * Manages the Blocklist. The Blocklist is a representation of the contents of 1.270 + * blocklist.xml and allows us to remotely disable / re-enable blocklisted 1.271 + * items managed by the Extension Manager with an item's appDisabled property. 1.272 + * It also blocklists plugins with data from blocklist.xml. 1.273 + */ 1.274 + 1.275 +function Blocklist() { 1.276 + let os = getObserverService(); 1.277 + os.addObserver(this, "xpcom-shutdown", false); 1.278 + gLoggingEnabled = getPref("getBoolPref", PREF_EM_LOGGING_ENABLED, false); 1.279 + gBlocklistEnabled = getPref("getBoolPref", PREF_BLOCKLIST_ENABLED, true); 1.280 + gBlocklistLevel = Math.min(getPref("getIntPref", PREF_BLOCKLIST_LEVEL, DEFAULT_LEVEL), 1.281 + MAX_BLOCK_LEVEL); 1.282 + gPref.addObserver("extensions.blocklist.", this, false); 1.283 + gPref.addObserver(PREF_EM_LOGGING_ENABLED, this, false); 1.284 +} 1.285 + 1.286 +Blocklist.prototype = { 1.287 + /** 1.288 + * Extension ID -> array of Version Ranges 1.289 + * Each value in the version range array is a JS Object that has the 1.290 + * following properties: 1.291 + * "minVersion" The minimum version in a version range (default = 0) 1.292 + * "maxVersion" The maximum version in a version range (default = *) 1.293 + * "targetApps" Application ID -> array of Version Ranges 1.294 + * (default = current application ID) 1.295 + * Each value in the version range array is a JS Object that 1.296 + * has the following properties: 1.297 + * "minVersion" The minimum version in a version range 1.298 + * (default = 0) 1.299 + * "maxVersion" The maximum version in a version range 1.300 + * (default = *) 1.301 + */ 1.302 + _addonEntries: null, 1.303 + _pluginEntries: null, 1.304 + 1.305 + observe: function Blocklist_observe(aSubject, aTopic, aData) { 1.306 + switch (aTopic) { 1.307 + case "xpcom-shutdown": 1.308 + let os = getObserverService(); 1.309 + os.removeObserver(this, "xpcom-shutdown"); 1.310 + gPref.removeObserver("extensions.blocklist.", this); 1.311 + gPref.removeObserver(PREF_EM_LOGGING_ENABLED, this); 1.312 + break; 1.313 + case "nsPref:changed": 1.314 + switch (aData) { 1.315 + case PREF_EM_LOGGING_ENABLED: 1.316 + gLoggingEnabled = getPref("getBoolPref", PREF_EM_LOGGING_ENABLED, false); 1.317 + break; 1.318 + case PREF_BLOCKLIST_ENABLED: 1.319 + gBlocklistEnabled = getPref("getBoolPref", PREF_BLOCKLIST_ENABLED, true); 1.320 + this._loadBlocklist(); 1.321 + this._blocklistUpdated(null, null); 1.322 + break; 1.323 + case PREF_BLOCKLIST_LEVEL: 1.324 + gBlocklistLevel = Math.min(getPref("getIntPref", PREF_BLOCKLIST_LEVEL, DEFAULT_LEVEL), 1.325 + MAX_BLOCK_LEVEL); 1.326 + this._blocklistUpdated(null, null); 1.327 + break; 1.328 + } 1.329 + break; 1.330 + } 1.331 + }, 1.332 + 1.333 + /* See nsIBlocklistService */ 1.334 + isAddonBlocklisted: function Blocklist_isAddonBlocklisted(addon, appVersion, toolkitVersion) { 1.335 + return this.getAddonBlocklistState(addon, appVersion, toolkitVersion) == 1.336 + Ci.nsIBlocklistService.STATE_BLOCKED; 1.337 + }, 1.338 + 1.339 + /* See nsIBlocklistService */ 1.340 + getAddonBlocklistState: function Blocklist_getAddonBlocklistState(addon, appVersion, toolkitVersion) { 1.341 + if (!this._addonEntries) 1.342 + this._loadBlocklist(); 1.343 + return this._getAddonBlocklistState(addon, this._addonEntries, 1.344 + appVersion, toolkitVersion); 1.345 + }, 1.346 + 1.347 + /** 1.348 + * Private version of getAddonBlocklistState that allows the caller to pass in 1.349 + * the add-on blocklist entries to compare against. 1.350 + * 1.351 + * @param id 1.352 + * The ID of the item to get the blocklist state for. 1.353 + * @param version 1.354 + * The version of the item to get the blocklist state for. 1.355 + * @param addonEntries 1.356 + * The add-on blocklist entries to compare against. 1.357 + * @param appVersion 1.358 + * The application version to compare to, will use the current 1.359 + * version if null. 1.360 + * @param toolkitVersion 1.361 + * The toolkit version to compare to, will use the current version if 1.362 + * null. 1.363 + * @returns The blocklist state for the item, one of the STATE constants as 1.364 + * defined in nsIBlocklistService. 1.365 + */ 1.366 + _getAddonBlocklistState: function Blocklist_getAddonBlocklistStateCall(addon, 1.367 + addonEntries, appVersion, toolkitVersion) { 1.368 + if (!gBlocklistEnabled) 1.369 + return Ci.nsIBlocklistService.STATE_NOT_BLOCKED; 1.370 + 1.371 + if (!appVersion) 1.372 + appVersion = gApp.version; 1.373 + if (!toolkitVersion) 1.374 + toolkitVersion = gApp.platformVersion; 1.375 + 1.376 + var blItem = this._findMatchingAddonEntry(addonEntries, addon); 1.377 + if (!blItem) 1.378 + return Ci.nsIBlocklistService.STATE_NOT_BLOCKED; 1.379 + 1.380 + for (let currentblItem of blItem.versions) { 1.381 + if (currentblItem.includesItem(addon.version, appVersion, toolkitVersion)) 1.382 + return currentblItem.severity >= gBlocklistLevel ? Ci.nsIBlocklistService.STATE_BLOCKED : 1.383 + Ci.nsIBlocklistService.STATE_SOFTBLOCKED; 1.384 + } 1.385 + return Ci.nsIBlocklistService.STATE_NOT_BLOCKED; 1.386 + }, 1.387 + 1.388 + /** 1.389 + * Returns the set of prefs of the add-on stored in the blocklist file 1.390 + * (probably to revert them on disabling). 1.391 + * @param addon 1.392 + * The add-on whose to-be-reset prefs are to be found. 1.393 + */ 1.394 + _getAddonPrefs: function Blocklist_getAddonPrefs(addon) { 1.395 + let entry = this._findMatchingAddonEntry(this._addonEntries, addon); 1.396 + return entry.prefs.slice(0); 1.397 + }, 1.398 + 1.399 + _findMatchingAddonEntry: function Blocklist_findMatchingAddonEntry(aAddonEntries, 1.400 + aAddon) { 1.401 + if (!aAddon) 1.402 + return null; 1.403 + // Returns true if the params object passes the constraints set by entry. 1.404 + // (For every non-null property in entry, the same key must exist in 1.405 + // params and value must be the same) 1.406 + function checkEntry(entry, params) { 1.407 + for (let [key, value] of entry) { 1.408 + if (value === null || value === undefined) 1.409 + continue; 1.410 + if (params[key]) { 1.411 + if (value instanceof RegExp) { 1.412 + if (!value.test(params[key])) { 1.413 + return false; 1.414 + } 1.415 + } else if (value !== params[key]) { 1.416 + return false; 1.417 + } 1.418 + } else { 1.419 + return false; 1.420 + } 1.421 + } 1.422 + return true; 1.423 + } 1.424 + 1.425 + let params = {}; 1.426 + for (let filter of EXTENSION_BLOCK_FILTERS) { 1.427 + params[filter] = aAddon[filter]; 1.428 + } 1.429 + if (params.creator) 1.430 + params.creator = params.creator.name; 1.431 + for (let entry of aAddonEntries) { 1.432 + if (checkEntry(entry.attributes, params)) { 1.433 + return entry; 1.434 + } 1.435 + } 1.436 + return null; 1.437 + }, 1.438 + 1.439 + /* See nsIBlocklistService */ 1.440 + getAddonBlocklistURL: function Blocklist_getAddonBlocklistURL(addon, appVersion, toolkitVersion) { 1.441 + if (!gBlocklistEnabled) 1.442 + return ""; 1.443 + 1.444 + if (!this._addonEntries) 1.445 + this._loadBlocklist(); 1.446 + 1.447 + let blItem = this._findMatchingAddonEntry(this._addonEntries, addon); 1.448 + if (!blItem || !blItem.blockID) 1.449 + return null; 1.450 + 1.451 + return this._createBlocklistURL(blItem.blockID); 1.452 + }, 1.453 + 1.454 + _createBlocklistURL: function Blocklist_createBlocklistURL(id) { 1.455 + let url = Services.urlFormatter.formatURLPref(PREF_BLOCKLIST_ITEM_URL); 1.456 + url = url.replace(/%blockID%/g, id); 1.457 + 1.458 + return url; 1.459 + }, 1.460 + 1.461 + notify: function Blocklist_notify(aTimer) { 1.462 + if (!gBlocklistEnabled) 1.463 + return; 1.464 + 1.465 + try { 1.466 + var dsURI = gPref.getCharPref(PREF_BLOCKLIST_URL); 1.467 + } 1.468 + catch (e) { 1.469 + LOG("Blocklist::notify: The " + PREF_BLOCKLIST_URL + " preference" + 1.470 + " is missing!"); 1.471 + return; 1.472 + } 1.473 + 1.474 + var pingCountVersion = getPref("getIntPref", PREF_BLOCKLIST_PINGCOUNTVERSION, 0); 1.475 + var pingCountTotal = getPref("getIntPref", PREF_BLOCKLIST_PINGCOUNTTOTAL, 1); 1.476 + var daysSinceLastPing = 0; 1.477 + if (pingCountVersion == 0) { 1.478 + daysSinceLastPing = "new"; 1.479 + } 1.480 + else { 1.481 + // Seconds in one day is used because nsIUpdateTimerManager stores the 1.482 + // last update time in seconds. 1.483 + let secondsInDay = 60 * 60 * 24; 1.484 + let lastUpdateTime = getPref("getIntPref", PREF_BLOCKLIST_LASTUPDATETIME, 0); 1.485 + if (lastUpdateTime == 0) { 1.486 + daysSinceLastPing = "invalid"; 1.487 + } 1.488 + else { 1.489 + let now = Math.round(Date.now() / 1000); 1.490 + daysSinceLastPing = Math.floor((now - lastUpdateTime) / secondsInDay); 1.491 + } 1.492 + 1.493 + if (daysSinceLastPing == 0 || daysSinceLastPing == "invalid") { 1.494 + pingCountVersion = pingCountTotal = "invalid"; 1.495 + } 1.496 + } 1.497 + 1.498 + if (pingCountVersion < 1) 1.499 + pingCountVersion = 1; 1.500 + if (pingCountTotal < 1) 1.501 + pingCountTotal = 1; 1.502 + 1.503 + dsURI = dsURI.replace(/%APP_ID%/g, gApp.ID); 1.504 + dsURI = dsURI.replace(/%APP_VERSION%/g, gApp.version); 1.505 + dsURI = dsURI.replace(/%PRODUCT%/g, gApp.name); 1.506 + dsURI = dsURI.replace(/%VERSION%/g, gApp.version); 1.507 + dsURI = dsURI.replace(/%BUILD_ID%/g, gApp.appBuildID); 1.508 + dsURI = dsURI.replace(/%BUILD_TARGET%/g, gApp.OS + "_" + gABI); 1.509 + dsURI = dsURI.replace(/%OS_VERSION%/g, gOSVersion); 1.510 + dsURI = dsURI.replace(/%LOCALE%/g, getLocale()); 1.511 + dsURI = dsURI.replace(/%CHANNEL%/g, UpdateChannel.get()); 1.512 + dsURI = dsURI.replace(/%PLATFORM_VERSION%/g, gApp.platformVersion); 1.513 + dsURI = dsURI.replace(/%DISTRIBUTION%/g, 1.514 + getDistributionPrefValue(PREF_APP_DISTRIBUTION)); 1.515 + dsURI = dsURI.replace(/%DISTRIBUTION_VERSION%/g, 1.516 + getDistributionPrefValue(PREF_APP_DISTRIBUTION_VERSION)); 1.517 + dsURI = dsURI.replace(/%PING_COUNT%/g, pingCountVersion); 1.518 + dsURI = dsURI.replace(/%TOTAL_PING_COUNT%/g, pingCountTotal); 1.519 + dsURI = dsURI.replace(/%DAYS_SINCE_LAST_PING%/g, daysSinceLastPing); 1.520 + dsURI = dsURI.replace(/\+/g, "%2B"); 1.521 + 1.522 + // Under normal operations it will take around 5,883,516 years before the 1.523 + // preferences used to store pingCountVersion and pingCountTotal will rollover 1.524 + // so this code doesn't bother trying to do the "right thing" here. 1.525 + if (pingCountVersion != "invalid") { 1.526 + pingCountVersion++; 1.527 + if (pingCountVersion > 2147483647) { 1.528 + // Rollover to -1 if the value is greater than what is support by an 1.529 + // integer preference. The -1 indicates that the counter has been reset. 1.530 + pingCountVersion = -1; 1.531 + } 1.532 + gPref.setIntPref(PREF_BLOCKLIST_PINGCOUNTVERSION, pingCountVersion); 1.533 + } 1.534 + 1.535 + if (pingCountTotal != "invalid") { 1.536 + pingCountTotal++; 1.537 + if (pingCountTotal > 2147483647) { 1.538 + // Rollover to 1 if the value is greater than what is support by an 1.539 + // integer preference. 1.540 + pingCountTotal = -1; 1.541 + } 1.542 + gPref.setIntPref(PREF_BLOCKLIST_PINGCOUNTTOTAL, pingCountTotal); 1.543 + } 1.544 + 1.545 + // Verify that the URI is valid 1.546 + try { 1.547 + var uri = newURI(dsURI); 1.548 + } 1.549 + catch (e) { 1.550 + LOG("Blocklist::notify: There was an error creating the blocklist URI\r\n" + 1.551 + "for: " + dsURI + ", error: " + e); 1.552 + return; 1.553 + } 1.554 + 1.555 + LOG("Blocklist::notify: Requesting " + uri.spec); 1.556 + var request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]. 1.557 + createInstance(Ci.nsIXMLHttpRequest); 1.558 + request.open("GET", uri.spec, true); 1.559 + request.channel.notificationCallbacks = new gCertUtils.BadCertHandler(); 1.560 + request.overrideMimeType("text/xml"); 1.561 + request.setRequestHeader("Cache-Control", "no-cache"); 1.562 + request.QueryInterface(Components.interfaces.nsIJSXMLHttpRequest); 1.563 + 1.564 + var self = this; 1.565 + request.addEventListener("error", function errorEventListener(event) { 1.566 + self.onXMLError(event); }, false); 1.567 + request.addEventListener("load", function loadEventListener(event) { 1.568 + self.onXMLLoad(event); }, false); 1.569 + request.send(null); 1.570 + 1.571 + // When the blocklist loads we need to compare it to the current copy so 1.572 + // make sure we have loaded it. 1.573 + if (!this._addonEntries) 1.574 + this._loadBlocklist(); 1.575 + }, 1.576 + 1.577 + onXMLLoad: Task.async(function* (aEvent) { 1.578 + let request = aEvent.target; 1.579 + try { 1.580 + gCertUtils.checkCert(request.channel); 1.581 + } 1.582 + catch (e) { 1.583 + LOG("Blocklist::onXMLLoad: " + e); 1.584 + return; 1.585 + } 1.586 + let responseXML = request.responseXML; 1.587 + if (!responseXML || responseXML.documentElement.namespaceURI == XMLURI_PARSE_ERROR || 1.588 + (request.status != 200 && request.status != 0)) { 1.589 + LOG("Blocklist::onXMLLoad: there was an error during load"); 1.590 + return; 1.591 + } 1.592 + 1.593 + var oldAddonEntries = this._addonEntries; 1.594 + var oldPluginEntries = this._pluginEntries; 1.595 + this._addonEntries = []; 1.596 + this._pluginEntries = []; 1.597 + 1.598 + this._loadBlocklistFromString(request.responseText); 1.599 + this._blocklistUpdated(oldAddonEntries, oldPluginEntries); 1.600 + 1.601 + try { 1.602 + let path = OS.Path.join(OS.Constants.Path.profileDir, FILE_BLOCKLIST); 1.603 + yield OS.File.writeAtomic(path, request.responseText, {tmpPath: path + ".tmp"}); 1.604 + } catch (e) { 1.605 + LOG("Blocklist::onXMLLoad: " + e); 1.606 + } 1.607 + }), 1.608 + 1.609 + onXMLError: function Blocklist_onXMLError(aEvent) { 1.610 + try { 1.611 + var request = aEvent.target; 1.612 + // the following may throw (e.g. a local file or timeout) 1.613 + var status = request.status; 1.614 + } 1.615 + catch (e) { 1.616 + request = aEvent.target.channel.QueryInterface(Ci.nsIRequest); 1.617 + status = request.status; 1.618 + } 1.619 + var statusText = "nsIXMLHttpRequest channel unavailable"; 1.620 + // When status is 0 we don't have a valid channel. 1.621 + if (status != 0) { 1.622 + try { 1.623 + statusText = request.statusText; 1.624 + } catch (e) { 1.625 + } 1.626 + } 1.627 + LOG("Blocklist:onError: There was an error loading the blocklist file\r\n" + 1.628 + statusText); 1.629 + }, 1.630 + 1.631 + /** 1.632 + * Finds the newest blocklist file from the application and the profile and 1.633 + * load it or does nothing if neither exist. 1.634 + */ 1.635 + _loadBlocklist: function Blocklist_loadBlocklist() { 1.636 + this._addonEntries = []; 1.637 + this._pluginEntries = []; 1.638 + var profFile = FileUtils.getFile(KEY_PROFILEDIR, [FILE_BLOCKLIST]); 1.639 + if (profFile.exists()) { 1.640 + this._loadBlocklistFromFile(profFile); 1.641 + return; 1.642 + } 1.643 + var appFile = FileUtils.getFile(KEY_APPDIR, [FILE_BLOCKLIST]); 1.644 + if (appFile.exists()) { 1.645 + this._loadBlocklistFromFile(appFile); 1.646 + return; 1.647 + } 1.648 + LOG("Blocklist::_loadBlocklist: no XML File found"); 1.649 + }, 1.650 + 1.651 + /** 1.652 +# The blocklist XML file looks something like this: 1.653 +# 1.654 +# <blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist"> 1.655 +# <emItems> 1.656 +# <emItem id="item_1@domain" blockID="i1"> 1.657 +# <prefs> 1.658 +# <pref>accessibility.accesskeycausesactivation</pref> 1.659 +# <pref>accessibility.blockautorefresh</pref> 1.660 +# </prefs> 1.661 +# <versionRange minVersion="1.0" maxVersion="2.0.*"> 1.662 +# <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"> 1.663 +# <versionRange minVersion="1.5" maxVersion="1.5.*"/> 1.664 +# <versionRange minVersion="1.7" maxVersion="1.7.*"/> 1.665 +# </targetApplication> 1.666 +# <targetApplication id="toolkit@mozilla.org"> 1.667 +# <versionRange minVersion="1.9" maxVersion="1.9.*"/> 1.668 +# </targetApplication> 1.669 +# </versionRange> 1.670 +# <versionRange minVersion="3.0" maxVersion="3.0.*"> 1.671 +# <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"> 1.672 +# <versionRange minVersion="1.5" maxVersion="1.5.*"/> 1.673 +# </targetApplication> 1.674 +# <targetApplication id="toolkit@mozilla.org"> 1.675 +# <versionRange minVersion="1.9" maxVersion="1.9.*"/> 1.676 +# </targetApplication> 1.677 +# </versionRange> 1.678 +# </emItem> 1.679 +# <emItem id="item_2@domain" blockID="i2"> 1.680 +# <versionRange minVersion="3.1" maxVersion="4.*"/> 1.681 +# </emItem> 1.682 +# <emItem id="item_3@domain"> 1.683 +# <versionRange> 1.684 +# <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"> 1.685 +# <versionRange minVersion="1.5" maxVersion="1.5.*"/> 1.686 +# </targetApplication> 1.687 +# </versionRange> 1.688 +# </emItem> 1.689 +# <emItem id="item_4@domain" blockID="i3"> 1.690 +# <versionRange> 1.691 +# <targetApplication> 1.692 +# <versionRange minVersion="1.5" maxVersion="1.5.*"/> 1.693 +# </targetApplication> 1.694 +# </versionRange> 1.695 +# <emItem id="/@badperson\.com$/"/> 1.696 +# </emItems> 1.697 +# <pluginItems> 1.698 +# <pluginItem blockID="i4"> 1.699 +# <!-- All match tags must match a plugin to blocklist a plugin --> 1.700 +# <match name="name" exp="some plugin"/> 1.701 +# <match name="description" exp="1[.]2[.]3"/> 1.702 +# </pluginItem> 1.703 +# </pluginItems> 1.704 +# </blocklist> 1.705 + */ 1.706 + 1.707 + _loadBlocklistFromFile: function Blocklist_loadBlocklistFromFile(file) { 1.708 + if (!gBlocklistEnabled) { 1.709 + LOG("Blocklist::_loadBlocklistFromFile: blocklist is disabled"); 1.710 + return; 1.711 + } 1.712 + 1.713 + if (!file.exists()) { 1.714 + LOG("Blocklist::_loadBlocklistFromFile: XML File does not exist " + file.path); 1.715 + return; 1.716 + } 1.717 + 1.718 + let text = ""; 1.719 + let fstream = null; 1.720 + let cstream = null; 1.721 + 1.722 + try { 1.723 + fstream = Components.classes["@mozilla.org/network/file-input-stream;1"] 1.724 + .createInstance(Components.interfaces.nsIFileInputStream); 1.725 + cstream = Components.classes["@mozilla.org/intl/converter-input-stream;1"] 1.726 + .createInstance(Components.interfaces.nsIConverterInputStream); 1.727 + 1.728 + fstream.init(file, FileUtils.MODE_RDONLY, FileUtils.PERMS_FILE, 0); 1.729 + cstream.init(fstream, "UTF-8", 0, 0); 1.730 + 1.731 + let (str = {}) { 1.732 + let read = 0; 1.733 + 1.734 + do { 1.735 + read = cstream.readString(0xffffffff, str); // read as much as we can and put it in str.value 1.736 + text += str.value; 1.737 + } while (read != 0); 1.738 + } 1.739 + } catch (e) { 1.740 + LOG("Blocklist::_loadBlocklistFromFile: Failed to load XML file " + e); 1.741 + } finally { 1.742 + cstream.close(); 1.743 + fstream.close(); 1.744 + } 1.745 + 1.746 + text && this._loadBlocklistFromString(text); 1.747 + }, 1.748 + 1.749 + _loadBlocklistFromString : function Blocklist_loadBlocklistFromString(text) { 1.750 + try { 1.751 + var parser = Cc["@mozilla.org/xmlextras/domparser;1"]. 1.752 + createInstance(Ci.nsIDOMParser); 1.753 + var doc = parser.parseFromString(text, "text/xml"); 1.754 + if (doc.documentElement.namespaceURI != XMLURI_BLOCKLIST) { 1.755 + LOG("Blocklist::_loadBlocklistFromFile: aborting due to incorrect " + 1.756 + "XML Namespace.\r\nExpected: " + XMLURI_BLOCKLIST + "\r\n" + 1.757 + "Received: " + doc.documentElement.namespaceURI); 1.758 + return; 1.759 + } 1.760 + 1.761 + var childNodes = doc.documentElement.childNodes; 1.762 + for (let element of childNodes) { 1.763 + if (!(element instanceof Ci.nsIDOMElement)) 1.764 + continue; 1.765 + switch (element.localName) { 1.766 + case "emItems": 1.767 + this._addonEntries = this._processItemNodes(element.childNodes, "em", 1.768 + this._handleEmItemNode); 1.769 + break; 1.770 + case "pluginItems": 1.771 + this._pluginEntries = this._processItemNodes(element.childNodes, "plugin", 1.772 + this._handlePluginItemNode); 1.773 + break; 1.774 + default: 1.775 + Services.obs.notifyObservers(element, 1.776 + "blocklist-data-" + element.localName, 1.777 + null); 1.778 + } 1.779 + } 1.780 + } 1.781 + catch (e) { 1.782 + LOG("Blocklist::_loadBlocklistFromFile: Error constructing blocklist " + e); 1.783 + return; 1.784 + } 1.785 + }, 1.786 + 1.787 + _processItemNodes: function Blocklist_processItemNodes(itemNodes, prefix, handler) { 1.788 + var result = []; 1.789 + var itemName = prefix + "Item"; 1.790 + for (var i = 0; i < itemNodes.length; ++i) { 1.791 + var blocklistElement = itemNodes.item(i); 1.792 + if (!(blocklistElement instanceof Ci.nsIDOMElement) || 1.793 + blocklistElement.localName != itemName) 1.794 + continue; 1.795 + 1.796 + handler(blocklistElement, result); 1.797 + } 1.798 + return result; 1.799 + }, 1.800 + 1.801 + _handleEmItemNode: function Blocklist_handleEmItemNode(blocklistElement, result) { 1.802 + if (!matchesOSABI(blocklistElement)) 1.803 + return; 1.804 + 1.805 + let blockEntry = { 1.806 + versions: [], 1.807 + prefs: [], 1.808 + blockID: null, 1.809 + attributes: new Map() 1.810 + // Atleast one of EXTENSION_BLOCK_FILTERS must get added to attributes 1.811 + }; 1.812 + 1.813 + // Any filter starting with '/' is interpreted as a regex. So if an attribute 1.814 + // starts with a '/' it must be checked via a regex. 1.815 + function regExpCheck(attr) { 1.816 + return attr.startsWith("/") ? parseRegExp(attr) : attr; 1.817 + } 1.818 + 1.819 + for (let filter of EXTENSION_BLOCK_FILTERS) { 1.820 + let attr = blocklistElement.getAttribute(filter); 1.821 + if (attr) 1.822 + blockEntry.attributes.set(filter, regExpCheck(attr)); 1.823 + } 1.824 + 1.825 + var childNodes = blocklistElement.childNodes; 1.826 + 1.827 + for (let x = 0; x < childNodes.length; x++) { 1.828 + var childElement = childNodes.item(x); 1.829 + if (!(childElement instanceof Ci.nsIDOMElement)) 1.830 + continue; 1.831 + if (childElement.localName === "prefs") { 1.832 + let prefElements = childElement.childNodes; 1.833 + for (let i = 0; i < prefElements.length; i++) { 1.834 + let prefElement = prefElements.item(i); 1.835 + if (!(prefElement instanceof Ci.nsIDOMElement) || 1.836 + prefElement.localName !== "pref") 1.837 + continue; 1.838 + blockEntry.prefs.push(prefElement.textContent); 1.839 + } 1.840 + } 1.841 + else if (childElement.localName === "versionRange") 1.842 + blockEntry.versions.push(new BlocklistItemData(childElement)); 1.843 + } 1.844 + // if only the extension ID is specified block all versions of the 1.845 + // extension for the current application. 1.846 + if (blockEntry.versions.length == 0) 1.847 + blockEntry.versions.push(new BlocklistItemData(null)); 1.848 + 1.849 + blockEntry.blockID = blocklistElement.getAttribute("blockID"); 1.850 + 1.851 + result.push(blockEntry); 1.852 + }, 1.853 + 1.854 + _handlePluginItemNode: function Blocklist_handlePluginItemNode(blocklistElement, result) { 1.855 + if (!matchesOSABI(blocklistElement)) 1.856 + return; 1.857 + 1.858 + var matchNodes = blocklistElement.childNodes; 1.859 + var blockEntry = { 1.860 + matches: {}, 1.861 + versions: [], 1.862 + blockID: null, 1.863 + }; 1.864 + var hasMatch = false; 1.865 + for (var x = 0; x < matchNodes.length; ++x) { 1.866 + var matchElement = matchNodes.item(x); 1.867 + if (!(matchElement instanceof Ci.nsIDOMElement)) 1.868 + continue; 1.869 + if (matchElement.localName == "match") { 1.870 + var name = matchElement.getAttribute("name"); 1.871 + var exp = matchElement.getAttribute("exp"); 1.872 + try { 1.873 + blockEntry.matches[name] = new RegExp(exp, "m"); 1.874 + hasMatch = true; 1.875 + } catch (e) { 1.876 + // Ignore invalid regular expressions 1.877 + } 1.878 + } 1.879 + if (matchElement.localName == "versionRange") 1.880 + blockEntry.versions.push(new BlocklistItemData(matchElement)); 1.881 + } 1.882 + // Plugin entries require *something* to match to an actual plugin 1.883 + if (!hasMatch) 1.884 + return; 1.885 + // Add a default versionRange if there wasn't one specified 1.886 + if (blockEntry.versions.length == 0) 1.887 + blockEntry.versions.push(new BlocklistItemData(null)); 1.888 + 1.889 + blockEntry.blockID = blocklistElement.getAttribute("blockID"); 1.890 + 1.891 + result.push(blockEntry); 1.892 + }, 1.893 + 1.894 + /* See nsIBlocklistService */ 1.895 + getPluginBlocklistState: function Blocklist_getPluginBlocklistState(plugin, 1.896 + appVersion, toolkitVersion) { 1.897 + if (!this._pluginEntries) 1.898 + this._loadBlocklist(); 1.899 + return this._getPluginBlocklistState(plugin, this._pluginEntries, 1.900 + appVersion, toolkitVersion); 1.901 + }, 1.902 + 1.903 + /** 1.904 + * Private version of getPluginBlocklistState that allows the caller to pass in 1.905 + * the plugin blocklist entries. 1.906 + * 1.907 + * @param plugin 1.908 + * The nsIPluginTag to get the blocklist state for. 1.909 + * @param pluginEntries 1.910 + * The plugin blocklist entries to compare against. 1.911 + * @param appVersion 1.912 + * The application version to compare to, will use the current 1.913 + * version if null. 1.914 + * @param toolkitVersion 1.915 + * The toolkit version to compare to, will use the current version if 1.916 + * null. 1.917 + * @returns The blocklist state for the item, one of the STATE constants as 1.918 + * defined in nsIBlocklistService. 1.919 + */ 1.920 + _getPluginBlocklistState: function Blocklist_getPluginBlocklistState(plugin, 1.921 + pluginEntries, appVersion, toolkitVersion) { 1.922 + if (!gBlocklistEnabled) 1.923 + return Ci.nsIBlocklistService.STATE_NOT_BLOCKED; 1.924 + 1.925 + if (!appVersion) 1.926 + appVersion = gApp.version; 1.927 + if (!toolkitVersion) 1.928 + toolkitVersion = gApp.platformVersion; 1.929 + 1.930 + for each (var blockEntry in pluginEntries) { 1.931 + var matchFailed = false; 1.932 + for (var name in blockEntry.matches) { 1.933 + if (!(name in plugin) || 1.934 + typeof(plugin[name]) != "string" || 1.935 + !blockEntry.matches[name].test(plugin[name])) { 1.936 + matchFailed = true; 1.937 + break; 1.938 + } 1.939 + } 1.940 + 1.941 + if (matchFailed) 1.942 + continue; 1.943 + 1.944 + for (let blockEntryVersion of blockEntry.versions) { 1.945 + if (blockEntryVersion.includesItem(plugin.version, appVersion, 1.946 + toolkitVersion)) { 1.947 + if (blockEntryVersion.severity >= gBlocklistLevel) 1.948 + return Ci.nsIBlocklistService.STATE_BLOCKED; 1.949 + if (blockEntryVersion.severity == SEVERITY_OUTDATED) { 1.950 + let vulnerabilityStatus = blockEntryVersion.vulnerabilityStatus; 1.951 + if (vulnerabilityStatus == VULNERABILITYSTATUS_UPDATE_AVAILABLE) 1.952 + return Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE; 1.953 + if (vulnerabilityStatus == VULNERABILITYSTATUS_NO_UPDATE) 1.954 + return Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE; 1.955 + return Ci.nsIBlocklistService.STATE_OUTDATED; 1.956 + } 1.957 + return Ci.nsIBlocklistService.STATE_SOFTBLOCKED; 1.958 + } 1.959 + } 1.960 + } 1.961 + 1.962 + return Ci.nsIBlocklistService.STATE_NOT_BLOCKED; 1.963 + }, 1.964 + 1.965 + /* See nsIBlocklistService */ 1.966 + getPluginBlocklistURL: function Blocklist_getPluginBlocklistURL(plugin) { 1.967 + if (!gBlocklistEnabled) 1.968 + return ""; 1.969 + 1.970 + if (!this._pluginEntries) 1.971 + this._loadBlocklist(); 1.972 + 1.973 + for each (let blockEntry in this._pluginEntries) { 1.974 + let matchFailed = false; 1.975 + for (let name in blockEntry.matches) { 1.976 + if (!(name in plugin) || 1.977 + typeof(plugin[name]) != "string" || 1.978 + !blockEntry.matches[name].test(plugin[name])) { 1.979 + matchFailed = true; 1.980 + break; 1.981 + } 1.982 + } 1.983 + 1.984 + if (!matchFailed) { 1.985 + if(!blockEntry.blockID) 1.986 + return null; 1.987 + else 1.988 + return this._createBlocklistURL(blockEntry.blockID); 1.989 + } 1.990 + } 1.991 + }, 1.992 + 1.993 + _blocklistUpdated: function Blocklist_blocklistUpdated(oldAddonEntries, oldPluginEntries) { 1.994 + var addonList = []; 1.995 + 1.996 + // A helper function that reverts the prefs passed to default values. 1.997 + function resetPrefs(prefs) { 1.998 + for (let pref of prefs) 1.999 + gPref.clearUserPref(pref); 1.1000 + } 1.1001 + var self = this; 1.1002 + const types = ["extension", "theme", "locale", "dictionary", "service"]; 1.1003 + AddonManager.getAddonsByTypes(types, function blocklistUpdated_getAddonsByTypes(addons) { 1.1004 + 1.1005 + for (let addon of addons) { 1.1006 + let oldState = Ci.nsIBlocklistService.STATE_NOTBLOCKED; 1.1007 + if (oldAddonEntries) 1.1008 + oldState = self._getAddonBlocklistState(addon, oldAddonEntries); 1.1009 + let state = self.getAddonBlocklistState(addon); 1.1010 + 1.1011 + LOG("Blocklist state for " + addon.id + " changed from " + 1.1012 + oldState + " to " + state); 1.1013 + 1.1014 + // We don't want to re-warn about add-ons 1.1015 + if (state == oldState) 1.1016 + continue; 1.1017 + 1.1018 + if (state === Ci.nsIBlocklistService.STATE_BLOCKED) { 1.1019 + // It's a hard block. We must reset certain preferences. 1.1020 + let prefs = self._getAddonPrefs(addon); 1.1021 + resetPrefs(prefs); 1.1022 + } 1.1023 + 1.1024 + // Ensure that softDisabled is false if the add-on is not soft blocked 1.1025 + if (state != Ci.nsIBlocklistService.STATE_SOFTBLOCKED) 1.1026 + addon.softDisabled = false; 1.1027 + 1.1028 + // Don't warn about add-ons becoming unblocked. 1.1029 + if (state == Ci.nsIBlocklistService.STATE_NOT_BLOCKED) 1.1030 + continue; 1.1031 + 1.1032 + // If an add-on has dropped from hard to soft blocked just mark it as 1.1033 + // soft disabled and don't warn about it. 1.1034 + if (state == Ci.nsIBlocklistService.STATE_SOFTBLOCKED && 1.1035 + oldState == Ci.nsIBlocklistService.STATE_BLOCKED) { 1.1036 + addon.softDisabled = true; 1.1037 + continue; 1.1038 + } 1.1039 + 1.1040 + // If the add-on is already disabled for some reason then don't warn 1.1041 + // about it 1.1042 + if (!addon.isActive) 1.1043 + continue; 1.1044 + 1.1045 + addonList.push({ 1.1046 + name: addon.name, 1.1047 + version: addon.version, 1.1048 + icon: addon.iconURL, 1.1049 + disable: false, 1.1050 + blocked: state == Ci.nsIBlocklistService.STATE_BLOCKED, 1.1051 + item: addon, 1.1052 + url: self.getAddonBlocklistURL(addon), 1.1053 + }); 1.1054 + } 1.1055 + 1.1056 + AddonManagerPrivate.updateAddonAppDisabledStates(); 1.1057 + 1.1058 + var phs = Cc["@mozilla.org/plugin/host;1"]. 1.1059 + getService(Ci.nsIPluginHost); 1.1060 + var plugins = phs.getPluginTags(); 1.1061 + 1.1062 + for (let plugin of plugins) { 1.1063 + let oldState = -1; 1.1064 + if (oldPluginEntries) 1.1065 + oldState = self._getPluginBlocklistState(plugin, oldPluginEntries); 1.1066 + let state = self.getPluginBlocklistState(plugin); 1.1067 + LOG("Blocklist state for " + plugin.name + " changed from " + 1.1068 + oldState + " to " + state); 1.1069 + // We don't want to re-warn about items 1.1070 + if (state == oldState) 1.1071 + continue; 1.1072 + 1.1073 + if (oldState == Ci.nsIBlocklistService.STATE_BLOCKED) { 1.1074 + if (state == Ci.nsIBlocklistService.STATE_SOFTBLOCKED) 1.1075 + plugin.enabledState = Ci.nsIPluginTag.STATE_DISABLED; 1.1076 + } 1.1077 + else if (!plugin.disabled && state != Ci.nsIBlocklistService.STATE_NOT_BLOCKED) { 1.1078 + if (state == Ci.nsIBlocklistService.STATE_OUTDATED) { 1.1079 + gPref.setBoolPref(PREF_PLUGINS_NOTIFYUSER, true); 1.1080 + } 1.1081 + else if (state != Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE && 1.1082 + state != Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE) { 1.1083 + addonList.push({ 1.1084 + name: plugin.name, 1.1085 + version: plugin.version, 1.1086 + icon: "chrome://mozapps/skin/plugins/pluginGeneric.png", 1.1087 + disable: false, 1.1088 + blocked: state == Ci.nsIBlocklistService.STATE_BLOCKED, 1.1089 + item: plugin, 1.1090 + url: self.getPluginBlocklistURL(plugin), 1.1091 + }); 1.1092 + } 1.1093 + } 1.1094 + } 1.1095 + 1.1096 + if (addonList.length == 0) { 1.1097 + Services.obs.notifyObservers(self, "blocklist-updated", ""); 1.1098 + return; 1.1099 + } 1.1100 + 1.1101 + if ("@mozilla.org/addons/blocklist-prompt;1" in Cc) { 1.1102 + try { 1.1103 + let blockedPrompter = Cc["@mozilla.org/addons/blocklist-prompt;1"] 1.1104 + .getService(Ci.nsIBlocklistPrompt); 1.1105 + blockedPrompter.prompt(addonList); 1.1106 + } catch (e) { 1.1107 + LOG(e); 1.1108 + } 1.1109 + Services.obs.notifyObservers(self, "blocklist-updated", ""); 1.1110 + return; 1.1111 + } 1.1112 + 1.1113 + var args = { 1.1114 + restart: false, 1.1115 + list: addonList 1.1116 + }; 1.1117 + // This lets the dialog get the raw js object 1.1118 + args.wrappedJSObject = args; 1.1119 + 1.1120 + /* 1.1121 + Some tests run without UI, so the async code listens to a message 1.1122 + that can be sent programatically 1.1123 + */ 1.1124 + let applyBlocklistChanges = function blocklistUpdated_applyBlocklistChanges() { 1.1125 + for (let addon of addonList) { 1.1126 + if (!addon.disable) 1.1127 + continue; 1.1128 + 1.1129 + if (addon.item instanceof Ci.nsIPluginTag) 1.1130 + addon.item.enabledState = Ci.nsIPluginTag.STATE_DISABLED; 1.1131 + else { 1.1132 + // This add-on is softblocked. 1.1133 + addon.item.softDisabled = true; 1.1134 + // We must revert certain prefs. 1.1135 + let prefs = self._getAddonPrefs(addon.item); 1.1136 + resetPrefs(prefs); 1.1137 + } 1.1138 + } 1.1139 + 1.1140 + if (args.restart) 1.1141 + restartApp(); 1.1142 + 1.1143 + Services.obs.notifyObservers(self, "blocklist-updated", ""); 1.1144 + Services.obs.removeObserver(applyBlocklistChanges, "addon-blocklist-closed"); 1.1145 + } 1.1146 + 1.1147 + Services.obs.addObserver(applyBlocklistChanges, "addon-blocklist-closed", false); 1.1148 + 1.1149 + if (getPref("getBoolPref", PREF_BLOCKLIST_SUPPRESSUI, false)) { 1.1150 + applyBlocklistChanges(); 1.1151 + return; 1.1152 + } 1.1153 + 1.1154 + function blocklistUnloadHandler(event) { 1.1155 + if (event.target.location == URI_BLOCKLIST_DIALOG) { 1.1156 + applyBlocklistChanges(); 1.1157 + blocklistWindow.removeEventListener("unload", blocklistUnloadHandler); 1.1158 + } 1.1159 + } 1.1160 + 1.1161 + let blocklistWindow = Services.ww.openWindow(null, URI_BLOCKLIST_DIALOG, "", 1.1162 + "chrome,centerscreen,dialog,titlebar", args); 1.1163 + if (blocklistWindow) 1.1164 + blocklistWindow.addEventListener("unload", blocklistUnloadHandler, false); 1.1165 + }); 1.1166 + }, 1.1167 + 1.1168 + classID: Components.ID("{66354bc9-7ed1-4692-ae1d-8da97d6b205e}"), 1.1169 + QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, 1.1170 + Ci.nsIBlocklistService, 1.1171 + Ci.nsITimerCallback]), 1.1172 +}; 1.1173 + 1.1174 +/** 1.1175 + * Helper for constructing a blocklist. 1.1176 + */ 1.1177 +function BlocklistItemData(versionRangeElement) { 1.1178 + var versionRange = this.getBlocklistVersionRange(versionRangeElement); 1.1179 + this.minVersion = versionRange.minVersion; 1.1180 + this.maxVersion = versionRange.maxVersion; 1.1181 + if (versionRangeElement && versionRangeElement.hasAttribute("severity")) 1.1182 + this.severity = versionRangeElement.getAttribute("severity"); 1.1183 + else 1.1184 + this.severity = DEFAULT_SEVERITY; 1.1185 + if (versionRangeElement && versionRangeElement.hasAttribute("vulnerabilitystatus")) { 1.1186 + this.vulnerabilityStatus = versionRangeElement.getAttribute("vulnerabilitystatus"); 1.1187 + } else { 1.1188 + this.vulnerabilityStatus = VULNERABILITYSTATUS_NONE; 1.1189 + } 1.1190 + this.targetApps = { }; 1.1191 + var found = false; 1.1192 + 1.1193 + if (versionRangeElement) { 1.1194 + for (var i = 0; i < versionRangeElement.childNodes.length; ++i) { 1.1195 + var targetAppElement = versionRangeElement.childNodes.item(i); 1.1196 + if (!(targetAppElement instanceof Ci.nsIDOMElement) || 1.1197 + targetAppElement.localName != "targetApplication") 1.1198 + continue; 1.1199 + found = true; 1.1200 + // default to the current application if id is not provided. 1.1201 + var appID = targetAppElement.hasAttribute("id") ? targetAppElement.getAttribute("id") : gApp.ID; 1.1202 + this.targetApps[appID] = this.getBlocklistAppVersions(targetAppElement); 1.1203 + } 1.1204 + } 1.1205 + // Default to all versions of the current application when no targetApplication 1.1206 + // elements were found 1.1207 + if (!found) 1.1208 + this.targetApps[gApp.ID] = this.getBlocklistAppVersions(null); 1.1209 +} 1.1210 + 1.1211 +BlocklistItemData.prototype = { 1.1212 + /** 1.1213 + * Tests if a version of an item is included in the version range and target 1.1214 + * application information represented by this BlocklistItemData using the 1.1215 + * provided application and toolkit versions. 1.1216 + * @param version 1.1217 + * The version of the item being tested. 1.1218 + * @param appVersion 1.1219 + * The application version to test with. 1.1220 + * @param toolkitVersion 1.1221 + * The toolkit version to test with. 1.1222 + * @returns True if the version range covers the item version and application 1.1223 + * or toolkit version. 1.1224 + */ 1.1225 + includesItem: function BlocklistItemData_includesItem(version, appVersion, toolkitVersion) { 1.1226 + // Some platforms have no version for plugins, these don't match if there 1.1227 + // was a min/maxVersion provided 1.1228 + if (!version && (this.minVersion || this.maxVersion)) 1.1229 + return false; 1.1230 + 1.1231 + // Check if the item version matches 1.1232 + if (!this.matchesRange(version, this.minVersion, this.maxVersion)) 1.1233 + return false; 1.1234 + 1.1235 + // Check if the application version matches 1.1236 + if (this.matchesTargetRange(gApp.ID, appVersion)) 1.1237 + return true; 1.1238 + 1.1239 + // Check if the toolkit version matches 1.1240 + return this.matchesTargetRange(TOOLKIT_ID, toolkitVersion); 1.1241 + }, 1.1242 + 1.1243 + /** 1.1244 + * Checks if a version is higher than or equal to the minVersion (if provided) 1.1245 + * and lower than or equal to the maxVersion (if provided). 1.1246 + * @param version 1.1247 + * The version to test. 1.1248 + * @param minVersion 1.1249 + * The minimum version. If null it is assumed that version is always 1.1250 + * larger. 1.1251 + * @param maxVersion 1.1252 + * The maximum version. If null it is assumed that version is always 1.1253 + * smaller. 1.1254 + */ 1.1255 + matchesRange: function BlocklistItemData_matchesRange(version, minVersion, maxVersion) { 1.1256 + if (minVersion && gVersionChecker.compare(version, minVersion) < 0) 1.1257 + return false; 1.1258 + if (maxVersion && gVersionChecker.compare(version, maxVersion) > 0) 1.1259 + return false; 1.1260 + return true; 1.1261 + }, 1.1262 + 1.1263 + /** 1.1264 + * Tests if there is a matching range for the given target application id and 1.1265 + * version. 1.1266 + * @param appID 1.1267 + * The application ID to test for, may be for an application or toolkit 1.1268 + * @param appVersion 1.1269 + * The version of the application to test for. 1.1270 + * @returns True if this version range covers the application version given. 1.1271 + */ 1.1272 + matchesTargetRange: function BlocklistItemData_matchesTargetRange(appID, appVersion) { 1.1273 + var blTargetApp = this.targetApps[appID]; 1.1274 + if (!blTargetApp) 1.1275 + return false; 1.1276 + 1.1277 + for (let app of blTargetApp) { 1.1278 + if (this.matchesRange(appVersion, app.minVersion, app.maxVersion)) 1.1279 + return true; 1.1280 + } 1.1281 + 1.1282 + return false; 1.1283 + }, 1.1284 + 1.1285 + /** 1.1286 + * Retrieves a version range (e.g. minVersion and maxVersion) for a 1.1287 + * blocklist item's targetApplication element. 1.1288 + * @param targetAppElement 1.1289 + * A targetApplication blocklist element. 1.1290 + * @returns An array of JS objects with the following properties: 1.1291 + * "minVersion" The minimum version in a version range (default = null). 1.1292 + * "maxVersion" The maximum version in a version range (default = null). 1.1293 + */ 1.1294 + getBlocklistAppVersions: function BlocklistItemData_getBlocklistAppVersions(targetAppElement) { 1.1295 + var appVersions = [ ]; 1.1296 + 1.1297 + if (targetAppElement) { 1.1298 + for (var i = 0; i < targetAppElement.childNodes.length; ++i) { 1.1299 + var versionRangeElement = targetAppElement.childNodes.item(i); 1.1300 + if (!(versionRangeElement instanceof Ci.nsIDOMElement) || 1.1301 + versionRangeElement.localName != "versionRange") 1.1302 + continue; 1.1303 + appVersions.push(this.getBlocklistVersionRange(versionRangeElement)); 1.1304 + } 1.1305 + } 1.1306 + // return minVersion = null and maxVersion = null if no specific versionRange 1.1307 + // elements were found 1.1308 + if (appVersions.length == 0) 1.1309 + appVersions.push(this.getBlocklistVersionRange(null)); 1.1310 + return appVersions; 1.1311 + }, 1.1312 + 1.1313 + /** 1.1314 + * Retrieves a version range (e.g. minVersion and maxVersion) for a blocklist 1.1315 + * versionRange element. 1.1316 + * @param versionRangeElement 1.1317 + * The versionRange blocklist element. 1.1318 + * @returns A JS object with the following properties: 1.1319 + * "minVersion" The minimum version in a version range (default = null). 1.1320 + * "maxVersion" The maximum version in a version range (default = null). 1.1321 + */ 1.1322 + getBlocklistVersionRange: function BlocklistItemData_getBlocklistVersionRange(versionRangeElement) { 1.1323 + var minVersion = null; 1.1324 + var maxVersion = null; 1.1325 + if (!versionRangeElement) 1.1326 + return { minVersion: minVersion, maxVersion: maxVersion }; 1.1327 + 1.1328 + if (versionRangeElement.hasAttribute("minVersion")) 1.1329 + minVersion = versionRangeElement.getAttribute("minVersion"); 1.1330 + if (versionRangeElement.hasAttribute("maxVersion")) 1.1331 + maxVersion = versionRangeElement.getAttribute("maxVersion"); 1.1332 + 1.1333 + return { minVersion: minVersion, maxVersion: maxVersion }; 1.1334 + } 1.1335 +}; 1.1336 + 1.1337 +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([Blocklist]);