home *** CD-ROM | disk | FTP | other *** search
/ PC Welt 2006 November (DVD) / PCWELT_11_2006.ISO / casper / filesystem.squashfs / usr / lib / firefox / components / nsUrlClassifierListManager.js < prev    next >
Encoding:
Text File  |  2006-08-18  |  29.6 KB  |  868 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. const Cc = Components.classes;
  39. const Ci = Components.interfaces;
  40.  
  41. /* ***** BEGIN LICENSE BLOCK *****
  42.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  43.  *
  44.  * The contents of this file are subject to the Mozilla Public License Version
  45.  * 1.1 (the "License"); you may not use this file except in compliance with
  46.  * the License. You may obtain a copy of the License at
  47.  * http://www.mozilla.org/MPL/
  48.  *
  49.  * Software distributed under the License is distributed on an "AS IS" basis,
  50.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  51.  * for the specific language governing rights and limitations under the
  52.  * License.
  53.  *
  54.  * The Original Code is Google Safe Browsing.
  55.  *
  56.  * The Initial Developer of the Original Code is Google Inc.
  57.  * Portions created by the Initial Developer are Copyright (C) 2006
  58.  * the Initial Developer. All Rights Reserved.
  59.  *
  60.  * Contributor(s):
  61.  *   Niels Provos <niels@google.com> (original author)
  62.  *   Fritz Schneider <fritz@google.com>
  63.  *
  64.  * Alternatively, the contents of this file may be used under the terms of
  65.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  66.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  67.  * in which case the provisions of the GPL or the LGPL are applicable instead
  68.  * of those above. If you wish to allow use of your version of this file only
  69.  * under the terms of either the GPL or the LGPL, and not to allow others to
  70.  * use your version of this file under the terms of the MPL, indicate your
  71.  * decision by deleting the provisions above and replace them with the notice
  72.  * and other provisions required by the GPL or the LGPL. If you do not delete
  73.  * the provisions above, a recipient may use your version of this file under
  74.  * the terms of any one of the MPL, the GPL or the LGPL.
  75.  *
  76.  * ***** END LICENSE BLOCK ***** */
  77.  
  78.  
  79. // A class that manages lists, namely white and black lists for
  80. // phishing or malware protection. The ListManager knows how to fetch,
  81. // update, and store lists, and knows the "kind" of list each is (is
  82. // it a whitelist? a blacklist? etc). However it doesn't know how the
  83. // lists are serialized or deserialized (the wireformat classes know
  84. // this) nor the specific format of each list. For example, the list
  85. // could be a map of domains to "1" if the domain is phishy. Or it
  86. // could be a map of hosts to regular expressions to match, who knows?
  87. // Answer: the trtable knows. List are serialized/deserialized by the
  88. // wireformat reader from/to trtables, and queried by the listmanager.
  89. //
  90. // There is a single listmanager for the whole application.
  91. //
  92. // The listmanager is used only in privacy mode; in advanced protection
  93. // mode a remote server is queried.
  94. //
  95. // How to add a new table:
  96. // 1) get it up on the server
  97. // 2) add it to tablesKnown
  98. // 3) if it is not a known table type (trtable.js), add an implementation
  99. //    for it in trtable.js
  100. // 4) add a check for it in the phishwarden's isXY() method, for example
  101. //    isBlackURL()
  102. //
  103. // TODO: obviously the way this works could use a lot of improvement. In
  104. //       particular adding a list should just be a matter of adding
  105. //       its name to the listmanager and an implementation to trtable
  106. //       (or not if a talbe of that type exists). The format and semantics
  107. //       of the list comprise its name, so the listmanager should easily
  108. //       be able to figure out what to do with what list (i.e., no
  109. //       need for step 4).
  110. // TODO more comprehensive update tests, for example add unittest check 
  111. //      that the listmanagers tables are properly written on updates
  112.  
  113. /**
  114.  * The base pref name for where we keep table version numbers.
  115.  * We add append the table name to this and set the value to
  116.  * the version.  E.g., tableversion.goog-black-enchash may have
  117.  * a value of 1.1234.
  118.  */
  119. const kTableVersionPrefPrefix = "urlclassifier.tableversion.";
  120.  
  121. /**
  122.  * A ListManager keeps track of black and white lists and knows
  123.  * how to update them.
  124.  *
  125.  * @constructor
  126.  */
  127. function PROT_ListManager() {
  128.   this.debugZone = "listmanager";
  129.   G_debugService.enableZone(this.debugZone);
  130.  
  131.   this.currentUpdateChecker_ = null;   // set when we toggle updates
  132.   this.prefs_ = new G_Preferences();
  133.  
  134.   this.updateserverURL_ = null;
  135.  
  136.   // The lists we know about and the parses we can use to read
  137.   // them. Default all to the earlies possible version (1.-1); this
  138.   // version will get updated when successfully read from disk or
  139.   // fetch updates.
  140.   this.tablesKnown_ = {};
  141.   this.isTesting_ = false;
  142.   
  143.   if (this.isTesting_) {
  144.     // populate with some tables for unittesting
  145.     this.tablesKnown_ = {
  146.       // A major version of zero means local, so don't ask for updates       
  147.       "test1-foo-domain" : new PROT_VersionParser("test1-foo-domain", 0, -1),
  148.       "test2-foo-domain" : new PROT_VersionParser("test2-foo-domain", 0, -1),
  149.       "test-white-domain" : 
  150.         new PROT_VersionParser("test-white-domain", 0, -1, true /* require mac*/),
  151.       "test-mac-domain" :
  152.         new PROT_VersionParser("test-mac-domain", 0, -1, true /* require mac */)
  153.     };
  154.     
  155.     // expose the object for unittesting
  156.     this.wrappedJSObject = this;
  157.   }
  158.  
  159.   this.tablesData = {};
  160.  
  161.   this.observerServiceObserver_ = new G_ObserverServiceObserver(
  162.                                           'xpcom-shutdown',
  163.                                           BindToObject(this.shutdown_, this),
  164.                                           true /*only once*/);
  165.  
  166.   // Lazily create urlCrypto (see tr-fetcher.js)
  167.   this.urlCrypto_ = null;
  168. }
  169.  
  170. /**
  171.  * xpcom-shutdown callback
  172.  * Delete all of our data tables which seem to leak otherwise.
  173.  */
  174. PROT_ListManager.prototype.shutdown_ = function() {
  175.   for (var name in this.tablesData) {
  176.     delete this.tablesData[name];
  177.   }
  178. }
  179.  
  180. /**
  181.  * Set the url we check for updates.  If the new url is valid and different,
  182.  * update our table list.
  183.  * 
  184.  * After setting the update url, the caller is responsible for registering
  185.  * tables and then toggling update checking.  All the code for this logic is
  186.  * currently in browser/components/safebrowsing.  Maybe it should be part of
  187.  * the listmanger?
  188.  */
  189. PROT_ListManager.prototype.setUpdateUrl = function(url) {
  190.   G_Debug(this, "Set update url: " + url);
  191.   if (url != this.updateserverURL_) {
  192.     this.updateserverURL_ = url;
  193.     
  194.     // Remove old tables which probably aren't valid for the new provider.
  195.     for (var name in this.tablesData) {
  196.       delete this.tablesData[name];
  197.       delete this.tablesKnown_[name];
  198.     }
  199.   }
  200. }
  201.  
  202. /**
  203.  * Register a new table table
  204.  * @param tableName - the name of the table
  205.  * @param opt_requireMac true if a mac is required on update, false otherwise
  206.  * @returns true if the table could be created; false otherwise
  207.  */
  208. PROT_ListManager.prototype.registerTable = function(tableName, 
  209.                                                     opt_requireMac) {
  210.   var table = new PROT_VersionParser(tableName, 1, -1, opt_requireMac);
  211.   if (!table)
  212.     return false;
  213.   this.tablesKnown_[tableName] = table;
  214.   this.tablesData[tableName] = newUrlClassifierTable(tableName);
  215.  
  216.   return true;
  217. }
  218.  
  219. /**
  220.  * Enable updates for some tables
  221.  * @param tables - an array of table names that need updating
  222.  */
  223. PROT_ListManager.prototype.enableUpdate = function(tableName) {
  224.   var changed = false;
  225.   var table = this.tablesKnown_[tableName];
  226.   if (table) {
  227.     G_Debug(this, "Enabling table updates for " + tableName);
  228.     table.needsUpdate = true;
  229.     changed = true;
  230.   }
  231.  
  232.   if (changed === true)
  233.     this.maybeToggleUpdateChecking();
  234. }
  235.  
  236. /**
  237.  * Disables updates for some tables
  238.  * @param tables - an array of table names that no longer need updating
  239.  */
  240. PROT_ListManager.prototype.disableUpdate = function(tableName) {
  241.   var changed = false;
  242.   var table = this.tablesKnown_[tableName];
  243.   if (table) {
  244.     G_Debug(this, "Disabling table updates for " + tableName);
  245.     table.needsUpdate = false;
  246.     changed = true;
  247.   }
  248.  
  249.   if (changed === true)
  250.     this.maybeToggleUpdateChecking();
  251. }
  252.  
  253. /**
  254.  * Determine if we have some tables that need updating.
  255.  */
  256. PROT_ListManager.prototype.requireTableUpdates = function() {
  257.   for (var type in this.tablesKnown_) {
  258.     // All tables with a major of 0 are internal tables that we never
  259.     // update remotely.
  260.     if (this.tablesKnown_[type].major == 0)
  261.       continue;
  262.      
  263.     // Tables that need updating even if other tables dont require it
  264.     if (this.tablesKnown_[type].needsUpdate)
  265.       return true;
  266.   }
  267.  
  268.   return false;
  269. }
  270.  
  271. /**
  272.  * Start managing the lists we know about. We don't do this automatically
  273.  * when the listmanager is instantiated because their profile directory
  274.  * (where we store the lists) might not be available.
  275.  */
  276. PROT_ListManager.prototype.maybeStartManagingUpdates = function() {
  277.   if (this.isTesting_)
  278.     return;
  279.  
  280.   // We might have been told about tables already, so see if we should be
  281.   // actually updating.
  282.   this.maybeToggleUpdateChecking();
  283. }
  284.  
  285. /**
  286.  * Determine if we have any tables that require updating.  Different
  287.  * Wardens may call us with new tables that need to be updated.
  288.  */ 
  289. PROT_ListManager.prototype.maybeToggleUpdateChecking = function() {
  290.   // If we are testing or dont have an application directory yet, we should
  291.   // not start reading tables from disk or schedule remote updates
  292.   if (this.isTesting_)
  293.     return;
  294.  
  295.   // We update tables if we have some tables that want updates.  If there
  296.   // are no tables that want to be updated - we dont need to check anything.
  297.   if (this.requireTableUpdates() === true) {
  298.     G_Debug(this, "Starting managing lists");
  299.     // Multiple warden can ask us to reenable updates at the same time, but we
  300.     // really just need to schedule a single update.
  301.     if (!this.currentUpdateChecker_)
  302.       this.currentUpdateChecker_ =
  303.         new G_Alarm(BindToObject(this.checkForUpdates, this), 3000);
  304.     this.startUpdateChecker();
  305.   } else {
  306.     G_Debug(this, "Stopping managing lists (if currently active)");
  307.     this.stopUpdateChecker();                    // Cancel pending updates
  308.   }
  309. }
  310.  
  311. /**
  312.  * Start periodic checks for updates. Idempotent.
  313.  */
  314. PROT_ListManager.prototype.startUpdateChecker = function() {
  315.   this.stopUpdateChecker();
  316.   
  317.   // Schedule a check for updates every so often
  318.   // TODO(tc): PREF NEW
  319.   var sixtyMinutes = 60 * 60 * 1000;
  320.   this.updateChecker_ = new G_Alarm(BindToObject(this.checkForUpdates, this), 
  321.                                     sixtyMinutes, true /* repeat */);
  322. }
  323.  
  324. /**
  325.  * Stop checking for updates. Idempotent.
  326.  */
  327. PROT_ListManager.prototype.stopUpdateChecker = function() {
  328.   if (this.updateChecker_) {
  329.     this.updateChecker_.cancel();
  330.     this.updateChecker_ = null;
  331.   }
  332. }
  333.  
  334. /**
  335.  * Provides an exception free way to look up the data in a table. We
  336.  * use this because at certain points our tables might not be loaded,
  337.  * and querying them could throw.
  338.  *
  339.  * @param table String Name of the table that we want to consult
  340.  * @param key String Key for table lookup
  341.  * @param callback nsIUrlListManagerCallback (ie., Function) given false or the
  342.  *        value in the table corresponding to key.  If the table name does not
  343.  *        exist, we return false, too.
  344.  */
  345. PROT_ListManager.prototype.safeExists = function(table, key, callback) {
  346.   try {
  347.     G_Debug(this, "safeExists: " + table + ", " + key);
  348.     var map = this.tablesData[table];
  349.     map.exists(key, callback);
  350.   } catch(e) {
  351.     G_Debug(this, "safeExists masked failure for " + table + ", key " + key + ": " + e);
  352.     callback.handleEvent(false);
  353.   }
  354. }
  355.  
  356. /**
  357.  * We store table versions in user prefs.  This method pulls the values out of
  358.  * the user prefs and into the tablesKnown objects.
  359.  */
  360. PROT_ListManager.prototype.loadTableVersions_ = function() {
  361.   // Pull values out of prefs.
  362.   var prefBase = kTableVersionPrefPrefix;
  363.   for (var table in this.tablesKnown_) {
  364.     var version = this.prefs_.getPref(prefBase + table, "1.-1");
  365.     G_Debug(this, "loadTableVersion " + table + ": " + version);
  366.     var tokens = version.split(".");
  367.     G_Assert(this, tokens.length == 2, "invalid version number");
  368.     
  369.     this.tablesKnown_[table].major = tokens[0];
  370.     this.tablesKnown_[table].minor = tokens[1];
  371.   }
  372. }
  373.  
  374. /**
  375.  * Callback from db update service.  As new tables are added to the db,
  376.  * this callback is fired so we can update the version number.
  377.  * @param versionString String containing the table update response from the
  378.  *        server
  379.  */
  380. PROT_ListManager.prototype.setTableVersion_ = function(versionString) {
  381.   G_Debug(this, "Got version string: " + versionString);
  382.   var versionParser = new PROT_VersionParser("");
  383.   if (versionParser.fromString(versionString)) {
  384.     var tableName = versionParser.type;
  385.     var versionNumber = versionParser.versionString();
  386.     var prefBase = kTableVersionPrefPrefix;
  387.  
  388.     this.prefs_.setPref(prefBase + tableName, versionNumber);
  389.     
  390.     if (!this.tablesKnown_[tableName]) {
  391.       this.tablesKnown_[tableName] = versionParser;
  392.     } else {
  393.       this.tablesKnown_[tableName].ImportVersion(versionParser);
  394.     }
  395.     
  396.     if (!this.tablesData[tableName])
  397.       this.tablesData[tableName] = newUrlClassifierTable(tableName);
  398.   }
  399. }
  400.  
  401. /**
  402.  * Prepares a URL to fetch upates from. Format is a squence of 
  403.  * type:major:minor, fields
  404.  * 
  405.  * @param url The base URL to which query parameters are appended; assumes
  406.  *            already has a trailing ?
  407.  * @returns the URL that we should request the table update from.
  408.  */
  409. PROT_ListManager.prototype.getRequestURL_ = function(url) {
  410.   url += "version=";
  411.   var firstElement = true;
  412.   var requestMac = false;
  413.  
  414.   for (var type in this.tablesKnown_) {
  415.     // All tables with a major of 0 are internal tables that we never
  416.     // update remotely.
  417.     if (this.tablesKnown_[type].major == 0)
  418.       continue;
  419.  
  420.     // Check if the table needs updating
  421.     if (this.tablesKnown_[type].needsUpdate == false)
  422.       continue;
  423.  
  424.     if (!firstElement) {
  425.       url += ","
  426.     } else {
  427.       firstElement = false;
  428.     }
  429.     url += type + ":" + this.tablesKnown_[type].toUrl();
  430.  
  431.     if (this.tablesKnown_[type].requireMac)
  432.       requestMac = true;
  433.   }
  434.  
  435.   // Request a mac only if at least one of the tables to be updated requires
  436.   // it
  437.   if (requestMac) {
  438.     // Add the wrapped key for requesting macs
  439.     if (!this.urlCrypto_)
  440.       this.urlCrypto_ = new PROT_UrlCrypto();
  441.  
  442.     url += "&wrkey=" +
  443.       encodeURIComponent(this.urlCrypto_.getManager().getWrappedKey());
  444.   }
  445.  
  446.   G_Debug(this, "getRequestURL returning: " + url);
  447.   return url;
  448. }
  449.  
  450. /**
  451.  * Updates our internal tables from the update server
  452.  *
  453.  * @returns true when a new request was scheduled, false if an old request
  454.  *          was still pending.
  455.  */
  456. PROT_ListManager.prototype.checkForUpdates = function() {
  457.   // Allow new updates to be scheduled from maybeToggleUpdateChecking()
  458.   this.currentUpdateChecker_ = null;
  459.  
  460.   if (!this.updateserverURL_) {
  461.     G_Debug(this, 'checkForUpdates: no update server url');
  462.     return false;
  463.   }
  464.   this.loadTableVersions_();
  465.  
  466.   G_Debug(this, 'checkForUpdates: scheduling request..');
  467.   var url = this.getRequestURL_(this.updateserverURL_);
  468.   var streamer = Cc["@mozilla.org/url-classifier/streamupdater;1"]
  469.                  .getService(Ci.nsIUrlClassifierStreamUpdater);
  470.   try {
  471.     streamer.updateUrl = url;
  472.   } catch (e) {
  473.     G_Debug(this, 'invalid url');
  474.     return false;
  475.   }
  476.  
  477.   return streamer.downloadUpdates(BindToObject(this.setTableVersion_, this));
  478. }
  479.  
  480. /**
  481.  * Given the server response, extract out the new table lines and table
  482.  * version numbers.  If the table has a mac, also check to see if it matches
  483.  * the data.
  484.  *
  485.  * @param data String update string from the server
  486.  * @return String The same update string sans tables with invalid macs.
  487.  */
  488. PROT_ListManager.prototype.checkMac_ = function(data) {
  489.   var dataTables = data.split('\n\n');
  490.   var returnString = "";
  491.  
  492.   for (var table = null, t = 0; table = dataTables[t]; ++t) {
  493.     var firstLineEnd = table.indexOf("\n");
  494.     while (firstLineEnd == 0) {
  495.       // Skip leading blank lines
  496.       table = table.substring(1);
  497.       firstLineEnd = table.indexOf("\n");
  498.     }
  499.     if (firstLineEnd == -1) {
  500.       continue;
  501.     }
  502.  
  503.     var versionLine = table.substring(0, firstLineEnd);
  504.     var versionParser = new PROT_VersionParser("dummy");
  505.     if (!versionParser.fromString(versionLine)) {
  506.       // Failed to parse the version string, skip this table.
  507.       G_Debug(this, "Failed to parse version string");
  508.       continue;
  509.     }
  510.  
  511.     if (versionParser.mac && versionParser.macval.length > 0) {
  512.       // Includes a mac, so we check it.
  513.       var updateData = table.substring(firstLineEnd + 1) + '\n';
  514.       if (!this.urlCrypto_)
  515.         this.urlCrypto_ = new PROT_UrlCrypto();
  516.  
  517.       var computedMac = this.urlCrypto_.computeMac(updateData);
  518.       if (computedMac != versionParser.macval) {
  519.         G_Debug(this, "mac doesn't match: " + computedMac + " != " +
  520.                       versionParser.macval)
  521.         continue;
  522.       }
  523.     } else {
  524.       // No mac in the return.  Check to see if it's required.  If it is
  525.       // required, skip this data.
  526.       if (this.tablesKnown_[versionParser.type] &&
  527.           this.tablesKnown_[versionParser.type].requireMac) {
  528.         continue;
  529.       }
  530.     }
  531.  
  532.     // Everything looks ok, add it to the return string.
  533.     returnString += table + "\n\n";
  534.   }
  535.  
  536.   return returnString;
  537. }
  538.  
  539. PROT_ListManager.prototype.QueryInterface = function(iid) {
  540.   if (iid.equals(Ci.nsISupports) ||
  541.       iid.equals(Ci.nsIUrlListManager) ||
  542.       iid.equals(Ci.nsITimerCallback))
  543.     return this;
  544.  
  545.   Components.returnCode = Components.results.NS_ERROR_NO_INTERFACE;
  546.   return null;
  547. }
  548.  
  549. // A simple factory function that creates nsIUrlClassifierTable instances based
  550. // on a name.  The name is a string of the format
  551. // provider_name-semantic_type-table_type.  For example, goog-white-enchash
  552. // or goog-black-url.
  553. function newUrlClassifierTable(name) {
  554.   G_Debug("protfactory", "Creating a new nsIUrlClassifierTable: " + name);
  555.   var tokens = name.split('-');
  556.   var type = tokens[2];
  557.   var table = Cc['@mozilla.org/url-classifier/table;1?type=' + type]
  558.                 .createInstance(Ci.nsIUrlClassifierTable);
  559.   table.name = name;
  560.   return table;
  561. }
  562. /* ***** BEGIN LICENSE BLOCK *****
  563.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  564.  *
  565.  * The contents of this file are subject to the Mozilla Public License Version
  566.  * 1.1 (the "License"); you may not use this file except in compliance with
  567.  * the License. You may obtain a copy of the License at
  568.  * http://www.mozilla.org/MPL/
  569.  *
  570.  * Software distributed under the License is distributed on an "AS IS" basis,
  571.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  572.  * for the specific language governing rights and limitations under the
  573.  * License.
  574.  *
  575.  * The Original Code is Google Safe Browsing.
  576.  *
  577.  * The Initial Developer of the Original Code is Google Inc.
  578.  * Portions created by the Initial Developer are Copyright (C) 2006
  579.  * the Initial Developer. All Rights Reserved.
  580.  *
  581.  * Contributor(s):
  582.  *   Niels Provos <niels@google.com> (original author)
  583.  *   Fritz Schneider <fritz@google.com>
  584.  *
  585.  * Alternatively, the contents of this file may be used under the terms of
  586.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  587.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  588.  * in which case the provisions of the GPL or the LGPL are applicable instead
  589.  * of those above. If you wish to allow use of your version of this file only
  590.  * under the terms of either the GPL or the LGPL, and not to allow others to
  591.  * use your version of this file under the terms of the MPL, indicate your
  592.  * decision by deleting the provisions above and replace them with the notice
  593.  * and other provisions required by the GPL or the LGPL. If you do not delete
  594.  * the provisions above, a recipient may use your version of this file under
  595.  * the terms of any one of the MPL, the GPL or the LGPL.
  596.  *
  597.  * ***** END LICENSE BLOCK ***** */
  598.  
  599.  
  600. // A class that serializes and deserializes opaque key/value string to
  601. // string maps to/from maps (trtables). It knows how to create
  602. // trtables from the serialized format, so it also understands
  603. // meta-information like the name of the table and the table's
  604. // version. See docs for the protocol description.
  605. // 
  606. // TODO: wireformatreader: if you have multiple updates for one table
  607. //       in a call to deserialize, the later ones will be merged 
  608. //       (all but the last will be ignored). To fix, merge instead
  609. //       of replace when you have an existing table, and only do so once.
  610. // TODO must have blank line between successive types -- problem?
  611. // TODO doesn't tolerate blank lines very well
  612. //
  613. // Maybe: These classes could use a LOT more cleanup, but it's not a
  614. //       priority at the moment. For example, the tablesData/Known
  615. //       maps should be combined into a single object, the parser
  616. //       for a given type should be separate from the version info,
  617. //       and there should be synchronous interfaces for testing.
  618.  
  619.  
  620. /**
  621.  * A class that knows how to serialize and deserialize meta-information.
  622.  * This meta information is the table name and version number, and 
  623.  * in its serialized form looks like the first line below:
  624.  * 
  625.  * [name-of-table X.Y update?]                
  626.  * ...key/value pairs to add or delete follow...
  627.  * <blank line ends the table>
  628.  *
  629.  * The X.Y is the version number and the optional "update" token means 
  630.  * that the table is a differential from the curent table the extension
  631.  * has. Its absence means that this is a full, new table.
  632.  */
  633. function PROT_VersionParser(type, opt_major, opt_minor, opt_requireMac) {
  634.   this.debugZone = "versionparser";
  635.   this.type = type;
  636.   this.major = 0;
  637.   this.minor = 0;
  638.  
  639.   this.badHeader = false;
  640.  
  641.   // Should the wireformatreader compute a mac?
  642.   this.mac = false;
  643.   this.macval = "";
  644.   this.macFailed = false;
  645.   this.requireMac = !!opt_requireMac;
  646.  
  647.   this.update = false;
  648.   this.needsUpdate = false;  // used by ListManager to determine update policy
  649.   // Used by ListerManager to see if we have read data for this table from
  650.   // disk.  Once we read a table from disk, we are not going to do so again
  651.   // but instead update remotely if necessary.
  652.   this.didRead = false;
  653.   if (opt_major)
  654.     this.major = parseInt(opt_major);
  655.   if (opt_minor)
  656.     this.minor = parseInt(opt_minor);
  657. }
  658.  
  659. /** Import the version information from another VersionParser
  660.  * @params version a version parser object
  661.  */
  662. PROT_VersionParser.prototype.ImportVersion = function(version) {
  663.   this.major = version.major;
  664.   this.minor = version.minor;
  665.  
  666.   this.mac = version.mac;
  667.   this.macFailed = version.macFailed;
  668.   this.macval = version.macval;
  669.   // Don't set requireMac, since we create vparsers from scratch and doesn't
  670.   // know about it
  671. }
  672.  
  673. /** 
  674.  * Creates a string like [goog-white-black 1.1] from internal information
  675.  * 
  676.  * @returns String
  677.  */
  678. PROT_VersionParser.prototype.toString = function() {
  679.   var s = "[" + this.type + " " + this.major + "." + this.minor + "]";
  680.   return s;
  681. }
  682.  
  683. /**
  684.  * Creates a string like 1.123 with the version number.  This is the
  685.  * format we store in prefs.
  686.  * @return String
  687.  */
  688. PROT_VersionParser.prototype.versionString = function() {
  689.   return this.major + "." + this.minor;
  690. }
  691.  
  692. /** 
  693.  * Creates a string like 1:1 from internal information used for
  694.  * fetching updates from the server. Called by the listmanager.
  695.  * 
  696.  * @returns String
  697.  */
  698. PROT_VersionParser.prototype.toUrl = function() {
  699.   return this.major + ":" + this.minor;
  700. }
  701.  
  702. /**
  703.  * Process the old format, [type major.minor [update]]
  704.  *
  705.  * @returns true if the string could be parsed, false otherwise
  706.  */
  707. PROT_VersionParser.prototype.processOldFormat_ = function(line) {
  708.   if (line[0] != '[' || line.slice(-1) != ']')
  709.     return false;
  710.  
  711.   var description = line.slice(1, -1);
  712.  
  713.   // Get the type name and version number of this table
  714.   var tokens = description.split(" ");
  715.   this.type = tokens[0];
  716.   var majorminor = tokens[1].split(".");
  717.   this.major = parseInt(majorminor[0]);
  718.   this.minor = parseInt(majorminor[1]);
  719.   if (isNaN(this.major) || isNaN(this.minor))
  720.     return false;
  721.  
  722.   if (tokens.length >= 3) {
  723.      this.update = tokens[2] == "update";
  724.   }
  725.  
  726.   return true;
  727. }
  728.  
  729. /**
  730.  * Takes a string like [name-of-table 1.1 [update]][mac=MAC] and figures out the
  731.  * type and corresponding version numbers.
  732.  * @returns true if the string could be parsed, false otherwise
  733.  */
  734. PROT_VersionParser.prototype.fromString = function(line) {
  735.   G_Debug(this, "Calling fromString with line: " + line);
  736.   if (line[0] != '[' || line.slice(-1) != ']')
  737.     return false;
  738.  
  739.   // There could be two [][], so take care of it
  740.   var secondBracket = line.indexOf('[', 1);
  741.   var firstPart = null;
  742.   var secondPart = null;
  743.  
  744.   if (secondBracket != -1) {
  745.     firstPart = line.substring(0, secondBracket);
  746.     secondPart = line.substring(secondBracket);
  747.     G_Debug(this, "First part: " + firstPart + " Second part: " + secondPart);
  748.   } else {
  749.     firstPart = line;
  750.     G_Debug(this, "Old format: " + firstPart);
  751.   }
  752.  
  753.   if (!this.processOldFormat_(firstPart))
  754.     return false;
  755.  
  756.   if (secondPart && !this.processOptTokens_(secondPart))
  757.     return false;
  758.  
  759.   return true;
  760. }
  761.  
  762. /**
  763.  * Process optional tokens
  764.  *
  765.  * @param line A string [token1=val1 token2=val2...]
  766.  * @returns true if the string could be parsed, false otherwise
  767.  */
  768. PROT_VersionParser.prototype.processOptTokens_ = function(line) {
  769.   if (line[0] != '[' || line.slice(-1) != ']')
  770.     return false;
  771.   var description = line.slice(1, -1);
  772.   // Get the type name and version number of this table
  773.   var tokens = description.split(" ");
  774.  
  775.   for (var i = 0; i < tokens.length; i++) {
  776.     G_Debug(this, "Processing optional token: " + tokens[i]);
  777.     var tokenparts = tokens[i].split("=");
  778.     switch(tokenparts[0]){
  779.     case "mac":
  780.       this.mac = true;
  781.       if (tokenparts.length < 2) {
  782.         G_Debug(this, "Found mac flag but not mac value!");
  783.         return false;
  784.       }
  785.       // The mac value may have "=" in it, so we can't just use tokenparts[1].
  786.       // Instead, just take the rest of tokens[i] after the first "="
  787.       this.macval = tokens[i].substr(tokens[i].indexOf("=")+1);
  788.       break;
  789.     default:
  790.       G_Debug(this, "Found unrecognized token: " + tokenparts[0]);
  791.       break;
  792.     }
  793.   }
  794.  
  795.   return true;
  796. }
  797.  
  798. //@line 43 "/build/buildd/firefox-1.99+2.0b1+dfsg/toolkit/components/url-classifier/src/nsUrlClassifierListManager.js"
  799.  
  800. var modScope = this;
  801. function Init() {
  802.   // Pull the library in.
  803.   var jslib = Cc["@mozilla.org/url-classifier/jslib;1"]
  804.               .getService().wrappedJSObject;
  805.   Function.prototype.inherits = jslib.Function.prototype.inherits;
  806.   modScope.G_Preferences = jslib.G_Preferences;
  807.   modScope.G_PreferenceObserver = jslib.G_PreferenceObserver;
  808.   modScope.G_ObserverServiceObserver = jslib.G_ObserverServiceObserver;
  809.   modScope.G_Debug = jslib.G_Debug;
  810.   modScope.G_Assert = jslib.G_Assert;
  811.   modScope.G_debugService = jslib.G_debugService;
  812.   modScope.G_Alarm = jslib.G_Alarm;
  813.   modScope.BindToObject = jslib.BindToObject;
  814.   modScope.PROT_XMLFetcher = jslib.PROT_XMLFetcher;
  815.  
  816.   // We only need to call Init once.
  817.   modScope.Init = function() {};
  818. }
  819.  
  820. // Module object
  821. function UrlClassifierListManagerMod() {
  822.   this.firstTime = true;
  823.   this.cid = Components.ID("{ca168834-cc00-48f9-b83c-fd018e58cae3}");
  824.   this.progid = "@mozilla.org/url-classifier/listmanager;1";
  825. }
  826.  
  827. UrlClassifierListManagerMod.prototype.registerSelf = function(compMgr, fileSpec, loc, type) {
  828.   if (this.firstTime) {
  829.     this.firstTime = false;
  830.     throw Components.results.NS_ERROR_FACTORY_REGISTER_AGAIN;
  831.   }
  832.   compMgr = compMgr.QueryInterface(Ci.nsIComponentRegistrar);
  833.   compMgr.registerFactoryLocation(this.cid,
  834.                                   "UrlClassifier List Manager Module",
  835.                                   this.progid,
  836.                                   fileSpec,
  837.                                   loc,
  838.                                   type);
  839. };
  840.  
  841. UrlClassifierListManagerMod.prototype.getClassObject = function(compMgr, cid, iid) {  
  842.   if (!cid.equals(this.cid))
  843.     throw Components.results.NS_ERROR_NO_INTERFACE;
  844.   if (!iid.equals(Ci.nsIFactory))
  845.     throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  846.  
  847.   return this.factory;
  848. }
  849.  
  850. UrlClassifierListManagerMod.prototype.canUnload = function(compMgr) {
  851.   return true;
  852. }
  853.  
  854. UrlClassifierListManagerMod.prototype.factory = {
  855.   createInstance: function(outer, iid) {
  856.     if (outer != null)
  857.       throw Components.results.NS_ERROR_NO_AGGREGATION;
  858.     Init();
  859.     return (new PROT_ListManager()).QueryInterface(iid);
  860.   }
  861. };
  862.  
  863. var ListManagerModInst = new UrlClassifierListManagerMod();
  864.  
  865. function NSGetModule(compMgr, fileSpec) {
  866.   return ListManagerModInst;
  867. }
  868.