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, ¤t, &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