// LOG LEVELS --- VERB=1; DBUG=2; INFO=3; NOTE=4; WARN=5; // PREFERENCE BRANCHES let PREFBRANCH_ROOT=0; let PREFBRANCH_RULE_TOGGLE=1; let PREFBRANCH_NONE=2; //--------------- https_domains = {}; // maps domain patterns (with at most one // wildcard) to RuleSets https_everywhere_blacklist = {}; // URLs we've given up on rewriting because // of redirection loops https_blacklist_domains = {}; // domains for which there is at least one // blacklisted URL // const CI = Components.interfaces; const CC = Components.classes; const CU = Components.utils; const CR = Components.results; const Ci = Components.interfaces; const Cc = Components.classes; const Cu = Components.utils; const Cr = Components.results; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/FileUtils.jsm"); const CP_SHOULDPROCESS = 4; const SERVICE_CTRID = "@eff.org/https-everywhere;1"; const SERVICE_ID=Components.ID("{32c165b4-fe5e-4964-9250-603c410631b4}"); const SERVICE_NAME = "Encrypts your communications with a number of major websites"; const LLVAR = "LogLevel"; const IOS = CC["@mozilla.org/network/io-service;1"].getService(CI.nsIIOService); const OS = CC['@mozilla.org/observer-service;1'].getService(CI.nsIObserverService); const LOADER = CC["@mozilla.org/moz/jssubscript-loader;1"].getService(CI.mozIJSSubScriptLoader); const _INCLUDED = {}; // NoScript uses this blob to include js constructs that stored in the chrome/ // directory, but are not attached to the Firefox UI (normally, js located // there is attached to an Overlay and therefore is part of the UI). // Reasons for this: things in components/ directory cannot be split into // separate files; things in chrome/ can be const INCLUDE = function(name) { if (arguments.length > 1) for (var j = 0, len = arguments.length; j < len; j++) INCLUDE(arguments[j]); else if (!_INCLUDED[name]) { // we used to try/catch here, but that was less useful because it didn't // produce line numbers for syntax errors LOADER.loadSubScript("chrome://https-everywhere/content/code/" + name + ".js"); _INCLUDED[name] = true; } }; const WP_STATE_START = CI.nsIWebProgressListener.STATE_START; const WP_STATE_STOP = CI.nsIWebProgressListener.STATE_STOP; const WP_STATE_DOC = CI.nsIWebProgressListener.STATE_IS_DOCUMENT; const WP_STATE_START_DOC = WP_STATE_START | WP_STATE_DOC; const WP_STATE_RESTORING = CI.nsIWebProgressListener.STATE_RESTORING; const LF_VALIDATE_ALWAYS = CI.nsIRequest.VALIDATE_ALWAYS; const LF_LOAD_BYPASS_ALL_CACHES = CI.nsIRequest.LOAD_BYPASS_CACHE | CI.nsICachingChannel.LOAD_BYPASS_LOCAL_CACHE; const NS_OK = 0; const NS_BINDING_ABORTED = 0x804b0002; const NS_BINDING_REDIRECTED = 0x804b0003; const NS_ERROR_UNKNOWN_HOST = 0x804b001e; const NS_ERROR_REDIRECT_LOOP = 0x804b001f; const NS_ERROR_CONNECTION_REFUSED = 0x804b000e; const NS_ERROR_NOT_AVAILABLE = 0x804b0111; const LOG_CONTENT_BLOCK = 1; const LOG_CONTENT_CALL = 2; const LOG_CONTENT_INTERCEPT = 4; const LOG_CHROME_WIN = 8; const LOG_XSS_FILTER = 16; const LOG_INJECTION_CHECK = 32; const LOG_DOM = 64; const LOG_JS = 128; const LOG_LEAKS = 1024; const LOG_SNIFF = 2048; const LOG_CLEARCLICK = 4096; const LOG_ABE = 8192; const HTML_NS = "http://www.w3.org/1999/xhtml"; const WHERE_UNTRUSTED = 1; const WHERE_TRUSTED = 2; const ANYWHERE = 3; const N_COHORTS = 1000; const DUMMY_OBJ = {}; DUMMY_OBJ.wrappedJSObject = DUMMY_OBJ; const DUMMY_FUNC = function() {}; const DUMMY_ARRAY = []; const EARLY_VERSION_CHECK = !("nsISessionStore" in CI && typeof(/ /) === "object"); // This is probably obsolete since the switch to the channel.redirectTo API const OBSERVER_TOPIC_URI_REWRITE = "https-everywhere-uri-rewrite"; // XXX: Better plan for this? // We need it to exist to make our updates of ChannelReplacement.js easier. var ABE = { consoleDump: false, log: function(str) { https_everywhereLog(WARN, str); } }; function xpcom_generateQI(iids) { var checks = []; for each (var iid in iids) { checks.push("CI." + iid.name + ".equals(iid)"); } var src = checks.length ? "if (" + checks.join(" || ") + ") return this;\n" : ""; return new Function("iid", src + "throw Components.results.NS_ERROR_NO_INTERFACE;"); } function xpcom_checkInterfaces(iid,iids,ex) { for (var j = iids.length; j-- >0;) { if (iid.equals(iids[j])) return true; } throw ex; } INCLUDE('ChannelReplacement', 'IOUtil', 'HTTPSRules', 'HTTPS', 'Thread', 'ApplicableList'); Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); // This is black magic for storing Expando data w/ an nsIDOMWindow // See http://pastebin.com/qY28Jwbv , // https://developer.mozilla.org/en/XPCOM_Interface_Reference/nsIControllers StorageController.prototype = { QueryInterface: XPCOMUtils.generateQI( [ Components.interfaces.nsISupports, Components.interfaces.nsIController ]), wrappedJSObject: null, // Initialized by constructor supportsCommand: function (cmd) {return (cmd == this.command);}, isCommandEnabled: function (cmd) {return (cmd == this.command);}, onEvent: function(eventName) {return true;}, doCommand: function() {return true;} }; function StorageController(command) { this.command = command; this.data = {}; this.wrappedJSObject = this; }; /*var Controller = Class("Controller", XPCOM(CI.nsIController), { init: function (command, data) { this.command = command; this.data = data; }, supportsCommand: function (cmd) cmd === this.command });*/ function HTTPSEverywhere() { // Set up logging in each component: HTTPS.log = HTTPSRules.log = RuleWriter.log = this.log = https_everywhereLog; this.log = https_everywhereLog; this.wrappedJSObject = this; this.https_rules = HTTPSRules; this.INCLUDE=INCLUDE; this.ApplicableList = ApplicableList; this.browser_initialised = false; // the browser is completely loaded this.prefs = this.get_prefs(); this.rule_toggle_prefs = this.get_prefs(PREFBRANCH_RULE_TOGGLE); this.httpNowhereEnabled = this.prefs.getBoolPref("http_nowhere.enabled"); this.isMobile = this.doMobileCheck(); // Disable SSLv3 to prevent POODLE attack. // https://www.imperialviolet.org/2014/10/14/poodle.html var root_prefs = this.get_prefs(PREFBRANCH_NONE); root_prefs.setIntPref("security.tls.version.min", 1); // We need to use observers instead of categories for FF3.0 for these: // https://developer.mozilla.org/en/Observer_Notifications // https://developer.mozilla.org/en/nsIObserverService. // https://developer.mozilla.org/en/nsIObserver // We also use the observer service to let other extensions know about URIs // we rewrite. this.obsService = CC["@mozilla.org/observer-service;1"] .getService(Components.interfaces.nsIObserverService); if (this.prefs.getBoolPref("globalEnabled")) { this.obsService.addObserver(this, "profile-before-change", false); this.obsService.addObserver(this, "profile-after-change", false); this.obsService.addObserver(this, "sessionstore-windows-restored", false); this.obsService.addObserver(this, "browser:purge-session-history", false); } else { // Need this to initialize FF for Android UI even when HTTPS-E is off if (this.isMobile) { this.obsService.addObserver(this, "sessionstore-windows-restored", false); } } var pref_service = Components.classes["@mozilla.org/preferences-service;1"] .getService(Components.interfaces.nsIPrefBranchInternal); var branch = pref_service.QueryInterface(Components.interfaces.nsIPrefBranchInternal); branch.addObserver("extensions.https_everywhere.enable_mixed_rulesets", this, false); branch.addObserver("security.mixed_content.block_active_content", this, false); return; } /* In recent versions of Firefox and HTTPS Everywhere, the call stack for performing an HTTP -> HTTPS rewrite looks like this: 1. HTTPSEverywhere.observe() gets a callback with the "http-on-modify-request" topic, and the channel as a subject 2. HTTPS.replaceChannel() 3. HTTPSRules.rewrittenURI() 4. HTTPSRules.potentiallyApplicableRulesets uses elements to identify relevant rulesets foreach RuleSet: 4. RuleSet.transformURI() 5. RuleSet.apply() does the tests and rewrites with RegExps, returning a string 4. RuleSet.transformURI() makes a new uri object for the destination string, if required 2. HTTPS.replaceChannel() calls channel.redirectTo() if a redirect is needed In addition, the following other important tasks happen along the way: HTTPSEverywhere.observe() aborts if there is a redirect loop finds a reference to the ApplicableList or alist that represents the toolbar context menu HTTPS.replaceChannel() notices redirect loops (and used to do much more complex XPCOM API work in the NoScript-based past) HTTPSRules.rewrittenURI() works around weird URI types like about: and http://user:pass@example.com/ and notifies the alist of what it should display for each ruleset */ // This defines for Mozilla what stuff HTTPSEverywhere will implement. // ChannelEventSink used to be necessary in order to handle redirects (eg // HTTP redirects) correctly. It may now be obsolete? XXX HTTPSEverywhere.prototype = { prefs: null, // properties required for XPCOM registration: classDescription: SERVICE_NAME, classID: SERVICE_ID, contractID: SERVICE_CTRID, _xpcom_factory: { createInstance: function (outer, iid) { if (outer != null) throw Components.results.NS_ERROR_NO_AGGREGATION; if (!HTTPSEverywhere.instance) HTTPSEverywhere.instance = new HTTPSEverywhere(); return HTTPSEverywhere.instance.QueryInterface(iid); }, QueryInterface: XPCOMUtils.generateQI( [ Components.interfaces.nsISupports, Components.interfaces.nsIModule, Components.interfaces.nsIFactory ]) }, // [optional] an array of categories to register this component in. _xpcom_categories: [ { category: "app-startup", } ], // QueryInterface implementation, e.g. using the generateQI helper QueryInterface: XPCOMUtils.generateQI( [ Components.interfaces.nsIObserver, Components.interfaces.nsISupports, Components.interfaces.nsISupportsWeakReference, Components.interfaces.nsIWebProgressListener, Components.interfaces.nsIWebProgressListener2, Components.interfaces.nsIChannelEventSink ]), wrappedJSObject: null, // Initialized by constructor getWeakReference: function () { return Components.utils.getWeakReference(this); }, // An "expando" is an attribute glued onto something. From NoScript. getExpando: function(domWin, key) { var c = domWin.controllers.getControllerForCommand("https-everywhere-storage"); try { if (c) { c = c.wrappedJSObject; //this.log(DBUG, "Found a controller, returning data"); return c.data[key]; } else { this.log(INFO, "No controller attached to " + domWin); return null; } } catch(e) { // Firefox 3.5 this.log(WARN,"exception in getExpando"); this.getExpando = this.getExpando_old; this.setExpando = this.setExpando_old; return this.getExpando_old(domWin, key, null); } }, setExpando: function(domWin, key, value) { var c = domWin.controllers.getControllerForCommand("https-everywhere-storage"); try { if (!c) { this.log(DBUG, "Appending new StorageController for " + domWin); c = new StorageController("https-everywhere-storage"); domWin.controllers.appendController(c); } else { c = c.wrappedJSObject; } c.data[key] = value; } catch(e) { this.log(WARN,"exception in setExpando"); this.getExpando = this.getExpando_old; this.setExpando = this.setExpando_old; this.setExpando_old(domWin, key, value); } }, // This method is straight out of NoScript... we fall back to it in FF 3.*? getExpando_old: function(domWin, key, defValue) { var domObject = domWin.document; return domObject && domObject.__httpsEStorage && domObject.__httpsEStorage[key] || (defValue ? this.setExpando(domObject, key, defValue) : null); }, setExpando_old: function(domWin, key, value) { var domObject = domWin.document; if (!domObject) return null; if (!domObject.__httpsEStorage) domObject.__httpsEStorage = {}; if (domObject.__httpsEStorage) domObject.__httpsEStorage[key] = value; else this.log(WARN, "Warning: cannot set expando " + key + " to value " + value); return value; }, // We use onLocationChange to make a fresh list of rulesets that could have // applied to the content in the current page (the "applicable list" is used // for the context menu in the UI). This will be appended to as various // content is embedded / requested by JavaScript. onLocationChange: function(wp, req, uri) { if (wp instanceof CI.nsIWebProgress) { if (!this.newApplicableListForDOMWin(wp.DOMWindow)) this.log(WARN,"Something went wrong in onLocationChange"); } else { this.log(WARN,"onLocationChange: no nsIWebProgress"); } }, getWindowForChannel: function(channel) { // Obtain an nsIDOMWindow from a channel let loadContext; try { loadContext = channel.notificationCallbacks.getInterface(CI.nsILoadContext); } catch(e) { try { loadContext = channel.loadGroup.notificationCallbacks.getInterface(CI.nsILoadContext); } catch(e) { this.log(NOTE, "No loadContext for " + channel.URI.spec); return null; } } if (!loadContext) { return null; } let domWin = loadContext.associatedWindow; if (!domWin) { this.log(NOTE, "failed to get DOMWin for " + channel.URI.spec); return null; } domWin = domWin.top; return domWin; }, // the lists get made when the urlbar is loading something new, but they // need to be appended to with reference only to the channel getApplicableListForChannel: function(channel) { var domWin = this.getWindowForChannel(channel); return this.getApplicableListForDOMWin(domWin, "on-modify-request w " + domWin); }, newApplicableListForDOMWin: function(domWin) { if (!domWin || !(domWin instanceof CI.nsIDOMWindow)) { this.log(WARN, "Get alist without domWin"); return null; } var dw = domWin.top; var alist = new ApplicableList(this.log,dw.document,dw); this.setExpando(dw,"applicable_rules",alist); return alist; }, getApplicableListForDOMWin: function(domWin, where) { if (!domWin || !(domWin instanceof CI.nsIDOMWindow)) { //this.log(WARN, "Get alist without domWin"); return null; } var dw = domWin.top; var alist= this.getExpando(dw,"applicable_rules"); if (alist) { //this.log(DBUG,"get AL success in " + where); return alist; } else { //this.log(DBUG, "Making new AL in getApplicableListForDOMWin in " + where); alist = new ApplicableList(this.log,dw.document,dw); this.setExpando(dw,"applicable_rules",alist); } return alist; }, observe: function(subject, topic, data) { // Top level glue for the nsIObserver API var channel = subject; //this.log(VERB,"Got observer topic: "+topic); if (topic == "http-on-modify-request") { if (!(channel instanceof CI.nsIHttpChannel)) return; this.log(DBUG,"Got http-on-modify-request: "+channel.URI.spec); var lst = this.getApplicableListForChannel(channel); // null if no window is associated (ex: xhr) if (channel.URI.spec in https_everywhere_blacklist) { this.log(DBUG, "Avoiding blacklisted " + channel.URI.spec); if (lst) lst.breaking_rule(https_everywhere_blacklist[channel.URI.spec]); else this.log(NOTE,"Failed to indicate breakage in content menu"); return; } HTTPS.replaceChannel(lst, channel, this.httpNowhereEnabled); } else if (topic == "http-on-examine-response") { this.log(DBUG, "Got http-on-examine-response @ "+ (channel.URI ? channel.URI.spec : '') ); HTTPS.handleSecureCookies(channel); } else if (topic == "http-on-examine-merged-response") { this.log(DBUG, "Got http-on-examine-merged-response "); HTTPS.handleSecureCookies(channel); } else if (topic == "cookie-changed") { // Javascript can add cookies via document.cookie that are insecure. if (data == "added" || data == "changed") { // subject can also be an nsIArray! bleh. try { subject.QueryInterface(CI.nsIArray); var elems = subject.enumerate(); while (elems.hasMoreElements()) { var cookie = elems.getNext() .QueryInterface(CI.nsICookie2); if (!cookie.isSecure) { HTTPS.handleInsecureCookie(cookie); } } } catch(e) { subject.QueryInterface(CI.nsICookie2); if(!subject.isSecure) { HTTPS.handleInsecureCookie(subject); } } } } else if (topic == "profile-before-change") { this.log(INFO, "Got profile-before-change"); var catman = Components.classes["@mozilla.org/categorymanager;1"] .getService(Components.interfaces.nsICategoryManager); catman.deleteCategoryEntry("net-channel-event-sinks", SERVICE_CTRID, true); Thread.hostRunning = false; } else if (topic == "profile-after-change") { this.log(DBUG, "Got profile-after-change"); if(this.prefs.getBoolPref("globalEnabled")){ OS.addObserver(this, "cookie-changed", false); OS.addObserver(this, "http-on-modify-request", false); OS.addObserver(this, "http-on-examine-merged-response", false); OS.addObserver(this, "http-on-examine-response", false); var dls = CC['@mozilla.org/docloaderservice;1'] .getService(CI.nsIWebProgress); dls.addProgressListener(this, CI.nsIWebProgress.NOTIFY_LOCATION); this.log(INFO,"ChannelReplacement.supported = "+ChannelReplacement.supported); HTTPSRules.init(); Thread.hostRunning = true; var catman = Components.classes["@mozilla.org/categorymanager;1"] .getService(Components.interfaces.nsICategoryManager); // hook on redirections (non persistent, otherwise crashes on 1.8.x) catman.addCategoryEntry("net-channel-event-sinks", SERVICE_CTRID, SERVICE_CTRID, false, true); } } else if (topic == "sessionstore-windows-restored") { this.log(DBUG,"Got sessionstore-windows-restored"); if (!this.isMobile) { this.maybeShowObservatoryPopup(); } else { this.log(WARN, "Initializing Firefox for Android UI"); Cu.import("chrome://https-everywhere/content/code/AndroidUI.jsm"); AndroidUI.init(); } this.browser_initialised = true; } else if (topic == "nsPref:changed") { // If the user toggles the Mixed Content Blocker settings, reload the rulesets // to enable/disable the mixedcontent ones // this pref gets set to false and then true during FF 26 startup! // so do nothing if we're being notified during startup if (!this.browser_initialised) return; switch (data) { case "security.mixed_content.block_active_content": case "extensions.https_everywhere.enable_mixed_rulesets": var p = CC["@mozilla.org/preferences-service;1"].getService(CI.nsIPrefBranch); var val = p.getBoolPref("security.mixed_content.block_active_content"); this.log(INFO,"nsPref:changed for "+data + " to " + val); HTTPSRules.init(); break; } } else if (topic == "browser:purge-session-history") { // The list of rulesets that have been loaded from the sqlite DB // constitutes a parallel history store, so we have to clear it. this.log(DBUG, "History cleared, reloading HTTPSRules to avoid information leak."); HTTPSRules.init(); } return; }, maybeShowObservatoryPopup: function() { // Show the popup at most once. Users who enabled the Observatory before // a version that would have shown it to them, don't need to see it // again. var ssl_observatory = CC["@eff.org/ssl-observatory;1"] .getService(Components.interfaces.nsISupports) .wrappedJSObject; var shown = ssl_observatory.myGetBoolPref("popup_shown"); var enabled = ssl_observatory.myGetBoolPref("enabled"); var that = this; var obs_popup_callback = function(result) { if (result) that.log(INFO, "Got positive proxy test."); else that.log(INFO, "Got negative proxy text."); // We are now ready to show the popup in its most informative state that.chrome_opener("chrome://https-everywhere/content/observatory-popup.xul"); }; if (!shown && !enabled) ssl_observatory.registerProxyTestNotification(obs_popup_callback); if (shown && enabled) this.maybeCleanupObservatoryPrefs(ssl_observatory); }, maybeCleanupObservatoryPrefs: function(ssl_observatory) { // Recover from a past UI processing bug that would leave the Obsevatory // accidentally disabled for some users // https://trac.torproject.org/projects/tor/ticket/10728 var clean = ssl_observatory.myGetBoolPref("clean_config"); if (clean) return; // unchanged: returns true if a pref has not been modified var unchanged = function(p){return !ssl_observatory.prefs.prefHasUserValue("extensions.https_everywhere._observatory."+p)}; var cleanup_obsprefs_callback = function(tor_avail) { // we only run this once ssl_observatory.prefs.setBoolPref("extensions.https_everywhere._observatory.clean_config", true); if (!tor_avail) { // use_custom_proxy is the variable that is often false when it should be true; if (!ssl_observatory.myGetBoolPref("use_custom_proxy")) { // however don't do anything if any of the prefs have been set by the user if (unchanged("alt_roots") && unchanged("self_signed") && unchanged ("send_asn") && unchanged("priv_dns")) { ssl_observatory.prefs.setBoolPref("extensions.https_everywhere._observatory.use_custom_proxy", true); } } } } ssl_observatory.registerProxyTestNotification(cleanup_obsprefs_callback); }, getExperimentalFeatureCohort: function() { // This variable is used for gradually turning on features for testing and // scalability purposes. It is a random integer [0,N_COHORTS) generated // once and stored thereafter. // // This is not currently used/called in the development branch var cohort; try { cohort = this.prefs.getIntPref("experimental_feature_cohort"); } catch(e) { cohort = Math.round(Math.random() * N_COHORTS); this.prefs.setIntPref("experimental_feature_cohort", cohort); } return cohort; }, // nsIChannelEventSink implementation // XXX This was here for rewrites in the past. Do we still need it? onChannelRedirect: function(oldChannel, newChannel, flags) { const uri = newChannel.URI; this.log(DBUG,"Got onChannelRedirect to "+uri.spec); if (!(newChannel instanceof CI.nsIHttpChannel)) { this.log(DBUG, newChannel + " is not an instance of nsIHttpChannel"); return; } var alist = this.juggleApplicableListsDuringRedirection(oldChannel, newChannel); HTTPS.replaceChannel(alist,newChannel, this.httpNowhereEnabled); }, juggleApplicableListsDuringRedirection: function(oldChannel, newChannel) { // If the new channel doesn't yet have a list of applicable rulesets, start // with the old one because that's probably a better representation of how // secure the load process was for this page var domWin = this.getWindowForChannel(oldChannel); var old_alist = null; if (domWin) old_alist = this.getExpando(domWin,"applicable_rules"); domWin = this.getWindowForChannel(newChannel); if (!domWin) return null; var new_alist = this.getExpando(domWin,"applicable_rules"); if (old_alist && !new_alist) { new_alist = old_alist; this.setExpando(domWin,"applicable_rules",new_alist); } else if (!new_alist) { new_alist = new ApplicableList(this.log, domWin.document, domWin); this.setExpando(domWin,"applicable_rules",new_alist); } return new_alist; }, asyncOnChannelRedirect: function(oldChannel, newChannel, flags, callback) { this.onChannelRedirect(oldChannel, newChannel, flags); callback.onRedirectVerifyCallback(0); }, get_prefs: function(prefBranch) { if(!prefBranch) prefBranch = PREFBRANCH_ROOT; // get our preferences branch object // FIXME: Ugly hack stolen from https var branch_name; if(prefBranch === PREFBRANCH_RULE_TOGGLE) branch_name = "extensions.https_everywhere.rule_toggle."; else if (prefBranch === PREFBRANCH_NONE) branch_name = ""; else branch_name = "extensions.https_everywhere."; var o_prefs = false; var o_branch = false; // this function needs to be called from inside https_everywhereLog, so // it needs to do its own logging... var econsole = Components.classes["@mozilla.org/consoleservice;1"] .getService(Components.interfaces.nsIConsoleService); o_prefs = Components.classes["@mozilla.org/preferences-service;1"] .getService(Components.interfaces.nsIPrefService); if (!o_prefs) { econsole.logStringMessage("HTTPS Everywhere: Failed to get preferences-service!"); return false; } o_branch = o_prefs.getBranch(branch_name); if (!o_branch) { econsole.logStringMessage("HTTPS Everywhere: Failed to get prefs branch!"); return false; } if(prefBranch == PREFBRANCH_ROOT) { // make sure there's an entry for our log level try { o_branch.getIntPref(LLVAR); } catch (e) { econsole.logStringMessage("Creating new about:config https_everywhere.LogLevel variable"); o_branch.setIntPref(LLVAR, WARN); } } return o_branch; }, // Are we on Firefox for Android? doMobileCheck: function() { let appInfo = CC["@mozilla.org/xre/app-info;1"].getService(CI.nsIXULAppInfo); let ANDROID_ID = "{aa3c5121-dab2-40e2-81ca-7ea25febc110}"; return (appInfo.ID === ANDROID_ID); }, chrome_opener: function(uri, args) { // we don't use window.open, because we need to work around TorButton's // state control args = args || 'chrome,centerscreen'; return CC['@mozilla.org/appshell/window-mediator;1'] .getService(CI.nsIWindowMediator) .getMostRecentWindow('navigator:browser') .open(uri,'', args ); }, tab_opener: function(uri) { var gb = CC['@mozilla.org/appshell/window-mediator;1'] .getService(CI.nsIWindowMediator) .getMostRecentWindow('navigator:browser') .gBrowser; var tab = gb.addTab(uri); gb.selectedTab = tab; return tab; }, toggleEnabledState: function() { if(this.prefs.getBoolPref("globalEnabled")){ try{ // toggling some of these after startup may be inconsequential... // this.obsService.removeObserver(this, "sessionstore-windows-restored"); this.obsService.removeObserver(this, "profile-before-change"); this.obsService.removeObserver(this, "profile-after-change"); OS.removeObserver(this, "cookie-changed"); OS.removeObserver(this, "http-on-modify-request"); OS.removeObserver(this, "http-on-examine-merged-response"); OS.removeObserver(this, "http-on-examine-response"); var catman = Components.classes["@mozilla.org/categorymanager;1"] .getService(Components.interfaces.nsICategoryManager); catman.deleteCategoryEntry("net-channel-event-sinks", SERVICE_CTRID, true); var dls = CC['@mozilla.org/docloaderservice;1'] .getService(CI.nsIWebProgress); dls.removeProgressListener(this); this.prefs.setBoolPref("globalEnabled", false); } catch(e){ this.log(WARN, "Couldn't remove observers: " + e); } } else{ try{ this.obsService.addObserver(this, "profile-before-change", false); this.obsService.addObserver(this, "profile-after-change", false); // this.obsService.addObserver(this, "sessionstore-windows-restored", false); OS.addObserver(this, "cookie-changed", false); OS.addObserver(this, "http-on-modify-request", false); OS.addObserver(this, "http-on-examine-merged-response", false); OS.addObserver(this, "http-on-examine-response", false); var dls = CC['@mozilla.org/docloaderservice;1'] .getService(CI.nsIWebProgress); dls.addProgressListener(this, CI.nsIWebProgress.NOTIFY_LOCATION); this.log(INFO,"ChannelReplacement.supported = "+ChannelReplacement.supported); if(!Thread.hostRunning) Thread.hostRunning = true; var catman = Components.classes["@mozilla.org/categorymanager;1"] .getService(Components.interfaces.nsICategoryManager); // hook on redirections (non persistent, otherwise crashes on 1.8.x) catman.addCategoryEntry("net-channel-event-sinks", SERVICE_CTRID, SERVICE_CTRID, false, true); HTTPSRules.init(); this.prefs.setBoolPref("globalEnabled", true); } catch(e){ this.log(WARN, "Couldn't add observers: " + e); } } }, toggleHttpNowhere: function() { let prefService = Services.prefs; let thisBranch = prefService.getBranch("extensions.https_everywhere.http_nowhere."); let securityBranch = prefService.getBranch("security."); // Whether cert is treated as invalid when OCSP connection fails let OCSP_REQUIRED = "OCSP.require"; // Branch to save original settings let ORIG_OCSP_REQUIRED = "orig.ocsp.required"; if (thisBranch.getBoolPref("enabled")) { // Restore original OCSP settings. TODO: What if user manually edits // these while HTTP Nowhere is enabled? let origOcspRequired = thisBranch.getBoolPref(ORIG_OCSP_REQUIRED); securityBranch.setBoolPref(OCSP_REQUIRED, origOcspRequired); thisBranch.setBoolPref("enabled", false); this.httpNowhereEnabled = false; } else { // Save original OCSP settings in HTTP Nowhere preferences branch. let origOcspRequired = securityBranch.getBoolPref(OCSP_REQUIRED); thisBranch.setBoolPref(ORIG_OCSP_REQUIRED, origOcspRequired); // Disable OCSP enforcement securityBranch.setBoolPref(OCSP_REQUIRED, false); thisBranch.setBoolPref("enabled", true); this.httpNowhereEnabled = true; } } }; var prefs = 0; var econsole = 0; function https_everywhereLog(level, str) { if (prefs == 0) { prefs = HTTPSEverywhere.instance.get_prefs(); econsole = Components.classes["@mozilla.org/consoleservice;1"] .getService(Components.interfaces.nsIConsoleService); } try { var threshold = prefs.getIntPref(LLVAR); } catch (e) { econsole.logStringMessage( "HTTPS Everywhere: Failed to read about:config LogLevel"); threshold = WARN; } if (level >= threshold) { dump("HTTPS Everywhere: "+str+"\n"); econsole.logStringMessage("HTTPS Everywhere: " +str); } } /** * XPCOMUtils.generateNSGetFactory was introduced in Mozilla 2 (Firefox 4). * XPCOMUtils.generateNSGetModule is for Mozilla 1.9.2 (Firefox 3.6). */ if (XPCOMUtils.generateNSGetFactory) var NSGetFactory = XPCOMUtils.generateNSGetFactory([HTTPSEverywhere]); else var NSGetModule = XPCOMUtils.generateNSGetModule([HTTPSEverywhere]); /* vim: set tabstop=4 expandtab: */