OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chrome/browser/component_updater/update_manifest.h" | |
6 #include <algorithm> | |
7 #include "base/memory/scoped_ptr.h" | |
8 #include "base/stl_util.h" | |
9 #include "base/strings/string_number_conversions.h" | |
10 #include "base/strings/string_util.h" | |
11 #include "base/strings/stringprintf.h" | |
12 #include "base/version.h" | |
13 #include "libxml/tree.h" | |
14 #include "third_party/libxml/chromium/libxml_utils.h" | |
15 | |
16 namespace component_updater { | |
17 | |
18 static const char* kExpectedResponseProtocol = "3.0"; | |
19 | |
20 UpdateManifest::UpdateManifest() {} | |
21 UpdateManifest::~UpdateManifest() {} | |
22 | |
23 UpdateManifest::Results::Results() : daystart_elapsed_seconds(kNoDaystart) {} | |
24 UpdateManifest::Results::~Results() {} | |
25 | |
26 UpdateManifest::Result::Result() {} | |
27 | |
28 UpdateManifest::Result::~Result() {} | |
29 | |
30 UpdateManifest::Result::Manifest::Manifest() {} | |
31 UpdateManifest::Result::Manifest::~Manifest() {} | |
32 | |
33 UpdateManifest::Result::Manifest::Package::Package() : size(0), sizediff(0) {} | |
34 UpdateManifest::Result::Manifest::Package::~Package() {} | |
35 | |
36 void UpdateManifest::ParseError(const char* details, ...) { | |
37 va_list args; | |
38 va_start(args, details); | |
39 | |
40 if (!errors_.empty()) { | |
41 errors_ += "\r\n"; | |
42 } | |
43 | |
44 base::StringAppendV(&errors_, details, args); | |
45 va_end(args); | |
46 } | |
47 | |
48 // Checks whether a given node's name matches |expected_name|. | |
49 static bool TagNameEquals(const xmlNode* node, const char* expected_name) { | |
50 return 0 == strcmp(expected_name, reinterpret_cast<const char*>(node->name)); | |
51 } | |
52 | |
53 // Returns child nodes of |root| with name |name|. | |
54 static std::vector<xmlNode*> GetChildren(xmlNode* root, const char* name) { | |
55 std::vector<xmlNode*> result; | |
56 for (xmlNode* child = root->children; child != NULL; child = child->next) { | |
57 if (!TagNameEquals(child, name)) { | |
58 continue; | |
59 } | |
60 result.push_back(child); | |
61 } | |
62 return result; | |
63 } | |
64 | |
65 // Returns the value of a named attribute, or the empty string. | |
66 static std::string GetAttribute(xmlNode* node, const char* attribute_name) { | |
67 const xmlChar* name = reinterpret_cast<const xmlChar*>(attribute_name); | |
68 for (xmlAttr* attr = node->properties; attr != NULL; attr = attr->next) { | |
69 if (!xmlStrcmp(attr->name, name) && attr->children && | |
70 attr->children->content) { | |
71 return std::string(reinterpret_cast<const char*>( | |
72 attr->children->content)); | |
73 } | |
74 } | |
75 return std::string(); | |
76 } | |
77 | |
78 // This is used for the xml parser to report errors. This assumes the context | |
79 // is a pointer to a std::string where the error message should be appended. | |
80 static void XmlErrorFunc(void *context, const char *message, ...) { | |
81 va_list args; | |
82 va_start(args, message); | |
83 std::string* error = static_cast<std::string*>(context); | |
84 base::StringAppendV(error, message, args); | |
85 va_end(args); | |
86 } | |
87 | |
88 // Utility class for cleaning up the xml document when leaving a scope. | |
89 class ScopedXmlDocument { | |
90 public: | |
91 explicit ScopedXmlDocument(xmlDocPtr document) : document_(document) {} | |
92 ~ScopedXmlDocument() { | |
93 if (document_) | |
94 xmlFreeDoc(document_); | |
95 } | |
96 | |
97 xmlDocPtr get() { | |
98 return document_; | |
99 } | |
100 | |
101 private: | |
102 xmlDocPtr document_; | |
103 }; | |
104 | |
105 // Parses the <package> tag. | |
106 bool ParsePackageTag(xmlNode* package, | |
107 UpdateManifest::Result* result, | |
108 std::string* error) { | |
109 UpdateManifest::Result::Manifest::Package p; | |
110 p.name = GetAttribute(package, "name"); | |
111 if (p.name.empty()) { | |
112 *error = "Missing name for package."; | |
113 return false; | |
114 } | |
115 | |
116 p.namediff = GetAttribute(package, "namediff"); | |
117 | |
118 // package_fingerprint is optional. It identifies the package, preferably | |
119 // with a modified sha256 hash of the package in hex format. | |
120 p.fingerprint = GetAttribute(package, "fp"); | |
121 | |
122 p.hash_sha256 = GetAttribute(package, "hash_sha256"); | |
123 int size = 0; | |
124 if (base::StringToInt(GetAttribute(package, "size"), &size)) { | |
125 p.size = size; | |
126 } | |
127 | |
128 p.hashdiff_sha256 = GetAttribute(package, "hashdiff_sha256"); | |
129 int sizediff = 0; | |
130 if (base::StringToInt(GetAttribute(package, "sizediff"), &sizediff)) { | |
131 p.sizediff = sizediff; | |
132 } | |
133 | |
134 result->manifest.packages.push_back(p); | |
135 | |
136 return true; | |
137 } | |
138 | |
139 // Parses the <manifest> tag. | |
140 bool ParseManifestTag(xmlNode* manifest, | |
141 UpdateManifest::Result* result, | |
142 std::string* error) { | |
143 // Get the version. | |
144 result->manifest.version = GetAttribute(manifest, "version"); | |
145 if (result->manifest.version.empty()) { | |
146 *error = "Missing version for manifest."; | |
147 return false; | |
148 } | |
149 Version version(result->manifest.version); | |
150 if (!version.IsValid()) { | |
151 *error = "Invalid version: '"; | |
152 *error += result->manifest.version; | |
153 *error += "'."; | |
154 return false; | |
155 } | |
156 | |
157 // Get the minimum browser version (not required). | |
158 result->manifest.browser_min_version = | |
159 GetAttribute(manifest, "prodversionmin"); | |
160 if (result->manifest.browser_min_version.length()) { | |
161 Version browser_min_version(result->manifest.browser_min_version); | |
162 if (!browser_min_version.IsValid()) { | |
163 *error = "Invalid prodversionmin: '"; | |
164 *error += result->manifest.browser_min_version; | |
165 *error += "'."; | |
166 return false; | |
167 } | |
168 } | |
169 | |
170 // Get the <packages> node. | |
171 std::vector<xmlNode*> packages = GetChildren(manifest, "packages"); | |
172 if (packages.empty()) { | |
173 *error = "Missing packages tag on manifest."; | |
174 return false; | |
175 } | |
176 | |
177 // Parse each of the <package> tags. | |
178 std::vector<xmlNode*> package = GetChildren(packages[0], "package"); | |
179 for (size_t i = 0; i != package.size(); ++i) { | |
180 if (!ParsePackageTag(package[i], result, error)) | |
181 return false; | |
182 } | |
183 | |
184 return true; | |
185 } | |
186 | |
187 // Parses the <urls> tag and its children in the <updatecheck>. | |
188 bool ParseUrlsTag(xmlNode* urls, | |
189 UpdateManifest::Result* result, | |
190 std::string* error) { | |
191 // Get the url nodes. | |
192 std::vector<xmlNode*> url = GetChildren(urls, "url"); | |
193 if (url.empty()) { | |
194 *error = "Missing url tags on urls."; | |
195 return false; | |
196 } | |
197 | |
198 // Get the list of urls for full and optionally, for diff updates. | |
199 // There can only be either a codebase or a codebasediff attribute in a tag. | |
200 for (size_t i = 0; i != url.size(); ++i) { | |
201 // Find the url to the crx file. | |
202 const GURL crx_url(GetAttribute(url[i], "codebase")); | |
203 if (crx_url.is_valid()) { | |
204 result->crx_urls.push_back(crx_url); | |
205 continue; | |
206 } | |
207 const GURL crx_diffurl(GetAttribute(url[i], "codebasediff")); | |
208 if (crx_diffurl.is_valid()) { | |
209 result->crx_diffurls.push_back(crx_diffurl); | |
210 continue; | |
211 } | |
212 } | |
213 | |
214 // Expect at least one url for full update. | |
215 if (result->crx_urls.empty()) { | |
216 *error = "Missing valid url for full update."; | |
217 return false; | |
218 } | |
219 | |
220 return true; | |
221 } | |
222 | |
223 // Parses the <updatecheck> tag. | |
224 bool ParseUpdateCheckTag(xmlNode* updatecheck, | |
225 UpdateManifest::Result* result, | |
226 std::string* error) { | |
227 if (GetAttribute(updatecheck, "status") == "noupdate") { | |
228 return true; | |
229 } | |
230 | |
231 // Get the <urls> tag. | |
232 std::vector<xmlNode*> urls = GetChildren(updatecheck, "urls"); | |
233 if (urls.empty()) { | |
234 *error = "Missing urls on updatecheck."; | |
235 return false; | |
236 } | |
237 | |
238 if (!ParseUrlsTag(urls[0], result, error)) { | |
239 return false; | |
240 } | |
241 | |
242 std::vector<xmlNode*> manifests = GetChildren(updatecheck, "manifest"); | |
243 if (urls.empty()) { | |
244 *error = "Missing urls on updatecheck."; | |
245 return false; | |
246 } | |
247 | |
248 return ParseManifestTag(manifests[0], result, error); | |
249 } | |
250 | |
251 | |
waffles
2013/11/18 22:15:59
Extra newline here.
Sorin Jianu
2013/11/19 04:25:45
Done.
| |
252 // Parses a single <app> tag. | |
253 bool ParseAppTag(xmlNode* app, | |
254 UpdateManifest::Result* result, | |
255 std::string* error) { | |
256 // Read the crx id. | |
257 result->extension_id = GetAttribute(app, "appid"); | |
258 if (result->extension_id.empty()) { | |
259 *error = "Missing appid on app node"; | |
260 return false; | |
261 } | |
262 | |
263 // Get the <updatecheck> tag. | |
264 std::vector<xmlNode*> updates = GetChildren(app, "updatecheck"); | |
265 if (updates.empty()) { | |
266 *error = "Missing updatecheck on app."; | |
267 return false; | |
268 } | |
269 | |
270 return ParseUpdateCheckTag(updates[0], result, error); | |
271 } | |
272 | |
273 bool UpdateManifest::Parse(const std::string& manifest_xml) { | |
274 results_.daystart_elapsed_seconds = kNoDaystart; | |
275 results_.list.clear(); | |
276 errors_.clear(); | |
277 | |
278 if (manifest_xml.length() < 1) { | |
279 ParseError("Empty xml"); | |
280 return false; | |
281 } | |
282 | |
283 std::string xml_errors; | |
284 ScopedXmlErrorFunc error_func(&xml_errors, &XmlErrorFunc); | |
285 | |
286 // Start up the xml parser with the manifest_xml contents. | |
287 ScopedXmlDocument document(xmlParseDoc( | |
288 reinterpret_cast<const xmlChar*>(manifest_xml.c_str()))); | |
289 if (!document.get()) { | |
290 ParseError("%s", xml_errors.c_str()); | |
291 return false; | |
292 } | |
293 | |
294 xmlNode *root = xmlDocGetRootElement(document.get()); | |
295 if (!root) { | |
296 ParseError("Missing root node"); | |
297 return false; | |
298 } | |
299 | |
300 if (!TagNameEquals(root, "response")) { | |
301 ParseError("Missing response tag"); | |
302 return false; | |
303 } | |
304 | |
305 // Check for the response "protocol" attribute. | |
306 if (GetAttribute(root, "protocol") != kExpectedResponseProtocol) { | |
307 ParseError("Missing/incorrect protocol on response tag " | |
308 "(expected '%s')", kExpectedResponseProtocol); | |
309 return false; | |
310 } | |
311 | |
312 // Parse the first <daystart> if it is present. | |
313 std::vector<xmlNode*> daystarts = GetChildren(root, "daystart"); | |
314 if (!daystarts.empty()) { | |
315 xmlNode* first = daystarts[0]; | |
316 std::string elapsed_seconds = GetAttribute(first, "elapsed_seconds"); | |
317 int parsed_elapsed = kNoDaystart; | |
318 if (base::StringToInt(elapsed_seconds, &parsed_elapsed)) { | |
319 results_.daystart_elapsed_seconds = parsed_elapsed; | |
320 } | |
321 } | |
322 | |
323 // Parse each of the <app> tags. | |
324 std::vector<xmlNode*> apps = GetChildren(root, "app"); | |
325 for (size_t i = 0; i != apps.size(); ++i) { | |
326 Result result; | |
327 std::string error; | |
328 if (ParseAppTag(apps[i], &result, &error)) { | |
329 results_.list.push_back(result); | |
330 } else { | |
331 ParseError("%s", error.c_str()); | |
332 } | |
333 } | |
334 | |
335 return true; | |
336 } | |
337 | |
338 } // namespace component_updater | |
OLD | NEW |