home *** CD-ROM | disk | FTP | other *** search
/ PC Welt 2006 November (DVD) / PCWELT_11_2006.ISO / casper / filesystem.squashfs / usr / lib / firefox / components / nsUrlClassifierLib.js < prev    next >
Encoding:
Text File  |  2006-08-18  |  129.5 KB  |  4,109 lines

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Mozilla Public License Version
  5.  * 1.1 (the "License"); you may not use this file except in compliance with
  6.  * the License. You may obtain a copy of the License at
  7.  * http://www.mozilla.org/MPL/
  8.  *
  9.  * Software distributed under the License is distributed on an "AS IS" basis,
  10.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11.  * for the specific language governing rights and limitations under the
  12.  * License.
  13.  *
  14.  * The Original Code is Url Classifier code
  15.  *
  16.  * The Initial Developer of the Original Code is
  17.  * Google Inc.
  18.  * Portions created by the Initial Developer are Copyright (C) 2006
  19.  * the Initial Developer. All Rights Reserved.
  20.  *
  21.  * Contributor(s):
  22.  *   Tony Chang <tony@ponderer.org>
  23.  *
  24.  * Alternatively, the contents of this file may be used under the terms of
  25.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  26.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  27.  * in which case the provisions of the GPL or the LGPL are applicable instead
  28.  * of those above. If you wish to allow use of your version of this file only
  29.  * under the terms of either the GPL or the LGPL, and not to allow others to
  30.  * use your version of this file under the terms of the MPL, indicate your
  31.  * decision by deleting the provisions above and replace them with the notice
  32.  * and other provisions required by the GPL or the LGPL. If you do not delete
  33.  * the provisions above, a recipient may use your version of this file under
  34.  * the terms of any one of the MPL, the GPL or the LGPL.
  35.  *
  36.  * ***** END LICENSE BLOCK ***** */
  37.  
  38. // We wastefully reload the same JS files across components.  This puts all
  39. // the common JS files used by safebrowsing and url-classifier into a
  40. // single component.
  41.  
  42. const Cc = Components.classes;
  43. const Ci = Components.interfaces;
  44. const G_GDEBUG = false;
  45.  
  46. // TODO: get rid of application.js and filesystem.js (not used much)
  47. /* ***** BEGIN LICENSE BLOCK *****
  48.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  49.  *
  50.  * The contents of this file are subject to the Mozilla Public License Version
  51.  * 1.1 (the "License"); you may not use this file except in compliance with
  52.  * the License. You may obtain a copy of the License at
  53.  * http://www.mozilla.org/MPL/
  54.  *
  55.  * Software distributed under the License is distributed on an "AS IS" basis,
  56.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  57.  * for the specific language governing rights and limitations under the
  58.  * License.
  59.  *
  60.  * The Original Code is Google Safe Browsing.
  61.  *
  62.  * The Initial Developer of the Original Code is Google Inc.
  63.  * Portions created by the Initial Developer are Copyright (C) 2006
  64.  * the Initial Developer. All Rights Reserved.
  65.  *
  66.  * Contributor(s):
  67.  *   Marius Schilder <mschilder@google.com> (original author)
  68.  *
  69.  * Alternatively, the contents of this file may be used under the terms of
  70.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  71.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  72.  * in which case the provisions of the GPL or the LGPL are applicable instead
  73.  * of those above. If you wish to allow use of your version of this file only
  74.  * under the terms of either the GPL or the LGPL, and not to allow others to
  75.  * use your version of this file under the terms of the MPL, indicate your
  76.  * decision by deleting the provisions above and replace them with the notice
  77.  * and other provisions required by the GPL or the LGPL. If you do not delete
  78.  * the provisions above, a recipient may use your version of this file under
  79.  * the terms of any one of the MPL, the GPL or the LGPL.
  80.  *
  81.  * ***** END LICENSE BLOCK ***** */
  82.  
  83.  
  84. /**
  85.  * ARC4 streamcipher implementation
  86.  * @constructor
  87.  */
  88. function ARC4() {
  89.   this._S = new Array(256);
  90.   this._i = 0;
  91.   this._j = 0;
  92. }
  93.  
  94. /**
  95.  * Initialize the cipher for use with new key.
  96.  * @param {byte[]} key is byte array containing key
  97.  * @param {int} opt_length indicates # of bytes to take from key
  98.  */
  99. ARC4.prototype.setKey = function(key, opt_length) {
  100.   if (!isArray(key)) {
  101.     throw new Error("Key parameter must be a byte array");
  102.   }
  103.  
  104.   if (!opt_length) {
  105.     opt_length = key.length;
  106.   }
  107.  
  108.   var S = this._S;
  109.  
  110.   for (var i = 0; i < 256; ++i) {
  111.     S[i] = i;
  112.   }
  113.  
  114.   var j = 0;
  115.   for (var i = 0; i < 256; ++i) {
  116.     j = (j + S[i] + key[i % opt_length]) & 255;
  117.  
  118.     var tmp = S[i];
  119.     S[i] = S[j];
  120.     S[j] = tmp;
  121.   }
  122.     
  123.   this._i = 0;
  124.   this._j = 0;
  125. }
  126.  
  127. /**
  128.  * Discard n bytes of the keystream.
  129.  * These days 1536 is considered a decent amount to drop to get
  130.  * the key state warmed-up enough for secure usage.
  131.  * This is not done in the constructor to preserve efficiency for
  132.  * use cases that do not need this.
  133.  * @param {int} n is # of bytes to disregard from stream
  134.  */
  135. ARC4.prototype.discard = function(n) {
  136.   var devnul = new Array(n);
  137.   this.crypt(devnul);
  138. }
  139.  
  140. /**
  141.  * En- or decrypt (same operation for streamciphers like ARC4)
  142.  * @param {byte[]} data gets xor-ed in place
  143.  * @param {int} opt_length indicated # of bytes to crypt
  144.  */
  145. ARC4.prototype.crypt = function(data, opt_length) {
  146.   if (!opt_length) {
  147.     opt_length = data.length;
  148.   }
  149.  
  150.   var i = this._i;
  151.   var j = this._j;
  152.   var S = this._S;
  153.  
  154.   for (var n = 0; n < opt_length; ++n) {
  155.     i = (i + 1) & 255;
  156.     j = (j + S[i]) & 255;
  157.  
  158.     var tmp = S[i];
  159.     S[i] = S[j];
  160.     S[j] = tmp;
  161.  
  162.     data[n] ^= S[(S[i] + S[j]) & 255];
  163.   }
  164.  
  165.   this._i = i;
  166.   this._j = j;
  167. }
  168. /* ***** BEGIN LICENSE BLOCK *****
  169.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  170.  *
  171.  * The contents of this file are subject to the Mozilla Public License Version
  172.  * 1.1 (the "License"); you may not use this file except in compliance with
  173.  * the License. You may obtain a copy of the License at
  174.  * http://www.mozilla.org/MPL/
  175.  *
  176.  * Software distributed under the License is distributed on an "AS IS" basis,
  177.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  178.  * for the specific language governing rights and limitations under the
  179.  * License.
  180.  *
  181.  * The Original Code is Google Safe Browsing.
  182.  *
  183.  * The Initial Developer of the Original Code is Google Inc.
  184.  * Portions created by the Initial Developer are Copyright (C) 2006
  185.  * the Initial Developer. All Rights Reserved.
  186.  *
  187.  * Contributor(s):
  188.  *   Aaron Boodman <aa@google.com> (original author)
  189.  *
  190.  * Alternatively, the contents of this file may be used under the terms of
  191.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  192.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  193.  * in which case the provisions of the GPL or the LGPL are applicable instead
  194.  * of those above. If you wish to allow use of your version of this file only
  195.  * under the terms of either the GPL or the LGPL, and not to allow others to
  196.  * use your version of this file under the terms of the MPL, indicate your
  197.  * decision by deleting the provisions above and replace them with the notice
  198.  * and other provisions required by the GPL or the LGPL. If you do not delete
  199.  * the provisions above, a recipient may use your version of this file under
  200.  * the terms of any one of the MPL, the GPL or the LGPL.
  201.  *
  202.  * ***** END LICENSE BLOCK ***** */
  203.  
  204. // This file has pure js helper functions. Hence you'll find metion
  205. // of browser-specific features in here.
  206.  
  207.  
  208. /**
  209.  * lang.js - The missing JavaScript language features
  210.  *
  211.  * WARNING: This class adds members to the prototypes of String, Array, and
  212.  * Function for convenience.
  213.  *
  214.  * The tradeoff is that the for/in statement will not work properly for those
  215.  * objects when this library is used.
  216.  *
  217.  * To work around this for Arrays, you may want to use the forEach() method,
  218.  * which is more fun and easier to read.
  219.  */
  220.  
  221. /**
  222.  * Returns true if the specified value is |null|
  223.  */
  224. function isNull(val) {
  225.   return val === null;
  226. }
  227.  
  228. /**
  229.  * Returns true if the specified value is an array
  230.  */
  231. function isArray(val) {
  232.   return isObject(val) && val.constructor == Array;
  233. }
  234.  
  235. /**
  236.  * Returns true if the specified value is a string
  237.  */
  238. function isString(val) {
  239.   return typeof val == "string";
  240. }
  241.  
  242. /**
  243.  * Returns true if the specified value is a boolean
  244.  */
  245. function isBoolean(val) {
  246.   return typeof val == "boolean";
  247. }
  248.  
  249. /**
  250.  * Returns true if the specified value is a number
  251.  */
  252. function isNumber(val) {
  253.   return typeof val == "number";
  254. }
  255.  
  256. /**
  257.  * Returns true if the specified value is a function
  258.  */
  259. function isFunction(val) {
  260.   return typeof val == "function";
  261. }
  262.  
  263. /**
  264.  * Returns true if the specified value is an object
  265.  */
  266. function isObject(val) {
  267.   return val && typeof val == "object";
  268. }
  269.  
  270. /**
  271.  * Returns an array of all the properties defined on an object
  272.  */
  273. function getObjectProps(obj) {
  274.   var ret = [];
  275.  
  276.   for (var p in obj) {
  277.     ret.push(p);
  278.   }
  279.  
  280.   return ret;
  281. }
  282.  
  283. /**
  284.  * Returns true if the specified value is an object which has no properties
  285.  * defined.
  286.  */
  287. function isEmptyObject(val) {
  288.   if (!isObject(val)) {
  289.     return false;
  290.   }
  291.  
  292.   for (var p in val) {
  293.     return false;
  294.   }
  295.  
  296.   return true;
  297. }
  298.  
  299. var getHashCode;
  300. var removeHashCode;
  301.  
  302. (function () {
  303.   var hashCodeProperty = "lang_hashCode_";
  304.  
  305.   /**
  306.    * Adds a lang_hashCode_ field to an object. The hash code is unique for the
  307.    * given object.
  308.    * @param obj {Object} The object to get the hash code for
  309.    * @returns {Number} The hash code for the object
  310.    */
  311.   getHashCode = function(obj) {
  312.     // In IE, DOM nodes do not extend Object so they do not have this method.
  313.     // we need to check hasOwnProperty because the proto might have this set.
  314.     if (obj.hasOwnProperty && obj.hasOwnProperty(hashCodeProperty)) {
  315.       return obj[hashCodeProperty];
  316.     }
  317.     if (!obj[hashCodeProperty]) {
  318.       obj[hashCodeProperty] = ++getHashCode.hashCodeCounter_;
  319.     }
  320.     return obj[hashCodeProperty];
  321.   };
  322.  
  323.   /**
  324.    * Removes the lang_hashCode_ field from an object.
  325.    * @param obj {Object} The object to remove the field from. 
  326.    */
  327.   removeHashCode = function(obj) {
  328.     obj.removeAttribute(hashCodeProperty);
  329.   };
  330.  
  331.   getHashCode.hashCodeCounter_ = 0;
  332. })();
  333.  
  334. /**
  335.  * Fast prefix-checker.
  336.  */
  337. String.prototype.startsWith = function(prefix) {
  338.   if (this.length < prefix.length) {
  339.     return false;
  340.   }
  341.  
  342.   if (this.substring(0, prefix.length) == prefix) {
  343.     return true;
  344.   }
  345.  
  346.   return false;
  347. }
  348.  
  349. /**
  350.  * Removes whitespace from the beginning and end of the string
  351.  */
  352. String.prototype.trim = function() {
  353.   return this.replace(/^\s+|\s+$/g, "");
  354. }
  355.  
  356. /**
  357.  * Does simple python-style string substitution.
  358.  * "foo%s hot%s".subs("bar", "dog") becomes "foobar hotdot".
  359.  * For more fully-featured templating, see template.js.
  360.  */
  361. String.prototype.subs = function() {
  362.   var ret = this;
  363.  
  364.   // this appears to be slow, but testing shows it compares more or less equiv.
  365.   // to the regex.exec method.
  366.   for (var i = 0; i < arguments.length; i++) {
  367.     ret = ret.replace(/\%s/, String(arguments[i]));
  368.   }
  369.  
  370.   return ret;
  371. }
  372.  
  373. /**
  374.  * Returns the last element on an array without removing it.
  375.  */
  376. Array.prototype.peek = function() {
  377.   return this[this.length - 1];
  378. }
  379.  
  380. // TODO(anyone): add splice the first time someone needs it and then implement
  381. // push, pop, shift, unshift in terms of it where possible.
  382.  
  383. // TODO(anyone): add the other neat-o functional methods like map(), etc.
  384.  
  385. /**
  386.  * Partially applies this function to a particular "this object" and zero or
  387.  * more arguments. The result is a new function with some arguments of the first
  388.  * function pre-filled and the value of |this| "pre-specified".
  389.  *
  390.  * Remaining arguments specified at call-time are appended to the pre-
  391.  * specified ones.
  392.  *
  393.  * Also see: partial().
  394.  *
  395.  * Note that bind and partial are optimized such that repeated calls to it do 
  396.  * not create more than one function object, so there is no additional cost for
  397.  * something like:
  398.  *
  399.  * var g = bind(f, obj);
  400.  * var h = partial(g, 1, 2, 3);
  401.  * var k = partial(h, a, b, c);
  402.  *
  403.  * Usage:
  404.  * var barMethBound = bind(myFunction, myObj, "arg1", "arg2");
  405.  * barMethBound("arg3", "arg4");
  406.  *
  407.  * @param thisObj {object} Specifies the object which |this| should point to
  408.  * when the function is run. If the value is null or undefined, it will default
  409.  * to the global object.
  410.  *
  411.  * @returns {function} A partially-applied form of the function bind() was
  412.  * invoked as a method of.
  413.  */
  414. function bind(fn, self, opt_args) {
  415.   var boundargs = (typeof fn.boundArgs_ != "undefined") ? fn.boundArgs_ : [];
  416.   boundargs = boundargs.concat(Array.prototype.slice.call(arguments, 2));
  417.  
  418.   if (typeof fn.boundSelf_ != "undefined") {
  419.     self = fn.boundSelf_;
  420.   }
  421.  
  422.   if (typeof fn.boundFn_ != "undefined") {
  423.     fn = fn.boundFn_;
  424.   }
  425.  
  426.   var newfn = function() {
  427.     // Combine the static args and the new args into one big array
  428.     var args = boundargs.concat(Array.prototype.slice.call(arguments));
  429.     return fn.apply(self, args);
  430.   }
  431.  
  432.   newfn.boundArgs_ = boundargs;
  433.   newfn.boundSelf_ = self;
  434.   newfn.boundFn_ = fn;
  435.  
  436.   return newfn;
  437. }
  438.  
  439. /**
  440.  * An alias to the bind() global function.
  441.  *
  442.  * Usage:
  443.  * var g = f.bind(obj, arg1, arg2);
  444.  * g(arg3, arg4);
  445.  */
  446. Function.prototype.bind = function(self, opt_args) {
  447.   return bind.apply(
  448.     null, [this, self].concat(Array.prototype.slice.call(arguments, 1)));
  449. }
  450.  
  451. /**
  452.  * Like bind(), except that a "this object" is not required. Useful when the
  453.  * target function is already bound.
  454.  * 
  455.  * Usage:
  456.  * var g = partial(f, arg1, arg2);
  457.  * g(arg3, arg4);
  458.  */
  459. function partial(fn, opt_args) {
  460.   return bind.apply(
  461.     null, [fn, null].concat(Array.prototype.slice.call(arguments, 1)));
  462. }
  463.  
  464. /**
  465.  * An alias to the partial() global function.
  466.  *
  467.  * Usage:
  468.  * var g = f.partial(arg1, arg2);
  469.  * g(arg3, arg4);
  470.  */
  471. Function.prototype.partial = function(opt_args) {
  472.   return bind.apply(
  473.     null, [this, null].concat(Array.prototype.slice.call(arguments)));
  474. }
  475.  
  476. /**
  477.  * Convenience. Binds all the methods of obj to itself. Calling this in the
  478.  * constructor before referencing any methods makes things a little more like
  479.  * Java or Python where methods are intrinsically bound to their instance.
  480.  */
  481. function bindMethods(obj) {
  482.   for (var p in obj) {
  483.     if (isFunction(obj[p])) {
  484.       obj[p] = obj[p].bind(obj);
  485.     }
  486.   }
  487. }
  488.  
  489. /**
  490.  * Inherit the prototype methods from one constructor into another.
  491.  *
  492.  * Usage:
  493.  * <pre>
  494.  * function ParentClass(a, b) { }
  495.  * ParentClass.prototype.foo = function(a) { }
  496.  *
  497.  * function ChildClass(a, b, c) {
  498.  *   ParentClass.call(this, a, b);
  499.  * }
  500.  *
  501.  * ChildClass.inherits(ParentClass);
  502.  *
  503.  * var child = new ChildClass("a", "b", "see");
  504.  * child.foo(); // works
  505.  * </pre>
  506.  *
  507.  * In addition, a superclass' implementation of a method can be invoked
  508.  * as follows:
  509.  *
  510.  * <pre>
  511.  * ChildClass.prototype.foo = function(a) {
  512.  *   ChildClass.superClass_.foo.call(this, a);
  513.  *   // other code
  514.  * };
  515.  * </pre>
  516.  */
  517. Function.prototype.inherits = function(parentCtor) {
  518.   var tempCtor = function(){};
  519.   tempCtor.prototype = parentCtor.prototype;
  520.   this.superClass_ = parentCtor.prototype;
  521.   this.prototype = new tempCtor();
  522. }
  523. //@line 49 "/build/buildd/firefox-1.99+2.0b1+dfsg/toolkit/components/url-classifier/src/nsUrlClassifierLib.js"
  524.  
  525. /* ***** BEGIN LICENSE BLOCK *****
  526.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  527.  *
  528.  * The contents of this file are subject to the Mozilla Public License Version
  529.  * 1.1 (the "License"); you may not use this file except in compliance with
  530.  * the License. You may obtain a copy of the License at
  531.  * http://www.mozilla.org/MPL/
  532.  *
  533.  * Software distributed under the License is distributed on an "AS IS" basis,
  534.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  535.  * for the specific language governing rights and limitations under the
  536.  * License.
  537.  *
  538.  * The Original Code is Google Safe Browsing.
  539.  *
  540.  * The Initial Developer of the Original Code is Google Inc.
  541.  * Portions created by the Initial Developer are Copyright (C) 2006
  542.  * the Initial Developer. All Rights Reserved.
  543.  *
  544.  * Contributor(s):
  545.  *   Fritz Schneider <fritz@google.com> (original author)
  546.  *
  547.  * Alternatively, the contents of this file may be used under the terms of
  548.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  549.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  550.  * in which case the provisions of the GPL or the LGPL are applicable instead
  551.  * of those above. If you wish to allow use of your version of this file only
  552.  * under the terms of either the GPL or the LGPL, and not to allow others to
  553.  * use your version of this file under the terms of the MPL, indicate your
  554.  * decision by deleting the provisions above and replace them with the notice
  555.  * and other provisions required by the GPL or the LGPL. If you do not delete
  556.  * the provisions above, a recipient may use your version of this file under
  557.  * the terms of any one of the MPL, the GPL or the LGPL.
  558.  *
  559.  * ***** END LICENSE BLOCK ***** */
  560.  
  561.  
  562. // Class for manipulating preferences. Aside from wrapping the pref
  563. // service, useful functionality includes:
  564. //
  565. // - abstracting prefobserving so that you can observe preferences
  566. //   without implementing nsIObserver 
  567. // 
  568. // - getters that return a default value when the pref doesn't exist 
  569. //   (instead of throwing)
  570. // 
  571. // - get-and-set getters
  572. //
  573. // Example:
  574. // 
  575. // var p = new PROT_Preferences();
  576. // alert(p.getPref("some-true-pref"));     // shows true
  577. // alert(p.getPref("no-such-pref", true)); // shows true   
  578. // alert(p.getPref("no-such-pref", null)); // shows null
  579. //
  580. // function observe(prefThatChanged) {
  581. //   alert("Pref changed: " + prefThatChanged);
  582. // };
  583. //
  584. // p.addObserver("somepref", observe);
  585. // p.setPref("somepref", true);            // alerts
  586. // p.removeObserver("somepref", observe);
  587. //
  588. // TODO: should probably have the prefobserver pass in the new and old
  589. //       values
  590.  
  591. // TODO(tc): Maybe remove this class and just call natively since we're no
  592. //           longer an extension.
  593.  
  594. /**
  595.  * A class that wraps the preferences service.
  596.  *
  597.  * @param opt_startPoint        A starting point on the prefs tree to resolve 
  598.  *                              names passed to setPref and getPref.
  599.  *
  600.  * @param opt_useDefaultPranch  Set to true to work against the default 
  601.  *                              preferences tree instead of the profile one.
  602.  *
  603.  * @constructor
  604.  */
  605. function G_Preferences(opt_startPoint, opt_getDefaultBranch) {
  606.   this.debugZone = "prefs";
  607.   this.observers_ = {};
  608.   this.getDefaultBranch_ = !!opt_getDefaultBranch;
  609.  
  610.   this.startPoint_ = opt_startPoint || null;
  611. }
  612.  
  613. G_Preferences.setterMap_ = { "string": "setCharPref",
  614.                              "boolean": "setBoolPref",
  615.                              "number": "setIntPref" };
  616.  
  617. G_Preferences.getterMap_ = {};
  618. G_Preferences.getterMap_[Ci.nsIPrefBranch.PREF_STRING] = "getCharPref";
  619. G_Preferences.getterMap_[Ci.nsIPrefBranch.PREF_BOOL] = "getBoolPref";
  620. G_Preferences.getterMap_[Ci.nsIPrefBranch.PREF_INT] = "getIntPref";
  621.  
  622. G_Preferences.prototype.__defineGetter__('prefs_', function() {
  623.   var prefs;
  624.   var prefSvc = Cc["@mozilla.org/preferences-service;1"]
  625.                   .getService(Ci.nsIPrefService);
  626.  
  627.   if (this.getDefaultBranch_) {
  628.     prefs = prefSvc.getDefaultBranch(this.startPoint_);
  629.   } else {
  630.     prefs = prefSvc.getBranch(this.startPoint_);
  631.   }
  632.  
  633.   // QI to prefs in case we want to add observers
  634.   prefs.QueryInterface(Ci.nsIPrefBranchInternal);
  635.   return prefs;
  636. });
  637.  
  638. /**
  639.  * Stores a key/value in a user preference. Valid types for val are string,
  640.  * boolean, and number. Complex values are not yet supported (but feel free to
  641.  * add them!).
  642.  */
  643. G_Preferences.prototype.setPref = function(key, val) {
  644.   var datatype = typeof(val);
  645.  
  646.   if (datatype == "number" && (val % 1 != 0)) {
  647.     throw new Error("Cannot store non-integer numbers in preferences.");
  648.   }
  649.  
  650.   var meth = G_Preferences.setterMap_[datatype];
  651.  
  652.   if (!meth) {
  653.     throw new Error("Pref datatype {" + datatype + "} not supported.");
  654.   }
  655.  
  656.   return this.prefs_[meth](key, val);
  657. }
  658.  
  659. /**
  660.  * Retrieves a user preference. Valid types for the value are the same as for
  661.  * setPref. If the preference is not found, opt_default will be returned 
  662.  * instead.
  663.  */
  664. G_Preferences.prototype.getPref = function(key, opt_default) {
  665.   var type = this.prefs_.getPrefType(key);
  666.  
  667.   // zero means that the specified pref didn't exist
  668.   if (type == Ci.nsIPrefBranch.PREF_INVALID) {
  669.     return opt_default;
  670.   }
  671.  
  672.   var meth = G_Preferences.getterMap_[type];
  673.  
  674.   if (!meth) {
  675.     throw new Error("Pref datatype {" + type + "} not supported.");
  676.   }
  677.  
  678.   // If a pref has been cleared, it will have a valid type but won't
  679.   // be gettable, so this will throw.
  680.   try {
  681.     return this.prefs_[meth](key);
  682.   } catch(e) {
  683.     return opt_default;
  684.   }
  685. }
  686.  
  687. /**
  688.  * Set a boolean preference
  689.  *
  690.  * @param which Name of preference to set
  691.  * @param value Boolean indicating value to set
  692.  *
  693.  * @deprecated  Just use setPref.
  694.  */
  695. G_Preferences.prototype.setBoolPref = function(which, value) {
  696.   return this.setPref(which, value);
  697. }
  698.  
  699. /**
  700.  * Get a boolean preference. WILL THROW IF PREFERENCE DOES NOT EXIST.
  701.  * If you don't want this behavior, use getBoolPrefOrDefault.
  702.  *
  703.  * @param which Name of preference to get.
  704.  *
  705.  * @deprecated  Just use getPref.
  706.  */
  707. G_Preferences.prototype.getBoolPref = function(which) {
  708.   return this.prefs_.getBoolPref(which);
  709. }
  710.  
  711. /**
  712.  * Get a boolean preference or return some default value if it doesn't
  713.  * exist. Note that the default doesn't have to be bool -- it could be
  714.  * anything (e.g., you could pass in null and check if the return
  715.  * value is === null to determine if the pref doesn't exist).
  716.  *
  717.  * @param which Name of preference to get.
  718.  * @param def Value to return if the preference doesn't exist
  719.  * @returns Boolean value of the pref if it exists, else def
  720.  *
  721.  * @deprecated  Just use getPref.
  722.  */
  723. G_Preferences.prototype.getBoolPrefOrDefault = function(which, def) {
  724.   return this.getPref(which, def);
  725. }
  726.  
  727. /**
  728.  * Get a boolean preference if it exists. If it doesn't, set its value
  729.  * to a default and return the default. Note that the default will be
  730.  * coherced to a bool if it is set, but not in the return value.
  731.  *
  732.  * @param which Name of preference to get.
  733.  * @param def Value to set and return if the preference doesn't exist
  734.  * @returns Boolean value of the pref if it exists, else def
  735.  *
  736.  * @deprecated  Just use getPref.
  737.  */
  738. G_Preferences.prototype.getBoolPrefOrDefaultAndSet = function(which, def) {
  739.   try {
  740.     return this.prefs_.getBoolPref(which);
  741.   } catch(e) {
  742.     this.prefs_.setBoolPref(which, !!def);  // The !! forces boolean conversion
  743.     return def;
  744.   }
  745. }
  746.  
  747. /**
  748.  * Delete a preference. 
  749.  *
  750.  * @param which Name of preference to obliterate
  751.  */
  752. G_Preferences.prototype.clearPref = function(which) {
  753.   try {
  754.     // This throws if the pref doesn't exist, which is fine because a 
  755.     // non-existent pref is cleared
  756.     this.prefs_.clearUserPref(which);
  757.   } catch(e) {}
  758. }
  759.  
  760. /**
  761.  * Add an observer for a given pref.
  762.  *
  763.  * @param which String containing the pref to listen to
  764.  * @param callback Function to be called when the pref changes. This
  765.  *                 function will receive a single argument, a string 
  766.  *                 holding the preference name that changed
  767.  */
  768. G_Preferences.prototype.addObserver = function(which, callback) {
  769.   var observer = new G_PreferenceObserver(callback);
  770.   // Need to store the observer we create so we can eventually unregister it
  771.   if (!this.observers_[which])
  772.     this.observers_[which] = new G_ObjectSafeMap();
  773.   this.observers_[which].insert(callback, observer);
  774.   this.prefs_.addObserver(which, observer, false /* strong reference */);
  775. }
  776.  
  777. /**
  778.  * Remove an observer for a given pref.
  779.  *
  780.  * @param which String containing the pref to stop listening to
  781.  * @param callback Function to remove as an observer
  782.  */
  783. G_Preferences.prototype.removeObserver = function(which, callback) {
  784.   var observer = this.observers_[which].find(callback);
  785.   G_Assert(this, !!observer, "Tried to unregister a nonexistant observer"); 
  786.   this.prefs_.removeObserver(which, observer);
  787.   this.observers_[which].erase(callback);
  788. }
  789.  
  790.  
  791. /**
  792.  * Helper class that knows how to observe preference changes and
  793.  * invoke a callback when they do
  794.  *
  795.  * @constructor
  796.  * @param callback Function to call when the preference changes
  797.  */
  798. function G_PreferenceObserver(callback) {
  799.   this.debugZone = "prefobserver";
  800.   this.callback_ = callback;
  801. }
  802.  
  803. /**
  804.  * Invoked by the pref system when a preference changes. Passes the
  805.  * message along to the callback.
  806.  *
  807.  * @param subject The nsIPrefBranch that changed
  808.  * @param topic String "nsPref:changed" (aka 
  809.  *              NS_PREFBRANCH_PREFCHANGE_OBSERVER_ID -- but where does it
  810.  *              live???)
  811.  * @param data Name of the pref that changed
  812.  */
  813. G_PreferenceObserver.prototype.observe = function(subject, topic, data) {
  814.   G_Debug(this, "Observed pref change: " + data);
  815.   this.callback_(data);
  816. }
  817.  
  818. /**
  819.  * XPCOM cruft
  820.  *
  821.  * @param iid Interface id of the interface the caller wants
  822.  */
  823. G_PreferenceObserver.prototype.QueryInterface = function(iid) {
  824.   var Ci = Ci;
  825.   if (iid.equals(Ci.nsISupports) || 
  826.       iid.equals(Ci.nsIObserves) ||
  827.       iid.equals(Ci.nsISupportsWeakReference))
  828.     return this;
  829.   throw Components.results.NS_ERROR_NO_INTERFACE;
  830. }
  831.  
  832. /* ***** BEGIN LICENSE BLOCK *****
  833.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  834.  *
  835.  * The contents of this file are subject to the Mozilla Public License Version
  836.  * 1.1 (the "License"); you may not use this file except in compliance with
  837.  * the License. You may obtain a copy of the License at
  838.  * http://www.mozilla.org/MPL/
  839.  *
  840.  * Software distributed under the License is distributed on an "AS IS" basis,
  841.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  842.  * for the specific language governing rights and limitations under the
  843.  * License.
  844.  *
  845.  * The Original Code is Google Safe Browsing.
  846.  *
  847.  * The Initial Developer of the Original Code is Google Inc.
  848.  * Portions created by the Initial Developer are Copyright (C) 2006
  849.  * the Initial Developer. All Rights Reserved.
  850.  *
  851.  * Contributor(s):
  852.  *   Aaron Boodman <aa@google.com> (original author)
  853.  *   Raphael Moll <raphael@google.com>
  854.  *   Fritz Schneider <fritz@google.com>
  855.  *
  856.  * Alternatively, the contents of this file may be used under the terms of
  857.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  858.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  859.  * in which case the provisions of the GPL or the LGPL are applicable instead
  860.  * of those above. If you wish to allow use of your version of this file only
  861.  * under the terms of either the GPL or the LGPL, and not to allow others to
  862.  * use your version of this file under the terms of the MPL, indicate your
  863.  * decision by deleting the provisions above and replace them with the notice
  864.  * and other provisions required by the GPL or the LGPL. If you do not delete
  865.  * the provisions above, a recipient may use your version of this file under
  866.  * the terms of any one of the MPL, the GPL or the LGPL.
  867.  *
  868.  * ***** END LICENSE BLOCK ***** */
  869.  
  870.  
  871. // Utilities for working with nsIFile and related interfaces.
  872.  
  873. /**
  874.  * Stub for an nsIFile wrapper which doesn't exist yet. Perhaps in the future
  875.  * we could add functionality to nsILocalFile which would be useful to us here,
  876.  * but for now, no need for such. This could be done by setting 
  877.  * __proto__ to an instance of nsIFile, for example. Neat.
  878.  */
  879. var G_File = {};
  880.  
  881. /**
  882.  * Returns an nsIFile pointing to the user's home directory, or optionally, a
  883.  * file inside that dir.
  884.  */
  885. G_File.getHomeFile = function(opt_file) {
  886.   return this.getSpecialFile("Home", opt_file);
  887. }
  888.  
  889. /**
  890.  * Returns an nsIFile pointing to the current profile folder, or optionally, a
  891.  * file inside that dir.
  892.  */
  893. G_File.getProfileFile = function(opt_file) {
  894.   return this.getSpecialFile("ProfD", opt_file);
  895. }
  896.  
  897. /**
  898.  * returns an nsIFile pointing to the temporary dir, or optionally, a file 
  899.  * inside that dir.
  900.  */
  901. G_File.getTempFile = function(opt_file) {
  902.   return this.getSpecialFile("TmpD", opt_file);
  903. }
  904.  
  905. /**
  906.  * Returns an nsIFile pointing to one of the special named directories defined 
  907.  * by Firefox, such as the user's home directory, the profile directory, etc. 
  908.  * 
  909.  * As a convenience, callers may specify the opt_file argument to get that file
  910.  * within the special directory instead.
  911.  *
  912.  * http://lxr.mozilla.org/seamonkey/source/xpcom/io/nsDirectoryServiceDefs.h
  913.  * http://kb.mozillazine.org/File_IO#Getting_special_files
  914.  */
  915. G_File.getSpecialFile = function(loc, opt_file) {
  916.   var file = Cc["@mozilla.org/file/directory_service;1"]
  917.              .getService(Ci.nsIProperties)
  918.              .get(loc, Ci.nsILocalFile);
  919.  
  920.   if (opt_file) {
  921.     file.append(opt_file);
  922.   }
  923.  
  924.   return file;
  925. }
  926.  
  927. /**
  928.  * Creates and returns a pointer to a unique file in the temporary directory
  929.  * with an optional base name.
  930.  */
  931. G_File.createUniqueTempFile = function(opt_baseName) {
  932.   var baseName = (opt_baseName || (new Date().getTime())) + ".tmp";
  933.  
  934.   var file = this.getSpecialFile("TmpD", baseName);
  935.   file.createUnique(file.NORMAL_FILE_TYPE, 0644);
  936.  
  937.   return file;
  938. }
  939.  
  940. /**
  941.  * Creates and returns a pointer to a unique temporary directory, with
  942.  * an optional base name.
  943.  */
  944. G_File.createUniqueTempDir = function(opt_baseName) {
  945.   var baseName = (opt_baseName || (new Date().getTime())) + ".tmp";
  946.  
  947.   var dir = this.getSpecialFile("TmpD", baseName);
  948.   dir.createUnique(dir.DIRECTORY_TYPE, 0744);
  949.  
  950.   return dir;
  951. }
  952.  
  953. /**
  954.  * Static method to retrieve an nsIFile from a file:// URI.
  955.  */
  956. G_File.fromFileURI = function(uri) {
  957.   // Ensure they use file:// url's: discourages platform-specific paths
  958.   if (uri.indexOf("file://") != 0)
  959.     throw new Error("File path must be a file:// URL");
  960.  
  961.   var fileHandler = Cc["@mozilla.org/network/protocol;1?name=file"]
  962.                     .getService(Ci.nsIFileProtocolHandler);
  963.   return fileHandler.getFileFromURLSpec(uri);
  964. }
  965.  
  966. // IO Constants
  967.  
  968. G_File.PR_RDONLY = 0x01;      // read-only
  969. G_File.PR_WRONLY = 0x02;      // write only
  970. G_File.PR_RDWR = 0x04;        // reading and writing
  971. G_File.PR_CREATE_FILE = 0x08; // create if it doesn't exist
  972. G_File.PR_APPEND = 0x10;      // file pntr reset to end prior to writes
  973. G_File.PR_TRUNCATE = 0x20;    // file exists its length is set to zero
  974. G_File.PR_SYNC = 0x40;        // writes wait for data to be physically written
  975. G_File.PR_EXCL = 0x80;        // file does not exist ? created : no action
  976.  
  977. // The character(s) to use for line-endings, which are platform-specific.
  978. // This doesn't work for mac os9, but I don't know of a good way to detect
  979. // OS9-ness from JS.
  980. G_File.__defineGetter__("LINE_END_CHAR", function() {
  981.   var end_char = Cc["@mozilla.org/xre/app-info;1"]
  982.                  .getService(Ci.nsIXULRuntime)
  983.                  .OS == "WINNT" ? "\r\n" : "\n";
  984.  
  985.   // Cache result
  986.   G_File.__defineGetter__("LINE_END_CHAR", function() { return end_char; });
  987.   return end_char;
  988. });
  989.  
  990. /**
  991.  * A class which can read a file incrementally or all at once. Parameter can be
  992.  * either an nsIFile instance or a string file:// URI.
  993.  * Note that this class is not compatible with non-ascii data.
  994.  */
  995. function G_FileReader(file) {
  996.   this.file_ = isString(file) ? G_File.fromFileURI(file) : file;
  997. }
  998.  
  999. /**
  1000.  * Utility method to read the entire contents of a file. Parameter can be either
  1001.  * an nsIFile instance or a string file:// URI.
  1002.  */
  1003. G_FileReader.readAll = function(file) { 
  1004.   var reader = new G_FileReader(file);
  1005.  
  1006.   try {
  1007.     return reader.read();
  1008.   } finally {
  1009.     reader.close();
  1010.   }
  1011. }
  1012.  
  1013. /**
  1014.  * Read up to opt_maxBytes from the stream. If opt_maxBytes is not specified, 
  1015.  * the entire file is read.
  1016.  */
  1017. G_FileReader.prototype.read = function(opt_maxBytes) {
  1018.   if (!this.stream_) {
  1019.     var fs = Cc["@mozilla.org/network/file-input-stream;1"]
  1020.                .createInstance(Ci.nsIFileInputStream);
  1021.     fs.init(this.file_, G_File.PR_RDONLY, 0444, 0);
  1022.  
  1023.     this.stream_ = Cc["@mozilla.org/scriptableinputstream;1"]
  1024.                      .createInstance(Ci.nsIScriptableInputStream);
  1025.     this.stream_.init(fs);
  1026.   }
  1027.   
  1028.   if (typeof opt_maxBytes == "undefined") {
  1029.     opt_maxBytes = this.stream_.available();
  1030.   }
  1031.  
  1032.   return this.stream_.read(opt_maxBytes);
  1033. }
  1034.  
  1035. /**
  1036.  * Close the stream. This step is required when reading is done.
  1037.  */
  1038. G_FileReader.prototype.close = function(opt_maxBytes) {
  1039.   if (this.stream_) {
  1040.     this.stream_.close();
  1041.     this.stream_ = null;
  1042.   }
  1043. }
  1044.  
  1045.  
  1046. // TODO(anyone): Implement G_LineReader. The interface should be something like:
  1047. // for (var line = null; line = reader.readLine();) {
  1048. //   // do something with line
  1049. // }
  1050.  
  1051.  
  1052. /**
  1053.  * Writes a file incrementally or all at once.
  1054.  * Note that this class is not compatible with non-ascii data.
  1055.  */
  1056. function G_FileWriter(file, opt_append) {
  1057.   this.file_ = typeof file == "string" ? G_File.fromFileURI(file) : file;
  1058.   this.append_ = !!opt_append;
  1059. }
  1060.  
  1061. /**
  1062.  * Helper to write to a file in one step.
  1063.  */
  1064. G_FileWriter.writeAll = function(file, data, opt_append) { 
  1065.   var writer = new G_FileWriter(file, opt_append);
  1066.  
  1067.   try {
  1068.     return writer.write(data);
  1069.   } finally {
  1070.     writer.close();
  1071.     return 0;
  1072.   }
  1073. }
  1074.  
  1075. /**
  1076.  * Write bytes out to the file. Returns the number of bytes written.
  1077.  */
  1078. G_FileWriter.prototype.write = function(data) {
  1079.   if (!this.stream_) {
  1080.     this.stream_ = Cc["@mozilla.org/network/file-output-stream;1"]
  1081.                      .createInstance(Ci.nsIFileOutputStream);
  1082.  
  1083.     var flags = G_File.PR_WRONLY | 
  1084.                 G_File.PR_CREATE_FILE | 
  1085.                 (this.append_ ? G_File.PR_APPEND : G_File.PR_TRUNCATE);
  1086.  
  1087.  
  1088.     this.stream_.init(this.file_, 
  1089.                       flags, 
  1090.                       -1 /* default perms */, 
  1091.                       0 /* no special behavior */);
  1092.   }
  1093.  
  1094.   return this.stream_.write(data, data.length);
  1095. }
  1096.  
  1097. /**
  1098.  * Writes bytes out to file followed by the approriate line-ending character for
  1099.  * the current platform.
  1100.  */
  1101. G_FileWriter.prototype.writeLine = function(data) {
  1102.   this.write(data + G_File.LINE_END_CHAR);
  1103. }
  1104.  
  1105.  
  1106. /**
  1107.  * Closes the file. This must becalled when writing is done.
  1108.  */
  1109. G_FileWriter.prototype.close = function() {
  1110.   if (this.stream_) {
  1111.     this.stream_.close();
  1112.     this.stream_ = null;
  1113.   }
  1114. }
  1115.  
  1116. /* ***** BEGIN LICENSE BLOCK *****
  1117.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  1118.  *
  1119.  * The contents of this file are subject to the Mozilla Public License Version
  1120.  * 1.1 (the "License"); you may not use this file except in compliance with
  1121.  * the License. You may obtain a copy of the License at
  1122.  * http://www.mozilla.org/MPL/
  1123.  *
  1124.  * Software distributed under the License is distributed on an "AS IS" basis,
  1125.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  1126.  * for the specific language governing rights and limitations under the
  1127.  * License.
  1128.  *
  1129.  * The Original Code is Google Safe Browsing.
  1130.  *
  1131.  * The Initial Developer of the Original Code is Google Inc.
  1132.  * Portions created by the Initial Developer are Copyright (C) 2006
  1133.  * the Initial Developer. All Rights Reserved.
  1134.  *
  1135.  * Contributor(s):
  1136.  *   Fritz Schneider <fritz@google.com> (original author)
  1137.  *   Annie Sullivan <sullivan@google.com>
  1138.  *   Aaron Boodman <aa@google.com>
  1139.  *
  1140.  * Alternatively, the contents of this file may be used under the terms of
  1141.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  1142.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  1143.  * in which case the provisions of the GPL or the LGPL are applicable instead
  1144.  * of those above. If you wish to allow use of your version of this file only
  1145.  * under the terms of either the GPL or the LGPL, and not to allow others to
  1146.  * use your version of this file under the terms of the MPL, indicate your
  1147.  * decision by deleting the provisions above and replace them with the notice
  1148.  * and other provisions required by the GPL or the LGPL. If you do not delete
  1149.  * the provisions above, a recipient may use your version of this file under
  1150.  * the terms of any one of the MPL, the GPL or the LGPL.
  1151.  *
  1152.  * ***** END LICENSE BLOCK ***** */
  1153.  
  1154. // Generic logging/debugging functionality that:
  1155. //
  1156. // (*) when disabled compiles to no-ops at worst (for calls to the service) 
  1157. //     and to nothing at best (calls to G_Debug() and similar are compiled
  1158. //     away when you use a jscompiler that strips dead code)
  1159. //
  1160. // (*) has dynamically configurable/creatable debugging "zones" enabling 
  1161. //     selective logging
  1162. // 
  1163. // (*) hides its plumbing so that all calls in different zones are uniform,
  1164. //     so you can drop files using this library into other apps that use it
  1165. //     without any configuration 
  1166. //
  1167. // (*) can be controlled programmatically or via preferences.  The
  1168. //     preferences that control the service and its zones are under
  1169. //     the preference branch "safebrowsing-debug-service."
  1170. //
  1171. // (*) outputs function call traces when the "loggifier" zone is enabled
  1172. // 
  1173. // (*) can write output to logfiles so that you can get a call trace
  1174. //     from someone who is having a problem
  1175. //
  1176. // Example:
  1177. //
  1178. // var G_GDEBUG = true                           // Enable this module
  1179. // var G_debugService = new G_DebugService();    // in global context
  1180. //
  1181. // // You can use it with arbitrary primitive first arguement
  1182. // G_Debug("myzone", "Yo yo yo");   // outputs: [myzone] Yo yo yo\n
  1183. //
  1184. // // But it's nice to use it with an object; it will probe for the zone name
  1185. // function Obj() {
  1186. //   this.debugZone = "someobj";
  1187. // }
  1188. // Obj.prototype.foo = function() {
  1189. //   G_Debug(this, "foo called");
  1190. // }
  1191. // (new Obj).foo();                        // outputs: [someobj] foo called\n
  1192. //
  1193. // G_debugService.loggifier.loggify(Obj.prototype);  // enable call tracing
  1194. //
  1195. // // En/disable specific zones programmatically (you can also use preferences)
  1196. // G_debugService.enableZone("somezone");
  1197. // G_debugService.disableZone("someotherzone");
  1198. // G_debugService.enableAllZones();
  1199. //
  1200. // // We also have asserts and errors:
  1201. // G_Error(this, "Some error occurred");                    // will throw
  1202. // G_Assert(this, (x > 3), "x not greater than three!");    // will throw
  1203. //
  1204. // See classes below for more methods. 
  1205. //
  1206. // TODO add abililty to alert() instead of dump()? Should be easy.
  1207. // TODO add code to set prefs when not found to the default value of a tristate
  1208. // TODO add error level support
  1209. // TODO add ability to turn off console output
  1210. //
  1211. // -------> TO START DEBUGGING: set G_GDEBUG to true
  1212.  
  1213. // These are the functions code will typically call. Everything is
  1214. // wrapped in if's so we can compile it away when G_GDEBUG is false.
  1215.  
  1216.  
  1217. if (typeof G_GDEBUG == "undefined") {
  1218.   throw new Error("G_GDEBUG constant must be set before loading debug.js");
  1219. }
  1220.  
  1221.  
  1222. /**
  1223.  * Write out a debugging message.
  1224.  *
  1225.  * @param who The thingy to convert into a zone name corresponding to the 
  1226.  *            zone to which this message belongs
  1227.  * @param msg Message to output
  1228.  */
  1229. function G_Debug(who, msg) {
  1230.   if (G_GDEBUG) {
  1231.     G_GetDebugZone(who).debug(msg);
  1232.   }
  1233. }
  1234.  
  1235. /**
  1236.  * Debugs loudly
  1237.  */
  1238. function G_DebugL(who, msg) {
  1239.   if (G_GDEBUG) {
  1240.     var zone = G_GetDebugZone(who);
  1241.  
  1242.     if (zone.zoneIsEnabled()) {
  1243.       G_debugService.dump(
  1244.         G_File.LINE_END_CHAR +
  1245.         "************************************************************" +
  1246.         G_File.LINE_END_CHAR);
  1247.  
  1248.       G_Debug(who, msg);
  1249.  
  1250.       G_debugService.dump(
  1251.         "************************************************************" +
  1252.         G_File.LINE_END_CHAR +
  1253.         G_File.LINE_END_CHAR);
  1254.     }
  1255.   }
  1256. }
  1257.  
  1258. /**
  1259.  * Write out a call tracing message
  1260.  *
  1261.  * @param who The thingy to convert into a zone name corresponding to the 
  1262.  *            zone to which this message belongs
  1263.  * @param msg Message to output
  1264.  */
  1265. function G_TraceCall(who, msg) {
  1266.   if (G_GDEBUG) {
  1267.     if (G_debugService.callTracingEnabled()) {
  1268.       G_debugService.dump(msg + G_File.LINE_END_CHAR);
  1269.     }
  1270.   }
  1271. }
  1272.  
  1273. /**
  1274.  * Write out an error (and throw)
  1275.  *
  1276.  * @param who The thingy to convert into a zone name corresponding to the 
  1277.  *            zone to which this message belongs
  1278.  * @param msg Message to output
  1279.  */
  1280. function G_Error(who, msg) {
  1281.   if (G_GDEBUG) {
  1282.     G_GetDebugZone(who).error(msg);
  1283.   }
  1284. }
  1285.  
  1286. /**
  1287.  * Assert something as true and signal an error if it's not
  1288.  *
  1289.  * @param who The thingy to convert into a zone name corresponding to the 
  1290.  *            zone to which this message belongs
  1291.  * @param condition Boolean condition to test
  1292.  * @param msg Message to output
  1293.  */
  1294. function G_Assert(who, condition, msg) {
  1295.   if (G_GDEBUG) {
  1296.     G_GetDebugZone(who).assert(condition, msg);
  1297.   }
  1298. }
  1299.  
  1300. /**
  1301.  * Assert two things are equal (as in ==).
  1302.  */
  1303. function G_AssertEqual(who, expected, actual, msg) {
  1304.   if (G_GDEBUG) {
  1305.     G_GetDebugZone(who).assert(
  1306.         expected == actual,
  1307.         msg + " Expected: {%s}, got: {%s}".subs(expected, actual));
  1308.   }
  1309. }
  1310.  
  1311. /**
  1312.  * Helper function that takes input and returns the DebugZone
  1313.  * corresponding to it.
  1314.  *
  1315.  * @param who Arbitrary input that will be converted into a zone name. Most
  1316.  *            likely an object that has .debugZone property, or a string.
  1317.  * @returns The DebugZone object corresponding to the input
  1318.  */
  1319. function G_GetDebugZone(who) {
  1320.   if (G_GDEBUG) {
  1321.     var zone = "?";
  1322.  
  1323.     if (who && who.debugZone) {
  1324.       zone = who.debugZone;
  1325.     } else if (isString(who)) {
  1326.       zone = who;
  1327.     }
  1328.  
  1329.     return G_debugService.getZone(zone);
  1330.   }
  1331. }
  1332.  
  1333. // Classes that implement the functionality.
  1334.  
  1335. /**
  1336.  * A debug "zone" is a string derived from arbitrary types (but
  1337.  * typically derived from another string or an object). All debugging
  1338.  * messages using a particular zone can be enabled or disabled
  1339.  * independent of other zones. This enables you to turn on/off logging
  1340.  * of particular objects or modules. This object implements a single
  1341.  * zone and the methods required to use it.
  1342.  *
  1343.  * @constructor
  1344.  * @param service Reference to the DebugService object we use for 
  1345.  *                registration
  1346.  * @param prefix String indicating the unique prefix we should use
  1347.  *               when creating preferences to control this zone
  1348.  * @param zone String indicating the name of the zone
  1349.  */
  1350. function G_DebugZone(service, prefix, zone) {
  1351.   if (G_GDEBUG) {
  1352.     this.debugService_ = service;
  1353.     this.prefix_ = prefix;
  1354.     this.zone_ = zone;
  1355.     this.zoneEnabledPrefName_ = prefix + ".zone." + this.zone_;
  1356.     this.settings_ = new G_DebugSettings();
  1357.   }
  1358. }
  1359.   
  1360. /**
  1361.  * @returns Boolean indicating if this zone is enabled
  1362.  */
  1363. G_DebugZone.prototype.zoneIsEnabled = function() {
  1364.   if (G_GDEBUG) {
  1365.     var explicit = this.settings_.getSetting(this.zoneEnabledPrefName_, null);
  1366.  
  1367.     if (explicit !== null) {
  1368.       return explicit;
  1369.     } else {
  1370.       return this.debugService_.allZonesEnabled();
  1371.     }
  1372.   }
  1373. }
  1374.  
  1375. /**
  1376.  * Enable this logging zone
  1377.  */
  1378. G_DebugZone.prototype.enableZone = function() {
  1379.   if (G_GDEBUG) {
  1380.     this.settings_.setDefault(this.zoneEnabledPrefName_, true);
  1381.   }
  1382. }
  1383.  
  1384. /**
  1385.  * Disable this logging zone
  1386.  */
  1387. G_DebugZone.prototype.disableZone = function() {
  1388.   if (G_GDEBUG) {
  1389.     this.settings_.setDefault(this.zoneEnabledPrefName_, false);
  1390.   }
  1391. }
  1392.  
  1393. /**
  1394.  * Write a debugging message to this zone
  1395.  *
  1396.  * @param msg String of message to write
  1397.  */
  1398. G_DebugZone.prototype.debug = function(msg) {
  1399.   if (G_GDEBUG) {
  1400.     if (this.zoneIsEnabled()) {
  1401.       this.debugService_.dump("[%s] %s%s".subs(this.zone_,
  1402.                                                msg,
  1403.                                                G_File.LINE_END_CHAR));
  1404.     }
  1405.   }
  1406. }
  1407.  
  1408. /**
  1409.  * Write an error to this zone and throw
  1410.  *
  1411.  * @param msg String of error to write
  1412.  */
  1413. G_DebugZone.prototype.error = function(msg) {
  1414.   if (G_GDEBUG) {
  1415.     this.debugService_.dump("[%s] %s%s".subs(this.zone_,
  1416.                                              msg,
  1417.                                              G_File.LINE_END_CHAR));
  1418.     throw new Error(msg);
  1419.     debugger;
  1420.   }
  1421. }
  1422.  
  1423. /**
  1424.  * Assert something as true and error if it is not
  1425.  *
  1426.  * @param condition Boolean condition to test
  1427.  * @param msg String of message to write if is false
  1428.  */
  1429. G_DebugZone.prototype.assert = function(condition, msg) {
  1430.   if (G_GDEBUG) {
  1431.     if (condition !== true) {
  1432.       G_Error(this.zone_, "ASSERT FAILED: " + msg);
  1433.     }
  1434.   }
  1435. }
  1436.  
  1437.  
  1438. /**
  1439.  * The debug service handles auto-registration of zones, namespacing
  1440.  * the zones preferences, and various global settings such as whether
  1441.  * all zones are enabled.
  1442.  *
  1443.  * @constructor
  1444.  * @param opt_prefix Optional string indicating the unique prefix we should 
  1445.  *                   use when creating preferences
  1446.  */
  1447. function G_DebugService(opt_prefix) {
  1448.   if (G_GDEBUG) {
  1449.     this.prefix_ = opt_prefix ? opt_prefix : "safebrowsing-debug-service";
  1450.     this.consoleEnabledPrefName_ = this.prefix_ + ".alsologtoconsole";
  1451.     this.allZonesEnabledPrefName_ = this.prefix_ + ".enableallzones";
  1452.     this.callTracingEnabledPrefName_ = this.prefix_ + ".trace-function-calls";
  1453.     this.logFileEnabledPrefName_ = this.prefix_ + ".logfileenabled";
  1454.     this.logFileErrorLevelPrefName_ = this.prefix_ + ".logfile-errorlevel";
  1455.     this.zones_ = {};
  1456.  
  1457.     this.loggifier = new G_Loggifier();
  1458.     this.settings_ = new G_DebugSettings();
  1459.  
  1460.     // We observe the console service so that we can echo errors that get 
  1461.     // reported there to the file log.
  1462.     Cc["@mozilla.org/consoleservice;1"]
  1463.       .getService(Ci.nsIConsoleService)
  1464.       .registerListener(this);
  1465.   }
  1466. }
  1467.  
  1468. // Error levels for reporting console messages to the log.
  1469. G_DebugService.ERROR_LEVEL_INFO = "INFO";
  1470. G_DebugService.ERROR_LEVEL_WARNING = "WARNING";
  1471. G_DebugService.ERROR_LEVEL_EXCEPTION = "EXCEPTION";
  1472.  
  1473.  
  1474. /**
  1475.  * @returns Boolean indicating if we should send messages to the jsconsole
  1476.  */
  1477. G_DebugService.prototype.alsoDumpToConsole = function() {
  1478.   if (G_GDEBUG) {
  1479.     return this.settings_.getSetting(this.consoleEnabledPrefName_, false);
  1480.   }
  1481. }
  1482.  
  1483. /**
  1484.  * @returns whether to log output to a file as well as the console.
  1485.  */
  1486. G_DebugService.prototype.logFileIsEnabled = function() {
  1487.   if (G_GDEBUG) {
  1488.     return this.settings_.getSetting(this.logFileEnabledPrefName_, false);
  1489.   }
  1490. }
  1491.  
  1492. /**
  1493.  * Turns on file logging. dump() output will also go to the file specified by
  1494.  * setLogFile()
  1495.  */
  1496. G_DebugService.prototype.enableLogFile = function() {
  1497.   if (G_GDEBUG) {
  1498.     this.settings_.setDefault(this.logFileEnabledPrefName_, true);
  1499.   }
  1500. }
  1501.  
  1502. /**
  1503.  * Turns off file logging
  1504.  */
  1505. G_DebugService.prototype.disableLogFile = function() {
  1506.   if (G_GDEBUG) {
  1507.     this.settings_.setDefault(this.logFileEnabledPrefName_, false);
  1508.   }
  1509. }
  1510.  
  1511. /**
  1512.  * @returns an nsIFile instance pointing to the current log file location
  1513.  */
  1514. G_DebugService.prototype.getLogFile = function() {
  1515.   if (G_GDEBUG) {
  1516.     return this.logFile_;
  1517.   }
  1518. }
  1519.  
  1520. /**
  1521.  * Sets a new log file location
  1522.  */
  1523. G_DebugService.prototype.setLogFile = function(file) {
  1524.   if (G_GDEBUG) {
  1525.     this.logFile_ = file;
  1526.   }
  1527. }
  1528.  
  1529. /**
  1530.  * Enables sending messages to the jsconsole
  1531.  */
  1532. G_DebugService.prototype.enableDumpToConsole = function() {
  1533.   if (G_GDEBUG) {
  1534.     this.settings_.setDefault(this.consoleEnabledPrefName_, true);
  1535.   }
  1536. }
  1537.  
  1538. /**
  1539.  * Disables sending messages to the jsconsole
  1540.  */
  1541. G_DebugService.prototype.disableDumpToConsole = function() {
  1542.   if (G_GDEBUG) {
  1543.     this.settings_.setDefault(this.consoleEnabledPrefName_, false);
  1544.   }
  1545. }
  1546.  
  1547. /**
  1548.  * @param zone Name of the zone to get
  1549.  * @returns The DebugZone object corresopnding to input. If not such
  1550.  *          zone exists, a new one is created and returned
  1551.  */
  1552. G_DebugService.prototype.getZone = function(zone) {
  1553.   if (G_GDEBUG) {
  1554.     if (!this.zones_[zone]) 
  1555.       this.zones_[zone] = new G_DebugZone(this, this.prefix_, zone);
  1556.     
  1557.     return this.zones_[zone];
  1558.   }
  1559. }
  1560.  
  1561. /**
  1562.  * @param zone Zone to enable debugging for
  1563.  */
  1564. G_DebugService.prototype.enableZone = function(zone) {
  1565.   if (G_GDEBUG) {
  1566.     var toEnable = this.getZone(zone);
  1567.     toEnable.enableZone();
  1568.   }
  1569. }
  1570.  
  1571. /**
  1572.  * @param zone Zone to disable debugging for
  1573.  */
  1574. G_DebugService.prototype.disableZone = function(zone) {
  1575.   if (G_GDEBUG) {
  1576.     var toDisable = this.getZone(zone);
  1577.     toDisable.disableZone();
  1578.   }
  1579. }
  1580.  
  1581. /**
  1582.  * @returns Boolean indicating whether debugging is enabled for all zones
  1583.  */
  1584. G_DebugService.prototype.allZonesEnabled = function() {
  1585.   if (G_GDEBUG) {
  1586.     return this.settings_.getSetting(this.allZonesEnabledPrefName_, false);
  1587.   }
  1588. }
  1589.  
  1590. /**
  1591.  * Enables all debugging zones
  1592.  */
  1593. G_DebugService.prototype.enableAllZones = function() {
  1594.   if (G_GDEBUG) {
  1595.     this.settings_.setDefault(this.allZonesEnabledPrefName_, true);
  1596.   }
  1597. }
  1598.  
  1599. /**
  1600.  * Disables all debugging zones
  1601.  */
  1602. G_DebugService.prototype.disableAllZones = function() {
  1603.   if (G_GDEBUG) {
  1604.     this.settings_.setDefault(this.allZonesEnabledPrefName_, false);
  1605.   }
  1606. }
  1607.  
  1608. /**
  1609.  * @returns Boolean indicating whether call tracing is enabled
  1610.  */
  1611. G_DebugService.prototype.callTracingEnabled = function() {
  1612.   if (G_GDEBUG) {
  1613.     return this.settings_.getSetting(this.callTracingEnabledPrefName_, false);
  1614.   }
  1615. }
  1616.  
  1617. /**
  1618.  * Enables call tracing
  1619.  */
  1620. G_DebugService.prototype.enableCallTracing = function() {
  1621.   if (G_GDEBUG) {
  1622.     this.settings_.setDefault(this.callTracingEnabledPrefName_, true);
  1623.   }
  1624. }
  1625.  
  1626. /**
  1627.  * Disables call tracing
  1628.  */
  1629. G_DebugService.prototype.disableCallTracing = function() {
  1630.   if (G_GDEBUG) {
  1631.     this.settings_.setDefault(this.callTracingEnabledPrefName_, false);
  1632.   }
  1633. }
  1634.  
  1635. /**
  1636.  * Gets the minimum error that will be reported to the log.
  1637.  */
  1638. G_DebugService.prototype.getLogFileErrorLevel = function() {
  1639.   if (G_GDEBUG) {
  1640.     var level = this.settings_.getSetting(this.logFileErrorLevelPrefName_, 
  1641.                                           G_DebugService.ERROR_LEVEL_EXCEPTION);
  1642.  
  1643.     return level.toUpperCase();
  1644.   }
  1645. }
  1646.  
  1647. /**
  1648.  * Sets the minimum error level that will be reported to the log.
  1649.  */
  1650. G_DebugService.prototype.setLogFileErrorLevel = function(level) {
  1651.   if (G_GDEBUG) {
  1652.     // normalize case just to make it slightly easier to not screw up.
  1653.     level = level.toUpperCase();
  1654.  
  1655.     if (level != G_DebugService.ERROR_LEVEL_INFO &&
  1656.         level != G_DebugService.ERROR_LEVEL_WARNING &&
  1657.         level != G_DebugService.ERROR_LEVEL_EXCEPTION) {
  1658.       throw new Error("Invalid error level specified: {" + level + "}");
  1659.     }
  1660.  
  1661.     this.settings_.setDefault(this.logFileErrorLevelPrefName_, level);
  1662.   }
  1663. }
  1664.  
  1665. /**
  1666.  * Internal dump() method
  1667.  *
  1668.  * @param msg String of message to dump
  1669.  */
  1670. G_DebugService.prototype.dump = function(msg) {
  1671.   if (G_GDEBUG) {
  1672.     dump(msg);
  1673.     
  1674.     if (this.alsoDumpToConsole()) {
  1675.       try {
  1676.         var console = Components.classes['@mozilla.org/consoleservice;1']
  1677.                       .getService(Components.interfaces.nsIConsoleService);
  1678.         console.logStringMessage(msg);
  1679.       } catch(e) {
  1680.         dump("G_DebugZone ERROR: COULD NOT DUMP TO CONSOLE" +
  1681.              G_File.LINE_END_CHAR);
  1682.       }
  1683.     }
  1684.  
  1685.     this.maybeDumpToFile(msg);
  1686.   }
  1687. }
  1688.  
  1689. /**
  1690.  * Writes the specified message to the log file, if file logging is enabled.
  1691.  */
  1692. G_DebugService.prototype.maybeDumpToFile = function(msg) {
  1693.   if (this.logFileIsEnabled() && this.logFile_) {
  1694.     if (!this.logWriter_) {
  1695.       this.logWriter_ = new G_FileWriter(this.logFile_, true);
  1696.     }
  1697.  
  1698.     this.logWriter_.write(msg);
  1699.   }
  1700. }
  1701.  
  1702. /**
  1703.  * Implements nsIConsoleListener.observe(). Gets called when an error message
  1704.  * gets reported to the console and sends it to the log file as well.
  1705.  */
  1706. G_DebugService.prototype.observe = function(consoleMessage) {
  1707.   if (G_GDEBUG) {
  1708.     var errorLevel = this.getLogFileErrorLevel();
  1709.  
  1710.     // consoleMessage can be either nsIScriptError or nsIConsoleMessage. The
  1711.     // latter does not have things like line number, etc. So we special case 
  1712.     // it first. 
  1713.     if (!(consoleMessage instanceof Ci.nsIScriptError)) {
  1714.       // Only report these messages if the error level is INFO.
  1715.       if (errorLevel == G_DebugService.ERROR_LEVEL_INFO) {
  1716.         this.maybeDumpToFile(G_DebugService.ERROR_LEVEL_INFO + ": " + 
  1717.                              consoleMessage.message + G_File.LINE_END_CHAR);
  1718.       }
  1719.  
  1720.       return;
  1721.     }
  1722.  
  1723.     // We make a local copy of these fields because writing to it doesn't seem
  1724.     // to work.
  1725.     var flags = consoleMessage.flags;
  1726.     var sourceName = consoleMessage.sourceName;
  1727.     var lineNumber = consoleMessage.lineNumber;
  1728.  
  1729.     // Sometimes, a scripterror instance won't have any flags set. We 
  1730.     // default to exception.
  1731.     if (!flags) {
  1732.       flags = Ci.nsIScriptError.exceptionFlag;
  1733.     }
  1734.  
  1735.     // Default the filename and line number if they aren't set.
  1736.     if (!sourceName) {
  1737.       sourceName = "<unknown>";
  1738.     }
  1739.  
  1740.     if (!lineNumber) {
  1741.       lineNumber = "<unknown>";
  1742.     }
  1743.  
  1744.     // Report the error in the log file.
  1745.     if (flags & Ci.nsIScriptError.warningFlag) {
  1746.       // Only report warnings if the error level is warning or better. 
  1747.       if (errorLevel == G_DebugService.ERROR_LEVEL_WARNING ||
  1748.           errorLevel == G_DebugService.ERROR_LEVEL_INFO) {
  1749.         this.reportScriptError_(consoleMessage.message,
  1750.                                 sourceName,
  1751.                                 lineNumber,
  1752.                                 G_DebugService.ERROR_LEVEL_WARNING);
  1753.       }
  1754.     } else if (flags & Ci.nsIScriptError.exceptionFlag) {
  1755.       // Always report exceptions.
  1756.       this.reportScriptError_(consoleMessage.message,
  1757.                               sourceName,
  1758.                               lineNumber,
  1759.                               G_DebugService.ERROR_LEVEL_EXCEPTION);
  1760.     }
  1761.   }
  1762. }
  1763.  
  1764. /**
  1765.  * Private helper to report an nsIScriptError instance to the log/console.
  1766.  */
  1767. G_DebugService.prototype.reportScriptError_ = function(message, sourceName, 
  1768.                                                        lineNumber, label) {
  1769.   var message = ["",
  1770.                  "------------------------------------------------------------",
  1771.                  label + ": " + message,
  1772.                  "location: " + sourceName + ", " + "line: " + lineNumber,
  1773.                  "------------------------------------------------------------",
  1774.                  "",
  1775.                  ""].join(G_File.LINE_END_CHAR);
  1776.  
  1777.   dump(message);
  1778.   this.maybeDumpToFile(message);
  1779. }
  1780.  
  1781.  
  1782.  
  1783. /**
  1784.  * A class that instruments methods so they output a call trace,
  1785.  * including the values of their actual parameters and return value.
  1786.  * This code is mostly stolen from Aaron Boodman's original
  1787.  * implementation in clobber utils.
  1788.  *
  1789.  * Note that this class uses the "loggifier" debug zone, so you'll see 
  1790.  * a complete call trace when that zone is enabled.
  1791.  *
  1792.  * @constructor
  1793.  */
  1794. function G_Loggifier() {
  1795.   if (G_GDEBUG) {
  1796.     // Careful not to loggify ourselves!
  1797.     this.mark_(this);  
  1798.   }
  1799. }
  1800.  
  1801. /**
  1802.  * Marks an object as having been loggified. Loggification is not 
  1803.  * idempotent :)
  1804.  *
  1805.  * @param obj Object to be marked
  1806.  */
  1807. G_Loggifier.prototype.mark_ = function(obj) {
  1808.   if (G_GDEBUG) {
  1809.     obj.__loggified_ = true;
  1810.   }
  1811. }
  1812.  
  1813. /**
  1814.  * @param obj Object to be examined
  1815.  * @returns Boolean indicating if the object has been loggified
  1816.  */
  1817. G_Loggifier.prototype.isLoggified = function(obj) {
  1818.   if (G_GDEBUG) {
  1819.     return !!obj.__loggified_;
  1820.   }
  1821. }
  1822.  
  1823. /**
  1824.  * Attempt to extract the class name from the constructor definition.
  1825.  * Assumes the object was created using new.
  1826.  *
  1827.  * @param constructor String containing the definition of a constructor,
  1828.  *                    for example what you'd get by examining obj.constructor
  1829.  * @returns Name of the constructor/object if it could be found, else "???"
  1830.  */
  1831. G_Loggifier.prototype.getFunctionName_ = function(constructor) {
  1832.   if (G_GDEBUG) {
  1833.     return constructor.name || "???";
  1834.   }
  1835. }
  1836.  
  1837. /**
  1838.  * Wraps all the methods in an object so that call traces are
  1839.  * automatically outputted.
  1840.  *
  1841.  * @param obj Object to loggify. SHOULD BE THE PROTOTYPE OF A USER-DEFINED
  1842.  *            object. You can get into trouble if you attempt to 
  1843.  *            loggify something that isn't, for example the Window.
  1844.  *
  1845.  * Any additional parameters are considered method names which should not be
  1846.  * loggified.
  1847.  *
  1848.  * Usage:
  1849.  * G_debugService.loggifier.loggify(MyClass.prototype,
  1850.  *                                  "firstMethodNotToLog",
  1851.  *                                  "secondMethodNotToLog",
  1852.  *                                  ... etc ...);
  1853.  */
  1854. G_Loggifier.prototype.loggify = function(obj) {
  1855.   if (G_GDEBUG) {
  1856.     if (!G_debugService.callTracingEnabled()) {
  1857.       return;
  1858.     }
  1859.  
  1860.     if (typeof window != "undefined" && obj == window || 
  1861.         this.isLoggified(obj))   // Don't go berserk!
  1862.       return;
  1863.  
  1864.     var zone = G_GetDebugZone(obj);
  1865.     if (!zone || !zone.zoneIsEnabled()) {
  1866.       return;
  1867.     }
  1868.  
  1869.     this.mark_(obj);
  1870.  
  1871.     // Helper function returns an instrumented version of
  1872.     // objName.meth, with "this" bound properly. (BTW, because we're
  1873.     // in a conditional here, functions will only be defined as
  1874.     // they're encountered during execution, so declare this helper
  1875.     // before using it.)
  1876.  
  1877.     function wrap(meth, objName, methName) {
  1878.       return function() {
  1879.         
  1880.         // First output the call along with actual parameters
  1881.         var args = new Array(arguments.length);
  1882.         var argsString = "";
  1883.         for (var i = 0; i < args.length; i++) {
  1884.           args[i] = arguments[i];
  1885.           argsString += (i == 0 ? "" : ", ");
  1886.           
  1887.           if (isFunction(args[i])) {
  1888.             argsString += "[function]";
  1889.           } else {
  1890.             argsString += args[i];
  1891.           }
  1892.         }
  1893.  
  1894.         G_TraceCall(this, "> " + objName + "." + methName + "(" + 
  1895.                     argsString + ")");
  1896.         
  1897.         // Then run the function, capturing the return value and throws
  1898.         try {
  1899.           var retVal = meth.apply(this, arguments);
  1900.           var reportedRetVal = retVal;
  1901.  
  1902.           if (typeof reportedRetVal == "undefined")
  1903.             reportedRetVal = "void";
  1904.           else if (reportedRetVal === "")
  1905.             reportedRetVal = "\"\" (empty string)";
  1906.         } catch (e) {
  1907.           if (e && !e.__logged) {
  1908.             G_TraceCall(this, "Error: " + e.message + ". " + 
  1909.                         e.fileName + ": " + e.lineNumber);
  1910.             try {
  1911.               e.__logged = true;
  1912.             } catch (e2) {
  1913.               // Sometimes we can't add the __logged flag because it's an
  1914.               // XPC wrapper
  1915.               throw e;
  1916.             }
  1917.           }
  1918.           
  1919.           throw e;      // Re-throw!
  1920.         }
  1921.  
  1922.         // And spit it out already
  1923.         G_TraceCall(
  1924.           this, 
  1925.           "< " + objName + "." + methName + ": " + reportedRetVal);
  1926.  
  1927.         return retVal;
  1928.       };
  1929.     };
  1930.  
  1931.     var ignoreLookup = {};
  1932.  
  1933.     if (arguments.length > 1) {
  1934.       for (var i = 1; i < arguments.length; i++) {
  1935.         ignoreLookup[arguments[i]] = true;
  1936.       }
  1937.     }
  1938.     
  1939.     // Wrap each method of obj
  1940.     for (var p in obj) {
  1941.       // Work around bug in Firefox. In ffox typeof RegExp is "function",
  1942.       // so make sure this really is a function. Bug as of FFox 1.5b2.
  1943.       if (typeof obj[p] == "function" && obj[p].call && !ignoreLookup[p]) {
  1944.         var objName = this.getFunctionName_(obj.constructor);
  1945.         obj[p] = wrap(obj[p], objName, p);
  1946.       }
  1947.     }
  1948.   }
  1949. }
  1950.  
  1951.  
  1952. /**
  1953.  * Simple abstraction around debug settings. The thing with debug settings is
  1954.  * that we want to be able to specify a default in the application's startup,
  1955.  * but have that default be overridable by the user via their prefs.
  1956.  *
  1957.  * To generalize this, we package up a dictionary of defaults with the 
  1958.  * preferences tree. If a setting isn't in the preferences tree, then we grab it
  1959.  * from the defaults.
  1960.  */
  1961. function G_DebugSettings() {
  1962.   this.defaults_ = {};
  1963.   this.prefs_ = new G_Preferences();
  1964. }
  1965.  
  1966. /**
  1967.  * Returns the value of a settings, optionally defaulting to a given value if it
  1968.  * doesn't exist. If no default is specified, the default is |undefined|.
  1969.  */
  1970. G_DebugSettings.prototype.getSetting = function(name, opt_default) {
  1971.   var override = this.prefs_.getPref(name, null);
  1972.  
  1973.   if (override !== null) {
  1974.     return override;
  1975.   } else if (typeof this.defaults_[name] != "undefined") {
  1976.     return this.defaults_[name];
  1977.   } else {
  1978.     return opt_default;
  1979.   }
  1980. }
  1981.  
  1982. /**
  1983.  * Sets the default value for a setting. If the user doesn't override it with a
  1984.  * preference, this is the value which will be returned by getSetting().
  1985.  */
  1986. G_DebugSettings.prototype.setDefault = function(name, val) {
  1987.   this.defaults_[name] = val;
  1988. }
  1989.  
  1990. var G_debugService = new G_DebugService(); // Instantiate us!
  1991.  
  1992. if (G_GDEBUG) {
  1993.   G_debugService.enableAllZones();
  1994. }
  1995. /* ***** BEGIN LICENSE BLOCK *****
  1996.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  1997.  *
  1998.  * The contents of this file are subject to the Mozilla Public License Version
  1999.  * 1.1 (the "License"); you may not use this file except in compliance with
  2000.  * the License. You may obtain a copy of the License at
  2001.  * http://www.mozilla.org/MPL/
  2002.  *
  2003.  * Software distributed under the License is distributed on an "AS IS" basis,
  2004.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  2005.  * for the specific language governing rights and limitations under the
  2006.  * License.
  2007.  *
  2008.  * The Original Code is Google Safe Browsing.
  2009.  *
  2010.  * The Initial Developer of the Original Code is Google Inc.
  2011.  * Portions created by the Initial Developer are Copyright (C) 2006
  2012.  * the Initial Developer. All Rights Reserved.
  2013.  *
  2014.  * Contributor(s):
  2015.  *   Fritz Schneider <fritz@google.com> (original author)
  2016.  *
  2017.  * Alternatively, the contents of this file may be used under the terms of
  2018.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  2019.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  2020.  * in which case the provisions of the GPL or the LGPL are applicable instead
  2021.  * of those above. If you wish to allow use of your version of this file only
  2022.  * under the terms of either the GPL or the LGPL, and not to allow others to
  2023.  * use your version of this file under the terms of the MPL, indicate your
  2024.  * decision by deleting the provisions above and replace them with the notice
  2025.  * and other provisions required by the GPL or the LGPL. If you do not delete
  2026.  * the provisions above, a recipient may use your version of this file under
  2027.  * the terms of any one of the MPL, the GPL or the LGPL.
  2028.  *
  2029.  * ***** END LICENSE BLOCK ***** */
  2030.  
  2031.  
  2032. // An Alarm fires a callback after a certain amount of time, or at
  2033. // regular intervals. It's a convenient replacement for
  2034. // setTimeout/Interval when you don't want to bind to a specific
  2035. // window.
  2036. //
  2037. // The ConditionalAlarm is an Alarm that cancels itself if its callback 
  2038. // returns a value that type-converts to true.
  2039. //
  2040. // Example:
  2041. //
  2042. //  function foo() { alert('hi'); };
  2043. //  new G_Alarm(foo, 10*1000);                   // Fire foo in 10 seconds
  2044. //  new G_Alarm(foo, 10*1000, true /*repeat*/);  // Fire foo every 10 seconds
  2045. //  new G_Alarm(foo, 10*1000, true, 7);          // Fire foo every 10 seconds
  2046. //                                               // seven times
  2047. //  new G_ConditionalAlarm(foo, 1000, true); // Fire every sec until foo()==true
  2048. //
  2049. //  // Fire foo every 10 seconds until foo returns true or until it fires seven
  2050. //  // times, whichever happens first.
  2051. //  new G_ConditionalAlarm(foo, 10*1000, true /*repeating*/, 7);
  2052. //
  2053. // TODO: maybe pass an isFinal flag to the callback if they opted to
  2054. // set maxTimes and this is the last iteration?
  2055.  
  2056.  
  2057. /**
  2058.  * Set an alarm to fire after a given amount of time, or at specific 
  2059.  * intervals.
  2060.  *
  2061.  * @param callback Function to call when the alarm fires
  2062.  * @param delayMS Number indicating the length of the alarm period in ms
  2063.  * @param opt_repeating Boolean indicating whether this should fire 
  2064.  *                      periodically
  2065.  * @param opt_maxTimes Number indicating a maximum number of times to 
  2066.  *                     repeat (obviously only useful when opt_repeating==true)
  2067.  */
  2068. function G_Alarm(callback, delayMS, opt_repeating, opt_maxTimes) {
  2069.   this.debugZone = "alarm";
  2070.   this.callback_ = callback;
  2071.   this.repeating_ = !!opt_repeating;
  2072.   this.timer_ = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
  2073.   var type = opt_repeating ? 
  2074.              this.timer_.TYPE_REPEATING_SLACK : 
  2075.              this.timer_.TYPE_ONE_SHOT;
  2076.   this.maxTimes_ = opt_maxTimes ? opt_maxTimes : null;
  2077.   this.nTimes_ = 0;
  2078.  
  2079.   this.observerServiceObserver_ = new G_ObserverServiceObserver(
  2080.                                         'xpcom-shutdown',
  2081.                                         BindToObject(this.cancel, this));
  2082.  
  2083.   // Ask the timer to use nsITimerCallback (.notify()) when ready
  2084.   this.timer_.initWithCallback(this, delayMS, type);
  2085. }
  2086.  
  2087. /**
  2088.  * Cancel this timer 
  2089.  */
  2090. G_Alarm.prototype.cancel = function() {
  2091.   if (!this.timer_) {
  2092.     return;
  2093.   }
  2094.  
  2095.   this.timer_.cancel();
  2096.   // Break circular reference created between this.timer_ and the G_Alarm
  2097.   // instance (this)
  2098.   this.timer_ = null;
  2099.   this.callback_ = null;
  2100.  
  2101.   // We don't need the shutdown observer anymore
  2102.   this.observerServiceObserver_.unregister();
  2103. }
  2104.  
  2105. /**
  2106.  * Invoked by the timer when it fires
  2107.  * 
  2108.  * @param timer Reference to the nsITimer which fired (not currently 
  2109.  *              passed along)
  2110.  */
  2111. G_Alarm.prototype.notify = function(timer) {
  2112.   // fire callback and save results
  2113.   var ret = this.callback_();
  2114.   
  2115.   // If they've given us a max number of times to fire, enforce it
  2116.   this.nTimes_++;
  2117.   if (this.repeating_ && 
  2118.       typeof this.maxTimes_ == "number" 
  2119.       && this.nTimes_ >= this.maxTimes_) {
  2120.     this.cancel();
  2121.   } else if (!this.repeating_) {
  2122.     // Clear out the callback closure for TYPE_ONE_SHOT timers
  2123.     this.cancel();
  2124.   }
  2125.   // We don't cancel/cleanup timers that repeat forever until either
  2126.   // xpcom-shutdown occurs or cancel() is called explicitly.
  2127.  
  2128.   return ret;
  2129. }
  2130.  
  2131. /**
  2132.  * XPCOM cruft
  2133.  */
  2134. G_Alarm.prototype.QueryInterface = function(iid) {
  2135.   if (iid.equals(Components.interfaces.nsISupports) ||
  2136.       iid.equals(Components.interfaces.nsITimerCallback))
  2137.     return this;
  2138.  
  2139.   throw Components.results.NS_ERROR_NO_INTERFACE;
  2140. }
  2141.  
  2142.  
  2143. /**
  2144.  * An alarm with the additional property that it cancels itself if its 
  2145.  * callback returns true.
  2146.  *
  2147.  * For parameter documentation, see G_Alarm
  2148.  */
  2149. function G_ConditionalAlarm(callback, delayMS, opt_repeating, opt_maxTimes) {
  2150.   G_Alarm.call(this, callback, delayMS, opt_repeating, opt_maxTimes);
  2151.   this.debugZone = "conditionalalarm";
  2152. }
  2153.  
  2154. G_ConditionalAlarm.inherits(G_Alarm);
  2155.  
  2156. /**
  2157.  * Invoked by the timer when it fires
  2158.  * 
  2159.  * @param timer Reference to the nsITimer which fired (not currently 
  2160.  *              passed along)
  2161.  */
  2162. G_ConditionalAlarm.prototype.notify = function(timer) {
  2163.   // Call G_Alarm::notify
  2164.   var rv = G_Alarm.prototype.notify.call(this, timer);
  2165.  
  2166.   if (this.repeating_ && rv) {
  2167.     G_Debug(this, "Callback of a repeating alarm returned true; cancelling.");
  2168.     this.cancel();
  2169.   }
  2170. }
  2171. /* ***** BEGIN LICENSE BLOCK *****
  2172.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  2173.  *
  2174.  * The contents of this file are subject to the Mozilla Public License Version
  2175.  * 1.1 (the "License"); you may not use this file except in compliance with
  2176.  * the License. You may obtain a copy of the License at
  2177.  * http://www.mozilla.org/MPL/
  2178.  *
  2179.  * Software distributed under the License is distributed on an "AS IS" basis,
  2180.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  2181.  * for the specific language governing rights and limitations under the
  2182.  * License.
  2183.  *
  2184.  * The Original Code is Google Safe Browsing.
  2185.  *
  2186.  * The Initial Developer of the Original Code is Google Inc.
  2187.  * Portions created by the Initial Developer are Copyright (C) 2006
  2188.  * the Initial Developer. All Rights Reserved.
  2189.  *
  2190.  * Contributor(s):
  2191.  *   Fritz Schneider <fritz@google.com> (original author)
  2192.  *
  2193.  * Alternatively, the contents of this file may be used under the terms of
  2194.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  2195.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  2196.  * in which case the provisions of the GPL or the LGPL are applicable instead
  2197.  * of those above. If you wish to allow use of your version of this file only
  2198.  * under the terms of either the GPL or the LGPL, and not to allow others to
  2199.  * use your version of this file under the terms of the MPL, indicate your
  2200.  * decision by deleting the provisions above and replace them with the notice
  2201.  * and other provisions required by the GPL or the LGPL. If you do not delete
  2202.  * the provisions above, a recipient may use your version of this file under
  2203.  * the terms of any one of the MPL, the GPL or the LGPL.
  2204.  *
  2205.  * ***** END LICENSE BLOCK ***** */
  2206.  
  2207.  
  2208. // Base64 en/decoding. Not much to say here except that we work with
  2209. // decoded values in arrays of bytes. By "byte" I mean a number in [0,
  2210. // 255].
  2211.  
  2212.  
  2213. /**
  2214.  * Base64 en/decoder. Useful in contexts that don't have atob/btoa, or
  2215.  * when you need a custom encoding function (e.g., websafe base64).
  2216.  *
  2217.  * @constructor
  2218.  */
  2219. function G_Base64() {
  2220.   this.byteToCharMap_ = {};
  2221.   this.charToByteMap_ = {};
  2222.   this.byteToCharMapWebSafe_ = {};
  2223.   this.charToByteMapWebSafe_ = {};
  2224.   this.init_();
  2225. }
  2226.  
  2227. /**
  2228.  * Our default alphabet. Value 64 (=) is special; it means "nothing."
  2229.  */ 
  2230. G_Base64.ENCODED_VALS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
  2231.                         "abcdefghijklmnopqrstuvwxyz" +
  2232.                         "0123456789+/=";
  2233.  
  2234. /**
  2235.  * Our websafe alphabet. Value 64 (=) is special; it means "nothing."
  2236.  */ 
  2237. G_Base64.ENCODED_VALS_WEBSAFE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
  2238.                                 "abcdefghijklmnopqrstuvwxyz" +
  2239.                                 "0123456789-_=";
  2240.  
  2241. /**
  2242.  * We want quick mappings back and forth, so we precompute two maps.
  2243.  */
  2244. G_Base64.prototype.init_ = function() {
  2245.   for (var i = 0; i < G_Base64.ENCODED_VALS.length; i++) {
  2246.     this.byteToCharMap_[i] = G_Base64.ENCODED_VALS.charAt(i);
  2247.     this.charToByteMap_[this.byteToCharMap_[i]] = i;
  2248.     this.byteToCharMapWebSafe_[i] = G_Base64.ENCODED_VALS_WEBSAFE.charAt(i);
  2249.     this.charToByteMapWebSafe_[this.byteToCharMapWebSafe_[i]] = i;
  2250.   }
  2251. }
  2252.  
  2253. /**
  2254.  * Base64-encode an array of bytes.
  2255.  *
  2256.  * @param input An array of bytes (numbers with value in [0, 255]) to encode
  2257.  *
  2258.  * @param opt_webSafe Boolean indicating we should use the alternative alphabet 
  2259.  *
  2260.  * @returns String containing the base64 encoding
  2261.  */
  2262. G_Base64.prototype.encodeByteArray = function(input, opt_webSafe) {
  2263.  
  2264.   if (!(input instanceof Array))
  2265.     throw new Error("encodeByteArray takes an array as a parameter");
  2266.  
  2267.   var byteToCharMap = opt_webSafe ? 
  2268.                       this.byteToCharMapWebSafe_ :
  2269.                       this.byteToCharMap_;
  2270.  
  2271.   var output = [];
  2272.  
  2273.   var i = 0;
  2274.   while (i < input.length) {
  2275.  
  2276.     var byte1 = input[i];
  2277.     var haveByte2 = i + 1 < input.length;
  2278.     var byte2 = haveByte2 ? input[i + 1] : 0;
  2279.     var haveByte3 = i + 2 < input.length;
  2280.     var byte3 = haveByte3 ? input[i + 2] : 0;
  2281.  
  2282.     var outByte1 = byte1 >> 2;
  2283.     var outByte2 = ((byte1 & 0x03) << 4) | (byte2 >> 4);
  2284.     var outByte3 = ((byte2 & 0x0F) << 2) | (byte3 >> 6);
  2285.     var outByte4 = byte3 & 0x3F;
  2286.  
  2287.     if (!haveByte3) {
  2288.       outByte4 = 64;
  2289.       
  2290.       if (!haveByte2)
  2291.         outByte3 = 64;
  2292.     }
  2293.     
  2294.     output.push(byteToCharMap[outByte1]);
  2295.     output.push(byteToCharMap[outByte2]);
  2296.     output.push(byteToCharMap[outByte3]);
  2297.     output.push(byteToCharMap[outByte4]);
  2298.  
  2299.     i += 3;
  2300.   }
  2301.  
  2302.   return output.join("");
  2303. }
  2304.  
  2305. /**
  2306.  * Base64-decode a string.
  2307.  *
  2308.  * @param input String to decode
  2309.  *
  2310.  * @param opt_webSafe Boolean indicating we should use the alternative alphabet 
  2311.  * 
  2312.  * @returns Array of bytes representing the decoded value.
  2313.  */
  2314. G_Base64.prototype.decodeString = function(input, opt_webSafe) {
  2315.  
  2316.   if (input.length % 4)
  2317.     throw new Error("Length of b64-encoded data must be zero mod four");
  2318.  
  2319.   var charToByteMap = opt_webSafe ? 
  2320.                       this.charToByteMapWebSafe_ :
  2321.                       this.charToByteMap_;
  2322.  
  2323.   var output = [];
  2324.  
  2325.   var i = 0;
  2326.   while (i < input.length) {
  2327.  
  2328.     var byte1 = charToByteMap[input.charAt(i)];
  2329.     var byte2 = charToByteMap[input.charAt(i + 1)];
  2330.     var byte3 = charToByteMap[input.charAt(i + 2)];
  2331.     var byte4 = charToByteMap[input.charAt(i + 3)];
  2332.  
  2333.     if (byte1 === undefined || byte2 === undefined ||
  2334.         byte3 === undefined || byte4 === undefined)
  2335.       throw new Error("String contains characters not in our alphabet: " +
  2336.                       input);
  2337.  
  2338.     var outByte1 = (byte1 << 2) | (byte2 >> 4);
  2339.     output.push(outByte1);
  2340.     
  2341.     if (byte3 != 64) {
  2342.       var outByte2 = ((byte2 << 4) & 0xF0) | (byte3 >> 2);
  2343.       output.push(outByte2);
  2344.       
  2345.       if (byte4 != 64) {
  2346.         var outByte3 = ((byte3 << 6) & 0xC0) | byte4;
  2347.         output.push(outByte3);
  2348.       }
  2349.     }
  2350.  
  2351.     i += 4;
  2352.   }
  2353.  
  2354.   return output;
  2355. }
  2356.  
  2357. /**
  2358.  * Helper function that turns a string into an array of numbers. 
  2359.  *
  2360.  * @param str String to arrify
  2361.  *
  2362.  * @returns Array holding numbers corresponding to the UCS character codes
  2363.  *          of each character in str
  2364.  */
  2365. G_Base64.prototype.arrayifyString = function(str) {
  2366.   var output = [];
  2367.   for (var i = 0; i < str.length; i++)
  2368.     output.push(str.charCodeAt(i));
  2369.   return output;
  2370. }
  2371.  
  2372. /**
  2373.  * Helper function that turns an array of numbers into the string
  2374.  * given by the concatenation of the characters to which the numbesr
  2375.  * correspond (got that?).
  2376.  *
  2377.  * @param array Array of numbers representing characters
  2378.  *
  2379.  * @returns Stringification of the array
  2380.  */ 
  2381. G_Base64.prototype.stringifyArray = function(array) {
  2382.   var output = [];
  2383.   for (var i = 0; i < array.length; i++)
  2384.     output[i] = String.fromCharCode(array[i]);
  2385.   return output.join("");
  2386. }
  2387.  
  2388.  
  2389. /* ***** BEGIN LICENSE BLOCK *****
  2390.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  2391.  *
  2392.  * The contents of this file are subject to the Mozilla Public License Version
  2393.  * 1.1 (the "License"); you may not use this file except in compliance with
  2394.  * the License. You may obtain a copy of the License at
  2395.  * http://www.mozilla.org/MPL/
  2396.  *
  2397.  * Software distributed under the License is distributed on an "AS IS" basis,
  2398.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  2399.  * for the specific language governing rights and limitations under the
  2400.  * License.
  2401.  *
  2402.  * The Original Code is Google Safe Browsing.
  2403.  *
  2404.  * The Initial Developer of the Original Code is Google Inc.
  2405.  * Portions created by the Initial Developer are Copyright (C) 2006
  2406.  * the Initial Developer. All Rights Reserved.
  2407.  *
  2408.  * Contributor(s):
  2409.  *   Fritz Schneider <fritz@google.com> (original author)
  2410.  *
  2411.  * Alternatively, the contents of this file may be used under the terms of
  2412.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  2413.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  2414.  * in which case the provisions of the GPL or the LGPL are applicable instead
  2415.  * of those above. If you wish to allow use of your version of this file only
  2416.  * under the terms of either the GPL or the LGPL, and not to allow others to
  2417.  * use your version of this file under the terms of the MPL, indicate your
  2418.  * decision by deleting the provisions above and replace them with the notice
  2419.  * and other provisions required by the GPL or the LGPL. If you do not delete
  2420.  * the provisions above, a recipient may use your version of this file under
  2421.  * the terms of any one of the MPL, the GPL or the LGPL.
  2422.  *
  2423.  * ***** END LICENSE BLOCK ***** */
  2424.  
  2425.  
  2426. // A very thin wrapper around nsICryptoHash. It's not strictly
  2427. // necessary, but makes the code a bit cleaner and gives us the
  2428. // opportunity to verify that our implementations give the results that
  2429. // we expect, for example if we have to interoperate with a server.
  2430. //
  2431. // The digest* methods reset the state of the hasher, so it's
  2432. // necessary to call init() explicitly after them.
  2433. //
  2434. // Works only in Firefox 1.5+.
  2435. //
  2436. // IMPORTANT NOTE: Due to https://bugzilla.mozilla.org/show_bug.cgi?id=321024
  2437. // you cannot use the cryptohasher before app-startup. The symptom of doing
  2438. // so is a segfault in NSS.
  2439.  
  2440. /**
  2441.  * Instantiate a new hasher. You must explicitly call init() before use!
  2442.  */
  2443. function G_CryptoHasher() {
  2444.   this.debugZone = "cryptohasher";
  2445.   this.decoder_ = new G_Base64();
  2446.   this.hasher_ = Cc["@mozilla.org/security/hash;1"]
  2447.                  .createInstance(Ci.nsICryptoHash);
  2448.  
  2449.   this.initialized_ = false;
  2450. }
  2451.  
  2452. G_CryptoHasher.algorithms = {
  2453.   MD2: Ci.nsICryptoHash.MD2,
  2454.   MD5: Ci.nsICryptoHash.MD5,
  2455.   SHA1: Ci.nsICryptoHash.SHA1,
  2456.   SHA256: Ci.nsICryptoHash.SHA256,
  2457.   SHA384: Ci.nsICryptoHash.SHA384,
  2458.   SHA512: Ci.nsICryptoHash.SHA512,
  2459. };
  2460.  
  2461. /**
  2462.  * Initialize the hasher. This function must be called after every call
  2463.  * to one of the digest* methods.
  2464.  *
  2465.  * @param algorithm Constant from G_CryptoHasher.algorithms specifying the
  2466.  *                  algorithm this hasher will use
  2467.  */ 
  2468. G_CryptoHasher.prototype.init = function(algorithm) {
  2469.   var validAlgorithm = false;
  2470.   for (var alg in G_CryptoHasher.algorithms)
  2471.     if (algorithm == G_CryptoHasher.algorithms[alg])
  2472.       validAlgorithm = true;
  2473.  
  2474.   if (!validAlgorithm)
  2475.     throw new Error("Invalid algorithm: " + algorithm);
  2476.  
  2477.   this.initialized_ = true;
  2478.   this.hasher_.init(algorithm);
  2479. }
  2480.  
  2481. /**
  2482.  * Update the hash's internal state with input given in a string. Can be
  2483.  * called multiple times for incrementeal hash updates. Note that this function
  2484.  * is slllloooowww since it uses the a javascript implementation to convert the
  2485.  * string to an array. If you need something faster, use updateFromStream() with
  2486.  * an XPCOM stream.
  2487.  *
  2488.  * @param input String containing data to hash.
  2489.  */ 
  2490. G_CryptoHasher.prototype.updateFromString = function(input) {
  2491.   if (!this.initialized_)
  2492.     throw new Error("You must initialize the hasher first!");
  2493.  
  2494.   this.hasher_.update(this.decoder_.arrayifyString(input), input.length);
  2495. }
  2496.  
  2497. /**
  2498.  * Update the hash's internal state with input given in an array. Can be
  2499.  * called multiple times for incremental hash updates.
  2500.  *
  2501.  * @param input Array containing data to hash.
  2502.  */ 
  2503. G_CryptoHasher.prototype.updateFromArray = function(input) {
  2504.   if (!this.initialized_)
  2505.     throw new Error("You must initialize the hasher first!");
  2506.  
  2507.   this.hasher_.update(input, input.length);
  2508. }
  2509.  
  2510. /**
  2511.  * Update the hash's internal state with input given in a stream. Can be
  2512.  * called multiple times from incremental hash updates.
  2513.  */
  2514. G_CryptoHasher.prototype.updateFromStream = function(stream) {
  2515.   if (!this.initialized_)
  2516.     throw new Error("You must initialize the hasher first!");
  2517.  
  2518.   this.hasher_.updateFromStream(stream, stream.available());
  2519. }
  2520.  
  2521. /**
  2522.  * @returns The hash value as a string (sequence of 8-bit values)
  2523.  */ 
  2524. G_CryptoHasher.prototype.digestRaw = function() {
  2525.   return this.hasher_.finish(false /* not b64 encoded */);
  2526. }
  2527.  
  2528. /**
  2529.  * @returns The hash value as a base64-encoded string
  2530.  */ 
  2531. G_CryptoHasher.prototype.digestBase64 = function() {
  2532.   return this.hasher_.finish(true /* b64 encoded */);
  2533. }
  2534.  
  2535. /**
  2536.  * @returns The hash value as a hex-encoded string
  2537.  */ 
  2538. G_CryptoHasher.prototype.digestHex = function() {
  2539.   var raw = this.digestRaw();
  2540.   return this.toHex_(raw);
  2541. }
  2542.  
  2543. /**
  2544.  * Converts a sequence of values to a hex-encoded string. The input is a
  2545.  * a string, so you can stick 16-bit values in each character.
  2546.  *
  2547.  * @param str String to conver to hex. (Often this is just a sequence of
  2548.  *            16-bit values)
  2549.  *
  2550.  * @returns String containing the hex representation of the input
  2551.  */ 
  2552. G_CryptoHasher.prototype.toHex_ = function(str) {
  2553.   var hexchars = '0123456789ABCDEF';
  2554.   var hexrep = new Array(str.length * 2);
  2555.  
  2556.   for (var i = 0; i < str.length; ++i) {
  2557.     hexrep[i * 2] = hexchars.charAt((str.charCodeAt(i) >> 4) & 15);
  2558.     hexrep[i * 2 + 1] = hexchars.charAt(str.charCodeAt(i) & 15);
  2559.   }
  2560.   return hexrep.join('');
  2561. }
  2562.  
  2563. /* ***** BEGIN LICENSE BLOCK *****
  2564.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  2565.  *
  2566.  * The contents of this file are subject to the Mozilla Public License Version
  2567.  * 1.1 (the "License"); you may not use this file except in compliance with
  2568.  * the License. You may obtain a copy of the License at
  2569.  * http://www.mozilla.org/MPL/
  2570.  *
  2571.  * Software distributed under the License is distributed on an "AS IS" basis,
  2572.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  2573.  * for the specific language governing rights and limitations under the
  2574.  * License.
  2575.  *
  2576.  * The Original Code is Google Safe Browsing.
  2577.  *
  2578.  * The Initial Developer of the Original Code is Google Inc.
  2579.  * Portions created by the Initial Developer are Copyright (C) 2006
  2580.  * the Initial Developer. All Rights Reserved.
  2581.  *
  2582.  * Contributor(s):
  2583.  *   Aaron Boodman <aa@google.com> (original author)
  2584.  *
  2585.  * Alternatively, the contents of this file may be used under the terms of
  2586.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  2587.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  2588.  * in which case the provisions of the GPL or the LGPL are applicable instead
  2589.  * of those above. If you wish to allow use of your version of this file only
  2590.  * under the terms of either the GPL or the LGPL, and not to allow others to
  2591.  * use your version of this file under the terms of the MPL, indicate your
  2592.  * decision by deleting the provisions above and replace them with the notice
  2593.  * and other provisions required by the GPL or the LGPL. If you do not delete
  2594.  * the provisions above, a recipient may use your version of this file under
  2595.  * the terms of any one of the MPL, the GPL or the LGPL.
  2596.  *
  2597.  * ***** END LICENSE BLOCK ***** */
  2598.  
  2599.  
  2600. // Firefox-specific additions to lib/js/lang.js.
  2601.  
  2602.  
  2603. /**
  2604.  * The always-useful alert. 
  2605.  */
  2606. function alert(msg, opt_title) {
  2607.   opt_title |= "message";
  2608.  
  2609.   Cc["@mozilla.org/embedcomp/prompt-service;1"]
  2610.     .getService(Ci.nsIPromptService)
  2611.     .alert(null, opt_title, msg.toString());
  2612. }
  2613.  
  2614.  
  2615. /**
  2616.  * The instanceof operator cannot be used on a pure js object to determine if 
  2617.  * it implements a certain xpcom interface. The QueryInterface method can, but
  2618.  * it throws an error which makes things more complex.
  2619.  */
  2620. function jsInstanceOf(obj, iid) {
  2621.   try {
  2622.     obj.QueryInterface(iid);
  2623.     return true;
  2624.   } catch (e) {
  2625.     if (e == Components.results.NS_ERROR_NO_INTERFACE) {
  2626.       return false;
  2627.     } else {
  2628.       throw e;
  2629.     }
  2630.   }
  2631. }
  2632.  
  2633.  
  2634. /**
  2635.  * Unbelievably, Function inheritence is broken in chrome in Firefox
  2636.  * (still as of FFox 1.5b1). Hence if you're working in an extension
  2637.  * and not using the subscriptloader, you can't use the method
  2638.  * above. Instead, use this global function that does roughly the same
  2639.  * thing.
  2640.  *
  2641.  ***************************************************************************
  2642.  *   NOTE THE REVERSED ORDER OF FUNCTION AND OBJECT REFERENCES AS bind()   *
  2643.  ***************************************************************************
  2644.  * 
  2645.  * // Example to bind foo.bar():
  2646.  * var bound = BindToObject(bar, foo, "arg1", "arg2");
  2647.  * bound("arg3", "arg4");
  2648.  * 
  2649.  * @param func {string} Reference to the function to be bound
  2650.  *
  2651.  * @param obj {object} Specifies the object which |this| should point to
  2652.  * when the function is run. If the value is null or undefined, it will default
  2653.  * to the global object.
  2654.  *
  2655.  * @param opt_{...} Dummy optional arguments to make a jscompiler happy
  2656.  *
  2657.  * @returns {function} A partially-applied form of the speficied function.
  2658.  */
  2659. function BindToObject(func, obj, opt_A, opt_B, opt_C, opt_D, opt_E, opt_F) {
  2660.   // This is the sick product of Aaron's mind. Not for the feint of heart.
  2661.   var args = Array.prototype.splice.call(arguments, 1, arguments.length);
  2662.   return Function.prototype.bind.apply(func, args);
  2663. }
  2664. /* ***** BEGIN LICENSE BLOCK *****
  2665.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  2666.  *
  2667.  * The contents of this file are subject to the Mozilla Public License Version
  2668.  * 1.1 (the "License"); you may not use this file except in compliance with
  2669.  * the License. You may obtain a copy of the License at
  2670.  * http://www.mozilla.org/MPL/
  2671.  *
  2672.  * Software distributed under the License is distributed on an "AS IS" basis,
  2673.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  2674.  * for the specific language governing rights and limitations under the
  2675.  * License.
  2676.  *
  2677.  * The Original Code is Google Safe Browsing.
  2678.  *
  2679.  * The Initial Developer of the Original Code is Google Inc.
  2680.  * Portions created by the Initial Developer are Copyright (C) 2006
  2681.  * the Initial Developer. All Rights Reserved.
  2682.  *
  2683.  * Contributor(s):
  2684.  *   Fritz Schneider <fritz@google.com> (original author)
  2685.  *
  2686.  * Alternatively, the contents of this file may be used under the terms of
  2687.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  2688.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  2689.  * in which case the provisions of the GPL or the LGPL are applicable instead
  2690.  * of those above. If you wish to allow use of your version of this file only
  2691.  * under the terms of either the GPL or the LGPL, and not to allow others to
  2692.  * use your version of this file under the terms of the MPL, indicate your
  2693.  * decision by deleting the provisions above and replace them with the notice
  2694.  * and other provisions required by the GPL or the LGPL. If you do not delete
  2695.  * the provisions above, a recipient may use your version of this file under
  2696.  * the terms of any one of the MPL, the GPL or the LGPL.
  2697.  *
  2698.  * ***** END LICENSE BLOCK ***** */
  2699.  
  2700.  
  2701. // ObjectSafeMap is, shockingly, a Map with which it is safe to use
  2702. // objects as keys. It currently uses parallel arrays for storage,
  2703. // rendering it inefficient (linear) for large maps. We can always
  2704. // swap out the implementation if this becomes a problem. Note that
  2705. // this class uses strict equality to determine equivalent keys.
  2706. // 
  2707. // Interface:
  2708. //
  2709. //   insert(key, value)
  2710. //   erase(key)          // Returns true if key erased, false if not found
  2711. //   find(key)           // Returns undefined if key not found
  2712. //   replace(otherMap)   // Clones otherMap, replacing the current map
  2713. //   forEach(func)
  2714. //   size()              // Returns number of items in the map
  2715. //
  2716. // TODO: should probably make it iterable by implementing getList();
  2717.  
  2718.  
  2719. /**
  2720.  * Create a new ObjectSafeMap.
  2721.  *
  2722.  * @param opt_name A string used to name the map
  2723.  *
  2724.  * @constructor
  2725.  */
  2726. function G_ObjectSafeMap(opt_name) {
  2727.   this.debugZone = "objectsafemap";
  2728.   this.name_ = opt_name ? opt_name : "noname";
  2729.   this.keys_ = [];
  2730.   this.values_ = [];
  2731. }
  2732.  
  2733. /**
  2734.  * Helper function to return the index of a key. 
  2735.  *
  2736.  * @param key An key to find
  2737.  *
  2738.  * @returns Index in the keys array where the key is found, -1 if not
  2739.  */
  2740. G_ObjectSafeMap.prototype.indexOfKey_ = function(key) {
  2741.   for (var i = 0; i < this.keys_.length; i++)
  2742.     if (this.keys_[i] === key)
  2743.       return i;
  2744.   return -1;
  2745. }
  2746.  
  2747. /**
  2748.  * Add an item
  2749.  *
  2750.  * @param key An key to add (overwrites previous key)
  2751.  *
  2752.  * @param value The value to store at that key
  2753.  */
  2754. G_ObjectSafeMap.prototype.insert = function(key, value) {
  2755.   if (key === null)
  2756.     throw new Error("Can't use null as a key");
  2757.   if (value === undefined)
  2758.     throw new Error("Can't store undefined values in this map");
  2759.  
  2760.   var i = this.indexOfKey_(key);
  2761.   if (i == -1) {
  2762.     this.keys_.push(key);
  2763.     this.values_.push(value);
  2764.   } else {
  2765.     this.keys_[i] = key;
  2766.     this.values_[i] = value;
  2767.   }
  2768.  
  2769.   G_Assert(this, this.keys_.length == this.values_.length, 
  2770.            "Different number of keys than values!");
  2771. }
  2772.  
  2773. /**
  2774.  * Remove a key from the map
  2775.  *
  2776.  * @param key The key to remove
  2777.  *
  2778.  * @returns Boolean indicating if the key was removed
  2779.  */
  2780. G_ObjectSafeMap.prototype.erase = function(key) {
  2781.   var keyLocation = this.indexOfKey_(key);
  2782.   var keyFound = keyLocation != -1;
  2783.   if (keyFound) {
  2784.     this.keys_.splice(keyLocation, 1);
  2785.     this.values_.splice(keyLocation, 1);
  2786.   }
  2787.   G_Assert(this, this.keys_.length == this.values_.length, 
  2788.            "Different number of keys than values!");
  2789.  
  2790.   return keyFound;
  2791. }
  2792.  
  2793. /**
  2794.  * Look up a key in the map
  2795.  *
  2796.  * @param key The key to look up
  2797.  * 
  2798.  * @returns The value at that key or undefined if it doesn't exist
  2799.  */
  2800. G_ObjectSafeMap.prototype.find = function(key) {
  2801.   var keyLocation = this.indexOfKey_(key);
  2802.   return keyLocation == -1 ? undefined : this.values_[keyLocation];
  2803. }
  2804.  
  2805. /**
  2806.  * Replace one map with the content of another
  2807.  *
  2808.  * @param map input map that needs to be merged into our map
  2809.  */
  2810. G_ObjectSafeMap.prototype.replace = function(other) {
  2811.   this.keys_ = [];
  2812.   this.values_ = [];
  2813.   for (var i = 0; i < other.keys_.length; i++) {
  2814.     this.keys_.push(other.keys_[i]);
  2815.     this.values_.push(other.values_[i]);
  2816.   }
  2817.  
  2818.   G_Assert(this, this.keys_.length == this.values_.length, 
  2819.            "Different number of keys than values!");
  2820. }
  2821.  
  2822. /**
  2823.  * Apply a function to each of the key value pairs.
  2824.  *
  2825.  * @param func Function to apply to the map's key value pairs
  2826.  */
  2827. G_ObjectSafeMap.prototype.forEach = function(func) {
  2828.   if (typeof func != "function")
  2829.     throw new Error("argument to forEach is not a function, it's a(n) " + 
  2830.                     typeof func);
  2831.  
  2832.   for (var i = 0; i < this.keys_.length; i++)
  2833.     func(this.keys_[i], this.values_[i]);
  2834. }
  2835.  
  2836. /**
  2837.  * @returns The number of keys in the map
  2838.  */
  2839. G_ObjectSafeMap.prototype.size = function() {
  2840.   return this.keys_.length;
  2841. }
  2842.  
  2843. /* ***** BEGIN LICENSE BLOCK *****
  2844.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  2845.  *
  2846.  * The contents of this file are subject to the Mozilla Public License Version
  2847.  * 1.1 (the "License"); you may not use this file except in compliance with
  2848.  * the License. You may obtain a copy of the License at
  2849.  * http://www.mozilla.org/MPL/
  2850.  *
  2851.  * Software distributed under the License is distributed on an "AS IS" basis,
  2852.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  2853.  * for the specific language governing rights and limitations under the
  2854.  * License.
  2855.  *
  2856.  * The Original Code is Google Safe Browsing.
  2857.  *
  2858.  * The Initial Developer of the Original Code is Google Inc.
  2859.  * Portions created by the Initial Developer are Copyright (C) 2006
  2860.  * the Initial Developer. All Rights Reserved.
  2861.  *
  2862.  * Contributor(s):
  2863.  *   Fritz Schneider <fritz@google.com> (original author)
  2864.  *
  2865.  * Alternatively, the contents of this file may be used under the terms of
  2866.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  2867.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  2868.  * in which case the provisions of the GPL or the LGPL are applicable instead
  2869.  * of those above. If you wish to allow use of your version of this file only
  2870.  * under the terms of either the GPL or the LGPL, and not to allow others to
  2871.  * use your version of this file under the terms of the MPL, indicate your
  2872.  * decision by deleting the provisions above and replace them with the notice
  2873.  * and other provisions required by the GPL or the LGPL. If you do not delete
  2874.  * the provisions above, a recipient may use your version of this file under
  2875.  * the terms of any one of the MPL, the GPL or the LGPL.
  2876.  *
  2877.  * ***** END LICENSE BLOCK ***** */
  2878.  
  2879.  
  2880. // A couple of classes to simplify creating observers. 
  2881. //
  2882. // // Example1:
  2883. //
  2884. // function doSomething() { ... }
  2885. // var observer = new G_ObserverWrapper(topic, doSomething);
  2886. // someObj.addObserver(topic, observer);
  2887. //
  2888. // // Example2: 
  2889. //
  2890. // function doSomething() { ... }
  2891. // new G_ObserverServiceObserver("profile-after-change", 
  2892. //                               doSomething,
  2893. //                               true /* run only once */);
  2894.  
  2895.  
  2896. /**
  2897.  * This class abstracts the admittedly simple boilerplate required of
  2898.  * an nsIObserver. It saves you the trouble of implementing the
  2899.  * indirection of your own observe() function.
  2900.  *
  2901.  * @param topic String containing the topic the observer will filter for
  2902.  *
  2903.  * @param observeFunction Reference to the function to call when the 
  2904.  *                        observer fires
  2905.  *
  2906.  * @constructor
  2907.  */
  2908. function G_ObserverWrapper(topic, observeFunction) {
  2909.   this.debugZone = "observer";
  2910.   this.topic_ = topic;
  2911.   this.observeFunction_ = observeFunction;
  2912. }
  2913.  
  2914. /**
  2915.  * XPCOM
  2916.  */
  2917. G_ObserverWrapper.prototype.QueryInterface = function(iid) {
  2918.   if (iid.equals(Ci.nsISupports) || iid.equals(Ci.nsIObserver))
  2919.     return this;
  2920.   throw Components.results.NS_ERROR_NO_INTERFACE;
  2921. }
  2922.  
  2923. /**
  2924.  * Invoked by the thingy being observed
  2925.  */
  2926. G_ObserverWrapper.prototype.observe = function(subject, topic, data) {
  2927.   if (topic == this.topic_)
  2928.     this.observeFunction_(subject, topic, data);
  2929. }
  2930.  
  2931.  
  2932. /**
  2933.  * This class abstracts the admittedly simple boilerplate required of
  2934.  * observing an observerservice topic. It implements the indirection
  2935.  * required, and automatically registers to hear the topic.
  2936.  *
  2937.  * @param topic String containing the topic the observer will filter for
  2938.  *
  2939.  * @param observeFunction Reference to the function to call when the 
  2940.  *                        observer fires
  2941.  *
  2942.  * @param opt_onlyOnce Boolean indicating if the observer should unregister
  2943.  *                     after it has fired
  2944.  *
  2945.  * @constructor
  2946.  */
  2947. function G_ObserverServiceObserver(topic, observeFunction, opt_onlyOnce) {
  2948.   this.debugZone = "observerserviceobserver";
  2949.   this.topic_ = topic;
  2950.   this.observeFunction_ = observeFunction;
  2951.   this.onlyOnce_ = !!opt_onlyOnce;
  2952.   
  2953.   this.observer_ = new G_ObserverWrapper(this.topic_, 
  2954.                                          BindToObject(this.observe_, this));
  2955.   this.observerService_ = Cc["@mozilla.org/observer-service;1"]
  2956.                           .getService(Ci.nsIObserverService);
  2957.   this.observerService_.addObserver(this.observer_, this.topic_, false);
  2958. }
  2959.  
  2960. /**
  2961.  * Unregister the observer from the observerservice
  2962.  */
  2963. G_ObserverServiceObserver.prototype.unregister = function() {
  2964.   this.observerService_.removeObserver(this.observer_, this.topic_);
  2965.   this.observerService_ = null;
  2966. }
  2967.  
  2968. /**
  2969.  * Invoked by the observerservice
  2970.  */
  2971. G_ObserverServiceObserver.prototype.observe_ = function(subject, topic, data) {
  2972.   this.observeFunction_(subject, topic, data);
  2973.   if (this.onlyOnce_)
  2974.     this.unregister();
  2975. }
  2976.  
  2977. /* ***** BEGIN LICENSE BLOCK *****
  2978.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  2979.  *
  2980.  * The contents of this file are subject to the Mozilla Public License Version
  2981.  * 1.1 (the "License"); you may not use this file except in compliance with
  2982.  * the License. You may obtain a copy of the License at
  2983.  * http://www.mozilla.org/MPL/
  2984.  *
  2985.  * Software distributed under the License is distributed on an "AS IS" basis,
  2986.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  2987.  * for the specific language governing rights and limitations under the
  2988.  * License.
  2989.  *
  2990.  * The Original Code is Google Safe Browsing.
  2991.  *
  2992.  * The Initial Developer of the Original Code is Google Inc.
  2993.  * Portions created by the Initial Developer are Copyright (C) 2006
  2994.  * the Initial Developer. All Rights Reserved.
  2995.  *
  2996.  * Contributor(s):
  2997.  *   Fritz Schneider <fritz@google.com> (original author)
  2998.  *
  2999.  * Alternatively, the contents of this file may be used under the terms of
  3000.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  3001.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  3002.  * in which case the provisions of the GPL or the LGPL are applicable instead
  3003.  * of those above. If you wish to allow use of your version of this file only
  3004.  * under the terms of either the GPL or the LGPL, and not to allow others to
  3005.  * use your version of this file under the terms of the MPL, indicate your
  3006.  * decision by deleting the provisions above and replace them with the notice
  3007.  * and other provisions required by the GPL or the LGPL. If you do not delete
  3008.  * the provisions above, a recipient may use your version of this file under
  3009.  * the terms of any one of the MPL, the GPL or the LGPL.
  3010.  *
  3011.  * ***** END LICENSE BLOCK ***** */
  3012.  
  3013.  
  3014. // A helper class that knows how to parse from and serialize to
  3015. // protocol4. This is a simple, historical format used by some Google
  3016. // interfaces, for example the Toolbar (i.e., ancient services).
  3017. //
  3018. // Protocol4 consists of a newline-separated sequence of name/value
  3019. // pairs (strings). Each line consists of the name, the value length,
  3020. // and the value itself, all separated by colons. Example:
  3021. //
  3022. // foo:6:barbaz\n
  3023. // fritz:33:issickofdynamicallytypedlanguages\n
  3024.  
  3025.  
  3026. /**
  3027.  * This class knows how to serialize/deserialize maps to/from their
  3028.  * protocol4 representation.
  3029.  *
  3030.  * @constructor
  3031.  */
  3032. function G_Protocol4Parser() {
  3033.   this.debugZone = "protocol4";
  3034.  
  3035.   this.protocol4RegExp_ = new RegExp("([^:]+):\\d+:(.*)$");
  3036.   this.newlineRegExp_ = new RegExp("(\\r)?\\n");
  3037. }
  3038.  
  3039. /**
  3040.  * Create a map from a protocol4 string. Silently skips invalid lines.
  3041.  *
  3042.  * @param text String holding the protocol4 representation
  3043.  * 
  3044.  * @returns Object as an associative array with keys and values 
  3045.  *          given in text. The empty object is returned if none
  3046.  *          are parsed.
  3047.  */
  3048. G_Protocol4Parser.prototype.parse = function(text) {
  3049.  
  3050.   var response = {};
  3051.   if (!text)
  3052.     return response;
  3053.  
  3054.   // Responses are protocol4: (repeated) name:numcontentbytes:content\n
  3055.   var lines = text.split(this.newlineRegExp_);
  3056.   for (var i = 0; i < lines.length; i++)
  3057.     if (this.protocol4RegExp_.exec(lines[i]))
  3058.       response[RegExp.$1] = RegExp.$2;
  3059.  
  3060.   return response;
  3061. }
  3062.  
  3063. /**
  3064.  * Create a protocol4 string from a map (object). Throws an error on 
  3065.  * an invalid input.
  3066.  *
  3067.  * @param map Object as an associative array with keys and values 
  3068.  *            given as strings.
  3069.  *
  3070.  * @returns text String holding the protocol4 representation
  3071.  */
  3072. G_Protocol4Parser.prototype.serialize = function(map) {
  3073.   if (typeof map != "object")
  3074.     throw new Error("map must be an object");
  3075.  
  3076.   var text = "";
  3077.   for (var key in map) {
  3078.     if (typeof map[key] != "string")
  3079.       throw new Error("Keys and values must be strings");
  3080.     
  3081.     text += key + ":" + map[key].length + ":" + map[key] + "\n";
  3082.   }
  3083.   
  3084.   return text;
  3085. }
  3086.  
  3087. //@line 60 "/build/buildd/firefox-1.99+2.0b1+dfsg/toolkit/components/url-classifier/src/nsUrlClassifierLib.js"
  3088.  
  3089. /* ***** BEGIN LICENSE BLOCK *****
  3090.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3091.  *
  3092.  * The contents of this file are subject to the Mozilla Public License Version
  3093.  * 1.1 (the "License"); you may not use this file except in compliance with
  3094.  * the License. You may obtain a copy of the License at
  3095.  * http://www.mozilla.org/MPL/
  3096.  *
  3097.  * Software distributed under the License is distributed on an "AS IS" basis,
  3098.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  3099.  * for the specific language governing rights and limitations under the
  3100.  * License.
  3101.  *
  3102.  * The Original Code is Google Safe Browsing.
  3103.  *
  3104.  * The Initial Developer of the Original Code is Google Inc.
  3105.  * Portions created by the Initial Developer are Copyright (C) 2006
  3106.  * the Initial Developer. All Rights Reserved.
  3107.  *
  3108.  * Contributor(s):
  3109.  *   Fritz Schneider <fritz@google.com> (original author)
  3110.  *
  3111.  * Alternatively, the contents of this file may be used under the terms of
  3112.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  3113.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  3114.  * in which case the provisions of the GPL or the LGPL are applicable instead
  3115.  * of those above. If you wish to allow use of your version of this file only
  3116.  * under the terms of either the GPL or the LGPL, and not to allow others to
  3117.  * use your version of this file under the terms of the MPL, indicate your
  3118.  * decision by deleting the provisions above and replace them with the notice
  3119.  * and other provisions required by the GPL or the LGPL. If you do not delete
  3120.  * the provisions above, a recipient may use your version of this file under
  3121.  * the terms of any one of the MPL, the GPL or the LGPL.
  3122.  *
  3123.  * ***** END LICENSE BLOCK ***** */
  3124.  
  3125. // TODO: We don't use this class very much.  Try to use native nsIFile instead
  3126. //       and remove this file.
  3127.  
  3128. /**
  3129.  * A simple helper class that enables us to get or create the
  3130.  * directory in which our app will store stuff.
  3131.  */
  3132. function PROT_ApplicationDirectory() {
  3133.   this.debugZone = "appdir";
  3134.   this.appDir_ = G_File.getProfileFile();
  3135.   G_Debug(this, "Application directory is " + this.appDir_.path);
  3136. }
  3137.  
  3138. /**
  3139.  * @returns Boolean indicating if the directory exists
  3140.  */
  3141. PROT_ApplicationDirectory.prototype.exists = function() {
  3142.   return this.appDir_.exists() && this.appDir_.isDirectory();
  3143. }
  3144.  
  3145. /**
  3146.  * Creates the directory
  3147.  */
  3148. PROT_ApplicationDirectory.prototype.create = function() {
  3149.   G_Debug(this, "Creating app directory: " + this.appDir_.path);
  3150.   try {
  3151.     this.appDir_.create(Ci.nsIFile.DIRECTORY_TYPE, 0700);
  3152.   } catch(e) {
  3153.     G_Error(this, this.appDir_.path + " couldn't be created.");
  3154.   }
  3155. }
  3156.  
  3157. /**
  3158.  * @returns The nsIFile interface of the directory
  3159.  */
  3160. PROT_ApplicationDirectory.prototype.getAppDirFileInterface = function() {
  3161.   return this.appDir_;
  3162. }
  3163. /* ***** BEGIN LICENSE BLOCK *****
  3164.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3165.  *
  3166.  * The contents of this file are subject to the Mozilla Public License Version
  3167.  * 1.1 (the "License"); you may not use this file except in compliance with
  3168.  * the License. You may obtain a copy of the License at
  3169.  * http://www.mozilla.org/MPL/
  3170.  *
  3171.  * Software distributed under the License is distributed on an "AS IS" basis,
  3172.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  3173.  * for the specific language governing rights and limitations under the
  3174.  * License.
  3175.  *
  3176.  * The Original Code is Google Safe Browsing.
  3177.  *
  3178.  * The Initial Developer of the Original Code is Google Inc.
  3179.  * Portions created by the Initial Developer are Copyright (C) 2006
  3180.  * the Initial Developer. All Rights Reserved.
  3181.  *
  3182.  * Contributor(s):
  3183.  *   Fritz Schneider <fritz@google.com> (original author)
  3184.  *   Monica Chew <mmc@google.com>
  3185.  *
  3186.  * Alternatively, the contents of this file may be used under the terms of
  3187.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  3188.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  3189.  * in which case the provisions of the GPL or the LGPL are applicable instead
  3190.  * of those above. If you wish to allow use of your version of this file only
  3191.  * under the terms of either the GPL or the LGPL, and not to allow others to
  3192.  * use your version of this file under the terms of the MPL, indicate your
  3193.  * decision by deleting the provisions above and replace them with the notice
  3194.  * and other provisions required by the GPL or the LGPL. If you do not delete
  3195.  * the provisions above, a recipient may use your version of this file under
  3196.  * the terms of any one of the MPL, the GPL or the LGPL.
  3197.  *
  3198.  * ***** END LICENSE BLOCK ***** */
  3199.  
  3200.  
  3201. // This file implements our query param encryption. You hand it a set
  3202. // of query params, and it will hand you a set of (maybe) encrypted
  3203. // query params back. It takes the query params you give it, 
  3204. // encodes and encrypts them into a encrypted query param, and adds
  3205. // the extra query params the server will need to decrypt them
  3206. // (e.g., the version of encryption and the decryption key).
  3207. // 
  3208. // The key manager provides the keys we need; this class just focuses
  3209. // on encrypting query params. See the url crypto key manager for
  3210. // details of our protocol, but essentially encryption is
  3211. // RC4_key(input) with key == MD5(K_C || nonce) where nonce is a
  3212. // 32-bit integer appended big-endian and K_C is the client's key.
  3213. //
  3214. // If for some reason we don't have an encryption key, encrypting is the 
  3215. // identity function.
  3216.  
  3217. /**
  3218.  * This class knows how to encrypt query parameters that will be
  3219.  * understood by the lookupserver.
  3220.  * 
  3221.  * @constructor
  3222.  */
  3223. function PROT_UrlCrypto() {
  3224.   this.debugZone = "urlcrypto";
  3225.   this.hasher_ = new G_CryptoHasher();
  3226.   this.base64_ = new G_Base64();
  3227.   this.rc4_ = new ARC4();
  3228.  
  3229.   if (!this.manager_) {
  3230.     // Create a UrlCryptoKeyManager to reads keys from profile directory if
  3231.     // one doesn't already exist.  UrlCryptoKeyManager puts a reference to
  3232.     // itself on PROT_UrlCrypto.prototype (this also prevents garbage
  3233.     // collection).
  3234.     new PROT_UrlCryptoKeyManager();
  3235.   }
  3236.  
  3237.   // Convenience properties
  3238.   this.VERSION = PROT_UrlCrypto.VERSION;
  3239.   this.RC4_DISCARD_BYTES = PROT_UrlCrypto.RC4_DISCARD_BYTES;
  3240.   this.VERSION_QUERY_PARAM_NAME = PROT_UrlCrypto.QPS.VERSION_QUERY_PARAM_NAME;
  3241.   this.ENCRYPTED_PARAMS_PARAM_NAME = 
  3242.     PROT_UrlCrypto.QPS.ENCRYPTED_PARAMS_PARAM_NAME;
  3243.   this.COUNT_QUERY_PARAM_NAME = PROT_UrlCrypto.QPS.COUNT_QUERY_PARAM_NAME;
  3244.   this.WRAPPEDKEY_QUERY_PARAM_NAME = 
  3245.     PROT_UrlCrypto.QPS.WRAPPEDKEY_QUERY_PARAM_NAME;
  3246.  
  3247.   // Properties for computing macs
  3248.   this.macer_ = new G_CryptoHasher(); // don't use hasher_
  3249.   this.macInitialized_ = false;
  3250.   // Separator to prevent leakage between key and data when computing mac
  3251.   this.separator_ = ":coolgoog:";
  3252.   this.separatorArray_ = this.base64_.arrayifyString(this.separator_);
  3253. }
  3254.  
  3255. // The version of encryption we implement
  3256. PROT_UrlCrypto.VERSION = "1";
  3257.  
  3258. PROT_UrlCrypto.RC4_DISCARD_BYTES = 1600;
  3259.  
  3260. // The query params are we going to send to let the server know what is
  3261. // encrypted, and how
  3262. PROT_UrlCrypto.QPS = {};
  3263. PROT_UrlCrypto.QPS.VERSION_QUERY_PARAM_NAME = "encver";
  3264. PROT_UrlCrypto.QPS.ENCRYPTED_PARAMS_PARAM_NAME = "encparams";
  3265. PROT_UrlCrypto.QPS.COUNT_QUERY_PARAM_NAME = "nonce";
  3266. PROT_UrlCrypto.QPS.WRAPPEDKEY_QUERY_PARAM_NAME = "wrkey";
  3267.  
  3268. /**
  3269.  * @returns Reference to the keymanager (if one exists), else undefined
  3270.  */
  3271. PROT_UrlCrypto.prototype.getManager = function() {
  3272.   return this.manager_;
  3273. }
  3274.  
  3275. /**
  3276.  * Helper method that takes a map of query params (param name ->
  3277.  * value) and turns them into a query string. Note that it encodes
  3278.  * the values as it writes the string.
  3279.  *
  3280.  * @param params Object (map) of query names to values. Values should
  3281.  *               not be uriencoded.
  3282.  *
  3283.  * @returns String of query params from the map. Values will be uri
  3284.  *          encoded
  3285.  */
  3286. PROT_UrlCrypto.prototype.appendParams_ = function(params) {
  3287.   var queryString = "";
  3288.   for (var param in params)
  3289.     queryString += "&" + param + "=" + encodeURIComponent(params[param]);
  3290.                    
  3291.   return queryString;
  3292. }
  3293.  
  3294. /**
  3295.  * Encrypt a set of query params if we can. If we can, we return a new
  3296.  * set of query params that should be added to a query string. The set
  3297.  * of query params WILL BE different than the input query params if we
  3298.  * can encrypt (e.g., there will be extra query params with meta-
  3299.  * information such as the version of encryption we're using). If we
  3300.  * can't encrypt, we just return the query params we're passed.
  3301.  *
  3302.  * @param params Object (map) of query param names to values. Values should
  3303.  *               not be uriencoded.
  3304.  *
  3305.  * @returns Object (map) of query param names to values. Values are NOT
  3306.  *          uriencoded; the caller should encode them as it writes them
  3307.  *          to a proper query string.
  3308.  */
  3309. PROT_UrlCrypto.prototype.maybeCryptParams = function(params) {
  3310.   if (!this.manager_)
  3311.     throw new Error("Need a key manager for UrlCrypto");
  3312.   if (typeof params != "object")
  3313.     throw new Error("params is an associative array of name/value params");
  3314.  
  3315.   var clientKeyArray = this.manager_.getClientKeyArray();
  3316.   var wrappedKey = this.manager_.getWrappedKey();
  3317.  
  3318.   // No keys? Can't encrypt. Damn.
  3319.   if (!clientKeyArray || !wrappedKey) {
  3320.     G_Debug(this, "No key; can't encrypt query params");
  3321.     return params;
  3322.   }
  3323.  
  3324.   // Serialize query params to a query string that we will then
  3325.   // encrypt and place in a special query param the front-end knows is
  3326.   // encrypted.
  3327.   var queryString = this.appendParams_(params);
  3328.   
  3329.   // Nonce, really. We want 32 bits; make it so.
  3330.   var counter = this.getCount_();
  3331.   counter = counter & 0xFFFFFFFF;
  3332.   
  3333.   var encrypted = this.encryptV1(clientKeyArray, 
  3334.                                  this.VERSION,
  3335.                                  counter,
  3336.                                  this.base64_.arrayifyString(queryString));
  3337.  
  3338.   params = {};
  3339.   params[this.VERSION_QUERY_PARAM_NAME] = this.VERSION;
  3340.   params[this.COUNT_QUERY_PARAM_NAME] = counter;
  3341.   params[this.WRAPPEDKEY_QUERY_PARAM_NAME] = wrappedKey;
  3342.   params[this.ENCRYPTED_PARAMS_PARAM_NAME] = encrypted;
  3343.  
  3344.   return params;
  3345. }
  3346.  
  3347. /**
  3348.  * Encrypt something IN PLACE. Did you hear that? It works IN PLACE.
  3349.  * That is, it replaces the plaintext with ciphertext. It also returns
  3350.  * the websafe base64-encoded ciphertext. The client key is untouched.
  3351.  *
  3352.  * This method runs in about ~5ms on a 2Ghz P4. (Turn debugging off if
  3353.  * you see it much slower).
  3354.  *
  3355.  * @param clientKeyArray Array of bytes (numbers in [0,255]) composing K_C
  3356.  *
  3357.  * @param version String indicating the version of encryption we should use.
  3358.  *
  3359.  * @param counter Number that acts as a nonce for this encryption
  3360.  *
  3361.  * @param inOutArray Array of plaintext bytes that will be replaced
  3362.  *                   with the array of ciphertext bytes
  3363.  *
  3364.  * @returns String containing the websafe base64-encoded ciphertext
  3365.  */
  3366. PROT_UrlCrypto.prototype.encryptV1 = function(clientKeyArray,
  3367.                                               version, 
  3368.                                               counter,
  3369.                                               inOutArray) {
  3370.  
  3371.   // We're a version1 encrypter, after all
  3372.   if (version != "1") 
  3373.     throw new Error("Unknown encryption version");
  3374.  
  3375.   var key = this.deriveEncryptionKey(clientKeyArray, counter);
  3376.  
  3377.   this.rc4_.setKey(key, key.length);
  3378.  
  3379.   if (this.RC4_DISCARD_BYTES > 0)
  3380.     this.rc4_.discard(this.RC4_DISCARD_BYTES);
  3381.   
  3382.   // The crypt() method works in-place
  3383.   this.rc4_.crypt(inOutArray, inOutArray.length);
  3384.   
  3385.   return this.base64_.encodeByteArray(inOutArray, true /* websafe */);
  3386. }
  3387.   
  3388. /**
  3389.  * Create an encryption key from K_C and a nonce
  3390.  *
  3391.  * @param clientKeyArray Array of bytes comprising K_C
  3392.  *
  3393.  * @param count Number that acts as a nonce for this key
  3394.  *
  3395.  * @returns Array of bytes containing the encryption key
  3396.  */
  3397. PROT_UrlCrypto.prototype.deriveEncryptionKey = function(clientKeyArray, 
  3398.                                                         count) {
  3399.   G_Assert(this, clientKeyArray instanceof Array,
  3400.            "Client key should be an array of bytes");
  3401.   G_Assert(this, typeof count == "number", "Count should be a number");
  3402.   
  3403.   // Don't clobber the client key by appending the nonce; use another array
  3404.   var paddingArray = [];
  3405.   paddingArray.push(count >> 24);
  3406.   paddingArray.push((count >> 16) & 0xFF);
  3407.   paddingArray.push((count >> 8) & 0xFF);
  3408.   paddingArray.push(count & 0xFF);
  3409.  
  3410.   this.hasher_.init(G_CryptoHasher.algorithms.MD5);
  3411.   this.hasher_.updateFromArray(clientKeyArray);
  3412.   this.hasher_.updateFromArray(paddingArray);
  3413.  
  3414.   return this.base64_.arrayifyString(this.hasher_.digestRaw());
  3415. }
  3416.  
  3417. /**
  3418.  * Return a new nonce for us to use. Rather than keeping a counter and
  3419.  * the headaches that entails, just use the low ms since the epoch.
  3420.  *
  3421.  * @returns 32-bit number that is the nonce to use for this encryption
  3422.  */
  3423. PROT_UrlCrypto.prototype.getCount_ = function() {
  3424.   return ((new Date).getTime() & 0xFFFFFFFF);
  3425. }
  3426.  
  3427. /**
  3428.  * Init the mac.  This function is called by WireFormatReader if the update
  3429.  * server has sent along a mac param.  The caller must not call initMac again
  3430.  * before calling finishMac; instead, the caller should just use another
  3431.  * UrlCrypto object.
  3432.  *
  3433.  * @param opt_clientKeyArray Optional clientKeyArray, for testing
  3434.  */
  3435. PROT_UrlCrypto.prototype.initMac = function(opt_clientKeyArray) {
  3436.   if (this.macInitialized_) {
  3437.     throw new Error("Can't interleave calls to initMac.  Please use another " +
  3438.                     "UrlCrypto object.");
  3439.   }
  3440.  
  3441.   this.macInitialized_ = true;
  3442.  
  3443.   var clientKeyArray = null;
  3444.  
  3445.   if (!!opt_clientKeyArray) {
  3446.     clientKeyArray = opt_clientKeyArray;
  3447.   } else {
  3448.     clientKeyArray = this.manager_.getClientKeyArray();
  3449.   }
  3450.  
  3451.   // Don't re-use this.hasher_, in case someone calls deriveEncryptionKey
  3452.   // between initMac and finishMac
  3453.   this.macer_.init(G_CryptoHasher.algorithms.MD5);
  3454.  
  3455.   this.macer_.updateFromArray(clientKeyArray);
  3456.   this.macer_.updateFromArray(this.separatorArray_);
  3457. }
  3458.  
  3459. /**
  3460.  * Add a line to the mac.  Called by WireFormatReader.processLine.  Not thread
  3461.  * safe.
  3462.  *
  3463.  * @param s The string to add
  3464.  */
  3465. PROT_UrlCrypto.prototype.updateMacFromString = function(s) {
  3466.   if (!this.macInitialized_) {
  3467.     throw new Error ("Initialize mac first");
  3468.   }
  3469.  
  3470.   var arr = this.base64_.arrayifyString(s);
  3471.   this.macer_.updateFromArray(arr);
  3472. }
  3473.  
  3474. /**
  3475.  * Finish up computing the mac.  Not thread safe.
  3476.  *
  3477.  * @param opt_clientKeyArray Optional clientKeyArray, for testing
  3478.  */
  3479. PROT_UrlCrypto.prototype.finishMac = function(opt_clientKeyArray) {
  3480.   var clientKeyArray = null;
  3481.   if (!!opt_clientKeyArray) {
  3482.     clientKeyArray = opt_clientKeyArray;
  3483.   } else {
  3484.     clientKeyArray = this.manager_.getClientKeyArray();
  3485.   }
  3486.  
  3487.   if (!this.macInitialized_) {
  3488.     throw new Error ("Initialize mac first");
  3489.   }
  3490.   this.macer_.updateFromArray(this.separatorArray_);
  3491.   this.macer_.updateFromArray(clientKeyArray);
  3492.  
  3493.   this.macInitialized_ = false;
  3494.  
  3495.   return this.macer_.digestBase64();
  3496. }
  3497.  
  3498. /**
  3499.  * Compute a mac over the whole data string, and return the base64-encoded
  3500.  * string
  3501.  *
  3502.  * @param data A string
  3503.  * @param opt_outputRaw True for raw output, false for base64
  3504.  * @param opt_clientKeyArray An optional key to pass in for testing
  3505.  * @param opt_separatorArray An optional separator array to pass in for testing
  3506.  * @returns MD5(key+separator+data+separator+key)
  3507.  */
  3508. PROT_UrlCrypto.prototype.computeMac = function(data, 
  3509.                                                opt_outputRaw,
  3510.                                                opt_clientKeyArray,
  3511.                                                opt_separatorArray) {
  3512.   var clientKeyArray = null;
  3513.   var separatorArray = null;
  3514.  
  3515.   // Get keys and such for testing
  3516.   if (!!opt_clientKeyArray) {
  3517.     clientKeyArray = opt_clientKeyArray;
  3518.   } else {
  3519.     clientKeyArray = this.manager_.getClientKeyArray();
  3520.   }
  3521.  
  3522.   if (!!opt_separatorArray) {
  3523.     separatorArray = opt_separatorArray;
  3524.   } else {
  3525.     separatorArray = this.separatorArray_;
  3526.   }
  3527.  
  3528.   this.macer_.init(G_CryptoHasher.algorithms.MD5);
  3529.  
  3530.   this.macer_.updateFromArray(clientKeyArray);
  3531.   this.macer_.updateFromArray(separatorArray);
  3532.  
  3533.   // Note to self: calling G_CryptoHasher.updateFromString ain't the same as
  3534.   // arrayifying the string and then calling updateFromArray.  Not sure if
  3535.   // that's a bug in G_CryptoHasher or not.  Niels, what do you think?
  3536.   var arr = this.base64_.arrayifyString(data);
  3537.   this.macer_.updateFromArray(arr);
  3538.  
  3539.   this.macer_.updateFromArray(separatorArray);
  3540.   this.macer_.updateFromArray(clientKeyArray);
  3541.  
  3542.   if (!!opt_outputRaw) {
  3543.     return this.macer_.digestRaw();
  3544.   }
  3545.   return this.macer_.digestBase64();
  3546. }
  3547.  
  3548. /* ***** BEGIN LICENSE BLOCK *****
  3549.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3550.  *
  3551.  * The contents of this file are subject to the Mozilla Public License Version
  3552.  * 1.1 (the "License"); you may not use this file except in compliance with
  3553.  * the License. You may obtain a copy of the License at
  3554.  * http://www.mozilla.org/MPL/
  3555.  *
  3556.  * Software distributed under the License is distributed on an "AS IS" basis,
  3557.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  3558.  * for the specific language governing rights and limitations under the
  3559.  * License.
  3560.  *
  3561.  * The Original Code is Google Safe Browsing.
  3562.  *
  3563.  * The Initial Developer of the Original Code is Google Inc.
  3564.  * Portions created by the Initial Developer are Copyright (C) 2006
  3565.  * the Initial Developer. All Rights Reserved.
  3566.  *
  3567.  * Contributor(s):
  3568.  *   Fritz Schneider <fritz@google.com> (original author)
  3569.  *
  3570.  * Alternatively, the contents of this file may be used under the terms of
  3571.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  3572.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  3573.  * in which case the provisions of the GPL or the LGPL are applicable instead
  3574.  * of those above. If you wish to allow use of your version of this file only
  3575.  * under the terms of either the GPL or the LGPL, and not to allow others to
  3576.  * use your version of this file under the terms of the MPL, indicate your
  3577.  * decision by deleting the provisions above and replace them with the notice
  3578.  * and other provisions required by the GPL or the LGPL. If you do not delete
  3579.  * the provisions above, a recipient may use your version of this file under
  3580.  * the terms of any one of the MPL, the GPL or the LGPL.
  3581.  *
  3582.  * ***** END LICENSE BLOCK ***** */
  3583.  
  3584.  
  3585. // This file implements the tricky business of managing the keys for our 
  3586. // URL encryption. The protocol is:
  3587. //
  3588. // - Server generates secret key K_S
  3589. // - Client starts up and requests a new key K_C from the server via HTTPS
  3590. // - Server generates K_C and WrappedKey, which is K_C encrypted with K_S
  3591. // - Server resonse with K_C and WrappedKey
  3592. // - When client wants to encrypt a URL, it encrypts it with K_C and sends
  3593. //   the encrypted URL along with WrappedKey
  3594. // - Server decrypts WrappedKey with K_S to get K_C, and the URL with K_C
  3595. //
  3596. // This is, however, trickier than it sounds for two reasons. First,
  3597. // we want to keep the number of HTTPS requests to an aboslute minimum
  3598. // (like 1 or 2 per browser session). Second, the HTTPS request at
  3599. // startup might fail, for example the user might be offline or a URL
  3600. // fetch might need to be issued before the HTTPS request has
  3601. // completed.
  3602. //
  3603. // We implement the following policy:
  3604. // 
  3605. // - The extension will issue at most two HTTPS getkey requests per session
  3606. // - The extension will issue one HTTPS getkey request at startup
  3607. // - The extension will serialize to disk any key it gets
  3608. // - The extension will fall back on this serialized key until it has a
  3609. //   fresh key
  3610. // - The front-end can respond with a flag in a lookup request that tells
  3611. //   the client to re-key. The client will issue a new HTTPS getkey request
  3612. //   at this time if it has only issued one before
  3613.  
  3614. // We store the user key in this file.  The key can be used to verify signed
  3615. // server updates.
  3616. const kKeyFilename = "kf.txt";
  3617.  
  3618. // If we don't have a key, we can get one at this url.
  3619. // XXX We shouldn't be referencing browser.safebrowsing. from here.  This
  3620. // should be an constructor param or settable some other way.
  3621. const kGetKeyUrl = "browser.safebrowsing.provider.0.keyURL";
  3622.  
  3623. /**
  3624.  * A key manager for UrlCrypto. There should be exactly one of these
  3625.  * per appplication, and all UrlCrypto's should share it. This is
  3626.  * currently implemented by having the manager attach itself to the
  3627.  * UrlCrypto's prototype at startup. We could've opted for a global
  3628.  * instead, but I like this better, even though it is spooky action
  3629.  * at a distance.
  3630.  *
  3631.  * @param opt_keyFilename String containing the name of the 
  3632.  *                        file we should serialize keys to/from. Used
  3633.  *                        mostly for testing.
  3634.  *
  3635.  * @param opt_testing Boolean indicating whether we are testing. If we 
  3636.  *                    are, then we skip trying to read the old key from
  3637.  *                    file and automatically trying to rekey; presumably
  3638.  *                    the tester will drive these manually.
  3639.  *
  3640.  * @constructor
  3641.  */
  3642. function PROT_UrlCryptoKeyManager(opt_keyFilename, opt_testing) {
  3643.   this.debugZone = "urlcryptokeymanager";
  3644.   this.testing_ = !!opt_testing;
  3645.   this.base64_ = new G_Base64();
  3646.   this.clientKey_ = null;          // Base64-encoded, as fetched from server
  3647.   this.clientKeyArray_ = null;     // Base64-decoded into an array of numbers
  3648.   this.wrappedKey_ = null;         // Opaque websafe base64-encoded server key
  3649.   this.rekeyTries_ = 0;
  3650.  
  3651.   this.keyFilename_ = opt_keyFilename ? 
  3652.                       opt_keyFilename : kKeyFilename;
  3653.  
  3654.   // Convenience properties
  3655.   this.MAX_REKEY_TRIES = PROT_UrlCryptoKeyManager.MAX_REKEY_TRIES;
  3656.   this.CLIENT_KEY_NAME = PROT_UrlCryptoKeyManager.CLIENT_KEY_NAME;
  3657.   this.WRAPPED_KEY_NAME = PROT_UrlCryptoKeyManager.WRAPPED_KEY_NAME;
  3658.  
  3659.   if (!this.testing_) {
  3660.     G_Assert(this, !PROT_UrlCrypto.prototype.manager_,
  3661.              "Already have manager?");
  3662.     PROT_UrlCrypto.prototype.manager_ = this;
  3663.  
  3664.     this.maybeLoadOldKey();
  3665.     this.reKey();
  3666.   }
  3667. }
  3668.  
  3669. // Do ***** NOT ***** set this higher; HTTPS is expensive
  3670. PROT_UrlCryptoKeyManager.MAX_REKEY_TRIES = 2;
  3671.  
  3672. // These are the names the server will respond with in protocol4 format
  3673. PROT_UrlCryptoKeyManager.CLIENT_KEY_NAME = "clientkey";
  3674. PROT_UrlCryptoKeyManager.WRAPPED_KEY_NAME = "wrappedkey";
  3675.  
  3676.  
  3677. /**
  3678.  * Called by a UrlCrypto to get the current K_C
  3679.  *
  3680.  * @returns Array of numbers making up the client key or null if we 
  3681.  *          have no key
  3682.  */
  3683. PROT_UrlCryptoKeyManager.prototype.getClientKeyArray = function() {
  3684.   return this.clientKeyArray_;
  3685. }
  3686.  
  3687. /**
  3688.  * Called by a UrlCrypto to get WrappedKey
  3689.  *
  3690.  * @returns Opaque base64-encoded WrappedKey or null if we haven't
  3691.  *          gotten one
  3692.  */
  3693. PROT_UrlCryptoKeyManager.prototype.getWrappedKey = function() {
  3694.   return this.wrappedKey_;
  3695. }
  3696.  
  3697. /**
  3698.  * Tell the manager to re-key. For safety, this method still obeys the
  3699.  * max-tries limit. Clients should generally use maybeReKey() if they
  3700.  * want to try a re-keying: it's an error to call reKey() after we've
  3701.  * hit max-tries, but not an error to call maybeReKey().
  3702.  */
  3703. PROT_UrlCryptoKeyManager.prototype.reKey = function() {
  3704.   
  3705.   if (this.rekeyTries_ > this.MAX_REKEY_TRIES)
  3706.     throw new Error("Have already rekeyed " + this.rekeyTries_ + " times");
  3707.  
  3708.   this.rekeyTries_++;
  3709.  
  3710.   G_Debug(this, "Attempting to re-key");
  3711.   var prefs = new G_Preferences();
  3712.   var url = prefs.getPref(kGetKeyUrl, null);
  3713.   if (!this.testing_ && url)
  3714.     (new PROT_XMLFetcher()).get(url, 
  3715.                                 BindToObject(this.onGetKeyResponse, this));
  3716. }
  3717.  
  3718. /**
  3719.  * Try to re-key if we haven't already hit our limit. It's OK to call
  3720.  * this method multiple times, even if we've already tried to rekey
  3721.  * more than the max. It will simply refuse to do so.
  3722.  *
  3723.  * @returns Boolean indicating if it actually issued a rekey request (that
  3724.  *          is, if we haven' already hit the max)
  3725.  */
  3726. PROT_UrlCryptoKeyManager.prototype.maybeReKey = function() {
  3727.   if (this.rekeyTries_ > this.MAX_REKEY_TRIES) {
  3728.     G_Debug(this, "Not re-keying; already at max");
  3729.     return false;
  3730.   }
  3731.  
  3732.   this.reKey();
  3733.   return true;
  3734. }
  3735.  
  3736. /**
  3737.  * @returns Boolean indicating if we have a key we can use 
  3738.  */
  3739. PROT_UrlCryptoKeyManager.prototype.hasKey_ = function() {
  3740.   return this.clientKey_ != null && this.wrappedKey_ != null;
  3741. }
  3742.  
  3743. /**
  3744.  * Set a new key and serialize it to disk.
  3745.  *
  3746.  * @param clientKey String containing the base64-encoded client key 
  3747.  *                  we wish to use
  3748.  *
  3749.  * @param wrappedKey String containing the opaque base64-encoded WrappedKey
  3750.  *                   the server gave us (i.e., K_C encrypted with K_S)
  3751.  */
  3752. PROT_UrlCryptoKeyManager.prototype.replaceKey_ = function(clientKey, 
  3753.                                                           wrappedKey) {
  3754.   if (this.clientKey_)
  3755.     G_Debug(this, "Replacing " + this.clientKey_ + " with " + clientKey);
  3756.  
  3757.   this.clientKey_ = clientKey;
  3758.   this.clientKeyArray_ = this.base64_.decodeString(this.clientKey_);
  3759.   this.wrappedKey_ = wrappedKey;
  3760.  
  3761.   this.serializeKey_(this.clientKey_, this.wrappedKey_);
  3762. }
  3763.  
  3764. /**
  3765.  * Try to write the key to disk so we can fall back on it. Fail
  3766.  * silently if we cannot. The keys are serialized in protocol4 format.
  3767.  *
  3768.  * @returns Boolean indicating whether we succeeded in serializing
  3769.  */
  3770. PROT_UrlCryptoKeyManager.prototype.serializeKey_ = function() {
  3771.  
  3772.   var map = {};
  3773.   map[this.CLIENT_KEY_NAME] = this.clientKey_;
  3774.   map[this.WRAPPED_KEY_NAME] = this.wrappedKey_;
  3775.   
  3776.   try {  
  3777.  
  3778.     var appDir = new PROT_ApplicationDirectory();
  3779.     if (!appDir.exists())
  3780.       appDir.create();
  3781.     var keyfile = appDir.getAppDirFileInterface();
  3782.     keyfile.append(this.keyFilename_);
  3783.     G_FileWriter.writeAll(keyfile, (new G_Protocol4Parser).serialize(map));
  3784.     return true;
  3785.  
  3786.   } catch(e) {
  3787.  
  3788.     G_Error(this, "Failed to serialize new key: " + e);
  3789.     return false;
  3790.  
  3791.   }
  3792. }
  3793.  
  3794. /**
  3795.  * Invoked when we've received a protocol4 response to our getkey
  3796.  * request. Try to parse it and set this key as the new one if we can.
  3797.  *
  3798.  *  @param responseText String containing the protocol4 getkey response
  3799.  */ 
  3800. PROT_UrlCryptoKeyManager.prototype.onGetKeyResponse = function(responseText) {
  3801.  
  3802.   var response = (new G_Protocol4Parser).parse(responseText);
  3803.   var clientKey = response[this.CLIENT_KEY_NAME];
  3804.   var wrappedKey = response[this.WRAPPED_KEY_NAME];
  3805.  
  3806.   if (response && clientKey && wrappedKey) {
  3807.     G_Debug(this, "Got new key from: " + responseText);
  3808.     this.replaceKey_(clientKey, wrappedKey);
  3809.   } else {
  3810.     G_Debug(this, "Not a valid response for /getkey");
  3811.   }
  3812. }
  3813.  
  3814. /**
  3815.  * Attempt to read a key we've previously serialized from disk, so
  3816.  * that we can fall back on it in case we can't get one from the
  3817.  * server. If we get a key, only use it if we don't already have one
  3818.  * (i.e., if our startup HTTPS request died or hasn't yet completed).
  3819.  *
  3820.  * This method should be invoked early, like when the user's profile
  3821.  * becomes available.
  3822.  */ 
  3823. PROT_UrlCryptoKeyManager.prototype.maybeLoadOldKey = function() {
  3824.   
  3825.   var oldKey = null;
  3826.   try {  
  3827.     var appDir = new PROT_ApplicationDirectory();
  3828.     var keyfile = appDir.getAppDirFileInterface();
  3829.     keyfile.append(this.keyFilename_);
  3830.     if (keyfile.exists())
  3831.       oldKey = G_FileReader.readAll(keyfile);
  3832.   } catch(e) {
  3833.     G_Debug(this, "Caught " + e + " trying to read keyfile");
  3834.     return;
  3835.   }
  3836.    
  3837.   if (!oldKey) {
  3838.     G_Debug(this, "Couldn't find old key.");
  3839.     return;
  3840.   }
  3841.  
  3842.   oldKey = (new G_Protocol4Parser).parse(oldKey);
  3843.   var clientKey = oldKey[this.CLIENT_KEY_NAME];
  3844.   var wrappedKey = oldKey[this.WRAPPED_KEY_NAME];
  3845.  
  3846.   if (oldKey && clientKey && wrappedKey && !this.hasKey_()) {
  3847.     G_Debug(this, "Read old key from disk.");
  3848.     this.replaceKey_(clientKey, wrappedKey);
  3849.   }
  3850. }
  3851.  
  3852.  
  3853. /* ***** BEGIN LICENSE BLOCK *****
  3854.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3855.  *
  3856.  * The contents of this file are subject to the Mozilla Public License Version
  3857.  * 1.1 (the "License"); you may not use this file except in compliance with
  3858.  * the License. You may obtain a copy of the License at
  3859.  * http://www.mozilla.org/MPL/
  3860.  *
  3861.  * Software distributed under the License is distributed on an "AS IS" basis,
  3862.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  3863.  * for the specific language governing rights and limitations under the
  3864.  * License.
  3865.  *
  3866.  * The Original Code is Google Safe Browsing.
  3867.  *
  3868.  * The Initial Developer of the Original Code is Google Inc.
  3869.  * Portions created by the Initial Developer are Copyright (C) 2006
  3870.  * the Initial Developer. All Rights Reserved.
  3871.  *
  3872.  * Contributor(s):
  3873.  *   Fritz Schneider <fritz@google.com> (original author)
  3874.  *
  3875.  * Alternatively, the contents of this file may be used under the terms of
  3876.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  3877.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  3878.  * in which case the provisions of the GPL or the LGPL are applicable instead
  3879.  * of those above. If you wish to allow use of your version of this file only
  3880.  * under the terms of either the GPL or the LGPL, and not to allow others to
  3881.  * use your version of this file under the terms of the MPL, indicate your
  3882.  * decision by deleting the provisions above and replace them with the notice
  3883.  * and other provisions required by the GPL or the LGPL. If you do not delete
  3884.  * the provisions above, a recipient may use your version of this file under
  3885.  * the terms of any one of the MPL, the GPL or the LGPL.
  3886.  *
  3887.  * ***** END LICENSE BLOCK ***** */
  3888.  
  3889. // A simple class that encapsulates a request. You'll notice the
  3890. // style here is different from the rest of the extension; that's
  3891. // because this was re-used from really old code we had. At some
  3892. // point it might be nice to replace this with something better
  3893. // (e.g., something that has explicit onerror handler, ability
  3894. // to set headers, and so on).
  3895. //
  3896. // The only interesting thing here is its ability to strip cookies
  3897. // from the request.
  3898.  
  3899. /**
  3900.  * Because we might be in a component, we can't just assume that
  3901.  * XMLHttpRequest exists. So we use this tiny class to wrap the XPCOM
  3902.  * version.
  3903.  *
  3904.  * @constructor
  3905.  */
  3906. function PROT_XMLHttpRequest() {
  3907.   var Cc = Components.classes;
  3908.   var Ci = Components.interfaces;
  3909.   var request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
  3910.                 .createInstance(Ci.nsIXMLHttpRequest);
  3911.   // Need the following so we get onerror/load/progresschange
  3912.   request.QueryInterface(Ci.nsIJSXMLHttpRequest);
  3913.   return request;
  3914. }
  3915.  
  3916. /**
  3917.  * A helper class that does HTTP GETs and calls back a function with
  3918.  * the content it receives. Asynchronous, so uses a closure for the
  3919.  * callback.
  3920.  *
  3921.  * @param opt_stripCookies Boolean indicating whether we should strip
  3922.  *                         cookies from this request
  3923.  * 
  3924.  * @constructor
  3925.  */
  3926. function PROT_XMLFetcher(opt_stripCookies) {
  3927.   this.debugZone = "xmlfetcher";
  3928.   this._request = new PROT_XMLHttpRequest();
  3929.   this._stripCookies = !!opt_stripCookies;
  3930. }
  3931.  
  3932. PROT_XMLFetcher.prototype = {
  3933.   /**
  3934.    * Function that will be called back upon fetch completion.
  3935.    */
  3936.   _callback: null,
  3937.   
  3938.  
  3939.   /**
  3940.    * Fetches some content.
  3941.    * 
  3942.    * @param page URL to fetch
  3943.    * @param callback Function to call back when complete.
  3944.    */
  3945.   get: function(page, callback) {
  3946.     this._request.abort();                // abort() is asynchronous, so
  3947.     this._request = new PROT_XMLHttpRequest();
  3948.     this._callback = callback;
  3949.     var asynchronous = true;
  3950.     this._request.open("GET", page, asynchronous);
  3951.  
  3952.     if (this._stripCookies)
  3953.       new PROT_CookieStripper(this._request.channel);
  3954.  
  3955.     // Create a closure
  3956.     var self = this;
  3957.     this._request.onreadystatechange = function() {
  3958.       self.readyStateChange(self);
  3959.     }
  3960.  
  3961.     this._request.send(null);
  3962.   },
  3963.  
  3964.   /**
  3965.    * Called periodically by the request to indicate some state change. 4
  3966.    * means content has been received.
  3967.    */
  3968.   readyStateChange: function(fetcher) {
  3969.     if (fetcher._request.readyState != 4) // TODO: check status code 200
  3970.       return;
  3971.  
  3972.     // We occasionally get an NS_ERROR_NOT_AVAILABLE (it doesn't have
  3973.     // headers) when we try to read the response. Mask the exception
  3974.     // by returning null response. 
  3975.     // TODO maybe masking this should be an option?
  3976.     var responseText = null;
  3977.     try {
  3978.       G_Debug(this, "xml fetch status code: \"" + 
  3979.               fetcher._request.status + "\"");
  3980.       var responseText = fetcher._request.responseText;
  3981.     } catch(e) {
  3982.       G_Debug(this, "Caught exception trying to read xmlhttprequest " +
  3983.               "status/response.");
  3984.       G_Debug(this, e);
  3985.     }
  3986.     if (fetcher._callback)
  3987.       fetcher._callback(responseText);
  3988.   }
  3989. };
  3990.  
  3991.  
  3992. /**
  3993.  * This class knows how to strip cookies from an HTTP request. It
  3994.  * listens for http-on-modify-request, and modifies the request
  3995.  * accordingly. We can't do this using xmlhttprequest.setHeader() or
  3996.  * nsIChannel.setRequestHeader() before send()ing because the cookie
  3997.  * service is called after send().
  3998.  * 
  3999.  * @param channel nsIChannel in which the request is happening
  4000.  * @constructor
  4001.  */
  4002. function PROT_CookieStripper(channel) {
  4003.   this.debugZone = "cookiestripper";
  4004.   this.topic_ = "http-on-modify-request";
  4005.   this.channel_ = channel;
  4006.  
  4007.   var Cc = Components.classes;
  4008.   var Ci = Components.interfaces;
  4009.   this.observerService_ = Cc["@mozilla.org/observer-service;1"]
  4010.                           .getService(Ci.nsIObserverService);
  4011.   this.observerService_.addObserver(this, this.topic_, false);
  4012.  
  4013.   // If the request doesn't issue, don't hang around forever
  4014.   var twentySeconds = 20 * 1000;
  4015.   this.alarm_ = new G_Alarm(BindToObject(this.stopObserving, this), 
  4016.                             twentySeconds);
  4017. }
  4018.  
  4019. /**
  4020.  * Invoked by the observerservice. See nsIObserve.
  4021.  */
  4022. PROT_CookieStripper.prototype.observe = function(subject, topic, data) {
  4023.   if (topic != this.topic_ || subject != this.channel_)
  4024.     return;
  4025.  
  4026.   G_Debug(this, "Stripping cookies for channel.");
  4027.  
  4028.   this.channel_.QueryInterface(Components.interfaces.nsIHttpChannel);
  4029.   this.channel_.setRequestHeader("Cookie", "", false /* replace, not add */);
  4030.   this.alarm_.cancel();
  4031.   this.stopObserving();
  4032. }
  4033.  
  4034. /**
  4035.  * Remove us from the observerservice
  4036.  */
  4037. PROT_CookieStripper.prototype.stopObserving = function() {
  4038.   G_Debug(this, "Removing observer");
  4039.   this.observerService_.removeObserver(this, this.topic_);
  4040.   this.channel_ = this.alarm_ = this.observerService_ = null;
  4041. }
  4042.  
  4043. /**
  4044.  * XPCOM cruft
  4045.  */
  4046. PROT_CookieStripper.prototype.QueryInterface = function(iid) {
  4047.   var Ci = Components.interfaces;
  4048.   if (iid.equals(Ci.nsISupports) || iid.equals(Ci.nsIObserve))
  4049.     return this;
  4050.   throw Components.results.NS_ERROR_NO_INTERFACE;
  4051. }
  4052.  
  4053. //@line 65 "/build/buildd/firefox-1.99+2.0b1+dfsg/toolkit/components/url-classifier/src/nsUrlClassifierLib.js"
  4054.  
  4055. // Expose this whole component.
  4056. var lib = this;
  4057.  
  4058. function UrlClassifierLib() {
  4059.   this.wrappedJSObject = lib;
  4060. }
  4061.  
  4062. // Module object
  4063. function UrlClassifierLibMod() {
  4064.   this.firstTime = true;
  4065.   this.cid = Components.ID("{26a4a019-2827-4a89-a85c-5931a678823a}");
  4066.   this.progid = "@mozilla.org/url-classifier/jslib;1";
  4067. }
  4068.  
  4069. UrlClassifierLibMod.prototype.registerSelf = function(compMgr, fileSpec, loc, type) {
  4070.   if (this.firstTime) {
  4071.     this.firstTime = false;
  4072.     throw Components.results.NS_ERROR_FACTORY_REGISTER_AGAIN;
  4073.   }
  4074.   compMgr = compMgr.QueryInterface(Ci.nsIComponentRegistrar);
  4075.   compMgr.registerFactoryLocation(this.cid,
  4076.                                   "UrlClassifier JS Lib",
  4077.                                   this.progid,
  4078.                                   fileSpec,
  4079.                                   loc,
  4080.                                   type);
  4081. };
  4082.  
  4083. UrlClassifierLibMod.prototype.getClassObject = function(compMgr, cid, iid) {  
  4084.   if (!cid.equals(this.cid))
  4085.     throw Components.results.NS_ERROR_NO_INTERFACE;
  4086.   if (!iid.equals(Ci.nsIFactory))
  4087.     throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  4088.  
  4089.   return this.factory;
  4090. }
  4091.  
  4092. UrlClassifierLibMod.prototype.canUnload = function(compMgr) {
  4093.   return true;
  4094. }
  4095.  
  4096. UrlClassifierLibMod.prototype.factory = {
  4097.   createInstance: function(outer, iid) {
  4098.     if (outer != null)
  4099.       throw Components.results.NS_ERROR_NO_AGGREGATION;
  4100.     return new UrlClassifierLib();
  4101.   }
  4102. };
  4103.  
  4104. var LibModInst = new UrlClassifierLibMod();
  4105.  
  4106. function NSGetModule(compMgr, fileSpec) {
  4107.   return LibModInst;
  4108. }
  4109.