| OLD | NEW |
| (Empty) |
| 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 | |
| OLD | NEW |