This source file includes following definitions.
- sizediff
- ParseError
- TagNameEquals
- GetChildren
- GetAttribute
- XmlErrorFunc
- get
- ParsePackageTag
- ParseManifestTag
- ParseUrlsTag
- ParseUpdateCheckTag
- ParseAppTag
- Parse
#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);
}
static bool TagNameEquals(const xmlNode* node, const char* expected_name) {
return 0 == strcmp(expected_name, reinterpret_cast<const char*>(node->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;
}
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();
}
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);
}
class ScopedXmlDocument {
public:
explicit ScopedXmlDocument(xmlDocPtr document) : document_(document) {}
~ScopedXmlDocument() {
if (document_)
xmlFreeDoc(document_);
}
xmlDocPtr get() {
return document_;
}
private:
xmlDocPtr document_;
};
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");
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;
}
bool ParseManifestTag(xmlNode* manifest,
UpdateResponse::Result* result,
std::string* error) {
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;
}
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;
}
}
std::vector<xmlNode*> packages = GetChildren(manifest, "packages");
if (packages.empty()) {
*error = "Missing packages tag on manifest.";
return false;
}
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;
}
bool ParseUrlsTag(xmlNode* urls,
UpdateResponse::Result* result,
std::string* error) {
std::vector<xmlNode*> url = GetChildren(urls, "url");
if (url.empty()) {
*error = "Missing url tags on urls.";
return false;
}
for (size_t i = 0; i != url.size(); ++i) {
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;
}
}
if (result->crx_urls.empty()) {
*error = "Missing valid url for full update.";
return false;
}
return true;
}
bool ParseUpdateCheckTag(xmlNode* updatecheck,
UpdateResponse::Result* result,
std::string* error) {
if (GetAttribute(updatecheck, "status") == "noupdate") {
return true;
}
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);
}
bool ParseAppTag(xmlNode* app,
UpdateResponse::Result* result,
std::string* error) {
result->extension_id = GetAttribute(app, "appid");
if (result->extension_id.empty()) {
*error = "Missing appid on app node";
return false;
}
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);
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;
}
if (GetAttribute(root, "protocol") != kExpectedResponseProtocol) {
ParseError("Missing/incorrect protocol on response tag "
"(expected '%s')", kExpectedResponseProtocol);
return false;
}
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;
}
}
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;
}
}