diff options
Diffstat (limited to 'toolkit/components/url-classifier/content/wireformat.js')
-rw-r--r-- | toolkit/components/url-classifier/content/wireformat.js | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/toolkit/components/url-classifier/content/wireformat.js b/toolkit/components/url-classifier/content/wireformat.js new file mode 100644 index 000000000..a24b120e6 --- /dev/null +++ b/toolkit/components/url-classifier/content/wireformat.js @@ -0,0 +1,230 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +// A class that serializes and deserializes opaque key/value string to +// string maps to/from maps (trtables). It knows how to create +// trtables from the serialized format, so it also understands +// meta-information like the name of the table and the table's +// version. See docs for the protocol description. +// +// TODO: wireformatreader: if you have multiple updates for one table +// in a call to deserialize, the later ones will be merged +// (all but the last will be ignored). To fix, merge instead +// of replace when you have an existing table, and only do so once. +// TODO must have blank line between successive types -- problem? +// TODO doesn't tolerate blank lines very well +// +// Maybe: These classes could use a LOT more cleanup, but it's not a +// priority at the moment. For example, the tablesData/Known +// maps should be combined into a single object, the parser +// for a given type should be separate from the version info, +// and there should be synchronous interfaces for testing. + + +/** + * A class that knows how to serialize and deserialize meta-information. + * This meta information is the table name and version number, and + * in its serialized form looks like the first line below: + * + * [name-of-table X.Y update?] + * ...key/value pairs to add or delete follow... + * <blank line ends the table> + * + * The X.Y is the version number and the optional "update" token means + * that the table is a differential from the curent table the extension + * has. Its absence means that this is a full, new table. + */ +this.PROT_VersionParser = +function PROT_VersionParser(type, opt_major, opt_minor, opt_requireMac) { + this.debugZone = "versionparser"; + this.type = type; + this.major = 0; + this.minor = 0; + + this.badHeader = false; + + // Should the wireformatreader compute a mac? + this.mac = false; + this.macval = ""; + this.macFailed = false; + this.requireMac = !!opt_requireMac; + + this.update = false; + this.needsUpdate = false; // used by ListManager to determine update policy + // Used by ListerManager to see if we have read data for this table from + // disk. Once we read a table from disk, we are not going to do so again + // but instead update remotely if necessary. + this.didRead = false; + if (opt_major) + this.major = parseInt(opt_major); + if (opt_minor) + this.minor = parseInt(opt_minor); +} + +/** Import the version information from another VersionParser + * @params version a version parser object + */ +PROT_VersionParser.prototype.ImportVersion = function(version) { + this.major = version.major; + this.minor = version.minor; + + this.mac = version.mac; + this.macFailed = version.macFailed; + this.macval = version.macval; + // Don't set requireMac, since we create vparsers from scratch and doesn't + // know about it +} + +/** + * Creates a string like [goog-white-black 1.1] from internal information + * + * @returns String + */ +PROT_VersionParser.prototype.toString = function() { + var s = "[" + this.type + " " + this.major + "." + this.minor + "]"; + return s; +} + +/** + * Creates a string like 1.123 with the version number. This is the + * format we store in prefs. + * @return String + */ +PROT_VersionParser.prototype.versionString = function() { + return this.major + "." + this.minor; +} + +/** + * Creates a string like 1:1 from internal information used for + * fetching updates from the server. Called by the listmanager. + * + * @returns String + */ +PROT_VersionParser.prototype.toUrl = function() { + return this.major + ":" + this.minor; +} + +/** + * Process the old format, [type major.minor [update]] + * + * @returns true if the string could be parsed, false otherwise + */ +PROT_VersionParser.prototype.processOldFormat_ = function(line) { + if (line[0] != '[' || line.slice(-1) != ']') + return false; + + var description = line.slice(1, -1); + + // Get the type name and version number of this table + var tokens = description.split(" "); + this.type = tokens[0]; + var majorminor = tokens[1].split("."); + this.major = parseInt(majorminor[0]); + this.minor = parseInt(majorminor[1]); + if (isNaN(this.major) || isNaN(this.minor)) + return false; + + if (tokens.length >= 3) { + this.update = tokens[2] == "update"; + } + + return true; +} + +/** + * Takes a string like [name-of-table 1.1 [update]][mac=MAC] and figures out the + * type and corresponding version numbers. + * @returns true if the string could be parsed, false otherwise + */ +PROT_VersionParser.prototype.fromString = function(line) { + G_Debug(this, "Calling fromString with line: " + line); + if (line[0] != '[' || line.slice(-1) != ']') + return false; + + // There could be two [][], so take care of it + var secondBracket = line.indexOf('[', 1); + var firstPart = null; + var secondPart = null; + + if (secondBracket != -1) { + firstPart = line.substring(0, secondBracket); + secondPart = line.substring(secondBracket); + G_Debug(this, "First part: " + firstPart + " Second part: " + secondPart); + } else { + firstPart = line; + G_Debug(this, "Old format: " + firstPart); + } + + if (!this.processOldFormat_(firstPart)) + return false; + + if (secondPart && !this.processOptTokens_(secondPart)) + return false; + + return true; +} + +/** + * Process optional tokens + * + * @param line A string [token1=val1 token2=val2...] + * @returns true if the string could be parsed, false otherwise + */ +PROT_VersionParser.prototype.processOptTokens_ = function(line) { + if (line[0] != '[' || line.slice(-1) != ']') + return false; + var description = line.slice(1, -1); + // Get the type name and version number of this table + var tokens = description.split(" "); + + for (var i = 0; i < tokens.length; i++) { + G_Debug(this, "Processing optional token: " + tokens[i]); + var tokenparts = tokens[i].split("="); + switch(tokenparts[0]){ + case "mac": + this.mac = true; + if (tokenparts.length < 2) { + G_Debug(this, "Found mac flag but not mac value!"); + return false; + } + // The mac value may have "=" in it, so we can't just use tokenparts[1]. + // Instead, just take the rest of tokens[i] after the first "=" + this.macval = tokens[i].substr(tokens[i].indexOf("=")+1); + break; + default: + G_Debug(this, "Found unrecognized token: " + tokenparts[0]); + break; + } + } + + return true; +} + +#ifdef DEBUG +this.TEST_PROT_WireFormat = function TEST_PROT_WireFormat() { + if (G_GDEBUG) { + var z = "versionparser UNITTEST"; + G_Debug(z, "Starting"); + + var vp = new PROT_VersionParser("dummy"); + G_Assert(z, vp.fromString("[foo-bar-url 1.234]"), + "failed to parse old format"); + G_Assert(z, "foo-bar-url" == vp.type, "failed to parse type"); + G_Assert(z, "1" == vp.major, "failed to parse major"); + G_Assert(z, "234" == vp.minor, "failed to parse minor"); + + vp = new PROT_VersionParser("dummy"); + G_Assert(z, vp.fromString("[foo-bar-url 1.234][mac=567]"), + "failed to parse new format"); + G_Assert(z, "foo-bar-url" == vp.type, "failed to parse type"); + G_Assert(z, "1" == vp.major, "failed to parse major"); + G_Assert(z, "234" == vp.minor, "failed to parse minor"); + G_Assert(z, true == vp.mac, "failed to parse mac"); + G_Assert(z, "567" == vp.macval, "failed to parse macval"); + + G_Debug(z, "PASSED"); + } +} +#endif |