diff options
Diffstat (limited to 'chrome/browser/component_updater/update_response.cc')
-rw-r--r-- | chrome/browser/component_updater/update_response.cc | 338 |
1 files changed, 338 insertions, 0 deletions
diff --git a/chrome/browser/component_updater/update_response.cc b/chrome/browser/component_updater/update_response.cc new file mode 100644 index 0000000..cf93658 --- /dev/null +++ b/chrome/browser/component_updater/update_response.cc @@ -0,0 +1,338 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/component_updater/update_response.h" +#include <algorithm> +#include "base/memory/scoped_ptr.h" +#include "base/stl_util.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "base/version.h" +#include "libxml/tree.h" +#include "third_party/libxml/chromium/libxml_utils.h" + +namespace component_updater { + +static const char* kExpectedResponseProtocol = "3.0"; + +UpdateResponse::UpdateResponse() {} +UpdateResponse::~UpdateResponse() {} + +UpdateResponse::Results::Results() : daystart_elapsed_seconds(kNoDaystart) {} +UpdateResponse::Results::~Results() {} + +UpdateResponse::Result::Result() {} + +UpdateResponse::Result::~Result() {} + +UpdateResponse::Result::Manifest::Manifest() {} +UpdateResponse::Result::Manifest::~Manifest() {} + +UpdateResponse::Result::Manifest::Package::Package() : size(0), sizediff(0) {} +UpdateResponse::Result::Manifest::Package::~Package() {} + +void UpdateResponse::ParseError(const char* details, ...) { + va_list args; + va_start(args, details); + + if (!errors_.empty()) { + errors_ += "\r\n"; + } + + base::StringAppendV(&errors_, details, args); + va_end(args); +} + +// Checks whether a given node's name matches |expected_name|. +static bool TagNameEquals(const xmlNode* node, const char* expected_name) { + return 0 == strcmp(expected_name, reinterpret_cast<const char*>(node->name)); +} + +// Returns child nodes of |root| with name |name|. +static std::vector<xmlNode*> GetChildren(xmlNode* root, const char* name) { + std::vector<xmlNode*> result; + for (xmlNode* child = root->children; child != NULL; child = child->next) { + if (!TagNameEquals(child, name)) { + continue; + } + result.push_back(child); + } + return result; +} + +// Returns the value of a named attribute, or the empty string. +static std::string GetAttribute(xmlNode* node, const char* attribute_name) { + const xmlChar* name = reinterpret_cast<const xmlChar*>(attribute_name); + for (xmlAttr* attr = node->properties; attr != NULL; attr = attr->next) { + if (!xmlStrcmp(attr->name, name) && attr->children && + attr->children->content) { + return std::string(reinterpret_cast<const char*>( + attr->children->content)); + } + } + return std::string(); +} + +// This is used for the xml parser to report errors. This assumes the context +// is a pointer to a std::string where the error message should be appended. +static void XmlErrorFunc(void *context, const char *message, ...) { + va_list args; + va_start(args, message); + std::string* error = static_cast<std::string*>(context); + base::StringAppendV(error, message, args); + va_end(args); +} + +// Utility class for cleaning up the xml document when leaving a scope. +class ScopedXmlDocument { + public: + explicit ScopedXmlDocument(xmlDocPtr document) : document_(document) {} + ~ScopedXmlDocument() { + if (document_) + xmlFreeDoc(document_); + } + + xmlDocPtr get() { + return document_; + } + + private: + xmlDocPtr document_; +}; + +// Parses the <package> tag. +bool ParsePackageTag(xmlNode* package, + UpdateResponse::Result* result, + std::string* error) { + UpdateResponse::Result::Manifest::Package p; + p.name = GetAttribute(package, "name"); + if (p.name.empty()) { + *error = "Missing name for package."; + return false; + } + + p.namediff = GetAttribute(package, "namediff"); + + // package_fingerprint is optional. It identifies the package, preferably + // with a modified sha256 hash of the package in hex format. + p.fingerprint = GetAttribute(package, "fp"); + + p.hash_sha256 = GetAttribute(package, "hash_sha256"); + int size = 0; + if (base::StringToInt(GetAttribute(package, "size"), &size)) { + p.size = size; + } + + p.hashdiff_sha256 = GetAttribute(package, "hashdiff_sha256"); + int sizediff = 0; + if (base::StringToInt(GetAttribute(package, "sizediff"), &sizediff)) { + p.sizediff = sizediff; + } + + result->manifest.packages.push_back(p); + + return true; +} + +// Parses the <manifest> tag. +bool ParseManifestTag(xmlNode* manifest, + UpdateResponse::Result* result, + std::string* error) { + // Get the version. + result->manifest.version = GetAttribute(manifest, "version"); + if (result->manifest.version.empty()) { + *error = "Missing version for manifest."; + return false; + } + Version version(result->manifest.version); + if (!version.IsValid()) { + *error = "Invalid version: '"; + *error += result->manifest.version; + *error += "'."; + return false; + } + + // Get the minimum browser version (not required). + result->manifest.browser_min_version = + GetAttribute(manifest, "prodversionmin"); + if (result->manifest.browser_min_version.length()) { + Version browser_min_version(result->manifest.browser_min_version); + if (!browser_min_version.IsValid()) { + *error = "Invalid prodversionmin: '"; + *error += result->manifest.browser_min_version; + *error += "'."; + return false; + } + } + + // Get the <packages> node. + std::vector<xmlNode*> packages = GetChildren(manifest, "packages"); + if (packages.empty()) { + *error = "Missing packages tag on manifest."; + return false; + } + + // Parse each of the <package> tags. + std::vector<xmlNode*> package = GetChildren(packages[0], "package"); + for (size_t i = 0; i != package.size(); ++i) { + if (!ParsePackageTag(package[i], result, error)) + return false; + } + + return true; +} + +// Parses the <urls> tag and its children in the <updatecheck>. +bool ParseUrlsTag(xmlNode* urls, + UpdateResponse::Result* result, + std::string* error) { + // Get the url nodes. + std::vector<xmlNode*> url = GetChildren(urls, "url"); + if (url.empty()) { + *error = "Missing url tags on urls."; + return false; + } + + // Get the list of urls for full and optionally, for diff updates. + // There can only be either a codebase or a codebasediff attribute in a tag. + for (size_t i = 0; i != url.size(); ++i) { + // Find the url to the crx file. + const GURL crx_url(GetAttribute(url[i], "codebase")); + if (crx_url.is_valid()) { + result->crx_urls.push_back(crx_url); + continue; + } + const GURL crx_diffurl(GetAttribute(url[i], "codebasediff")); + if (crx_diffurl.is_valid()) { + result->crx_diffurls.push_back(crx_diffurl); + continue; + } + } + + // Expect at least one url for full update. + if (result->crx_urls.empty()) { + *error = "Missing valid url for full update."; + return false; + } + + return true; +} + +// Parses the <updatecheck> tag. +bool ParseUpdateCheckTag(xmlNode* updatecheck, + UpdateResponse::Result* result, + std::string* error) { + if (GetAttribute(updatecheck, "status") == "noupdate") { + return true; + } + + // Get the <urls> tag. + std::vector<xmlNode*> urls = GetChildren(updatecheck, "urls"); + if (urls.empty()) { + *error = "Missing urls on updatecheck."; + return false; + } + + if (!ParseUrlsTag(urls[0], result, error)) { + return false; + } + + std::vector<xmlNode*> manifests = GetChildren(updatecheck, "manifest"); + if (urls.empty()) { + *error = "Missing urls on updatecheck."; + return false; + } + + return ParseManifestTag(manifests[0], result, error); +} + +// Parses a single <app> tag. +bool ParseAppTag(xmlNode* app, + UpdateResponse::Result* result, + std::string* error) { + // Read the crx id. + result->extension_id = GetAttribute(app, "appid"); + if (result->extension_id.empty()) { + *error = "Missing appid on app node"; + return false; + } + + // Get the <updatecheck> tag. + std::vector<xmlNode*> updates = GetChildren(app, "updatecheck"); + if (updates.empty()) { + *error = "Missing updatecheck on app."; + return false; + } + + return ParseUpdateCheckTag(updates[0], result, error); +} + +bool UpdateResponse::Parse(const std::string& response_xml) { + results_.daystart_elapsed_seconds = kNoDaystart; + results_.list.clear(); + errors_.clear(); + + if (response_xml.length() < 1) { + ParseError("Empty xml"); + return false; + } + + std::string xml_errors; + ScopedXmlErrorFunc error_func(&xml_errors, &XmlErrorFunc); + + // Start up the xml parser with the manifest_xml contents. + ScopedXmlDocument document(xmlParseDoc( + reinterpret_cast<const xmlChar*>(response_xml.c_str()))); + if (!document.get()) { + ParseError("%s", xml_errors.c_str()); + return false; + } + + xmlNode* root = xmlDocGetRootElement(document.get()); + if (!root) { + ParseError("Missing root node"); + return false; + } + + if (!TagNameEquals(root, "response")) { + ParseError("Missing response tag"); + return false; + } + + // Check for the response "protocol" attribute. + if (GetAttribute(root, "protocol") != kExpectedResponseProtocol) { + ParseError("Missing/incorrect protocol on response tag " + "(expected '%s')", kExpectedResponseProtocol); + return false; + } + + // Parse the first <daystart> if it is present. + std::vector<xmlNode*> daystarts = GetChildren(root, "daystart"); + if (!daystarts.empty()) { + xmlNode* first = daystarts[0]; + std::string elapsed_seconds = GetAttribute(first, "elapsed_seconds"); + int parsed_elapsed = kNoDaystart; + if (base::StringToInt(elapsed_seconds, &parsed_elapsed)) { + results_.daystart_elapsed_seconds = parsed_elapsed; + } + } + + // Parse each of the <app> tags. + std::vector<xmlNode*> apps = GetChildren(root, "app"); + for (size_t i = 0; i != apps.size(); ++i) { + Result result; + std::string error; + if (ParseAppTag(apps[i], &result, &error)) { + results_.list.push_back(result); + } else { + ParseError("%s", error.c_str()); + } + } + + return true; +} + +} // namespace component_updater + |