|
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/. |
|
4 |
|
5 |
|
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. |
|
24 |
|
25 |
|
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; |
|
44 |
|
45 this.badHeader = false; |
|
46 |
|
47 // Should the wireformatreader compute a mac? |
|
48 this.mac = false; |
|
49 this.macval = ""; |
|
50 this.macFailed = false; |
|
51 this.requireMac = !!opt_requireMac; |
|
52 |
|
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 } |
|
64 |
|
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; |
|
71 |
|
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 } |
|
78 |
|
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 } |
|
88 |
|
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 } |
|
97 |
|
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 } |
|
107 |
|
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; |
|
116 |
|
117 var description = line.slice(1, -1); |
|
118 |
|
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; |
|
127 |
|
128 if (tokens.length >= 3) { |
|
129 this.update = tokens[2] == "update"; |
|
130 } |
|
131 |
|
132 return true; |
|
133 } |
|
134 |
|
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; |
|
144 |
|
145 // There could be two [][], so take care of it |
|
146 var secondBracket = line.indexOf('[', 1); |
|
147 var firstPart = null; |
|
148 var secondPart = null; |
|
149 |
|
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 } |
|
158 |
|
159 if (!this.processOldFormat_(firstPart)) |
|
160 return false; |
|
161 |
|
162 if (secondPart && !this.processOptTokens_(secondPart)) |
|
163 return false; |
|
164 |
|
165 return true; |
|
166 } |
|
167 |
|
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(" "); |
|
180 |
|
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 } |
|
200 |
|
201 return true; |
|
202 } |
|
203 |
|
204 #ifdef DEBUG |
|
205 function TEST_PROT_WireFormat() { |
|
206 if (G_GDEBUG) { |
|
207 var z = "versionparser UNITTEST"; |
|
208 G_Debug(z, "Starting"); |
|
209 |
|
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"); |
|
216 |
|
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"); |
|
225 |
|
226 G_Debug(z, "PASSED"); |
|
227 } |
|
228 } |
|
229 #endif |