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