1ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
28ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// Use of this source code is governed by a BSD-style license that can be
38ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// found in the LICENSE file.
48ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
58ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#include "chrome/common/extensions/update_manifest.h"
68ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
78ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#include <algorithm>
88ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
9ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/memory/scoped_ptr.h"
108ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#include "base/stl_util-inl.h"
118ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#include "base/string_util.h"
128ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#include "base/string_number_conversions.h"
1321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "base/stringprintf.h"
148ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#include "base/version.h"
158ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#include "chrome/common/libxml_utils.h"
168ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#include "libxml/tree.h"
178ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
188ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenstatic const char* kExpectedGupdateProtocol = "2.0";
198ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenstatic const char* kExpectedGupdateXmlns =
208ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    "http://www.google.com/update2/response";
218ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
2272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenUpdateManifest::Result::Result() {}
2372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
2472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenUpdateManifest::Result::~Result() {}
2572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
26731df977c0511bca2206b5f333555b1205ff1f43Iain MerrickUpdateManifest::Results::Results() : daystart_elapsed_seconds(kNoDaystart) {}
27731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
28731df977c0511bca2206b5f333555b1205ff1f43Iain MerrickUpdateManifest::Results::~Results() {}
29731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
308ae428e0fb7feea16d79853f29447469a93bedffKristian MonsenUpdateManifest::UpdateManifest() {
318ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
328ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
338ae428e0fb7feea16d79853f29447469a93bedffKristian MonsenUpdateManifest::~UpdateManifest() {}
348ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
358ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvoid UpdateManifest::ParseError(const char* details, ...) {
368ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  va_list args;
378ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  va_start(args, details);
388ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
398ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (errors_.length() > 0) {
408ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    // TODO(asargent) make a platform abstracted newline?
418ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    errors_ += "\r\n";
428ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
4321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  base::StringAppendV(&errors_, details, args);
448ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  va_end(args);
458ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
468ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
478ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// Checks whether a given node's name matches |expected_name| and
488ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// |expected_namespace|.
498ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenstatic bool TagNameEquals(const xmlNode* node, const char* expected_name,
508ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen                          const xmlNs* expected_namespace) {
518ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (node->ns != expected_namespace) {
528ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return false;
538ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
548ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  return 0 == strcmp(expected_name, reinterpret_cast<const char*>(node->name));
558ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
568ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
578ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// Returns child nodes of |root| with name |name| in namespace |xml_namespace|.
588ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenstatic std::vector<xmlNode*> GetChildren(xmlNode* root, xmlNs* xml_namespace,
598ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen                                         const char* name) {
608ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  std::vector<xmlNode*> result;
618ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  for (xmlNode* child = root->children; child != NULL; child = child->next) {
628ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    if (!TagNameEquals(child, name, xml_namespace)) {
638ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      continue;
648ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    }
658ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    result.push_back(child);
668ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
678ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  return result;
688ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
698ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
708ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// Returns the value of a named attribute, or the empty string.
718ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenstatic std::string GetAttribute(xmlNode* node, const char* attribute_name) {
728ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  const xmlChar* name = reinterpret_cast<const xmlChar*>(attribute_name);
738ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  for (xmlAttr* attr = node->properties; attr != NULL; attr = attr->next) {
748ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    if (!xmlStrcmp(attr->name, name) && attr->children &&
758ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen        attr->children->content) {
768ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      return std::string(reinterpret_cast<const char*>(
778ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen          attr->children->content));
788ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    }
798ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
808ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  return std::string();
818ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
828ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
838ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// This is used for the xml parser to report errors. This assumes the context
848ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// is a pointer to a std::string where the error message should be appended.
858ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenstatic void XmlErrorFunc(void *context, const char *message, ...) {
868ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  va_list args;
878ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  va_start(args, message);
888ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  std::string* error = static_cast<std::string*>(context);
8921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  base::StringAppendV(error, message, args);
908ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  va_end(args);
918ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
928ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
938ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// Utility class for cleaning up the xml document when leaving a scope.
948ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenclass ScopedXmlDocument {
958ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen public:
968ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  explicit ScopedXmlDocument(xmlDocPtr document) : document_(document) {}
978ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  ~ScopedXmlDocument() {
988ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    if (document_)
998ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      xmlFreeDoc(document_);
1008ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
1018ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1028ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  xmlDocPtr get() {
1038ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return document_;
1048ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
1058ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1068ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen private:
1078ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  xmlDocPtr document_;
1088ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen};
1098ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1108ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// Returns a pointer to the xmlNs on |node| with the |expected_href|, or
1118ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// NULL if there isn't one with that href.
1128ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenstatic xmlNs* GetNamespace(xmlNode* node, const char* expected_href) {
1138ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  const xmlChar* href = reinterpret_cast<const xmlChar*>(expected_href);
1148ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  for (xmlNs* ns = node->ns; ns != NULL; ns = ns->next) {
1158ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    if (ns->href && !xmlStrcmp(ns->href, href)) {
1168ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      return ns;
1178ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    }
1188ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
1198ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  return NULL;
1208ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
1218ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1228ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1238ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// Helper function that reads in values for a single <app> tag. It returns a
1248ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// boolean indicating success or failure. On failure, it writes a error message
1258ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// into |error_detail|.
1268ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenstatic bool ParseSingleAppTag(xmlNode* app_node, xmlNs* xml_namespace,
1278ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen                              UpdateManifest::Result* result,
1288ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen                              std::string *error_detail) {
1298ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // Read the extension id.
1308ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  result->extension_id = GetAttribute(app_node, "appid");
1318ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (result->extension_id.length() == 0) {
1328ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    *error_detail = "Missing appid on app node";
1338ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return false;
1348ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
1358ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1368ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // Get the updatecheck node.
1378ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  std::vector<xmlNode*> updates = GetChildren(app_node, xml_namespace,
1388ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen                                              "updatecheck");
1398ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (updates.size() > 1) {
1408ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    *error_detail = "Too many updatecheck tags on app (expecting only 1).";
1418ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return false;
1428ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
143dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  if (updates.empty()) {
1448ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    *error_detail = "Missing updatecheck on app.";
1458ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return false;
1468ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
1478ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  xmlNode *updatecheck = updates[0];
1488ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1498ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (GetAttribute(updatecheck, "status") == "noupdate") {
1508ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return true;
1518ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
1528ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1538ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // Find the url to the crx file.
1548ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  result->crx_url = GURL(GetAttribute(updatecheck, "codebase"));
1558ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (!result->crx_url.is_valid()) {
1568ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    *error_detail = "Invalid codebase url: '";
1578ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    *error_detail += GetAttribute(updatecheck, "codebase");
1588ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    *error_detail += "'.";
1598ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return false;
1608ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
1618ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1628ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // Get the version.
1638ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  result->version = GetAttribute(updatecheck, "version");
1648ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (result->version.length() == 0) {
1658ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    *error_detail = "Missing version for updatecheck.";
1668ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return false;
1678ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
1688ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  scoped_ptr<Version> version(Version::GetVersionFromString(result->version));
1698ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (!version.get()) {
1708ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    *error_detail = "Invalid version: '";
1718ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    *error_detail += result->version;
1728ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    *error_detail += "'.";
1738ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return false;
1748ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
1758ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1768ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // Get the minimum browser version (not required).
1778ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  result->browser_min_version = GetAttribute(updatecheck, "prodversionmin");
1788ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (result->browser_min_version.length()) {
1798ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    scoped_ptr<Version> browser_min_version(
1808ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      Version::GetVersionFromString(result->browser_min_version));
1818ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    if (!browser_min_version.get()) {
1828ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      *error_detail = "Invalid prodversionmin: '";
1838ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      *error_detail += result->browser_min_version;
1848ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      *error_detail += "'.";
1858ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      return false;
1868ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    }
1878ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
1888ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1898ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // package_hash is optional. It is only required for blacklist. It is a
1908ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // sha256 hash of the package in hex format.
1918ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  result->package_hash = GetAttribute(updatecheck, "hash");
1928ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1938ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  return true;
1948ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
1958ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1968ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1978ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenbool UpdateManifest::Parse(const std::string& manifest_xml) {
1988ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  results_.list.resize(0);
1998ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  results_.daystart_elapsed_seconds = kNoDaystart;
2008ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  errors_ = "";
2018ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
2028ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (manifest_xml.length() < 1) {
2038ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen     ParseError("Empty xml");
2048ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return false;
2058ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
2068ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
2078ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  std::string xml_errors;
2088ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  ScopedXmlErrorFunc error_func(&xml_errors, &XmlErrorFunc);
2098ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
2108ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // Start up the xml parser with the manifest_xml contents.
2118ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  ScopedXmlDocument document(xmlParseDoc(
2128ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      reinterpret_cast<const xmlChar*>(manifest_xml.c_str())));
2138ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (!document.get()) {
2148ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    ParseError("%s", xml_errors.c_str());
2158ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return false;
2168ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
2178ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
2188ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  xmlNode *root = xmlDocGetRootElement(document.get());
2198ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (!root) {
2208ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    ParseError("Missing root node");
2218ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return false;
2228ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
2238ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
2248ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // Look for the required namespace declaration.
2258ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  xmlNs* gupdate_ns = GetNamespace(root, kExpectedGupdateXmlns);
2268ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (!gupdate_ns) {
2278ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    ParseError("Missing or incorrect xmlns on gupdate tag");
2288ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return false;
2298ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
2308ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
2318ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (!TagNameEquals(root, "gupdate", gupdate_ns)) {
2328ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    ParseError("Missing gupdate tag");
2338ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return false;
2348ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
2358ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
2368ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // Check for the gupdate "protocol" attribute.
2378ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (GetAttribute(root, "protocol") != kExpectedGupdateProtocol) {
2388ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    ParseError("Missing/incorrect protocol on gupdate tag "
2398ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen        "(expected '%s')", kExpectedGupdateProtocol);
2408ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return false;
2418ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
2428ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
2438ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // Parse the first <daystart> if it's present.
2448ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  std::vector<xmlNode*> daystarts = GetChildren(root, gupdate_ns, "daystart");
245dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  if (!daystarts.empty()) {
2468ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    xmlNode* first = daystarts[0];
2478ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    std::string elapsed_seconds = GetAttribute(first, "elapsed_seconds");
2488ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    int parsed_elapsed = kNoDaystart;
2498ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    if (base::StringToInt(elapsed_seconds, &parsed_elapsed)) {
2508ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      results_.daystart_elapsed_seconds = parsed_elapsed;
2518ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    }
2528ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
2538ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
2548ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // Parse each of the <app> tags.
2558ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  std::vector<xmlNode*> apps = GetChildren(root, gupdate_ns, "app");
2568ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  for (unsigned int i = 0; i < apps.size(); i++) {
2578ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    Result current;
2588ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    std::string error;
2598ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    if (!ParseSingleAppTag(apps[i], gupdate_ns, &current, &error)) {
2608ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      ParseError("%s", error.c_str());
2618ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    } else {
2628ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      results_.list.push_back(current);
2638ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    }
2648ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
2658ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
2668ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  return true;
2678ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
268