Parse multiple packages from Omaha response.

The multi-payload info are stored in OmahaResponse and InstallPlan, but
we still can only apply the first payload for now.

Bug: 36252799
Test: mma -j
Test: update_engine_unittests

Change-Id: I5ca63944ae9082670d0e67888409374f140d4245
(cherry picked from commit 2aba8a87d4fac245a2e2d238b3159f8eabce630f)
diff --git a/omaha_request_action.cc b/omaha_request_action.cc
index 94e615d..cf37a9e 100644
--- a/omaha_request_action.cc
+++ b/omaha_request_action.cc
@@ -27,6 +27,7 @@
 #include <base/logging.h>
 #include <base/rand_util.h>
 #include <base/strings/string_number_conversions.h>
+#include <base/strings/string_split.h>
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
 #include <base/time/time.h>
@@ -357,11 +358,15 @@
   string daystart_elapsed_days;
   string daystart_elapsed_seconds;
   vector<string> url_codebase;
-  string package_name;
-  string package_size;
-  string package_hash;
   string manifest_version;
   map<string, string> action_postinstall_attrs;
+
+  struct Package {
+    string name;
+    string size;
+    string hash;
+  };
+  vector<Package> packages;
 };
 
 namespace {
@@ -415,12 +420,12 @@
   } else if (data->current_path == "/response/app/updatecheck/urls/url") {
     // Look at all <url> elements.
     data->url_codebase.push_back(attrs["codebase"]);
-  } else if (data->package_name.empty() && data->current_path ==
+  } else if (data->current_path ==
              "/response/app/updatecheck/manifest/packages/package") {
-    // Only look at the first <package>.
-    data->package_name = attrs["name"];
-    data->package_size = attrs["size"];
-    data->package_hash = attrs["hash_sha256"];
+    // Look at all <package> elements.
+    data->packages.push_back({.name = attrs["name"],
+                              .size = attrs["size"],
+                              .hash = attrs["hash_sha256"]});
   } else if (data->current_path == "/response/app/updatecheck/manifest") {
     // Get the version.
     data->manifest_version = attrs[kTagVersion];
@@ -787,17 +792,14 @@
   if (!ParseStatus(parser_data, output_object, completer))
     return false;
 
-  // Note: ParseUrls MUST be called before ParsePackage as ParsePackage
-  // appends the package name to the URLs populated in this method.
-  if (!ParseUrls(parser_data, output_object, completer))
-    return false;
-
-  if (!ParsePackage(parser_data, output_object, completer))
-    return false;
-
   if (!ParseParams(parser_data, output_object, completer))
     return false;
 
+  // Package has to be parsed after Params now because ParseParams need to make
+  // sure that postinstall action exists.
+  if (!ParsePackage(parser_data, output_object, completer))
+    return false;
+
   return true;
 }
 
@@ -822,60 +824,77 @@
   return true;
 }
 
-bool OmahaRequestAction::ParseUrls(OmahaParserData* parser_data,
-                                   OmahaResponse* output_object,
-                                   ScopedActionCompleter* completer) {
+bool OmahaRequestAction::ParsePackage(OmahaParserData* parser_data,
+                                      OmahaResponse* output_object,
+                                      ScopedActionCompleter* completer) {
+  if (parser_data->packages.empty()) {
+    LOG(ERROR) << "Omaha Response has no packages";
+    completer->set_code(ErrorCode::kOmahaResponseInvalid);
+    return false;
+  }
   if (parser_data->url_codebase.empty()) {
     LOG(ERROR) << "No Omaha Response URLs";
     completer->set_code(ErrorCode::kOmahaResponseInvalid);
     return false;
   }
-
   LOG(INFO) << "Found " << parser_data->url_codebase.size() << " url(s)";
-  output_object->payload_urls.clear();
-  for (const auto& codebase : parser_data->url_codebase) {
-    if (codebase.empty()) {
-      LOG(ERROR) << "Omaha Response URL has empty codebase";
+
+  vector<string> metadata_sizes =
+      base::SplitString(parser_data->action_postinstall_attrs[kTagMetadataSize],
+                        ":",
+                        base::TRIM_WHITESPACE,
+                        base::SPLIT_WANT_ALL);
+  vector<string> metadata_signatures = base::SplitString(
+      parser_data->action_postinstall_attrs[kTagMetadataSignatureRsa],
+      ":",
+      base::TRIM_WHITESPACE,
+      base::SPLIT_WANT_ALL);
+
+  for (size_t i = 0; i < parser_data->packages.size(); i++) {
+    const auto& package = parser_data->packages[i];
+    if (package.name.empty()) {
+      LOG(ERROR) << "Omaha Response has empty package name";
       completer->set_code(ErrorCode::kOmahaResponseInvalid);
       return false;
     }
-    output_object->payload_urls.push_back(codebase);
-  }
+    LOG(INFO) << "Found package " << package.name;
 
-  return true;
-}
+    OmahaResponse::Package out_package;
+    for (const string& codebase : parser_data->url_codebase) {
+      if (codebase.empty()) {
+        LOG(ERROR) << "Omaha Response URL has empty codebase";
+        completer->set_code(ErrorCode::kOmahaResponseInvalid);
+        return false;
+      }
+      out_package.payload_urls.push_back(codebase + package.name);
+    }
+    // Parse the payload size.
+    base::StringToUint64(package.size, &out_package.size);
+    if (out_package.size <= 0) {
+      LOG(ERROR) << "Omaha Response has invalid payload size: " << package.size;
+      completer->set_code(ErrorCode::kOmahaResponseInvalid);
+      return false;
+    }
+    LOG(INFO) << "Payload size = " << out_package.size << " bytes";
 
-bool OmahaRequestAction::ParsePackage(OmahaParserData* parser_data,
-                                      OmahaResponse* output_object,
-                                      ScopedActionCompleter* completer) {
-  if (parser_data->package_name.empty()) {
-    LOG(ERROR) << "Omaha Response has empty package name";
-    completer->set_code(ErrorCode::kOmahaResponseInvalid);
-    return false;
-  }
+    if (i < metadata_sizes.size())
+      base::StringToUint64(metadata_sizes[i], &out_package.metadata_size);
+    LOG(INFO) << "Payload metadata size = " << out_package.metadata_size
+              << " bytes";
 
-  // Append the package name to each URL in our list so that we don't
-  // propagate the urlBase vs packageName distinctions beyond this point.
-  // From now on, we only need to use payload_urls.
-  for (auto& payload_url : output_object->payload_urls)
-    payload_url += parser_data->package_name;
+    if (i < metadata_signatures.size())
+      out_package.metadata_signature = metadata_signatures[i];
+    LOG(INFO) << "Payload metadata signature = "
+              << out_package.metadata_signature;
 
-  // Parse the payload size.
-  off_t size = ParseInt(parser_data->package_size);
-  if (size <= 0) {
-    LOG(ERROR) << "Omaha Response has invalid payload size: " << size;
-    completer->set_code(ErrorCode::kOmahaResponseInvalid);
-    return false;
-  }
-  output_object->size = size;
-
-  LOG(INFO) << "Payload size = " << output_object->size << " bytes";
-
-  output_object->hash = parser_data->package_hash;
-  if (output_object->hash.empty()) {
-    LOG(ERROR) << "Omaha Response has empty hash_sha256 value";
-    completer->set_code(ErrorCode::kOmahaResponseInvalid);
-    return false;
+    out_package.hash = package.hash;
+    if (out_package.hash.empty()) {
+      LOG(ERROR) << "Omaha Response has empty hash_sha256 value";
+      completer->set_code(ErrorCode::kOmahaResponseInvalid);
+      return false;
+    }
+    LOG(INFO) << "Payload hash = " << out_package.hash;
+    output_object->packages.push_back(std::move(out_package));
   }
 
   return true;
@@ -903,8 +922,6 @@
 
   // Get the optional properties one by one.
   output_object->more_info_url = attrs[kTagMoreInfo];
-  output_object->metadata_size = ParseInt(attrs[kTagMetadataSize]);
-  output_object->metadata_signature = attrs[kTagMetadataSignatureRsa];
   output_object->prompt = ParseBool(attrs[kTagPrompt]);
   output_object->deadline = attrs[kTagDeadline];
   output_object->max_days_to_scatter = ParseInt(attrs[kTagMaxDaysToScatter]);
@@ -1134,10 +1151,12 @@
                    next_data_offset + next_data_length;
   }
 
+  // TODO(senj): Fix P2P for multiple package.
   brillo::Blob raw_hash;
-  if (!base::HexStringToBytes(response.hash, &raw_hash))
+  if (!base::HexStringToBytes(response.packages[0].hash, &raw_hash))
     return;
-  string file_id = utils::CalculateP2PFileId(raw_hash, response.size);
+  string file_id =
+      utils::CalculateP2PFileId(raw_hash, response.packages[0].size);
   if (system_state_->p2p_manager()) {
     LOG(INFO) << "Checking if payload is available via p2p, file_id=" << file_id
               << " minimum_size=" << minimum_size;