blob: f9e13160c25efee00a3c9b51064682e46bcd3dbb [file] [log] [blame]
[email protected]2cddef42013-11-22 08:23:221// Copyright 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
16namespace component_updater {
17
18static const char* kExpectedResponseProtocol = "3.0";
19
20UpdateManifest::UpdateManifest() {}
21UpdateManifest::~UpdateManifest() {}
22
23UpdateManifest::Results::Results() : daystart_elapsed_seconds(kNoDaystart) {}
24UpdateManifest::Results::~Results() {}
25
26UpdateManifest::Result::Result() {}
27
28UpdateManifest::Result::~Result() {}
29
30UpdateManifest::Result::Manifest::Manifest() {}
31UpdateManifest::Result::Manifest::~Manifest() {}
32
33UpdateManifest::Result::Manifest::Package::Package() : size(0), sizediff(0) {}
34UpdateManifest::Result::Manifest::Package::~Package() {}
35
36void 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|.
49static 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|.
54static 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.
66static 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.
80static 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.
89class 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.
106bool 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.
140bool 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>.
188bool 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.
224bool 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// Parses a single <app> tag.
252bool ParseAppTag(xmlNode* app,
253 UpdateManifest::Result* result,
254 std::string* error) {
255 // Read the crx id.
256 result->extension_id = GetAttribute(app, "appid");
257 if (result->extension_id.empty()) {
258 *error = "Missing appid on app node";
259 return false;
260 }
261
262 // Get the <updatecheck> tag.
263 std::vector<xmlNode*> updates = GetChildren(app, "updatecheck");
264 if (updates.empty()) {
265 *error = "Missing updatecheck on app.";
266 return false;
267 }
268
269 return ParseUpdateCheckTag(updates[0], result, error);
270}
271
272bool UpdateManifest::Parse(const std::string& manifest_xml) {
273 results_.daystart_elapsed_seconds = kNoDaystart;
274 results_.list.clear();
275 errors_.clear();
276
277 if (manifest_xml.length() < 1) {
278 ParseError("Empty xml");
279 return false;
280 }
281
282 std::string xml_errors;
283 ScopedXmlErrorFunc error_func(&xml_errors, &XmlErrorFunc);
284
285 // Start up the xml parser with the manifest_xml contents.
286 ScopedXmlDocument document(xmlParseDoc(
287 reinterpret_cast<const xmlChar*>(manifest_xml.c_str())));
288 if (!document.get()) {
289 ParseError("%s", xml_errors.c_str());
290 return false;
291 }
292
293 xmlNode* root = xmlDocGetRootElement(document.get());
294 if (!root) {
295 ParseError("Missing root node");
296 return false;
297 }
298
299 if (!TagNameEquals(root, "response")) {
300 ParseError("Missing response tag");
301 return false;
302 }
303
304 // Check for the response "protocol" attribute.
305 if (GetAttribute(root, "protocol") != kExpectedResponseProtocol) {
306 ParseError("Missing/incorrect protocol on response tag "
307 "(expected '%s')", kExpectedResponseProtocol);
308 return false;
309 }
310
311 // Parse the first <daystart> if it is present.
312 std::vector<xmlNode*> daystarts = GetChildren(root, "daystart");
313 if (!daystarts.empty()) {
314 xmlNode* first = daystarts[0];
315 std::string elapsed_seconds = GetAttribute(first, "elapsed_seconds");
316 int parsed_elapsed = kNoDaystart;
317 if (base::StringToInt(elapsed_seconds, &parsed_elapsed)) {
318 results_.daystart_elapsed_seconds = parsed_elapsed;
319 }
320 }
321
322 // Parse each of the <app> tags.
323 std::vector<xmlNode*> apps = GetChildren(root, "app");
324 for (size_t i = 0; i != apps.size(); ++i) {
325 Result result;
326 std::string error;
327 if (ParseAppTag(apps[i], &result, &error)) {
328 results_.list.push_back(result);
329 } else {
330 ParseError("%s", error.c_str());
331 }
332 }
333
334 return true;
335}
336
337} // namespace component_updater
338