Upgrade update_engine from Omaha v2 to v3 protocol.
Omaha had released the v3 protocol long back, but update_engine kept
using the v2 protocol as there was no immediate need to move. But now
that we need to support HTTP-based downloads, we need to supply multiple
URLs for each rule, one for HTTP, one for HTTPS fallback. Even for
HTTPs, it is also useful for scenarios such as specifying a Google
storage URL as the primary download point and keeping Lorry as a backup
URL. Multiple URL support is available only on Omaha v3 protocol.
So, this CL is to first upgrade of the client protocol from v2 to v3. It
does not add any new functionality and still supports only one URL. The
subsequent checkins will take advantage of the multiple URL support.
This CL also includes a sample xml file which illustrates how the new
response from the Omaha v3 server would look like, which should greatly
help in understand the changes.
As part of this change, I've also consolidated a few error codes which
had practically zero occurrence into one error code and reused those
error codes for the recently added errors (which haven't been shipped
anywhere yet). Since we're now publishing all the error codes in UMA, we
need to be conservative in defining distinct error codes in order to
reduce the memory usage of Chrome for UMA stats.
BUG=chromium-os:6594
TEST=Tested on ZGB with Omaha v3 server. Updated unit tests and they pass.
Change-Id: I187aa0500e39623684130ba9e3d1d948c0e60627
Reviewed-on: https://gerrit.chromium.org/gerrit/36758
Reviewed-by: Jay Srinivasan <jaysri@chromium.org>
Tested-by: Jay Srinivasan <jaysri@chromium.org>
Commit-Ready: Chris Sosa <sosa@chromium.org>
diff --git a/action_processor.h b/action_processor.h
index e193c67..3f8dcee 100644
--- a/action_processor.h
+++ b/action_processor.h
@@ -55,16 +55,14 @@
kActionCodeDownloadOperationHashMismatch = 29,
kActionCodeOmahaRequestEmptyResponseError = 30,
kActionCodeOmahaRequestXMLParseError = 31,
- kActionCodeOmahaRequestNoUpdateCheckNode = 32,
- kActionCodeOmahaRequestNoUpdateCheckStatus = 33,
- kActionCodeOmahaRequestBadUpdateCheckStatus = 34,
+ kActionCodeDownloadInvalidMetadataSize = 32,
+ kActionCodeDownloadInvalidMetadataSignature = 33,
+ kActionCodeOmahaResponseInvalid = 34,
kActionCodeOmahaUpdateIgnoredPerPolicy = 35,
kActionCodeOmahaUpdateDeferredPerPolicy = 36,
kActionCodeOmahaErrorInHTTPResponse = 37,
kActionCodeDownloadOperationHashMissingError = 38,
- kActionCodeDownloadInvalidMetadataSize = 39,
- kActionCodeDownloadInvalidMetadataSignature = 40,
- kActionCodeDownloadMetadataSignatureMissingError = 41,
+ kActionCodeDownloadMetadataSignatureMissingError = 39,
// Any code above this is sent to both Omaha and UMA as-is.
// Codes/flags below this line is sent only to Omaha and not to UMA.
diff --git a/omaha_request_action.cc b/omaha_request_action.cc
index a43c501..d2399c1 100644
--- a/omaha_request_action.cc
+++ b/omaha_request_action.cc
@@ -85,7 +85,7 @@
string ping_active = GetPingAttribute("a", ping_active_days);
string ping_roll_call = GetPingAttribute("r", ping_roll_call_days);
if (!ping_active.empty() || !ping_roll_call.empty()) {
- return StringPrintf(" <o:ping active=\"1\"%s%s></o:ping>\n",
+ return StringPrintf(" <ping active=\"1\"%s%s></ping>\n",
ping_active.c_str(),
ping_roll_call.c_str());
}
@@ -108,9 +108,9 @@
// the expected behavior when we move to Omaha v3.0 protocol, so it'll
// be consistent.
body += StringPrintf(
- " <o:updatecheck"
+ " <updatecheck"
" targetversionprefix=\"%s\""
- "></o:updatecheck>\n",
+ "></updatecheck>\n",
XmlEncode(params.target_version_prefix).c_str());
// If this is the first update check after a reboot following a previous
@@ -126,8 +126,8 @@
}
if (!prev_version.empty()) {
body += StringPrintf(
- " <o:event eventtype=\"%d\" eventresult=\"%d\" "
- "previousversion=\"%s\"></o:event>\n",
+ " <event eventtype=\"%d\" eventresult=\"%d\" "
+ "previousversion=\"%s\"></event>\n",
OmahaEvent::kTypeUpdateComplete,
OmahaEvent::kResultSuccessReboot,
XmlEncode(prev_version).c_str());
@@ -143,26 +143,26 @@
error_code = StringPrintf(" errorcode=\"%d\"", event->error_code);
}
body = StringPrintf(
- " <o:event eventtype=\"%d\" eventresult=\"%d\"%s></o:event>\n",
+ " <event eventtype=\"%d\" eventresult=\"%d\"%s></event>\n",
event->type, event->result, error_code.c_str());
}
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
- "<o:gupdate xmlns:o=\"http://www.google.com/update2/request\" "
+ "<request protocol=\"3.0\" "
"version=\"" + XmlEncode(kGupdateVersion) + "\" "
"updaterversion=\"" + XmlEncode(kGupdateVersion) + "\" "
- "protocol=\"2.0\" ismachine=\"1\">\n"
- " <o:os version=\"" + XmlEncode(params.os_version) + "\" platform=\"" +
+ "ismachine=\"1\">\n"
+ " <os version=\"" + XmlEncode(params.os_version) + "\" platform=\"" +
XmlEncode(params.os_platform) + "\" sp=\"" +
- XmlEncode(params.os_sp) + "\"></o:os>\n"
- " <o:app appid=\"" + XmlEncode(params.app_id) + "\" version=\"" +
+ XmlEncode(params.os_sp) + "\"></os>\n"
+ " <app appid=\"" + XmlEncode(params.app_id) + "\" version=\"" +
XmlEncode(params.app_version) + "\" "
"lang=\"" + XmlEncode(params.app_lang) + "\" track=\"" +
XmlEncode(params.app_track) + "\" board=\"" +
XmlEncode(params.os_board) + "\" hardware_class=\"" +
XmlEncode(params.hardware_class) + "\" delta_okay=\"" +
(params.delta_okay ? "true" : "false") + "\">\n" + body +
- " </o:app>\n"
- "</o:gupdate>\n";
+ " </app>\n"
+ "</request>\n";
}
} // namespace {}
@@ -271,8 +271,7 @@
// on the returned object.
// This code is roughly based on the libxml tutorial at:
// http://xmlsoft.org/tutorial/apd.html
-xmlXPathObject* GetNodeSet(xmlDoc* doc, const xmlChar* xpath,
- const xmlChar* ns, const xmlChar* ns_url) {
+xmlXPathObject* GetNodeSet(xmlDoc* doc, const xmlChar* xpath) {
xmlXPathObject* result = NULL;
scoped_ptr_malloc<xmlXPathContext, ScopedPtrXmlXPathContextFree> context(
@@ -281,19 +280,14 @@
LOG(ERROR) << "xmlXPathNewContext() returned NULL";
return NULL;
}
- if (xmlXPathRegisterNs(context.get(), ns, ns_url) < 0) {
- LOG(ERROR) << "xmlXPathRegisterNs() returned error";
- return NULL;
- }
result = xmlXPathEvalExpression(xpath, context.get());
-
if (result == NULL) {
- LOG(ERROR) << "xmlXPathEvalExpression returned error";
+ LOG(ERROR) << "Unable to find " << xpath << " in XML document";
return NULL;
}
if(xmlXPathNodeSetIsEmpty(result->nodesetval)){
- LOG(INFO) << "xpath not found in doc";
+ LOG(INFO) << "Nodeset is empty for " << xpath;
xmlXPathFreeObject(result);
return NULL;
}
@@ -329,15 +323,10 @@
// Update the last ping day preferences based on the server daystart
// response. Returns true on success, false otherwise.
bool UpdateLastPingDays(xmlDoc* doc, PrefsInterface* prefs) {
- static const char kNamespace[] = "x";
- static const char kDaystartNodeXpath[] = "/x:gupdate/x:daystart";
- static const char kNsUrl[] = "http://www.google.com/update2/response";
+ static const char kDaystartNodeXpath[] = "/response/daystart";
scoped_ptr_malloc<xmlXPathObject, ScopedPtrXmlXPathObjectFree>
- xpath_nodeset(GetNodeSet(doc,
- ConstXMLStr(kDaystartNodeXpath),
- ConstXMLStr(kNamespace),
- ConstXMLStr(kNsUrl)));
+ xpath_nodeset(GetNodeSet(doc, ConstXMLStr(kDaystartNodeXpath)));
TEST_AND_RETURN_FALSE(xpath_nodeset.get());
xmlNodeSet* nodeset = xpath_nodeset->nodesetval;
TEST_AND_RETURN_FALSE(nodeset && nodeset->nodeNr >= 1);
@@ -360,6 +349,233 @@
}
} // namespace {}
+bool OmahaRequestAction::ParseResponse(xmlDoc* doc,
+ OmahaResponse* output_object,
+ ScopedActionCompleter* completer) {
+ static const char* kUpdatecheckNodeXpath("/response/app/updatecheck");
+
+ scoped_ptr_malloc<xmlXPathObject, ScopedPtrXmlXPathObjectFree>
+ xpath_nodeset(GetNodeSet(doc, ConstXMLStr(kUpdatecheckNodeXpath)));
+ if (!xpath_nodeset.get()) {
+ completer->set_code(kActionCodeOmahaResponseInvalid);
+ return false;
+ }
+
+ xmlNodeSet* nodeset = xpath_nodeset->nodesetval;
+ CHECK(nodeset) << "XPath missing UpdateCheck NodeSet";
+ CHECK_GE(nodeset->nodeNr, 1);
+ xmlNode* update_check_node = nodeset->nodeTab[0];
+
+ // TODO(jaysri): The PollInterval is not supported by Omaha server currently.
+ // But still keeping this existing code in case we ever decide to slow down
+ // the request rate from the server-side. Note that the PollInterval is not
+ // persisted, so it has to be sent by the server on every response to
+ // guarantee that the UpdateCheckScheduler uses this value (otherwise, if the
+ // device got rebooted after the last server-indicated value, it'll revert to
+ // the default value). Also kDefaultMaxUpdateChecks value for the scattering
+ // logic is based on the assumption that we perform an update check every
+ // hour so that the max value of 8 will roughly be equivalent to one work
+ // day. If we decide to use PollInterval permanently, we should update
+ // the max_update_checks_allowed to take PollInterval into account. Note:
+ // The parsing for PollInterval happens even before parsing of the status
+ // because we may want to specify the PollInterval even when there's no
+ // update.
+ base::StringToInt(XmlGetProperty(update_check_node, "PollInterval"),
+ &output_object->poll_interval);
+
+ if (!ParseStatus(update_check_node, output_object, completer))
+ return false;
+
+ if (!ParseUrls(doc, output_object, completer))
+ return false;
+
+ if (!ParsePackage(doc, output_object, completer))
+ return false;
+
+ if (!ParseParams(doc, output_object, completer))
+ return false;
+
+ return true;
+}
+
+bool OmahaRequestAction::ParseStatus(xmlNode* update_check_node,
+ OmahaResponse* output_object,
+ ScopedActionCompleter* completer) {
+ // Get status.
+ if (!xmlHasProp(update_check_node, ConstXMLStr("status"))) {
+ LOG(ERROR) << "Omaha Response missing status";
+ completer->set_code(kActionCodeOmahaResponseInvalid);
+ return false;
+ }
+
+ const string status(XmlGetProperty(update_check_node, "status"));
+ if (status == "noupdate") {
+ LOG(INFO) << "No update.";
+ output_object->update_exists = false;
+ SetOutputObject(*output_object);
+ completer->set_code(kActionCodeSuccess);
+ return false;
+ }
+
+ if (status != "ok") {
+ LOG(ERROR) << "Unknown Omaha response status: " << status;
+ completer->set_code(kActionCodeOmahaResponseInvalid);
+ return false;
+ }
+
+ return true;
+}
+
+bool OmahaRequestAction::ParseUrls(xmlDoc* doc,
+ OmahaResponse* output_object,
+ ScopedActionCompleter* completer) {
+ // Get the update URL.
+ static const char* kUpdateUrlNodeXPath("/response/app/updatecheck/urls/url");
+
+ scoped_ptr_malloc<xmlXPathObject, ScopedPtrXmlXPathObjectFree>
+ xpath_nodeset(GetNodeSet(doc, ConstXMLStr(kUpdateUrlNodeXPath)));
+ if (!xpath_nodeset.get()) {
+ completer->set_code(kActionCodeOmahaResponseInvalid);
+ return false;
+ }
+
+ xmlNodeSet* nodeset = xpath_nodeset->nodesetval;
+ CHECK(nodeset) << "XPath missing " << kUpdateUrlNodeXPath;
+ CHECK_GE(nodeset->nodeNr, 1);
+
+ // TODO(jaysri): For now, just process only the first URL. In subsequent
+ // check-ins, we'll add the support to honor multiples URLs.
+ LOG(INFO) << "Processing first of " << nodeset->nodeNr << " url(s)";
+ xmlNode* url_node = nodeset->nodeTab[0];
+
+ const string codebase(XmlGetProperty(url_node, "codebase"));
+ if (codebase.empty()) {
+ LOG(ERROR) << "Omaha Response URL has empty codebase";
+ completer->set_code(kActionCodeOmahaResponseInvalid);
+ return false;
+ }
+
+ output_object->codebase = codebase;
+ return true;
+}
+
+bool OmahaRequestAction::ParsePackage(xmlDoc* doc,
+ OmahaResponse* output_object,
+ ScopedActionCompleter* completer) {
+ // Get the package node.
+ static const char* kPackageNodeXPath(
+ "/response/app/updatecheck/manifest/packages/package");
+
+ scoped_ptr_malloc<xmlXPathObject, ScopedPtrXmlXPathObjectFree>
+ xpath_nodeset(GetNodeSet(doc, ConstXMLStr(kPackageNodeXPath)));
+ if (!xpath_nodeset.get()) {
+ completer->set_code(kActionCodeOmahaResponseInvalid);
+ return false;
+ }
+
+ xmlNodeSet* nodeset = xpath_nodeset->nodesetval;
+ CHECK(nodeset) << "XPath missing " << kPackageNodeXPath;
+ CHECK_GE(nodeset->nodeNr, 1);
+
+ // We only care about the first package.
+ LOG(INFO) << "Processing first of " << nodeset->nodeNr << " package(s)";
+ xmlNode* package_node = nodeset->nodeTab[0];
+
+ // Get package properties one by one.
+
+ // Parse the payload name to be appended to the Url codebase.
+ const string package_name(XmlGetProperty(package_node, "name"));
+ LOG(INFO) << "Omaha Response package name = " << package_name;
+ if (package_name.empty()) {
+ LOG(ERROR) << "Omaha Response has empty package name";
+ completer->set_code(kActionCodeOmahaResponseInvalid);
+ return false;
+ }
+ output_object->codebase += package_name;
+
+ // Parse the payload size.
+ off_t size = ParseInt(XmlGetProperty(package_node, "size"));
+ if (size <= 0) {
+ LOG(ERROR) << "Omaha Response has invalid payload size: " << size;
+ completer->set_code(kActionCodeOmahaResponseInvalid);
+ return false;
+ }
+ output_object->size = size;
+
+ LOG(INFO) << "Download URL = " << output_object->codebase
+ << ", Paylod size = " << output_object->size;
+
+ return true;
+}
+
+bool OmahaRequestAction::ParseParams(xmlDoc* doc,
+ OmahaResponse* output_object,
+ ScopedActionCompleter* completer) {
+ // Get the action node where parameters are present.
+ static const char* kActionNodeXPath(
+ "/response/app/updatecheck/manifest/actions/action");
+
+ scoped_ptr_malloc<xmlXPathObject, ScopedPtrXmlXPathObjectFree>
+ xpath_nodeset(GetNodeSet(doc, ConstXMLStr(kActionNodeXPath)));
+ if (!xpath_nodeset.get()) {
+ completer->set_code(kActionCodeOmahaResponseInvalid);
+ return false;
+ }
+
+ xmlNodeSet* nodeset = xpath_nodeset->nodesetval;
+ CHECK(nodeset) << "XPath missing " << kActionNodeXPath;
+
+ // We only care about the action that has event "postinall", because this is
+ // where Omaha puts all the generic name/value pairs in the rule.
+ LOG(INFO) << "Found " << nodeset->nodeNr
+ << " action(s). Processing the postinstall action.";
+
+ // pie_action_node holds the action node corresponding to the
+ // postinstall event action, if present.
+ xmlNode* pie_action_node = NULL;
+ for (int i = 0; i < nodeset->nodeNr; i++) {
+ xmlNode* action_node = nodeset->nodeTab[i];
+ if (XmlGetProperty(action_node, "event") == "postinstall") {
+ pie_action_node = action_node;
+ break;
+ }
+ }
+
+ if (!pie_action_node) {
+ LOG(ERROR) << "Omaha Response has no postinstall event action";
+ completer->set_code(kActionCodeOmahaResponseInvalid);
+ return false;
+ }
+
+ output_object->hash = XmlGetProperty(pie_action_node, "sha256");
+ if (output_object->hash.empty()) {
+ LOG(ERROR) << "Omaha Response has empty sha256 value";
+ completer->set_code(kActionCodeOmahaResponseInvalid);
+ return false;
+ }
+
+ // Get the optional properties one by one.
+ output_object->display_version =
+ XmlGetProperty(pie_action_node, "DisplayVersion");
+ output_object->more_info_url = XmlGetProperty(pie_action_node, "MoreInfo");
+ output_object->metadata_size =
+ ParseInt(XmlGetProperty(pie_action_node, "MetadataSize"));
+ output_object->metadata_signature =
+ XmlGetProperty(pie_action_node, "MetadataSignatureRsa");
+ output_object->needs_admin =
+ XmlGetProperty(pie_action_node, "needsadmin") == "true";
+ output_object->prompt = XmlGetProperty(pie_action_node, "Prompt") == "true";
+ output_object->deadline = XmlGetProperty(pie_action_node, "deadline");
+ output_object->max_days_to_scatter =
+ ParseInt(XmlGetProperty(pie_action_node, "MaxDaysToScatter"));
+
+ output_object->update_exists = true;
+ SetOutputObject(*output_object);
+ completer->set_code(kActionCodeSuccess);
+
+ return true;
+}
+
// If the transfer was successful, this uses libxml2 to parse the response
// and fill in the appropriate fields of the output object. Also, notifies
// the processor that we're done.
@@ -421,51 +637,13 @@
return;
}
- static const char* kNamespace("x");
- static const char* kUpdatecheckNodeXpath("/x:gupdate/x:app/x:updatecheck");
- static const char* kNsUrl("http://www.google.com/update2/response");
-
- scoped_ptr_malloc<xmlXPathObject, ScopedPtrXmlXPathObjectFree>
- xpath_nodeset(GetNodeSet(doc.get(),
- ConstXMLStr(kUpdatecheckNodeXpath),
- ConstXMLStr(kNamespace),
- ConstXMLStr(kNsUrl)));
- if (!xpath_nodeset.get()) {
- completer.set_code(kActionCodeOmahaRequestNoUpdateCheckNode);
- return;
- }
- xmlNodeSet* nodeset = xpath_nodeset->nodesetval;
- CHECK(nodeset) << "XPath missing NodeSet";
- CHECK_GE(nodeset->nodeNr, 1);
-
- xmlNode* updatecheck_node = nodeset->nodeTab[0];
- // get status
- if (!xmlHasProp(updatecheck_node, ConstXMLStr("status"))) {
- LOG(ERROR) << "Response missing status";
- completer.set_code(kActionCodeOmahaRequestNoUpdateCheckStatus);
- return;
- }
-
OmahaResponse output_object;
- base::StringToInt(XmlGetProperty(updatecheck_node, "PollInterval"),
- &output_object.poll_interval);
- const string status(XmlGetProperty(updatecheck_node, "status"));
- if (status == "noupdate") {
- LOG(INFO) << "No update.";
- output_object.update_exists = false;
- SetOutputObject(output_object);
- completer.set_code(kActionCodeSuccess);
+ if (!ParseResponse(doc.get(), &output_object, &completer))
return;
- }
-
- if (status != "ok") {
- LOG(ERROR) << "Unknown status: " << status;
- completer.set_code(kActionCodeOmahaRequestBadUpdateCheckStatus);
- return;
- }
if (params_->update_disabled) {
LOG(INFO) << "Ignoring Omaha updates as updates are disabled by policy.";
+ output_object.update_exists = false;
completer.set_code(kActionCodeOmahaUpdateIgnoredPerPolicy);
// Note: We could technically delete the UpdateFirstSeenAt state here.
// If we do, it'll mean a device has to restart the UpdateFirstSeenAt
@@ -477,35 +655,15 @@
return;
}
- if (ShouldDeferDownload(updatecheck_node)) {
+ if (ShouldDeferDownload(&output_object)) {
+ output_object.update_exists = false;
LOG(INFO) << "Ignoring Omaha updates as updates are deferred by policy.";
completer.set_code(kActionCodeOmahaUpdateDeferredPerPolicy);
return;
}
-
- // In best-effort fashion, fetch the rest of the expected attributes
- // from the updatecheck node, then return the object
- output_object.update_exists = true;
- completer.set_code(kActionCodeSuccess);
-
- output_object.display_version =
- XmlGetProperty(updatecheck_node, "DisplayVersion");
- output_object.codebase = XmlGetProperty(updatecheck_node, "codebase");
- output_object.more_info_url = XmlGetProperty(updatecheck_node, "MoreInfo");
- output_object.hash = XmlGetProperty(updatecheck_node, "sha256");
- output_object.size = ParseInt(XmlGetProperty(updatecheck_node, "size"));
- output_object.metadata_size =
- ParseInt(XmlGetProperty(updatecheck_node, "MetadataSize"));
- output_object.metadata_signature =
- XmlGetProperty(updatecheck_node, "MetadataSignatureRsa");
- output_object.needs_admin =
- XmlGetProperty(updatecheck_node, "needsadmin") == "true";
- output_object.prompt = XmlGetProperty(updatecheck_node, "Prompt") == "true";
- output_object.deadline = XmlGetProperty(updatecheck_node, "deadline");
- SetOutputObject(output_object);
}
-bool OmahaRequestAction::ShouldDeferDownload(xmlNode* updatecheck_node) {
+bool OmahaRequestAction::ShouldDeferDownload(OmahaResponse* output_object) {
// We should defer the downloads only if we've first satisfied the
// wall-clock-based-waiting period and then the update-check-based waiting
// period, if required.
@@ -515,7 +673,7 @@
return false;
}
- switch (IsWallClockBasedWaitingSatisfied(updatecheck_node)) {
+ switch (IsWallClockBasedWaitingSatisfied(output_object)) {
case kWallClockWaitNotSatisfied:
// We haven't even satisfied the first condition, passing the
// wall-clock-based waiting period, so we should defer the downloads
@@ -526,7 +684,7 @@
case kWallClockWaitDoneButUpdateCheckWaitRequired:
LOG(INFO) << "wall-clock-based-wait satisfied and "
<< "update-check-based-wait required.";
- return !IsUpdateCheckCountBasedWaitingSatisfied(updatecheck_node);
+ return !IsUpdateCheckCountBasedWaitingSatisfied();
case kWallClockWaitDoneAndUpdateCheckWaitNotRequired:
// Wall-clock-based waiting period is satisfied, and it's determined
@@ -546,7 +704,7 @@
OmahaRequestAction::WallClockWaitResult
OmahaRequestAction::IsWallClockBasedWaitingSatisfied(
- xmlNode* updatecheck_node) {
+ OmahaResponse* output_object) {
Time update_first_seen_at;
int64 update_first_seen_at_int;
@@ -591,7 +749,7 @@
TimeDelta elapsed_time = Time::Now() - update_first_seen_at;
TimeDelta max_scatter_period = TimeDelta::FromDays(
- ParseInt(XmlGetProperty(updatecheck_node, "MaxDaysToScatter")));
+ output_object->max_days_to_scatter);
LOG(INFO) << "Waiting Period = "
<< utils::FormatSecs(params_->waiting_period.InSeconds())
@@ -600,7 +758,7 @@
<< ", MaxDaysToScatter = "
<< max_scatter_period.InDays();
- if (!XmlGetProperty(updatecheck_node, "deadline").empty()) {
+ if (!output_object->deadline.empty()) {
// The deadline is set for all rules which serve a delta update from a
// previous FSI, which means this update will be applied mostly in OOBE
// cases. For these cases, we shouldn't scatter so as to finish the OOBE
@@ -646,8 +804,7 @@
return kWallClockWaitNotSatisfied;
}
-bool OmahaRequestAction::IsUpdateCheckCountBasedWaitingSatisfied(
- xmlNode* updatecheck_node) {
+bool OmahaRequestAction::IsUpdateCheckCountBasedWaitingSatisfied() {
int64 update_check_count_value;
if (prefs_->Exists(kPrefsUpdateCheckCount)) {
@@ -701,3 +858,5 @@
}
} // namespace chromeos_update_engine
+
+
diff --git a/omaha_request_action.h b/omaha_request_action.h
index 73641b4..8a5a5d5 100644
--- a/omaha_request_action.h
+++ b/omaha_request_action.h
@@ -35,6 +35,7 @@
poll_interval(0),
size(0),
metadata_size(0),
+ max_days_to_scatter(0),
needs_admin(false),
prompt(false) {}
// True iff there is an update to be downloaded.
@@ -52,6 +53,7 @@
std::string deadline;
off_t size;
off_t metadata_size;
+ int max_days_to_scatter;
bool needs_admin;
bool prompt;
};
@@ -160,8 +162,8 @@
// Delegate methods (see http_fetcher.h)
virtual void ReceivedBytes(HttpFetcher *fetcher,
const char* bytes, int length);
- virtual void TransferComplete(HttpFetcher *fetcher, bool successful);
+ virtual void TransferComplete(HttpFetcher *fetcher, bool successful);
// Returns true if this is an Event request, false if it's an UpdateCheck.
bool IsEvent() const { return event_.get() != NULL; }
@@ -177,18 +179,54 @@
// Returns true if the download of a new update should be deferred.
// False if the update can be downloaded.
- bool ShouldDeferDownload(xmlNode* updatecheck_node);
+ bool ShouldDeferDownload(OmahaResponse* output_object);
// Returns true if the basic wall-clock-based waiting period has been
// satisfied based on the scattering policy setting. False otherwise.
// If true, it also indicates whether the additional update-check-count-based
// waiting period also needs to be satisfied before the download can begin.
WallClockWaitResult IsWallClockBasedWaitingSatisfied(
- xmlNode* updatecheck_node);
+ OmahaResponse* output_object);
// Returns true if the update-check-count-based waiting period has been
// satisfied. False otherwise.
- bool IsUpdateCheckCountBasedWaitingSatisfied(xmlNode* updatecheck_node);
+ bool IsUpdateCheckCountBasedWaitingSatisfied();
+
+ // Parses the response from Omaha that's available in |doc| using the other
+ // helper methods below and populates the |output_object| with the relevant
+ // values. Returns true if we should continue the parsing. False otherwise,
+ // in which case it sets any error code using |completer|.
+ bool ParseResponse(xmlDoc* doc,
+ OmahaResponse* output_object,
+ ScopedActionCompleter* completer);
+
+ // Parses the status property in the given update_check_node and populates
+ // |output_object| if valid. Returns true if we should continue the parsing.
+ // False otherwise, in which case it sets any error code using |completer|.
+ bool ParseStatus(xmlNode* update_check_node,
+ OmahaResponse* output_object,
+ ScopedActionCompleter* completer);
+
+ // Parses the URL nodes in the given XML document and populates
+ // |output_object| if valid. Returns true if we should continue the parsing.
+ // False otherwise, in which case it sets any error code using |completer|.
+ bool ParseUrls(xmlDoc* doc,
+ OmahaResponse* output_object,
+ ScopedActionCompleter* completer);
+
+ // Parses the package node in the given XML document and populates
+ // |output_object| if valid. Returns true if we should continue the parsing.
+ // False otherwise, in which case it sets any error code using |completer|.
+ bool ParsePackage(xmlDoc* doc,
+ OmahaResponse* output_object,
+ ScopedActionCompleter* completer);
+
+ // Parses the other parameters in the given XML document and populates
+ // |output_object| if valid. Returns true if we should continue the parsing.
+ // False otherwise, in which case it sets any error code using |completer|.
+ bool ParseParams(xmlDoc* doc,
+ OmahaResponse* output_object,
+ ScopedActionCompleter* completer);
// Access to the preferences store.
PrefsInterface* prefs_;
diff --git a/omaha_request_action_unittest.cc b/omaha_request_action_unittest.cc
index a336bdd..90c01a4 100644
--- a/omaha_request_action_unittest.cc
+++ b/omaha_request_action_unittest.cc
@@ -57,10 +57,10 @@
string GetNoUpdateResponse(const string& app_id) {
return string(
- "<?xml version=\"1.0\" encoding=\"UTF-8\"?><gupdate "
- "xmlns=\"http://www.google.com/update2/response\" protocol=\"2.0\"><app "
- "appid=\"") + app_id + "\" status=\"ok\"><ping "
- "status=\"ok\"/><updatecheck status=\"noupdate\"/></app></gupdate>";
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response protocol=\"3.0\">"
+ "<daystart elapsed_seconds=\"100\"/>"
+ "<app appid=\"") + app_id + "\" status=\"ok\"><ping "
+ "status=\"ok\"/><updatecheck status=\"noupdate\"/></app></response>";
}
string GetUpdateResponse2(const string& app_id,
@@ -68,28 +68,34 @@
const string& more_info_url,
const string& prompt,
const string& codebase,
+ const string& filename,
const string& hash,
const string& needsadmin,
const string& size,
const string& deadline,
const string& max_days_to_scatter) {
- return string(
- "<?xml version=\"1.0\" encoding=\"UTF-8\"?><gupdate "
- "xmlns=\"http://www.google.com/update2/response\" "
- "protocol=\"2.0\"><app "
- "appid=\"") + app_id + "\" status=\"ok\"><ping "
- "status=\"ok\"/><updatecheck DisplayVersion=\"" + display_version + "\" "
+ string response =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response "
+ "protocol=\"3.0\">"
+ "<daystart elapsed_seconds=\"100\"/>"
+ "<app appid=\"" + app_id + "\" status=\"ok\">"
+ "<ping status=\"ok\"/><updatecheck status=\"ok\">"
+ "<urls><url codebase=\"" + codebase + "\"/></urls>"
+ "<manifest version=\"" + display_version + "\">"
+ "<packages><package hash=\"not-used\" name=\"" + filename + "\" "
+ "size=\"" + size + "\"/></packages>"
+ "<actions><action event=\"postinstall\" "
+ "DisplayVersion=\"" + display_version + "\" "
"ChromeOSVersion=\"" + display_version + "\" "
"MoreInfo=\"" + more_info_url + "\" Prompt=\"" + prompt + "\" "
"IsDelta=\"true\" "
"MaxDaysToScatter=\"" + max_days_to_scatter + "\" "
- "codebase=\"" + codebase + "\" "
- "hash=\"not-applicable\" "
"sha256=\"" + hash + "\" "
- "needsadmin=\"" + needsadmin + "\" "
- "size=\"" + size + "\" " +
+ "needsadmin=\"" + needsadmin + "\" " +
(deadline.empty() ? "" : ("deadline=\"" + deadline + "\" ")) +
- "status=\"ok\"/></app></gupdate>";
+ "/></actions></manifest></updatecheck></app></response>";
+ LOG(INFO) << "Response = " << response;
+ return response;
}
string GetUpdateResponse(const string& app_id,
@@ -97,6 +103,7 @@
const string& more_info_url,
const string& prompt,
const string& codebase,
+ const string& filename,
const string& hash,
const string& needsadmin,
const string& size,
@@ -106,6 +113,7 @@
more_info_url,
prompt,
codebase,
+ filename,
hash,
needsadmin,
size,
@@ -278,7 +286,8 @@
"1.2.3.4", // version
"http://more/info",
"true", // prompt
- "http://code/base", // dl url
+ "http://code/base/", // dl url
+ "file.signed", // file name
"HASH1234=", // checksum
"false", // needs admin
"123", // size
@@ -291,7 +300,7 @@
EXPECT_TRUE(response.update_exists);
EXPECT_TRUE(response.update_exists);
EXPECT_EQ("1.2.3.4", response.display_version);
- EXPECT_EQ("http://code/base", response.codebase);
+ EXPECT_EQ("http://code/base/file.signed", response.codebase);
EXPECT_EQ("http://more/info", response.more_info_url);
EXPECT_EQ("HASH1234=", response.hash);
EXPECT_EQ(123, response.size);
@@ -311,7 +320,8 @@
"1.2.3.4", // version
"http://more/info",
"true", // prompt
- "http://code/base", // dl url
+ "http://code/base/", // dl url
+ "file.signed", // file name
"HASH1234=", // checksum
"false", // needs admin
"123", // size
@@ -363,7 +373,8 @@
"1.2.3.4", // version
"http://more/info",
"true", // prompt
- "http://code/base", // dl url
+ "http://code/base/", // dl url
+ "file.signed", // file name
"HASH1234=", // checksum
"false", // needs admin
"123", // size
@@ -403,7 +414,8 @@
"1.2.3.4", // version
"http://more/info",
"true", // prompt
- "http://code/base", // dl url
+ "http://code/base/", // dl url
+ "file.signed", // file name
"HASH1234=", // checksum
"false", // needs admin
"123", // size
@@ -443,7 +455,8 @@
"1.2.3.4", // version
"http://more/info",
"true", // prompt
- "http://code/base", // dl url
+ "http://code/base/", // dl url
+ "file.signed", // file name
"HASH1234=", // checksum
"false", // needs admin
"123", // size
@@ -484,7 +497,8 @@
"1.2.3.4", // version
"http://more/info",
"true", // prompt
- "http://code/base", // dl url
+ "http://code/base/", // dl url
+ "file.signed", // file name
"HASH1234=", // checksum
"false", // needs admin
"123", // size
@@ -528,7 +542,8 @@
"1.2.3.4", // version
"http://more/info",
"true", // prompt
- "http://code/base", // dl url
+ "http://code/base/", // dl url
+ "file.signed", // file name
"HASH1234=", // checksum
"false", // needs admin
"123", // size
@@ -574,7 +589,8 @@
"1.2.3.4", // version
"http://more/info",
"true", // prompt
- "http://code/base", // dl url
+ "http://code/base/", // dl url
+ "file.signed", // file name
"HASH1234=", // checksum
"false", // needs admin
"123", // size
@@ -651,13 +667,14 @@
ASSERT_FALSE(TestUpdateCheck(
NULL, // prefs
kDefaultTestParams,
- "<?xml version=\"1.0\" encoding=\"UTF-8\"?><gupdate "
- "xmlns=\"http://www.google.com/update2/response\" protocol=\"2.0\"><app "
- "appid=\"foo\" status=\"ok\"><ping "
- "status=\"ok\"/><updatecheck/></app></gupdate>",
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response protocol=\"3.0\">"
+ "<daystart elapsed_seconds=\"100\"/>"
+ "<app appid=\"foo\" status=\"ok\">"
+ "<ping status=\"ok\"/>"
+ "<updatecheck/></app></response>",
-1,
false, // ping_only
- kActionCodeOmahaRequestNoUpdateCheckStatus,
+ kActionCodeOmahaResponseInvalid,
&response,
NULL));
EXPECT_FALSE(response.update_exists);
@@ -668,13 +685,14 @@
ASSERT_FALSE(TestUpdateCheck(
NULL, // prefs
kDefaultTestParams,
- "<?xml version=\"1.0\" encoding=\"UTF-8\"?><gupdate "
- "xmlns=\"http://www.google.com/update2/response\" protocol=\"2.0\"><app "
- "appid=\"foo\" status=\"ok\"><ping "
- "status=\"ok\"/><updatecheck status=\"foo\"/></app></gupdate>",
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response protocol=\"3.0\">"
+ "<daystart elapsed_seconds=\"100\"/>"
+ "<app appid=\"foo\" status=\"ok\">"
+ "<ping status=\"ok\"/>"
+ "<updatecheck status=\"InvalidStatusTest\"/></app></response>",
-1,
false, // ping_only
- kActionCodeOmahaRequestBadUpdateCheckStatus,
+ kActionCodeOmahaResponseInvalid,
&response,
NULL));
EXPECT_FALSE(response.update_exists);
@@ -685,49 +703,54 @@
ASSERT_FALSE(TestUpdateCheck(
NULL, // prefs
kDefaultTestParams,
- "<?xml version=\"1.0\" encoding=\"UTF-8\"?><gupdate "
- "xmlns=\"http://www.google.com/update2/response\" protocol=\"2.0\"><app "
- "appid=\"foo\" status=\"ok\"><ping "
- "status=\"ok\"/></app></gupdate>",
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response protocol=\"3.0\">"
+ "<daystart elapsed_seconds=\"100\"/>"
+ "<app appid=\"foo\" status=\"ok\">"
+ "<ping status=\"ok\"/>"
+ "</app></response>",
-1,
false, // ping_only
- kActionCodeOmahaRequestNoUpdateCheckNode,
+ kActionCodeOmahaResponseInvalid,
&response,
NULL));
EXPECT_FALSE(response.update_exists);
}
TEST(OmahaRequestActionTest, MissingFieldTest) {
+ string input_response =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response protocol=\"3.0\">"
+ "<daystart elapsed_seconds=\"100\"/>"
+ "<app appid=\"xyz\" status=\"ok\">"
+ "<updatecheck status=\"ok\">"
+ "<urls><url codebase=\"http://missing/field/test/\"/></urls>"
+ "<manifest version=\"1.0.0.0\">"
+ "<packages><package hash=\"not-used\" name=\"f\" "
+ "size=\"587\"/></packages>"
+ "<actions><action event=\"postinstall\" "
+ "DisplayVersion=\"10.2.3.4\" "
+ "ChromeOSVersion=\"10.2.3.4\" "
+ "Prompt=\"false\" "
+ "IsDelta=\"true\" "
+ "sha256=\"lkq34j5345\" "
+ "needsadmin=\"true\" "
+ "/></actions></manifest></updatecheck></app></response>";
+ LOG(INFO) << "Input Response = " << input_response;
+
OmahaResponse response;
ASSERT_TRUE(TestUpdateCheck(NULL, // prefs
kDefaultTestParams,
- string("<?xml version=\"1.0\" "
- "encoding=\"UTF-8\"?><gupdate "
- "xmlns=\"http://www.google.com/"
- "update2/response\" "
- "protocol=\"2.0\"><app appid=\"") +
- OmahaRequestParams::kAppId
- + "\" status=\"ok\"><ping "
- "status=\"ok\"/><updatecheck "
- "DisplayVersion=\"1.2.3.4\" "
- "ChromeOSVersion=\"1.2.3.4\" "
- "Prompt=\"false\" "
- "IsDelta=\"true\" "
- "codebase=\"http://code/base\" hash=\"foo\" "
- "sha256=\"HASH1234=\" needsadmin=\"true\" "
- "size=\"123\" "
- "status=\"ok\"/></app></gupdate>",
+ input_response,
-1,
false, // ping_only
kActionCodeSuccess,
&response,
NULL));
EXPECT_TRUE(response.update_exists);
- EXPECT_EQ("1.2.3.4", response.display_version);
- EXPECT_EQ("http://code/base", response.codebase);
+ EXPECT_EQ("10.2.3.4", response.display_version);
+ EXPECT_EQ("http://missing/field/test/f", response.codebase);
EXPECT_EQ("", response.more_info_url);
- EXPECT_EQ("HASH1234=", response.hash);
- EXPECT_EQ(123, response.size);
+ EXPECT_EQ("lkq34j5345", response.hash);
+ EXPECT_EQ(587, response.size);
EXPECT_TRUE(response.needs_admin);
EXPECT_FALSE(response.prompt);
EXPECT_TRUE(response.deadline.empty());
@@ -828,7 +851,8 @@
"1.2.3.4", // version
"testthe<url", // more info
"true", // prompt
- "testthe&codebase", // dl url
+ "testthe&codebase/", // dl url
+ "file.signed", // file name
"HASH1234=", // checksum
"false", // needs admin
"123", // size
@@ -840,7 +864,7 @@
NULL));
EXPECT_EQ(response.more_info_url, "testthe<url");
- EXPECT_EQ(response.codebase, "testthe&codebase");
+ EXPECT_EQ(response.codebase, "testthe&codebase/file.signed");
EXPECT_EQ(response.deadline, "<20110101");
}
@@ -853,7 +877,8 @@
"1.2.3.4", // version
"theurl", // more info
"true", // prompt
- "thecodebase", // dl url
+ "thecodebase/", // dl url
+ "file.signed", // file name
"HASH1234=", // checksum
"false", // needs admin
// overflows int32:
@@ -885,14 +910,14 @@
// convert post_data to string
string post_str(&post_data[0], post_data.size());
EXPECT_NE(post_str.find(
- " <o:ping active=\"1\" a=\"-1\" r=\"-1\"></o:ping>\n"
- " <o:updatecheck"
+ " <ping active=\"1\" a=\"-1\" r=\"-1\"></ping>\n"
+ " <updatecheck"
" targetversionprefix=\"\""
- "></o:updatecheck>\n"),
+ "></updatecheck>\n"),
string::npos);
EXPECT_NE(post_str.find("hardware_class=\"OEM MODEL 09235 7471\""),
string::npos);
- EXPECT_EQ(post_str.find("o:event"), string::npos);
+ EXPECT_EQ(post_str.find("event"), string::npos);
}
@@ -915,14 +940,14 @@
// convert post_data to string
string post_str(&post_data[0], post_data.size());
EXPECT_NE(post_str.find(
- " <o:ping active=\"1\" a=\"-1\" r=\"-1\"></o:ping>\n"
- " <o:updatecheck"
+ " <ping active=\"1\" a=\"-1\" r=\"-1\"></ping>\n"
+ " <updatecheck"
" targetversionprefix=\"\""
- "></o:updatecheck>\n"),
+ "></updatecheck>\n"),
string::npos);
EXPECT_NE(post_str.find("hardware_class=\"OEM MODEL 09235 7471\""),
string::npos);
- EXPECT_EQ(post_str.find("o:event"), string::npos);
+ EXPECT_EQ(post_str.find("event"), string::npos);
}
TEST(OmahaRequestActionTest, FormatSuccessEventOutputTest) {
@@ -934,12 +959,12 @@
// convert post_data to string
string post_str(&post_data[0], post_data.size());
string expected_event = StringPrintf(
- " <o:event eventtype=\"%d\" eventresult=\"%d\"></o:event>\n",
+ " <event eventtype=\"%d\" eventresult=\"%d\"></event>\n",
OmahaEvent::kTypeUpdateDownloadStarted,
OmahaEvent::kResultSuccess);
EXPECT_NE(post_str.find(expected_event), string::npos);
- EXPECT_EQ(post_str.find("o:ping"), string::npos);
- EXPECT_EQ(post_str.find("o:updatecheck"), string::npos);
+ EXPECT_EQ(post_str.find("ping"), string::npos);
+ EXPECT_EQ(post_str.find("updatecheck"), string::npos);
}
TEST(OmahaRequestActionTest, FormatErrorEventOutputTest) {
@@ -953,13 +978,13 @@
// convert post_data to string
string post_str(&post_data[0], post_data.size());
string expected_event = StringPrintf(
- " <o:event eventtype=\"%d\" eventresult=\"%d\" "
- "errorcode=\"%d\"></o:event>\n",
+ " <event eventtype=\"%d\" eventresult=\"%d\" "
+ "errorcode=\"%d\"></event>\n",
OmahaEvent::kTypeDownloadComplete,
OmahaEvent::kResultError,
kActionCodeError);
EXPECT_NE(post_str.find(expected_event), string::npos);
- EXPECT_EQ(post_str.find("o:updatecheck"), string::npos);
+ EXPECT_EQ(post_str.find("updatecheck"), string::npos);
}
TEST(OmahaRequestActionTest, IsEventTest) {
@@ -1064,13 +1089,13 @@
NULL,
&post_data));
string post_str(&post_data[0], post_data.size());
- EXPECT_NE(post_str.find("<o:ping active=\"1\" a=\"6\" r=\"5\"></o:ping>"),
+ EXPECT_NE(post_str.find("<ping active=\"1\" a=\"6\" r=\"5\"></ping>"),
string::npos);
if (ping_only) {
- EXPECT_EQ(post_str.find("o:updatecheck"), string::npos);
+ EXPECT_EQ(post_str.find("updatecheck"), string::npos);
EXPECT_EQ(post_str.find("previousversion"), string::npos);
} else {
- EXPECT_NE(post_str.find("o:updatecheck"), string::npos);
+ EXPECT_NE(post_str.find("updatecheck"), string::npos);
EXPECT_NE(post_str.find("previousversion"), string::npos);
}
}
@@ -1096,7 +1121,7 @@
NULL,
&post_data));
string post_str(&post_data[0], post_data.size());
- EXPECT_NE(post_str.find("<o:ping active=\"1\" a=\"3\"></o:ping>"),
+ EXPECT_NE(post_str.find("<ping active=\"1\" a=\"3\"></ping>"),
string::npos);
}
@@ -1120,7 +1145,7 @@
NULL,
&post_data));
string post_str(&post_data[0], post_data.size());
- EXPECT_NE(post_str.find("<o:ping active=\"1\" r=\"4\"></o:ping>\n"),
+ EXPECT_NE(post_str.find("<ping active=\"1\" r=\"4\"></ping>\n"),
string::npos);
}
@@ -1145,7 +1170,7 @@
NULL,
&post_data));
string post_str(&post_data[0], post_data.size());
- EXPECT_EQ(post_str.find("o:ping"), string::npos);
+ EXPECT_EQ(post_str.find("ping"), string::npos);
}
TEST(OmahaRequestActionTest, IgnoreEmptyPingTest) {
@@ -1187,18 +1212,17 @@
ASSERT_TRUE(
TestUpdateCheck(&prefs,
kDefaultTestParams,
- "<?xml version=\"1.0\" encoding=\"UTF-8\"?><gupdate "
- "xmlns=\"http://www.google.com/update2/response\" "
- "protocol=\"2.0\"><daystart elapsed_seconds=\"100\"/>"
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response "
+ "protocol=\"3.0\"><daystart elapsed_seconds=\"100\"/>"
"<app appid=\"foo\" status=\"ok\"><ping status=\"ok\"/>"
- "<updatecheck status=\"noupdate\"/></app></gupdate>",
+ "<updatecheck status=\"noupdate\"/></app></response>",
-1,
false, // ping_only
kActionCodeSuccess,
NULL,
&post_data));
string post_str(&post_data[0], post_data.size());
- EXPECT_EQ(post_str.find("o:ping"), string::npos);
+ EXPECT_EQ(post_str.find("ping"), string::npos);
}
TEST(OmahaRequestActionTest, LastPingDayUpdateTest) {
@@ -1220,11 +1244,10 @@
ASSERT_TRUE(
TestUpdateCheck(&prefs,
kDefaultTestParams,
- "<?xml version=\"1.0\" encoding=\"UTF-8\"?><gupdate "
- "xmlns=\"http://www.google.com/update2/response\" "
- "protocol=\"2.0\"><daystart elapsed_seconds=\"200\"/>"
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response "
+ "protocol=\"3.0\"><daystart elapsed_seconds=\"200\"/>"
"<app appid=\"foo\" status=\"ok\"><ping status=\"ok\"/>"
- "<updatecheck status=\"noupdate\"/></app></gupdate>",
+ "<updatecheck status=\"noupdate\"/></app></response>",
-1,
false, // ping_only
kActionCodeSuccess,
@@ -1239,11 +1262,10 @@
ASSERT_TRUE(
TestUpdateCheck(&prefs,
kDefaultTestParams,
- "<?xml version=\"1.0\" encoding=\"UTF-8\"?><gupdate "
- "xmlns=\"http://www.google.com/update2/response\" "
- "protocol=\"2.0\"><daystart blah=\"200\"/>"
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response "
+ "protocol=\"3.0\"><daystart blah=\"200\"/>"
"<app appid=\"foo\" status=\"ok\"><ping status=\"ok\"/>"
- "<updatecheck status=\"noupdate\"/></app></gupdate>",
+ "<updatecheck status=\"noupdate\"/></app></response>",
-1,
false, // ping_only
kActionCodeSuccess,
@@ -1258,11 +1280,10 @@
ASSERT_TRUE(
TestUpdateCheck(&prefs,
kDefaultTestParams,
- "<?xml version=\"1.0\" encoding=\"UTF-8\"?><gupdate "
- "xmlns=\"http://www.google.com/update2/response\" "
- "protocol=\"2.0\"><daystart elapsed_seconds=\"x\"/>"
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response "
+ "protocol=\"3.0\"><daystart elapsed_seconds=\"x\"/>"
"<app appid=\"foo\" status=\"ok\"><ping status=\"ok\"/>"
- "<updatecheck status=\"noupdate\"/></app></gupdate>",
+ "<updatecheck status=\"noupdate\"/></app></response>",
-1,
false, // ping_only
kActionCodeSuccess,
@@ -1339,7 +1360,8 @@
"1.2.3.4", // version
"http://more/info",
"true", // prompt
- "http://code/base", // dl url
+ "http://code/base/", // dl url
+ "file.signed", // file name
"HASH1234=", // checksum
"false", // needs admin
"123", // size
@@ -1385,7 +1407,8 @@
"1.2.3.4", // version
"http://more/info",
"true", // prompt
- "http://code/base", // dl url
+ "http://code/base/", // dl url
+ "file.signed", // file name
"HASH1234=", // checksum
"false", // needs admin
"123", // size
diff --git a/sample_omaha_v3_response.xml b/sample_omaha_v3_response.xml
new file mode 100644
index 0000000..cb3bff7
--- /dev/null
+++ b/sample_omaha_v3_response.xml
@@ -0,0 +1,19 @@
+<response protocol="3.0" server="prod">
+ <daystart elapsed_seconds="56652"/>
+ <app appid="{90f229ce-83e2-4faf-8479-e368a34938b1}" status="ok">
+ <updatecheck status="ok">
+ <urls>
+ <url codebase="https://storage.googleapis.com/chromeos-releases-public/canary-channel/canary-channel/3095.0.0/"/>
+ </urls>
+ <manifest version="3095.0.0">
+ <packages>
+ <package hash="HVOmp67vBjPdvpWmOC2Uw4UDwsc=" name="chromeos_3095.0.0_x86-zgb_canary-channel_full_mp-v2.bin-df37843370ddf1e3819a2afeaa934faa.signed" required="true" size="400752559"/>
+ </packages>
+ <actions>
+ <action event="update" run="chromeos_3095.0.0_x86-zgb_canary-channel_full_mp-v2.bin-df37843370ddf1e3819a2afeaa934faa.signed"/>
+ <action ChromeOSVersion="3095.0.0" ChromeVersion="24.0.1307.0" IsDelta="true" MaxDaysToScatter="14" MetadataSignatureRsa="xXrO/LahHlKk3YmqEf1qE0PN587Sc2IJV+FN7J7x1h49waNQIy/QwYO4LaOySgETe5JZXtkAEzzqakfJwxQ2pVfzj1GkExwjd5LTn1He2GvA73B8fKbS4bfP7dbUFwD5039xCwf1U2gezFViOiOPiVURx/pEsdhv+Cqx/3HbjIuj5au2dooSyDxLC5AnODzAKyYfAcjMuiLON+9SqmctJW+VjzdY9SbJAnkH2qqVjFyBKAXsYT+hOTIJ3MJpg8OSVxMMtGB99PxbOJ52F37d2Y5Fws/AUkNnNEsan/WRJA1kuWoS6rpeR8JQYuVhLiK2u/KpOcvMVRw3Q2VUxtcAGw==" MetadataSize="58315" event="postinstall" sha256="DIAVxoI+8NpsudUawOA5U92VHlaxQBS3ejN4EPM6T2A="/>
+ </actions>
+ </manifest>
+ </updatecheck>
+ </app>
+</response>