| OLD | NEW |
| (Empty) |
| 1 // Copyright 2010 Google Inc. | |
| 2 // | |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
| 4 // you may not use this file except in compliance with the License. | |
| 5 // You may obtain a copy of the License at | |
| 6 // | |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 // | |
| 9 // Unless required by applicable law or agreed to in writing, software | |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, | |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 12 // See the License for the specific language governing permissions and | |
| 13 // limitations under the License. | |
| 14 // ======================================================================== | |
| 15 | |
| 16 #include "omaha/common/ping.h" | |
| 17 #include "base/scoped_ptr.h" | |
| 18 #include "omaha/base/constants.h" | |
| 19 #include "omaha/base/debug.h" | |
| 20 #include "omaha/base/logging.h" | |
| 21 #include "omaha/base/scoped_any.h" | |
| 22 #include "omaha/base/scoped_impersonation.h" | |
| 23 #include "omaha/base/string.h" | |
| 24 #include "omaha/base/utils.h" | |
| 25 #include "omaha/base/vista_utils.h" | |
| 26 #include "omaha/common/app_registry_utils.h" | |
| 27 #include "omaha/common/command_line.h" | |
| 28 #include "omaha/common/command_line_builder.h" | |
| 29 #include "omaha/common/config_manager.h" | |
| 30 #include "omaha/common/goopdate_utils.h" | |
| 31 #include "omaha/common/update_request.h" | |
| 32 #include "omaha/common/update_response.h" | |
| 33 #include "omaha/goopdate/app.h" | |
| 34 #include "omaha/goopdate/app_bundle.h" | |
| 35 #include "omaha/goopdate/update_request_utils.h" | |
| 36 #include "omaha/goopdate/update_response_utils.h" | |
| 37 | |
| 38 namespace omaha { | |
| 39 | |
| 40 const TCHAR* const Ping::kRegKeyPing = _T("Pings"); | |
| 41 const time64 Ping::kPingExpiry100ns = 10 * kDaysTo100ns; // 10 days. | |
| 42 | |
| 43 Ping::Ping(bool is_machine, | |
| 44 const CString& session_id, | |
| 45 const CString& install_source) | |
| 46 : is_machine_(is_machine), | |
| 47 ping_request_(xml::UpdateRequest::Create(is_machine, | |
| 48 session_id, | |
| 49 install_source, | |
| 50 CString())) { | |
| 51 } | |
| 52 | |
| 53 Ping::~Ping() { | |
| 54 } | |
| 55 | |
| 56 void Ping::BuildRequest(const App* app, bool is_update_check) { | |
| 57 update_request_utils::BuildRequest(app, is_update_check, ping_request_.get()); | |
| 58 } | |
| 59 | |
| 60 void Ping::LoadAppDataFromExtraArgs(const CommandLineExtraArgs& extra_args) { | |
| 61 const CString installation_id = GuidToString(extra_args.installation_id); | |
| 62 for (size_t i = 0; i != extra_args.apps.size(); ++i) { | |
| 63 AppData app_data; | |
| 64 app_data.app_id = GuidToString(extra_args.apps[i].app_guid); | |
| 65 app_data.language = extra_args.language; | |
| 66 app_data.brand_code = extra_args.brand_code; | |
| 67 app_data.client_id = extra_args.client_id; | |
| 68 app_data.installation_id = installation_id; | |
| 69 app_data.experiment_labels = extra_args.apps[i].experiment_labels; | |
| 70 apps_data_.push_back(app_data); | |
| 71 } | |
| 72 | |
| 73 omaha_data_.app_id = kGoogleUpdateAppId; | |
| 74 omaha_data_.language = extra_args.language; | |
| 75 omaha_data_.brand_code = extra_args.brand_code; | |
| 76 omaha_data_.client_id = extra_args.client_id; | |
| 77 omaha_data_.installation_id = installation_id; | |
| 78 omaha_data_.experiment_labels = extra_args.experiment_labels; | |
| 79 } | |
| 80 | |
| 81 void Ping::LoadOmahaDataFromRegistry() { | |
| 82 omaha_data_.app_id = kGoogleUpdateAppId; | |
| 83 app_registry_utils::GetClientStateData( | |
| 84 is_machine_, | |
| 85 kGoogleUpdateAppId, | |
| 86 NULL, | |
| 87 NULL, // ap is not used yet. | |
| 88 &omaha_data_.language, | |
| 89 &omaha_data_.brand_code, | |
| 90 &omaha_data_.client_id, | |
| 91 &omaha_data_.installation_id, | |
| 92 &omaha_data_.experiment_labels); | |
| 93 } | |
| 94 | |
| 95 void Ping::LoadAppDataFromRegistry(const std::vector<CString>& app_ids) { | |
| 96 for (size_t i = 0; i != app_ids.size(); ++i) { | |
| 97 AppData app_data; | |
| 98 app_data.app_id = app_ids[i]; | |
| 99 app_registry_utils::GetClientStateData( | |
| 100 is_machine_, | |
| 101 app_data.app_id, | |
| 102 &app_data.pv, | |
| 103 NULL, // ap is not used yet. | |
| 104 &app_data.language, | |
| 105 &app_data.brand_code, | |
| 106 &app_data.client_id, | |
| 107 &app_data.installation_id, | |
| 108 &app_data.experiment_labels); | |
| 109 apps_data_.push_back(app_data); | |
| 110 } | |
| 111 | |
| 112 LoadOmahaDataFromRegistry(); | |
| 113 } | |
| 114 | |
| 115 HRESULT Ping::Send(bool is_fire_and_forget) { | |
| 116 CORE_LOG(L3, (_T("[Ping::Send]"))); | |
| 117 | |
| 118 ASSERT1(ConfigManager::Instance()->CanUseNetwork(is_machine_)); | |
| 119 | |
| 120 if (ping_request_->IsEmpty()) { | |
| 121 CORE_LOG(L3, (_T("[Ping::Send did not send empty ping]"))); | |
| 122 return S_FALSE; | |
| 123 } | |
| 124 | |
| 125 CString request_string; | |
| 126 HRESULT hr = BuildRequestString(&request_string); | |
| 127 if (FAILED(hr)) { | |
| 128 CORE_LOG(LE, (_T("[BuildRequestString failed][0x%08x]"), hr)); | |
| 129 return hr; | |
| 130 } | |
| 131 | |
| 132 const DWORD wait_timeout_ms = is_fire_and_forget ? 0 : INFINITE; | |
| 133 hr = SendUsingGoogleUpdate(request_string, wait_timeout_ms); | |
| 134 if (SUCCEEDED(hr)) { | |
| 135 return hr; | |
| 136 } | |
| 137 | |
| 138 CORE_LOG(LE, (_T("[Ping::SendUsingGoogleUpdate failed][0x%x]"), hr)); | |
| 139 | |
| 140 hr = SendInProcess(request_string); | |
| 141 if (SUCCEEDED(hr)) { | |
| 142 return hr; | |
| 143 } | |
| 144 | |
| 145 CORE_LOG(LE, (_T("[Ping::SendInProcess failed][0x%x]"), hr)); | |
| 146 | |
| 147 return PersistPing(is_machine_, request_string); | |
| 148 } | |
| 149 | |
| 150 void Ping::BuildOmahaPing(const CString& version, | |
| 151 const CString& next_version, | |
| 152 const PingEventPtr& ping_event) { | |
| 153 xml::request::App app(BuildOmahaApp(version, next_version)); | |
| 154 app.ping_events.push_back(ping_event); | |
| 155 ping_request_->AddApp(app); | |
| 156 } | |
| 157 | |
| 158 void Ping::BuildOmahaPing(const CString& version, | |
| 159 const CString& next_version, | |
| 160 const PingEventPtr& ping_event1, | |
| 161 const PingEventPtr& ping_event2) { | |
| 162 xml::request::App app(BuildOmahaApp(version, next_version)); | |
| 163 app.ping_events.push_back(ping_event1); | |
| 164 app.ping_events.push_back(ping_event2); | |
| 165 ping_request_->AddApp(app); | |
| 166 } | |
| 167 | |
| 168 xml::request::App Ping::BuildOmahaApp(const CString& version, | |
| 169 const CString& next_version) const { | |
| 170 xml::request::App app; | |
| 171 | |
| 172 app.app_id = omaha_data_.app_id; | |
| 173 app.lang = omaha_data_.language; | |
| 174 app.brand_code = omaha_data_.brand_code; | |
| 175 app.client_id = omaha_data_.client_id; | |
| 176 app.experiments = omaha_data_.experiment_labels; | |
| 177 app.iid = omaha_data_.installation_id; | |
| 178 | |
| 179 app.version = version; | |
| 180 app.next_version = next_version; | |
| 181 | |
| 182 return app; | |
| 183 } | |
| 184 | |
| 185 void Ping::BuildAppsPing(const PingEventPtr& ping_event) { | |
| 186 for (size_t i = 0; i != apps_data_.size(); ++i) { | |
| 187 xml::request::App app; | |
| 188 | |
| 189 app.version = apps_data_[i].pv; | |
| 190 app.app_id = apps_data_[i].app_id; | |
| 191 app.lang = apps_data_[i].language; | |
| 192 app.brand_code = apps_data_[i].brand_code; | |
| 193 app.client_id = apps_data_[i].client_id; | |
| 194 app.experiments = apps_data_[i].experiment_labels; | |
| 195 app.iid = apps_data_[i].installation_id; | |
| 196 | |
| 197 app.ping_events.push_back(ping_event); | |
| 198 ping_request_->AddApp(app); | |
| 199 } | |
| 200 } | |
| 201 | |
| 202 HRESULT Ping::SendUsingGoogleUpdate(const CString& request_string, | |
| 203 DWORD wait_timeout_ms) const { | |
| 204 CStringA request_string_utf8(WideToUtf8(request_string)); | |
| 205 CStringA ping_string_utf8; | |
| 206 WebSafeBase64Escape(request_string_utf8, &ping_string_utf8); | |
| 207 | |
| 208 CommandLineBuilder builder(COMMANDLINE_MODE_PING); | |
| 209 builder.set_ping_string(Utf8ToWideChar(ping_string_utf8, | |
| 210 ping_string_utf8.GetLength())); | |
| 211 CString args = builder.GetCommandLineArgs(); | |
| 212 | |
| 213 scoped_process ping_process; | |
| 214 HRESULT hr = goopdate_utils::StartGoogleUpdateWithArgs(is_machine_, | |
| 215 args, | |
| 216 address(ping_process)); | |
| 217 if (FAILED(hr)) { | |
| 218 CORE_LOG(LE, (_T("[failed to start ping process][0x%08x]"), hr)); | |
| 219 return hr; | |
| 220 } | |
| 221 | |
| 222 if (wait_timeout_ms) { | |
| 223 DWORD result = ::WaitForSingleObject(get(ping_process), wait_timeout_ms); | |
| 224 DWORD exit_code(0); | |
| 225 if (result == WAIT_OBJECT_0 && | |
| 226 ::GetExitCodeProcess(get(ping_process), &exit_code)) { | |
| 227 ASSERT1(exit_code == 0 || FAILED(exit_code)); | |
| 228 return (exit_code == 0) ? S_OK : exit_code; | |
| 229 } else { | |
| 230 if (result == WAIT_TIMEOUT) { | |
| 231 CORE_LOG(LW, (_T("[ping process did not finish in time][pid=%u]"), | |
| 232 ::GetProcessId(get(ping_process)))); | |
| 233 VERIFY1(::TerminateProcess(get(ping_process), UINT_MAX)); | |
| 234 } | |
| 235 return E_FAIL; | |
| 236 } | |
| 237 } | |
| 238 | |
| 239 return S_OK; | |
| 240 } | |
| 241 | |
| 242 HRESULT Ping::SendInProcess(const CString& request_string) const { | |
| 243 HRESULT hr = SendString(is_machine_, HeadersVector(), request_string); | |
| 244 if (FAILED(hr)) { | |
| 245 CORE_LOG(LE, (_T("[SendString failed][0x%08x]"), hr)); | |
| 246 return hr; | |
| 247 } | |
| 248 | |
| 249 return S_OK; | |
| 250 } | |
| 251 | |
| 252 HRESULT Ping::BuildRequestString(CString* request_string) const { | |
| 253 ASSERT1(request_string); | |
| 254 return ping_request_->Serialize(request_string); | |
| 255 } | |
| 256 | |
| 257 CString Ping::GetPingRegPath(bool is_machine) { | |
| 258 CString ping_reg_path = is_machine ? MACHINE_REG_UPDATE : USER_REG_UPDATE; | |
| 259 return AppendRegKeyPath(ping_reg_path, kRegKeyPing); | |
| 260 } | |
| 261 | |
| 262 HRESULT Ping::LoadPersistedPings(bool is_machine, PingsVector* pings) { | |
| 263 ASSERT1(pings); | |
| 264 | |
| 265 RegKey ping_reg_key; | |
| 266 HRESULT hr = ping_reg_key.Open(GetPingRegPath(is_machine), KEY_READ); | |
| 267 if (FAILED(hr)) { | |
| 268 CORE_LOG(LW, (_T("[Unable to open Ping regkey][0x%x]"), hr)); | |
| 269 return hr; | |
| 270 } | |
| 271 | |
| 272 int num_pings = ping_reg_key.GetValueCount(); | |
| 273 for (int i = 0; i < num_pings; ++i) { | |
| 274 CString persisted_time_string; | |
| 275 hr = ping_reg_key.GetValueNameAt(i, &persisted_time_string, NULL); | |
| 276 if (FAILED(hr)) { | |
| 277 CORE_LOG(LW, (_T("[GetValueNameAt failed][%d]"), i)); | |
| 278 continue; | |
| 279 } | |
| 280 | |
| 281 time64 persisted_time = _tcstoui64(persisted_time_string, NULL, 10); | |
| 282 if (persisted_time == 0 || persisted_time == _UI64_MAX) { | |
| 283 CORE_LOG(LW, (_T("[Incorrect time value][%s]"), persisted_time_string)); | |
| 284 continue; | |
| 285 } | |
| 286 | |
| 287 CString ping_string; | |
| 288 hr = ping_reg_key.GetValue(persisted_time_string, &ping_string); | |
| 289 if (FAILED(hr)) { | |
| 290 CORE_LOG(LW, (_T("[GetValue failed][%s]"), persisted_time_string)); | |
| 291 continue; | |
| 292 } | |
| 293 | |
| 294 pings->push_back(std::make_pair(persisted_time, ping_string)); | |
| 295 } | |
| 296 | |
| 297 return S_OK; | |
| 298 } | |
| 299 | |
| 300 bool Ping::IsPingExpired(time64 persisted_time) { | |
| 301 const time64 now = GetCurrent100NSTime(); | |
| 302 | |
| 303 if (now < persisted_time) { | |
| 304 CORE_LOG(LW, (_T("[Incorrect clock time][%I64u][%I64u]"), | |
| 305 now, persisted_time)); | |
| 306 return true; | |
| 307 } | |
| 308 | |
| 309 const time64 time_difference = now - persisted_time; | |
| 310 CORE_LOG(L3, (_T("[%I64u][%I64u][%I64u]"), | |
| 311 now, persisted_time, time_difference)); | |
| 312 | |
| 313 const bool result = time_difference >= kPingExpiry100ns; | |
| 314 CORE_LOG(L3, (_T("[IsPingExpired][%d]"), result)); | |
| 315 return result; | |
| 316 } | |
| 317 | |
| 318 HRESULT Ping::DeletePersistedPing(bool is_machine, time64 persisted_time) { | |
| 319 CString persisted_time_string; | |
| 320 persisted_time_string.Format(_T("%I64u"), persisted_time); | |
| 321 CORE_LOG(L3, (_T("[Ping::DeletePersistedPing][%s]"), persisted_time_string)); | |
| 322 | |
| 323 CString ping_reg_path(GetPingRegPath(is_machine)); | |
| 324 HRESULT hr = RegKey::DeleteValue(ping_reg_path, persisted_time_string); | |
| 325 | |
| 326 if (RegKey::IsKeyEmpty(ping_reg_path)) { | |
| 327 VERIFY1(SUCCEEDED(RegKey::DeleteKey(ping_reg_path))); | |
| 328 } | |
| 329 | |
| 330 return hr; | |
| 331 } | |
| 332 | |
| 333 HRESULT Ping::PersistPing(bool is_machine, const CString& ping_string) { | |
| 334 CString time_now_str; | |
| 335 time_now_str.Format(_T("%I64u"), GetCurrent100NSTime()); | |
| 336 CORE_LOG(L3, (_T("[Ping::PersistPing][%s][%s]"), time_now_str, ping_string)); | |
| 337 | |
| 338 return RegKey::SetValue(GetPingRegPath(is_machine), | |
| 339 time_now_str, | |
| 340 ping_string); | |
| 341 } | |
| 342 | |
| 343 HRESULT Ping::SendPersistedPings(bool is_machine) { | |
| 344 PingsVector pings; | |
| 345 HRESULT hr = LoadPersistedPings(is_machine, &pings); | |
| 346 if (FAILED(hr) && (hr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))) { | |
| 347 return hr; | |
| 348 } | |
| 349 | |
| 350 for (size_t i = 0; i != pings.size(); ++i) { | |
| 351 time64 persisted_time = pings[i].first; | |
| 352 int32 request_age = Time64ToInt32(GetCurrent100NSTime()) - | |
| 353 Time64ToInt32(persisted_time); | |
| 354 const CString& ping_string(pings[i].second); | |
| 355 | |
| 356 CORE_LOG(L3, (_T("[Resending ping][%I64u][%d][%s]"), | |
| 357 persisted_time, request_age, ping_string)); | |
| 358 | |
| 359 CString request_age_string; | |
| 360 request_age_string.Format(_T("%d"), request_age); | |
| 361 HeadersVector headers; | |
| 362 headers.push_back(std::make_pair(kHeaderXRequestAge, request_age_string)); | |
| 363 | |
| 364 hr = SendString(is_machine, headers, ping_string); | |
| 365 | |
| 366 if (SUCCEEDED(hr) || IsPingExpired(persisted_time)) { | |
| 367 CORE_LOG(L3, (_T("[Deleting ping][0x%x]"), hr)); | |
| 368 VERIFY1(SUCCEEDED(DeletePersistedPing(is_machine, persisted_time))); | |
| 369 } | |
| 370 } | |
| 371 | |
| 372 return S_OK; | |
| 373 } | |
| 374 | |
| 375 // TODO(omaha): Ping support for authenticated proxies. | |
| 376 HRESULT Ping::SendString(bool is_machine, | |
| 377 const HeadersVector& headers, | |
| 378 const CString& request_string) { | |
| 379 ASSERT1(ConfigManager::Instance()->CanUseNetwork(is_machine)); | |
| 380 | |
| 381 CORE_LOG(L3, (_T("[ping request string][%s]"), request_string)); | |
| 382 | |
| 383 CString url; | |
| 384 ConfigManager::Instance()->GetPingUrl(&url); | |
| 385 | |
| 386 // Impersonate the user if the caller is machine, running as local system, | |
| 387 // and a user is logged on to the system. | |
| 388 scoped_handle impersonation_token( | |
| 389 goopdate_utils::GetImpersonationTokenForMachineProcess(is_machine)); | |
| 390 scoped_impersonation impersonate_user(get(impersonation_token)); | |
| 391 | |
| 392 WebServicesClient web_service_client(is_machine); | |
| 393 HRESULT hr(web_service_client.Initialize(url, headers, false)); | |
| 394 if (FAILED(hr)) { | |
| 395 CORE_LOG(LE, (_T("[WebServicesClient::Initialize failed][0x%08x]"), hr)); | |
| 396 return hr; | |
| 397 } | |
| 398 | |
| 399 scoped_ptr<xml::UpdateResponse> response(xml::UpdateResponse::Create()); | |
| 400 hr = web_service_client.SendString(&request_string, response.get()); | |
| 401 if (FAILED(hr)) { | |
| 402 CORE_LOG(LE, (_T("[WebServicesClient::SendString failed][0x%08x]"), hr)); | |
| 403 return hr; | |
| 404 } | |
| 405 | |
| 406 // If a ping is sent but the response is corrupted in some way (or we can't | |
| 407 // persist the labels for some reason), returning a failure code would result | |
| 408 // in the ping being persisted and re-sent later. For this reason, we always | |
| 409 // return a success code if we sent the ping, even if following actions fail. | |
| 410 VERIFY1(SUCCEEDED(update_response_utils::ApplyExperimentLabelDeltas( | |
| 411 is_machine, | |
| 412 response.get()))); | |
| 413 | |
| 414 return S_OK; | |
| 415 } | |
| 416 | |
| 417 HRESULT Ping::HandlePing(bool is_machine, const CString& ping_string) { | |
| 418 CORE_LOG(L3, (_T("[Ping::HandlePing][%s]"), ping_string)); | |
| 419 | |
| 420 CStringA ping_string_utf8(WideToUtf8(ping_string)); | |
| 421 | |
| 422 CStringA request_string_utf8; | |
| 423 int out_buffer_length = ping_string_utf8.GetLength(); | |
| 424 char* out_buffer = request_string_utf8.GetBufferSetLength(out_buffer_length); | |
| 425 | |
| 426 out_buffer_length = WebSafeBase64Unescape(ping_string_utf8, | |
| 427 ping_string_utf8.GetLength(), | |
| 428 out_buffer, | |
| 429 out_buffer_length); | |
| 430 ASSERT1(out_buffer_length <= ping_string_utf8.GetLength()); | |
| 431 request_string_utf8.ReleaseBufferSetLength(out_buffer_length); | |
| 432 | |
| 433 CString request_string(Utf8ToWideChar(request_string_utf8, | |
| 434 request_string_utf8.GetLength())); | |
| 435 | |
| 436 return Ping::SendString(is_machine, HeadersVector(), request_string); | |
| 437 } | |
| 438 | |
| 439 } // namespace omaha | |
| 440 | |
| OLD | NEW |