toolkit/components/url-classifier/content/wireformat.js

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

michael@0 1 # This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 # License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
michael@0 4
michael@0 5
michael@0 6 // A class that serializes and deserializes opaque key/value string to
michael@0 7 // string maps to/from maps (trtables). It knows how to create
michael@0 8 // trtables from the serialized format, so it also understands
michael@0 9 // meta-information like the name of the table and the table's
michael@0 10 // version. See docs for the protocol description.
michael@0 11 //
michael@0 12 // TODO: wireformatreader: if you have multiple updates for one table
michael@0 13 // in a call to deserialize, the later ones will be merged
michael@0 14 // (all but the last will be ignored). To fix, merge instead
michael@0 15 // of replace when you have an existing table, and only do so once.
michael@0 16 // TODO must have blank line between successive types -- problem?
michael@0 17 // TODO doesn't tolerate blank lines very well
michael@0 18 //
michael@0 19 // Maybe: These classes could use a LOT more cleanup, but it's not a
michael@0 20 // priority at the moment. For example, the tablesData/Known
michael@0 21 // maps should be combined into a single object, the parser
michael@0 22 // for a given type should be separate from the version info,
michael@0 23 // and there should be synchronous interfaces for testing.
michael@0 24
michael@0 25
michael@0 26 /**
michael@0 27 * A class that knows how to serialize and deserialize meta-information.
michael@0 28 * This meta information is the table name and version number, and
michael@0 29 * in its serialized form looks like the first line below:
michael@0 30 *
michael@0 31 * [name-of-table X.Y update?]
michael@0 32 * ...key/value pairs to add or delete follow...
michael@0 33 * <blank line ends the table>
michael@0 34 *
michael@0 35 * The X.Y is the version number and the optional "update" token means
michael@0 36 * that the table is a differential from the curent table the extension
michael@0 37 * has. Its absence means that this is a full, new table.
michael@0 38 */
michael@0 39 function PROT_VersionParser(type, opt_major, opt_minor, opt_requireMac) {
michael@0 40 this.debugZone = "versionparser";
michael@0 41 this.type = type;
michael@0 42 this.major = 0;
michael@0 43 this.minor = 0;
michael@0 44
michael@0 45 this.badHeader = false;
michael@0 46
michael@0 47 // Should the wireformatreader compute a mac?
michael@0 48 this.mac = false;
michael@0 49 this.macval = "";
michael@0 50 this.macFailed = false;
michael@0 51 this.requireMac = !!opt_requireMac;
michael@0 52
michael@0 53 this.update = false;
michael@0 54 this.needsUpdate = false; // used by ListManager to determine update policy
michael@0 55 // Used by ListerManager to see if we have read data for this table from
michael@0 56 // disk. Once we read a table from disk, we are not going to do so again
michael@0 57 // but instead update remotely if necessary.
michael@0 58 this.didRead = false;
michael@0 59 if (opt_major)
michael@0 60 this.major = parseInt(opt_major);
michael@0 61 if (opt_minor)
michael@0 62 this.minor = parseInt(opt_minor);
michael@0 63 }
michael@0 64
michael@0 65 /** Import the version information from another VersionParser
michael@0 66 * @params version a version parser object
michael@0 67 */
michael@0 68 PROT_VersionParser.prototype.ImportVersion = function(version) {
michael@0 69 this.major = version.major;
michael@0 70 this.minor = version.minor;
michael@0 71
michael@0 72 this.mac = version.mac;
michael@0 73 this.macFailed = version.macFailed;
michael@0 74 this.macval = version.macval;
michael@0 75 // Don't set requireMac, since we create vparsers from scratch and doesn't
michael@0 76 // know about it
michael@0 77 }
michael@0 78
michael@0 79 /**
michael@0 80 * Creates a string like [goog-white-black 1.1] from internal information
michael@0 81 *
michael@0 82 * @returns String
michael@0 83 */
michael@0 84 PROT_VersionParser.prototype.toString = function() {
michael@0 85 var s = "[" + this.type + " " + this.major + "." + this.minor + "]";
michael@0 86 return s;
michael@0 87 }
michael@0 88
michael@0 89 /**
michael@0 90 * Creates a string like 1.123 with the version number. This is the
michael@0 91 * format we store in prefs.
michael@0 92 * @return String
michael@0 93 */
michael@0 94 PROT_VersionParser.prototype.versionString = function() {
michael@0 95 return this.major + "." + this.minor;
michael@0 96 }
michael@0 97
michael@0 98 /**
michael@0 99 * Creates a string like 1:1 from internal information used for
michael@0 100 * fetching updates from the server. Called by the listmanager.
michael@0 101 *
michael@0 102 * @returns String
michael@0 103 */
michael@0 104 PROT_VersionParser.prototype.toUrl = function() {
michael@0 105 return this.major + ":" + this.minor;
michael@0 106 }
michael@0 107
michael@0 108 /**
michael@0 109 * Process the old format, [type major.minor [update]]
michael@0 110 *
michael@0 111 * @returns true if the string could be parsed, false otherwise
michael@0 112 */
michael@0 113 PROT_VersionParser.prototype.processOldFormat_ = function(line) {
michael@0 114 if (line[0] != '[' || line.slice(-1) != ']')
michael@0 115 return false;
michael@0 116
michael@0 117 var description = line.slice(1, -1);
michael@0 118
michael@0 119 // Get the type name and version number of this table
michael@0 120 var tokens = description.split(" ");
michael@0 121 this.type = tokens[0];
michael@0 122 var majorminor = tokens[1].split(".");
michael@0 123 this.major = parseInt(majorminor[0]);
michael@0 124 this.minor = parseInt(majorminor[1]);
michael@0 125 if (isNaN(this.major) || isNaN(this.minor))
michael@0 126 return false;
michael@0 127
michael@0 128 if (tokens.length >= 3) {
michael@0 129 this.update = tokens[2] == "update";
michael@0 130 }
michael@0 131
michael@0 132 return true;
michael@0 133 }
michael@0 134
michael@0 135 /**
michael@0 136 * Takes a string like [name-of-table 1.1 [update]][mac=MAC] and figures out the
michael@0 137 * type and corresponding version numbers.
michael@0 138 * @returns true if the string could be parsed, false otherwise
michael@0 139 */
michael@0 140 PROT_VersionParser.prototype.fromString = function(line) {
michael@0 141 G_Debug(this, "Calling fromString with line: " + line);
michael@0 142 if (line[0] != '[' || line.slice(-1) != ']')
michael@0 143 return false;
michael@0 144
michael@0 145 // There could be two [][], so take care of it
michael@0 146 var secondBracket = line.indexOf('[', 1);
michael@0 147 var firstPart = null;
michael@0 148 var secondPart = null;
michael@0 149
michael@0 150 if (secondBracket != -1) {
michael@0 151 firstPart = line.substring(0, secondBracket);
michael@0 152 secondPart = line.substring(secondBracket);
michael@0 153 G_Debug(this, "First part: " + firstPart + " Second part: " + secondPart);
michael@0 154 } else {
michael@0 155 firstPart = line;
michael@0 156 G_Debug(this, "Old format: " + firstPart);
michael@0 157 }
michael@0 158
michael@0 159 if (!this.processOldFormat_(firstPart))
michael@0 160 return false;
michael@0 161
michael@0 162 if (secondPart && !this.processOptTokens_(secondPart))
michael@0 163 return false;
michael@0 164
michael@0 165 return true;
michael@0 166 }
michael@0 167
michael@0 168 /**
michael@0 169 * Process optional tokens
michael@0 170 *
michael@0 171 * @param line A string [token1=val1 token2=val2...]
michael@0 172 * @returns true if the string could be parsed, false otherwise
michael@0 173 */
michael@0 174 PROT_VersionParser.prototype.processOptTokens_ = function(line) {
michael@0 175 if (line[0] != '[' || line.slice(-1) != ']')
michael@0 176 return false;
michael@0 177 var description = line.slice(1, -1);
michael@0 178 // Get the type name and version number of this table
michael@0 179 var tokens = description.split(" ");
michael@0 180
michael@0 181 for (var i = 0; i < tokens.length; i++) {
michael@0 182 G_Debug(this, "Processing optional token: " + tokens[i]);
michael@0 183 var tokenparts = tokens[i].split("=");
michael@0 184 switch(tokenparts[0]){
michael@0 185 case "mac":
michael@0 186 this.mac = true;
michael@0 187 if (tokenparts.length < 2) {
michael@0 188 G_Debug(this, "Found mac flag but not mac value!");
michael@0 189 return false;
michael@0 190 }
michael@0 191 // The mac value may have "=" in it, so we can't just use tokenparts[1].
michael@0 192 // Instead, just take the rest of tokens[i] after the first "="
michael@0 193 this.macval = tokens[i].substr(tokens[i].indexOf("=")+1);
michael@0 194 break;
michael@0 195 default:
michael@0 196 G_Debug(this, "Found unrecognized token: " + tokenparts[0]);
michael@0 197 break;
michael@0 198 }
michael@0 199 }
michael@0 200
michael@0 201 return true;
michael@0 202 }
michael@0 203
michael@0 204 #ifdef DEBUG
michael@0 205 function TEST_PROT_WireFormat() {
michael@0 206 if (G_GDEBUG) {
michael@0 207 var z = "versionparser UNITTEST";
michael@0 208 G_Debug(z, "Starting");
michael@0 209
michael@0 210 var vp = new PROT_VersionParser("dummy");
michael@0 211 G_Assert(z, vp.fromString("[foo-bar-url 1.234]"),
michael@0 212 "failed to parse old format");
michael@0 213 G_Assert(z, "foo-bar-url" == vp.type, "failed to parse type");
michael@0 214 G_Assert(z, "1" == vp.major, "failed to parse major");
michael@0 215 G_Assert(z, "234" == vp.minor, "failed to parse minor");
michael@0 216
michael@0 217 vp = new PROT_VersionParser("dummy");
michael@0 218 G_Assert(z, vp.fromString("[foo-bar-url 1.234][mac=567]"),
michael@0 219 "failed to parse new format");
michael@0 220 G_Assert(z, "foo-bar-url" == vp.type, "failed to parse type");
michael@0 221 G_Assert(z, "1" == vp.major, "failed to parse major");
michael@0 222 G_Assert(z, "234" == vp.minor, "failed to parse minor");
michael@0 223 G_Assert(z, true == vp.mac, "failed to parse mac");
michael@0 224 G_Assert(z, "567" == vp.macval, "failed to parse macval");
michael@0 225
michael@0 226 G_Debug(z, "PASSED");
michael@0 227 }
michael@0 228 }
michael@0 229 #endif

mercurial