| OLD | NEW |
| (Empty) |
| 1 // Copyright 2008-2009 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/tools/omahacompatibility/compatibility_test.h" | |
| 17 #include <Windows.h> | |
| 18 #include <tchar.h> | |
| 19 #include "base/basictypes.h" | |
| 20 #include "base/scoped_ptr.h" | |
| 21 #include "omaha/common/apply_tag.h" | |
| 22 #include "omaha/common/constants.h" | |
| 23 #include "omaha/common/debug.h" | |
| 24 #include "omaha/common/error.h" | |
| 25 #include "omaha/common/file.h" | |
| 26 #include "omaha/common/logging.h" | |
| 27 #include "omaha/common/path.h" | |
| 28 #include "omaha/common/reg_key.h" | |
| 29 #include "omaha/common/scope_guard.h" | |
| 30 #include "omaha/common/system.h" | |
| 31 #include "omaha/common/utils.h" | |
| 32 #include "omaha/goopdate/config_manager.h" | |
| 33 #include "omaha/goopdate/const_goopdate.h" | |
| 34 #include "omaha/tools/omahacompatibility/common/ping_observer.h" | |
| 35 #include "omaha/tools/omahacompatibility/httpserver/http_server.h" | |
| 36 #include "omaha/tools/omahacompatibility/httpserver/update_check_handler.h" | |
| 37 #include "omaha/tools/omahacompatibility/httpserver/download_handler.h" | |
| 38 | |
| 39 namespace omaha { | |
| 40 | |
| 41 const TCHAR* const kUpdateCheckUrlPath = _T("/service/update2"); | |
| 42 const TCHAR* const kDownloadUrlPath = _T("/download"); | |
| 43 const DWORD kAuCheckPeriodMs = 5 * 60 * 1000; | |
| 44 const TCHAR* const kHost = _T("localhost"); | |
| 45 const TCHAR* const kUrl = _T("http://localhost:8001/"); | |
| 46 const TCHAR* const kDownloadUrl = _T("http://localhost:8001/download"); | |
| 47 const TCHAR* const kUpdateCheckUrl = | |
| 48 _T("http://localhost:8001/service/update2"); | |
| 49 const int kPort = 8001; | |
| 50 | |
| 51 int CompatibilityTest::Main(bool test_omaha) { | |
| 52 CORE_LOG(L1, (_T("[Main]"))); | |
| 53 | |
| 54 if (IsOmahaInstalled()) { | |
| 55 printf("\nOmaha is already installed on this machine.\n"); | |
| 56 printf("The test may not work correctly.\n"); | |
| 57 printf("Ensure that the version of omaha being pointed to\n"); | |
| 58 printf("is atleast >= the registered versions reported above.\n"); | |
| 59 printf("Are you sure you want to run this test (Y/N)?"); | |
| 60 | |
| 61 int response = getchar(); | |
| 62 if (response == 'n' || response == 'N') { | |
| 63 return -1; | |
| 64 } | |
| 65 } | |
| 66 | |
| 67 // Read the config file. | |
| 68 HRESULT hr = ReadConfigFile(config_file_, | |
| 69 kDownloadUrl, | |
| 70 &config_responses_); | |
| 71 if (FAILED(hr)) { | |
| 72 return -1; | |
| 73 } | |
| 74 | |
| 75 // Create the observer with the guid. | |
| 76 console_writer_.reset(new ConsoleWriter( | |
| 77 GuidToString(config_responses_[0].guid))); | |
| 78 | |
| 79 // Setup the registry to make omaha talk to the local server. | |
| 80 // TODO(omaha): Warn user about changing user settings. | |
| 81 hr = SetupRegistry(config_responses_[0].needs_admin); | |
| 82 if (FAILED(hr)) { | |
| 83 return -1; | |
| 84 } | |
| 85 ON_SCOPE_EXIT(&CompatibilityTest::RestoreRegistry); | |
| 86 | |
| 87 printf("\nStarting the local http server.\n"); | |
| 88 hr = StartHttpServer(); | |
| 89 if (FAILED(hr)) { | |
| 90 printf("\nLocal Http server start failed.\n"); | |
| 91 return -1; | |
| 92 } | |
| 93 | |
| 94 if (test_omaha) { | |
| 95 printf("\nStarting Googleupdate to install application."); | |
| 96 printf("Please wait .....\n"); | |
| 97 printf("Omaha UI should show up in a sec."); | |
| 98 printf("If it does not, please kill this process and restart test.\n"); | |
| 99 | |
| 100 hr = StartGoogleUpdate(); | |
| 101 if (FAILED(hr)) { | |
| 102 // TODO(omaha): Maybe report a verbose error message indicating | |
| 103 // what could be wrong. | |
| 104 printf("\nThe installation failed.\n"); | |
| 105 printf("Please refer to the omaha integration documentation\n"); | |
| 106 printf("for more information, it is possible that when you rerun\n"); | |
| 107 printf("you get a warning about omaha already running,\n"); | |
| 108 printf("please choose to continue in this case\n"); | |
| 109 return -1; | |
| 110 } | |
| 111 | |
| 112 // TODO(omaha): Add a wait on the UI process, instead of depending on | |
| 113 // the user correctly dismissing the UI. | |
| 114 printf("\nThe installation completed successfully.\n"); | |
| 115 printf("Please dismiss the installer UI.\n"); | |
| 116 printf("Continue update test (Y/N)?"); | |
| 117 fflush(stdin); | |
| 118 int response = getchar(); | |
| 119 if (response =='n' || response == 'N') { | |
| 120 return -1; | |
| 121 } | |
| 122 | |
| 123 hr = StartApplicationUpdate(); | |
| 124 if (FAILED(hr)) { | |
| 125 printf("\nThe update failed.\n"); | |
| 126 printf("Please take a look at the omaha integration documentation\n"); | |
| 127 printf("and rerun the test. It is possible that when you rerun\n"); | |
| 128 printf("you might get a warning about omaha already running,\n"); | |
| 129 printf("please choose to continue in this case\n"); | |
| 130 return -1; | |
| 131 } | |
| 132 | |
| 133 printf("\nUpdate succeeded. Wee.\n"); | |
| 134 printf("Congratulations on a successful install and update.\n"); | |
| 135 printf("Your friendly compatibility assistant will take your leave now.\n"); | |
| 136 printf("Bye\n"); | |
| 137 } else { | |
| 138 while (true) { | |
| 139 ::SleepEx(10000, false); | |
| 140 } | |
| 141 } | |
| 142 | |
| 143 return 0; | |
| 144 } | |
| 145 | |
| 146 bool CompatibilityTest::IsOmahaInstalled() { | |
| 147 bool is_omaha_installed = false; | |
| 148 CString goopdate_key_name = | |
| 149 ConfigManager::Instance()->registry_clients_goopdate(true); | |
| 150 CString machine_version; | |
| 151 HRESULT hr = RegKey::GetValue(goopdate_key_name, kRegValueProductVersion, | |
| 152 &machine_version); | |
| 153 if (SUCCEEDED(hr)) { | |
| 154 is_omaha_installed = true; | |
| 155 } | |
| 156 | |
| 157 goopdate_key_name = | |
| 158 ConfigManager::Instance()->registry_clients_goopdate(false); | |
| 159 CString user_version; | |
| 160 hr = RegKey::GetValue(goopdate_key_name, | |
| 161 kRegValueProductVersion, | |
| 162 &user_version); | |
| 163 if (SUCCEEDED(hr)) { | |
| 164 is_omaha_installed = true; | |
| 165 } | |
| 166 | |
| 167 if (is_omaha_installed) { | |
| 168 printf("\nOmaha Installed on machine!\n"); | |
| 169 if (!machine_version.IsEmpty()) { | |
| 170 printf("Machine Omaha version: %s\n", CT2A(machine_version)); | |
| 171 } | |
| 172 | |
| 173 if (!user_version.IsEmpty()) { | |
| 174 printf("User Omaha version: %s\n", CT2A(user_version)); | |
| 175 } | |
| 176 } | |
| 177 | |
| 178 return is_omaha_installed; | |
| 179 } | |
| 180 | |
| 181 // Starts the http server on a different thread. | |
| 182 HRESULT CompatibilityTest::StartHttpServer() { | |
| 183 CORE_LOG(L1, (_T("[StartHttpServer]"))); | |
| 184 | |
| 185 reset(thread_, | |
| 186 ::CreateThread(NULL, 0, | |
| 187 &CompatibilityTest::StartStartHttpServerInternal, | |
| 188 this, 0, &thread_id_)); | |
| 189 return S_OK; | |
| 190 } | |
| 191 | |
| 192 DWORD WINAPI CompatibilityTest::StartStartHttpServerInternal( | |
| 193 void* param) { | |
| 194 CompatibilityTest* omaha_compat = | |
| 195 static_cast<CompatibilityTest*>(param); | |
| 196 omaha_compat->RunHttpServerInternal(); | |
| 197 return 0; | |
| 198 } | |
| 199 | |
| 200 HRESULT CompatibilityTest::RunHttpServerInternal() { | |
| 201 CORE_LOG(L1, (_T("Starting local http server"))); | |
| 202 | |
| 203 scoped_co_init init_com_apt(COINIT_MULTITHREADED); | |
| 204 HRESULT hr = init_com_apt.hresult(); | |
| 205 if (FAILED(hr)) { | |
| 206 CORE_LOG(L1, (_T("[CoInitialize Failed.][0x%08x]"), hr)); | |
| 207 return hr; | |
| 208 } | |
| 209 | |
| 210 HttpServer server(kHost, kPort); | |
| 211 hr = server.Initialize(); | |
| 212 if (FAILED(hr)) { | |
| 213 CORE_LOG(L1, (_T("[HttpServer Initialize failed.][0x%08x]"), hr)); | |
| 214 return hr; | |
| 215 } | |
| 216 | |
| 217 scoped_ptr<UpdateCheckHandler> update_handler( | |
| 218 new UpdateCheckHandler(kUpdateCheckUrlPath, console_writer_.get())); | |
| 219 hr = server.AddUrlHandler(update_handler.get()); | |
| 220 if (FAILED(hr)) { | |
| 221 CORE_LOG(L1, (_T("[Failed to add update handler.][0x%08x]"), hr)); | |
| 222 return hr; | |
| 223 } | |
| 224 | |
| 225 scoped_ptr<DownloadHandler> download_handler( | |
| 226 new DownloadHandler(kDownloadUrlPath)); | |
| 227 hr = server.AddUrlHandler(download_handler.get()); | |
| 228 if (FAILED(hr)) { | |
| 229 CORE_LOG(L1, (_T("[Failed to add download handler.][0x%08x]"), hr)); | |
| 230 return hr; | |
| 231 } | |
| 232 | |
| 233 for (size_t i = 0; i < config_responses_.size(); ++i) { | |
| 234 update_handler->AddAppVersionResponse(config_responses_[i]); | |
| 235 download_handler->AddDownloadFile(config_responses_[i]); | |
| 236 } | |
| 237 | |
| 238 update_handler.release(); | |
| 239 download_handler.release(); | |
| 240 server.Start(); | |
| 241 return S_OK; | |
| 242 } | |
| 243 | |
| 244 HRESULT CompatibilityTest::RestoreRegistry(void) { | |
| 245 CORE_LOG(L1, (_T("[RestoreRegistry]"))); | |
| 246 HRESULT hr = RegKey::DeleteValue(MACHINE_REG_UPDATE_DEV, | |
| 247 kRegValueMonitorLastChecked); | |
| 248 if (FAILED(hr)) { | |
| 249 return hr; | |
| 250 } | |
| 251 | |
| 252 hr = RegKey::DeleteValue(MACHINE_REG_UPDATE_DEV, | |
| 253 kRegValueNamePingUrl); | |
| 254 if (FAILED(hr)) { | |
| 255 return hr; | |
| 256 } | |
| 257 | |
| 258 hr = RegKey::DeleteValue(MACHINE_REG_UPDATE_DEV, | |
| 259 kRegValueNameUrl); | |
| 260 if (FAILED(hr)) { | |
| 261 return hr; | |
| 262 } | |
| 263 | |
| 264 hr = RegKey::DeleteValue(MACHINE_REG_UPDATE_DEV, | |
| 265 kRegValueAuCheckPeriodMs); | |
| 266 if (FAILED(hr)) { | |
| 267 return hr; | |
| 268 } | |
| 269 | |
| 270 hr = RegKey::DeleteValue(MACHINE_REG_UPDATE_DEV, | |
| 271 kRegValueNameOverInstall); | |
| 272 if (FAILED(hr)) { | |
| 273 return hr; | |
| 274 } | |
| 275 | |
| 276 return S_OK; | |
| 277 } | |
| 278 | |
| 279 HRESULT CompatibilityTest::SetupRegistry(bool needs_admin) { | |
| 280 CORE_LOG(L1, (_T("[SetupRegistry]"))); | |
| 281 | |
| 282 // Override the url, pingurl, AuCheckPeriod, overinstall. | |
| 283 // Create the lastchecked value monitor. | |
| 284 // Also create the last checked key, to allow a /ua launch | |
| 285 // on deletion. | |
| 286 HRESULT hr = RegKey::SetValue(MACHINE_REG_UPDATE_DEV, | |
| 287 kRegValueMonitorLastChecked, | |
| 288 static_cast<DWORD>(1)); | |
| 289 if (FAILED(hr)) { | |
| 290 CORE_LOG(L1, (_T("[SetValue failed.][0x%08x]"), hr)); | |
| 291 return hr; | |
| 292 } | |
| 293 | |
| 294 hr = RegKey::SetValue(MACHINE_REG_UPDATE_DEV, | |
| 295 kRegValueNamePingUrl, | |
| 296 kUpdateCheckUrl); | |
| 297 if (FAILED(hr)) { | |
| 298 CORE_LOG(L1, (_T("[SetValue failed.][0x%08x]"), hr)); | |
| 299 return hr; | |
| 300 } | |
| 301 | |
| 302 hr = RegKey::SetValue(MACHINE_REG_UPDATE_DEV, | |
| 303 kRegValueNameUrl, | |
| 304 kUpdateCheckUrl); | |
| 305 if (FAILED(hr)) { | |
| 306 CORE_LOG(L1, (_T("[SetValue failed.][0x%08x]"), hr)); | |
| 307 return hr; | |
| 308 } | |
| 309 | |
| 310 hr = RegKey::SetValue(MACHINE_REG_UPDATE_DEV, | |
| 311 kRegValueAuCheckPeriodMs, | |
| 312 kAuCheckPeriodMs); | |
| 313 if (FAILED(hr)) { | |
| 314 CORE_LOG(L1, (_T("[SetValue failed.][0x%08x]"), hr)); | |
| 315 return hr; | |
| 316 } | |
| 317 | |
| 318 hr = RegKey::SetValue(MACHINE_REG_UPDATE_DEV, | |
| 319 kRegValueNameOverInstall, | |
| 320 static_cast<DWORD>(1)); | |
| 321 if (FAILED(hr)) { | |
| 322 CORE_LOG(L1, (_T("[SetValue failed.][0x%08x]"), hr)); | |
| 323 return hr; | |
| 324 } | |
| 325 | |
| 326 CString update_key_name = | |
| 327 ConfigManager::Instance()->registry_update(needs_admin); | |
| 328 hr = RegKey::SetValue(update_key_name, | |
| 329 kRegValueLastChecked, | |
| 330 static_cast<DWORD>(0)); | |
| 331 if (FAILED(hr)) { | |
| 332 CORE_LOG(L1, (_T("[SetValue failed.][0x%08x]"), hr)); | |
| 333 return hr; | |
| 334 } | |
| 335 | |
| 336 return S_OK; | |
| 337 } | |
| 338 | |
| 339 // Builds the googleupdate file name to be run. | |
| 340 HRESULT CompatibilityTest::BuildTaggedGoogleUpdatePath() { | |
| 341 CORE_LOG(L1, (_T("[BuildTaggedGoogleUpdatePath]"))); | |
| 342 | |
| 343 GUID guid(GUID_NULL); | |
| 344 HRESULT hr = ::CoCreateGuid(&guid); | |
| 345 if (FAILED(hr)) { | |
| 346 CORE_LOG(L1, (_T("[CoCreateGuid failed 0x%08x]"), hr)); | |
| 347 return hr; | |
| 348 } | |
| 349 | |
| 350 CString unique_exe(GuidToString(guid)); | |
| 351 unique_exe += _T(".exe"); | |
| 352 | |
| 353 TCHAR temp_path[MAX_PATH] = {0}; | |
| 354 if (!::GetTempPath(MAX_PATH, temp_path)) { | |
| 355 CORE_LOG(L1, (_T("[GetTempPath failed.]"))); | |
| 356 return HRESULTFromLastError(); | |
| 357 } | |
| 358 | |
| 359 tagged_google_update_path_ = ConcatenatePath(temp_path, unique_exe); | |
| 360 return S_OK; | |
| 361 } | |
| 362 | |
| 363 // Sets up the correct ap value to cause the server to send back | |
| 364 // an update response. | |
| 365 HRESULT CompatibilityTest::StartApplicationUpdate() { | |
| 366 CORE_LOG(L1, (_T("[StartApplicationUpdate]"))); | |
| 367 | |
| 368 // Set the ap value to "update_app". | |
| 369 bool needs_admin = config_responses_[0].needs_admin; | |
| 370 CString reg_key_name = | |
| 371 ConfigManager::Instance()->registry_client_state(needs_admin); | |
| 372 CString app_client_state_key_name = AppendRegKeyPath( | |
| 373 reg_key_name, | |
| 374 GuidToString(config_responses_[0].guid)); | |
| 375 HRESULT hr = RegKey::SetValue(app_client_state_key_name, | |
| 376 kRegValueAdditionalParams, | |
| 377 _T("update_app")); | |
| 378 if (FAILED(hr)) { | |
| 379 return hr; | |
| 380 } | |
| 381 | |
| 382 // Delete the last checked value to force an update check. | |
| 383 CString update_key_name = | |
| 384 ConfigManager::Instance()->registry_update(needs_admin); | |
| 385 hr = RegKey::DeleteValue(update_key_name, kRegValueLastChecked); | |
| 386 if (FAILED(hr)) { | |
| 387 return hr; | |
| 388 } | |
| 389 | |
| 390 printf("\nSignaled Googleupdate to start application update."); | |
| 391 printf("Waiting for events...\n"); | |
| 392 while (true) { | |
| 393 ::SleepEx(1000, false); | |
| 394 if (console_writer_->update_completed()) { | |
| 395 PingEvent::Results result = console_writer_->update_result(); | |
| 396 if (result == PingEvent::EVENT_RESULT_SUCCESS) { | |
| 397 CORE_LOG(L1, (_T("[Successfully completed update]"))); | |
| 398 return S_OK; | |
| 399 } else { | |
| 400 CORE_LOG(L1, (_T("[Update failed %d]"), result)); | |
| 401 return E_FAIL; | |
| 402 } | |
| 403 } | |
| 404 } | |
| 405 | |
| 406 return S_OK; | |
| 407 } | |
| 408 | |
| 409 // Stamps googleupdatesetup with the information from the config and | |
| 410 // runs it. | |
| 411 HRESULT CompatibilityTest::StartGoogleUpdate() { | |
| 412 CORE_LOG(L1, (_T("[StartGoogleUpdate]"))); | |
| 413 | |
| 414 // Stamp the googleupdatesetupe.exe with the information from | |
| 415 // the config. | |
| 416 HRESULT hr = BuildTaggedGoogleUpdatePath(); | |
| 417 if (FAILED(hr)) { | |
| 418 CORE_LOG(L1, (_T("[BuildTaggedGoogleUpdatePath failed 0x%08x]"), hr)); | |
| 419 return hr; | |
| 420 } | |
| 421 | |
| 422 // For now we only read the information from the first config and use | |
| 423 // that to stamp the binary. | |
| 424 CString tag_str; | |
| 425 tag_str.Format( | |
| 426 _T("appguid=%s&appname=%s&needsadmin=%s&lang=%s"), | |
| 427 GuidToString(config_responses_[0].guid), | |
| 428 config_responses_[0].app_name, | |
| 429 config_responses_[0].needs_admin ? _T("True") : _T("False"), | |
| 430 config_responses_[0].language); | |
| 431 | |
| 432 ApplyTag tag; | |
| 433 hr = tag.Init(googleupdate_setup_path_, | |
| 434 CT2CA(tag_str), | |
| 435 lstrlenA(CT2CA(tag_str)), | |
| 436 tagged_google_update_path_, | |
| 437 false); | |
| 438 if (FAILED(hr)) { | |
| 439 CORE_LOG(L1, (_T("[ApplyTag.Init failed 0x%08x]"), hr)); | |
| 440 return hr; | |
| 441 } | |
| 442 | |
| 443 hr = tag.EmbedTagString(); | |
| 444 if (FAILED(hr)) { | |
| 445 CORE_LOG(L1, (_T("[ApplyTag.EmbedTagString failed 0x%08x]"), hr)); | |
| 446 return hr; | |
| 447 } | |
| 448 | |
| 449 // Start omaha to install the application. | |
| 450 hr = System::ShellExecuteProcess(tagged_google_update_path_, | |
| 451 NULL, | |
| 452 NULL, | |
| 453 NULL); | |
| 454 if (FAILED(hr)) { | |
| 455 CORE_LOG(L1, (_T("[Start installer failed 0x%08x]"), hr)); | |
| 456 return hr; | |
| 457 } | |
| 458 | |
| 459 // Now wait for the install to complete. We poll the | |
| 460 // observer for a install complete event. | |
| 461 while (true) { | |
| 462 ::SleepEx(1000, false); | |
| 463 if (console_writer_->install_completed()) { | |
| 464 PingEvent::Results result = console_writer_->install_result(); | |
| 465 if (result == PingEvent::EVENT_RESULT_SUCCESS) { | |
| 466 CORE_LOG(L1, (_T("[Successfully completed install]"))); | |
| 467 return S_OK; | |
| 468 } else { | |
| 469 CORE_LOG(L1, (_T("[Start installer failed %d]"), result)); | |
| 470 return E_FAIL; | |
| 471 } | |
| 472 } | |
| 473 } | |
| 474 | |
| 475 return S_OK; | |
| 476 } | |
| 477 | |
| 478 } // namespace omaha | |
| 479 | |
| OLD | NEW |