Index: omaha_request_action.cc |
diff --git a/omaha_request_action.cc b/omaha_request_action.cc |
index cca2094dfa5188d45e4e89151fa828f0110074c2..7eca9e07e434ce0d837958abc6d9812a2e5600b1 100644 |
--- a/omaha_request_action.cc |
+++ b/omaha_request_action.cc |
@@ -11,11 +11,15 @@ |
#include <libxml/xpathInternals.h> |
#include "base/string_util.h" |
+#include "base/time.h" |
#include "chromeos/obsolete_logging.h" |
#include "update_engine/action_pipe.h" |
#include "update_engine/omaha_request_params.h" |
+#include "update_engine/prefs_interface.h" |
#include "update_engine/utils.h" |
+using base::Time; |
+using base::TimeDelta; |
using std::string; |
namespace chromeos_update_engine { |
@@ -54,13 +58,43 @@ class ScopedPtrXmlXPathContextFree { |
} |
}; |
+// Returns true if |ping_days| has a value that needs to be sent, |
+// false otherwise. |
+bool ShouldPing(int ping_days) { |
+ return ping_days > 0 || ping_days == OmahaRequestAction::kNeverPinged; |
+} |
+ |
+// Returns an XML ping element attribute assignment with attribute |
+// |name| and value |ping_days| if |ping_days| has a value that needs |
+// to be sent, or an empty string otherwise. |
+string GetPingAttribute(const string& name, int ping_days) { |
+ if (ShouldPing(ping_days)) { |
+ return StringPrintf(" %s=\"%d\"", name.c_str(), ping_days); |
+ } |
+ return ""; |
+} |
+ |
+// Returns an XML ping element if any of the elapsed days need to be |
+// sent, or an empty string otherwise. |
+string GetPingBody(int ping_active_days, int ping_roll_call_days) { |
+ 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%s%s></o:ping>\n", |
+ ping_active.c_str(), |
+ ping_roll_call.c_str()); |
+ } |
+ return ""; |
+} |
+ |
string FormatRequest(const OmahaEvent* event, |
- const OmahaRequestParams& params) { |
+ const OmahaRequestParams& params, |
+ int ping_active_days, |
+ int ping_roll_call_days) { |
string body; |
if (event == NULL) { |
- body = string( |
- " <o:ping active=\"0\"></o:ping>\n" |
- " <o:updatecheck></o:updatecheck>\n"); |
+ body = GetPingBody(ping_active_days, ping_roll_call_days) + |
+ " <o:updatecheck></o:updatecheck>\n"; |
} else { |
// The error code is an optional attribute so append it only if |
// the result is not success. |
@@ -72,13 +106,11 @@ string FormatRequest(const OmahaEvent* event, |
" <o:event eventtype=\"%d\" eventresult=\"%d\"%s></o:event>\n", |
event->type, event->result, error_code.c_str()); |
} |
- return string("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" |
- "<o:gupdate xmlns:o=\"http://www.google.com/update2/request\" " |
- "version=\"" + XmlEncode(kGupdateVersion) + "\" " |
- "updaterversion=\"" + XmlEncode(kGupdateVersion) + "\" " |
- "protocol=\"2.0\" " |
- "machineid=\"") + XmlEncode(params.machine_id) + |
- "\" ismachine=\"1\" userid=\"" + XmlEncode(params.user_id) + "\">\n" |
+ return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" |
+ "<o:gupdate xmlns:o=\"http://www.google.com/update2/request\" " |
+ "version=\"" + XmlEncode(kGupdateVersion) + "\" " |
+ "updaterversion=\"" + XmlEncode(kGupdateVersion) + "\" " |
+ "protocol=\"2.0\" ismachine=\"1\">\n" |
" <o:os version=\"" + XmlEncode(params.os_version) + "\" platform=\"" + |
XmlEncode(params.os_platform) + "\" sp=\"" + |
XmlEncode(params.os_sp) + "\"></o:os>\n" |
@@ -91,6 +123,7 @@ string FormatRequest(const OmahaEvent* event, |
" </o:app>\n" |
"</o:gupdate>\n"; |
} |
+ |
} // namespace {} |
// Encodes XML entities in a given string with libxml2. input must be |
@@ -109,18 +142,58 @@ string XmlEncode(const string& input) { |
return string(reinterpret_cast<const char *>(str.get())); |
} |
-OmahaRequestAction::OmahaRequestAction(const OmahaRequestParams& params, |
+OmahaRequestAction::OmahaRequestAction(PrefsInterface* prefs, |
+ const OmahaRequestParams& params, |
OmahaEvent* event, |
HttpFetcher* http_fetcher) |
- : params_(params), |
+ : prefs_(prefs), |
+ params_(params), |
event_(event), |
- http_fetcher_(http_fetcher) {} |
+ http_fetcher_(http_fetcher), |
+ ping_active_days_(0), |
+ ping_roll_call_days_(0) {} |
OmahaRequestAction::~OmahaRequestAction() {} |
+// Calculates the value to use for the ping days parameter. |
+int OmahaRequestAction::CalculatePingDays(const string& key) { |
+ int days = kNeverPinged; |
+ int64_t last_ping = 0; |
+ if (prefs_->GetInt64(key, &last_ping) && last_ping >= 0) { |
+ days = (Time::Now() - Time::FromInternalValue(last_ping)).InDays(); |
+ if (days < 0) { |
+ // If |days| is negative, then the system clock must have jumped |
+ // back in time since the ping was sent. Mark the value so that |
+ // it doesn't get sent to the server but we still update the |
+ // last ping daystart preference. This way the next ping time |
+ // will be correct, hopefully. |
+ days = kPingTimeJump; |
+ LOG(WARNING) << |
+ "System clock jumped back in time. Resetting ping daystarts."; |
+ } |
+ } |
+ return days; |
+} |
+ |
+void OmahaRequestAction::InitPingDays() { |
+ // We send pings only along with update checks, not with events. |
+ if (IsEvent()) { |
+ return; |
+ } |
+ // TODO(petkov): Figure a way to distinguish active use pings |
+ // vs. roll call pings. Currently, the two pings are identical. A |
+ // fix needs to change this code as well as UpdateLastPingDays. |
+ ping_active_days_ = CalculatePingDays(kPrefsLastActivePingDay); |
+ ping_roll_call_days_ = CalculatePingDays(kPrefsLastRollCallPingDay); |
+} |
+ |
void OmahaRequestAction::PerformAction() { |
http_fetcher_->set_delegate(this); |
- string request_post(FormatRequest(event_.get(), params_)); |
+ InitPingDays(); |
+ string request_post(FormatRequest(event_.get(), |
+ params_, |
+ ping_active_days_, |
+ ping_roll_call_days_)); |
http_fetcher_->SetPostData(request_post.data(), request_post.size()); |
LOG(INFO) << "Posting an Omaha request to " << params_.update_url; |
LOG(INFO) << "Request: " << request_post; |
@@ -199,6 +272,39 @@ off_t ParseInt(const string& str) { |
} |
return ret; |
} |
+ |
+// 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"; |
+ |
+ scoped_ptr_malloc<xmlXPathObject, ScopedPtrXmlXPathObjectFree> |
+ xpath_nodeset(GetNodeSet(doc, |
+ ConstXMLStr(kDaystartNodeXpath), |
+ ConstXMLStr(kNamespace), |
+ ConstXMLStr(kNsUrl))); |
+ TEST_AND_RETURN_FALSE(xpath_nodeset.get()); |
+ xmlNodeSet* nodeset = xpath_nodeset->nodesetval; |
+ TEST_AND_RETURN_FALSE(nodeset && nodeset->nodeNr >= 1); |
+ xmlNode* daystart_node = nodeset->nodeTab[0]; |
+ TEST_AND_RETURN_FALSE(xmlHasProp(daystart_node, |
+ ConstXMLStr("elapsed_seconds"))); |
+ |
+ int64_t elapsed_seconds = 0; |
+ TEST_AND_RETURN_FALSE(StringToInt64(XmlGetProperty(daystart_node, |
+ "elapsed_seconds"), |
+ &elapsed_seconds)); |
+ TEST_AND_RETURN_FALSE(elapsed_seconds >= 0); |
+ |
+ // Remember the local time that matches the server's last midnight |
+ // time. |
+ Time daystart = Time::Now() - TimeDelta::FromSeconds(elapsed_seconds); |
+ prefs->SetInt64(kPrefsLastActivePingDay, daystart.ToInternalValue()); |
+ prefs->SetInt64(kPrefsLastRollCallPingDay, daystart.ToInternalValue()); |
+ return true; |
+} |
} // namespace {} |
// If the transfer was successful, this uses libxml2 to parse the response |
@@ -236,6 +342,16 @@ void OmahaRequestAction::TransferComplete(HttpFetcher *fetcher, |
return; |
} |
+ // If a ping was sent, update the last ping day preferences based on |
+ // the server daystart response. |
+ if (ShouldPing(ping_active_days_) || |
+ ShouldPing(ping_roll_call_days_) || |
+ ping_active_days_ == kPingTimeJump || |
+ ping_roll_call_days_ == kPingTimeJump) { |
+ LOG_IF(ERROR, !UpdateLastPingDays(doc.get(), prefs_)) |
+ << "Failed to update the last ping day preferences!"; |
+ } |
+ |
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"); |