Upstream version 10.38.222.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / component_updater / update_response.cc
1 // 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_response.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 UpdateResponse::UpdateResponse() {}
21 UpdateResponse::~UpdateResponse() {}
22
23 UpdateResponse::Results::Results() : daystart_elapsed_seconds(kNoDaystart) {}
24 UpdateResponse::Results::~Results() {}
25
26 UpdateResponse::Result::Result() {}
27
28 UpdateResponse::Result::~Result() {}
29
30 UpdateResponse::Result::Manifest::Manifest() {}
31 UpdateResponse::Result::Manifest::~Manifest() {}
32
33 UpdateResponse::Result::Manifest::Package::Package() : size(0), sizediff(0) {}
34 UpdateResponse::Result::Manifest::Package::~Package() {}
35
36 void UpdateResponse::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(
72           reinterpret_cast<const char*>(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() { return document_; }
98
99  private:
100   xmlDocPtr document_;
101 };
102
103 // Parses the <package> tag.
104 bool ParsePackageTag(xmlNode* package,
105                      UpdateResponse::Result* result,
106                      std::string* error) {
107   UpdateResponse::Result::Manifest::Package p;
108   p.name = GetAttribute(package, "name");
109   if (p.name.empty()) {
110     *error = "Missing name for package.";
111     return false;
112   }
113
114   p.namediff = GetAttribute(package, "namediff");
115
116   // package_fingerprint is optional. It identifies the package, preferably
117   // with a modified sha256 hash of the package in hex format.
118   p.fingerprint = GetAttribute(package, "fp");
119
120   p.hash_sha256 = GetAttribute(package, "hash_sha256");
121   int size = 0;
122   if (base::StringToInt(GetAttribute(package, "size"), &size)) {
123     p.size = size;
124   }
125
126   p.hashdiff_sha256 = GetAttribute(package, "hashdiff_sha256");
127   int sizediff = 0;
128   if (base::StringToInt(GetAttribute(package, "sizediff"), &sizediff)) {
129     p.sizediff = sizediff;
130   }
131
132   result->manifest.packages.push_back(p);
133
134   return true;
135 }
136
137 // Parses the <manifest> tag.
138 bool ParseManifestTag(xmlNode* manifest,
139                       UpdateResponse::Result* result,
140                       std::string* error) {
141   // Get the version.
142   result->manifest.version = GetAttribute(manifest, "version");
143   if (result->manifest.version.empty()) {
144     *error = "Missing version for manifest.";
145     return false;
146   }
147   Version version(result->manifest.version);
148   if (!version.IsValid()) {
149     *error = "Invalid version: '";
150     *error += result->manifest.version;
151     *error += "'.";
152     return false;
153   }
154
155   // Get the minimum browser version (not required).
156   result->manifest.browser_min_version =
157       GetAttribute(manifest, "prodversionmin");
158   if (result->manifest.browser_min_version.length()) {
159     Version browser_min_version(result->manifest.browser_min_version);
160     if (!browser_min_version.IsValid()) {
161       *error = "Invalid prodversionmin: '";
162       *error += result->manifest.browser_min_version;
163       *error += "'.";
164       return false;
165     }
166   }
167
168   // Get the <packages> node.
169   std::vector<xmlNode*> packages = GetChildren(manifest, "packages");
170   if (packages.empty()) {
171     *error = "Missing packages tag on manifest.";
172     return false;
173   }
174
175   // Parse each of the <package> tags.
176   std::vector<xmlNode*> package = GetChildren(packages[0], "package");
177   for (size_t i = 0; i != package.size(); ++i) {
178     if (!ParsePackageTag(package[i], result, error))
179       return false;
180   }
181
182   return true;
183 }
184
185 // Parses the <urls> tag and its children in the <updatecheck>.
186 bool ParseUrlsTag(xmlNode* urls,
187                   UpdateResponse::Result* result,
188                   std::string* error) {
189   // Get the url nodes.
190   std::vector<xmlNode*> url = GetChildren(urls, "url");
191   if (url.empty()) {
192     *error = "Missing url tags on urls.";
193     return false;
194   }
195
196   // Get the list of urls for full and optionally, for diff updates.
197   // There can only be either a codebase or a codebasediff attribute in a tag.
198   for (size_t i = 0; i != url.size(); ++i) {
199     // Find the url to the crx file.
200     const GURL crx_url(GetAttribute(url[i], "codebase"));
201     if (crx_url.is_valid()) {
202       result->crx_urls.push_back(crx_url);
203       continue;
204     }
205     const GURL crx_diffurl(GetAttribute(url[i], "codebasediff"));
206     if (crx_diffurl.is_valid()) {
207       result->crx_diffurls.push_back(crx_diffurl);
208       continue;
209     }
210   }
211
212   // Expect at least one url for full update.
213   if (result->crx_urls.empty()) {
214     *error = "Missing valid url for full update.";
215     return false;
216   }
217
218   return true;
219 }
220
221 // Parses the <updatecheck> tag.
222 bool ParseUpdateCheckTag(xmlNode* updatecheck,
223                          UpdateResponse::Result* result,
224                          std::string* error) {
225   if (GetAttribute(updatecheck, "status") == "noupdate") {
226     return true;
227   }
228
229   // Get the <urls> tag.
230   std::vector<xmlNode*> urls = GetChildren(updatecheck, "urls");
231   if (urls.empty()) {
232     *error = "Missing urls on updatecheck.";
233     return false;
234   }
235
236   if (!ParseUrlsTag(urls[0], result, error)) {
237     return false;
238   }
239
240   std::vector<xmlNode*> manifests = GetChildren(updatecheck, "manifest");
241   if (urls.empty()) {
242     *error = "Missing urls on updatecheck.";
243     return false;
244   }
245
246   return ParseManifestTag(manifests[0], result, error);
247 }
248
249 // Parses a single <app> tag.
250 bool ParseAppTag(xmlNode* app,
251                  UpdateResponse::Result* result,
252                  std::string* error) {
253   // Read the crx id.
254   result->extension_id = GetAttribute(app, "appid");
255   if (result->extension_id.empty()) {
256     *error = "Missing appid on app node";
257     return false;
258   }
259
260   // Get the <updatecheck> tag.
261   std::vector<xmlNode*> updates = GetChildren(app, "updatecheck");
262   if (updates.empty()) {
263     *error = "Missing updatecheck on app.";
264     return false;
265   }
266
267   return ParseUpdateCheckTag(updates[0], result, error);
268 }
269
270 bool UpdateResponse::Parse(const std::string& response_xml) {
271   results_.daystart_elapsed_seconds = kNoDaystart;
272   results_.list.clear();
273   errors_.clear();
274
275   if (response_xml.length() < 1) {
276     ParseError("Empty xml");
277     return false;
278   }
279
280   std::string xml_errors;
281   ScopedXmlErrorFunc error_func(&xml_errors, &XmlErrorFunc);
282
283   // Start up the xml parser with the manifest_xml contents.
284   ScopedXmlDocument document(
285       xmlParseDoc(reinterpret_cast<const xmlChar*>(response_xml.c_str())));
286   if (!document.get()) {
287     ParseError("%s", xml_errors.c_str());
288     return false;
289   }
290
291   xmlNode* root = xmlDocGetRootElement(document.get());
292   if (!root) {
293     ParseError("Missing root node");
294     return false;
295   }
296
297   if (!TagNameEquals(root, "response")) {
298     ParseError("Missing response tag");
299     return false;
300   }
301
302   // Check for the response "protocol" attribute.
303   if (GetAttribute(root, "protocol") != kExpectedResponseProtocol) {
304     ParseError(
305         "Missing/incorrect protocol on response tag "
306         "(expected '%s')",
307         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