| OLD | NEW |
| (Empty) |
| 1 // Copyright 2007-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 // The download manager uses the network request to download the remote file. | |
| 17 // Once the download is complete, the download manager stores the file in | |
| 18 // the package cache, then it copies the file out to a location specified | |
| 19 // by the caller. | |
| 20 | |
| 21 // TODO(omaha): the path where to copy the file is hardcoded. Change the | |
| 22 // class interface to allow the path as a parameter. | |
| 23 | |
| 24 #include "omaha/goopdate/download_manager.h" | |
| 25 #include <algorithm> | |
| 26 #include <vector> | |
| 27 #include "omaha/base/debug.h" | |
| 28 #include "omaha/base/error.h" | |
| 29 #include "omaha/base/file.h" | |
| 30 #include "omaha/base/logging.h" | |
| 31 #include "omaha/base/path.h" | |
| 32 #include "omaha/base/scoped_impersonation.h" | |
| 33 #include "omaha/base/safe_format.h" | |
| 34 #include "omaha/base/string.h" | |
| 35 #include "omaha/base/synchronized.h" | |
| 36 #include "omaha/base/user_rights.h" | |
| 37 #include "omaha/base/utils.h" | |
| 38 #include "omaha/common/config_manager.h" | |
| 39 #include "omaha/common/const_goopdate.h" | |
| 40 #include "omaha/goopdate/model.h" | |
| 41 #include "omaha/goopdate/package_cache.h" | |
| 42 #include "omaha/goopdate/server_resource.h" | |
| 43 #include "omaha/goopdate/string_formatter.h" | |
| 44 #include "omaha/goopdate/worker_metrics.h" | |
| 45 #include "omaha/goopdate/worker_utils.h" | |
| 46 #include "omaha/net/bits_request.h" | |
| 47 #include "omaha/net/http_client.h" | |
| 48 #include "omaha/net/network_request.h" | |
| 49 #include "omaha/net/net_utils.h" | |
| 50 #include "omaha/net/simple_request.h" | |
| 51 | |
| 52 namespace omaha { | |
| 53 | |
| 54 namespace { | |
| 55 | |
| 56 // Creates and initializes an instance of the NetworkRequest for the | |
| 57 // DownloadManager to use. Defines the fallback chain: BITS, WinHttp. | |
| 58 HRESULT CreateNetworkRequest(NetworkRequest** network_request_ptr) { | |
| 59 NetworkConfig* network_config = NULL; | |
| 60 NetworkConfigManager& network_manager = NetworkConfigManager::Instance(); | |
| 61 HRESULT hr = network_manager.GetUserNetworkConfig(&network_config); | |
| 62 if (FAILED(hr)) { | |
| 63 return hr; | |
| 64 } | |
| 65 const NetworkConfig::Session& session(network_config->session()); | |
| 66 NetworkRequest* network_request(new NetworkRequest(session)); | |
| 67 | |
| 68 // TODO(omaha): provide a mechanism for different timeout values in | |
| 69 // silent and interactive downloads. | |
| 70 | |
| 71 // BITS transfers files only when the job owner is logged on. If the process | |
| 72 // "Run As" another user, an empty BITS job gets created in suspended state | |
| 73 // but there is no way to manipulate the job, nor cancel it. | |
| 74 bool is_logged_on = false; | |
| 75 hr = UserRights::UserIsLoggedOnInteractively(&is_logged_on); | |
| 76 if (SUCCEEDED(hr) && is_logged_on) { | |
| 77 BitsRequest* bits_request(new BitsRequest); | |
| 78 bits_request->set_minimum_retry_delay(kSecPerMin); | |
| 79 bits_request->set_no_progress_timeout(5 * kSecPerMin); | |
| 80 network_request->AddHttpRequest(bits_request); | |
| 81 } | |
| 82 | |
| 83 network_request->AddHttpRequest(new SimpleRequest); | |
| 84 | |
| 85 network_request->set_num_retries(3); | |
| 86 *network_request_ptr = network_request; | |
| 87 return S_OK; | |
| 88 } | |
| 89 | |
| 90 // TODO(omaha): Unit test this method. | |
| 91 HRESULT ValidateSize(const CString& file_path, uint64 expected_size) { | |
| 92 CORE_LOG(L3, (_T("[ValidateSize][%s][%lld]"), file_path, expected_size)); | |
| 93 ASSERT1(File::Exists(file_path)); | |
| 94 ASSERT1(expected_size != 0); | |
| 95 ASSERT(expected_size <= UINT_MAX, | |
| 96 (_T("TODO(omaha): Add uint64 support to GetFileSizeUnopen()."))); | |
| 97 | |
| 98 uint32 file_size(0); | |
| 99 HRESULT hr = File::GetFileSizeUnopen(file_path, &file_size); | |
| 100 ASSERT1(SUCCEEDED(hr)); | |
| 101 if (FAILED(hr)) { | |
| 102 return hr; | |
| 103 } | |
| 104 | |
| 105 if (0 == file_size) { | |
| 106 return GOOPDATEDOWNLOAD_E_FILE_SIZE_ZERO; | |
| 107 } else if (file_size < expected_size) { | |
| 108 return GOOPDATEDOWNLOAD_E_FILE_SIZE_SMALLER; | |
| 109 } else if (file_size > expected_size) { | |
| 110 return GOOPDATEDOWNLOAD_E_FILE_SIZE_LARGER; | |
| 111 } | |
| 112 | |
| 113 ASSERT1(file_size == expected_size); | |
| 114 return S_OK; | |
| 115 } | |
| 116 | |
| 117 } // namespace | |
| 118 | |
| 119 DownloadManager::DownloadManager(bool is_machine) | |
| 120 : lock_(NULL), is_machine_(false) { | |
| 121 CORE_LOG(L3, (_T("[DownloadManager::DownloadManager]"))); | |
| 122 | |
| 123 omaha::interlocked_exchange_pointer(&lock_, | |
| 124 static_cast<Lockable*>(new LLock)); | |
| 125 __mutexScope(lock()); | |
| 126 | |
| 127 is_machine_ = is_machine; | |
| 128 | |
| 129 package_cache_root_ = | |
| 130 is_machine ? | |
| 131 ConfigManager::Instance()->GetMachineSecureDownloadStorageDir() : | |
| 132 ConfigManager::Instance()->GetUserDownloadStorageDir(); | |
| 133 | |
| 134 CORE_LOG(L3, (_T("[package_cache_root][%s]"), package_cache_root())); | |
| 135 | |
| 136 package_cache_.reset(new PackageCache); | |
| 137 } | |
| 138 | |
| 139 DownloadManager::~DownloadManager() { | |
| 140 CORE_LOG(L3, (_T("[DownloadManager::~DownloadManager]"))); | |
| 141 | |
| 142 ASSERT1(!IsBusy()); | |
| 143 | |
| 144 delete &lock(); | |
| 145 omaha::interlocked_exchange_pointer(&lock_, static_cast<Lockable*>(NULL)); | |
| 146 } | |
| 147 | |
| 148 const Lockable& DownloadManager::lock() const { | |
| 149 return *omaha::interlocked_exchange_pointer(&lock_, lock_); | |
| 150 } | |
| 151 | |
| 152 bool DownloadManager::is_machine() const { | |
| 153 __mutexScope(lock()); | |
| 154 return is_machine_; | |
| 155 } | |
| 156 | |
| 157 CString DownloadManager::package_cache_root() const { | |
| 158 __mutexScope(lock()); | |
| 159 return package_cache_root_; | |
| 160 } | |
| 161 | |
| 162 PackageCache* DownloadManager::package_cache() { | |
| 163 __mutexScope(lock()); | |
| 164 return package_cache_.get(); | |
| 165 } | |
| 166 | |
| 167 const PackageCache* DownloadManager::package_cache() const { | |
| 168 __mutexScope(lock()); | |
| 169 return package_cache_.get(); | |
| 170 } | |
| 171 | |
| 172 HRESULT DownloadManager::Initialize() { | |
| 173 HRESULT hr = package_cache()->Initialize(package_cache_root()); | |
| 174 if (FAILED(hr)) { | |
| 175 CORE_LOG(LE, (_T("[failed to initialize the package cache]0x%08x]"), hr)); | |
| 176 return hr; | |
| 177 } | |
| 178 | |
| 179 hr = package_cache()->PurgeOldPackagesIfNecessary(); | |
| 180 if (FAILED(hr)) { | |
| 181 CORE_LOG(LW, (_T("[PurgeOldPackagesIfNecessary failed][0x%08x]"), hr)); | |
| 182 } | |
| 183 | |
| 184 return S_OK; | |
| 185 } | |
| 186 | |
| 187 CString DownloadManager::GetMessageForError(const ErrorContext& error_context, | |
| 188 const CString& language) { | |
| 189 CString message; | |
| 190 StringFormatter formatter(language); | |
| 191 | |
| 192 switch (error_context.error_code) { | |
| 193 case SIGS_E_INVALID_SIGNATURE: | |
| 194 case GOOPDATEDOWNLOAD_E_FILE_SIZE_ZERO: | |
| 195 case GOOPDATEDOWNLOAD_E_FILE_SIZE_SMALLER: | |
| 196 case GOOPDATEDOWNLOAD_E_FILE_SIZE_LARGER: | |
| 197 VERIFY1(SUCCEEDED(formatter.LoadString(IDS_DOWNLOAD_HASH_MISMATCH, | |
| 198 &message))); | |
| 199 break; | |
| 200 case GOOPDATEDOWNLOAD_E_CACHING_FAILED: | |
| 201 VERIFY1(SUCCEEDED(formatter.FormatMessage( | |
| 202 &message, IDS_CACHING_ERROR, error_context.extra_code1, &message))); | |
| 203 break; | |
| 204 default: | |
| 205 if (!worker_utils::FormatMessageForNetworkError(error_context.error_code, | |
| 206 language, | |
| 207 &message)) { | |
| 208 VERIFY1(SUCCEEDED(formatter.LoadString(IDS_DOWNLOAD_ERROR, &message))); | |
| 209 } | |
| 210 break; | |
| 211 } | |
| 212 | |
| 213 ASSERT1(!message.IsEmpty()); | |
| 214 return message; | |
| 215 } | |
| 216 | |
| 217 HRESULT DownloadManager::DownloadApp(App* app) { | |
| 218 CORE_LOG(L3, (_T("[DownloadManager::DownloadApp][0x%p]"), app)); | |
| 219 ASSERT1(app); | |
| 220 | |
| 221 // TODO(omaha3): Maybe rename these to include "app_". Maybe add package | |
| 222 // metrics too. | |
| 223 ++metric_worker_download_total; | |
| 224 | |
| 225 // We assume the number of packages does not change after download is started. | |
| 226 // TODO(omaha3): Could be a problem if we allow installers to request more | |
| 227 // packages (http://b/1969071), but we will have lots of other problems then. | |
| 228 AppVersion* app_version = app->working_version(); | |
| 229 const size_t num_packages = app_version->GetNumberOfPackages(); | |
| 230 | |
| 231 State* state = NULL; | |
| 232 HRESULT hr = CreateStateForApp(app, &state); | |
| 233 if (FAILED(hr)) { | |
| 234 CORE_LOG(LE, (_T("[CreateStateForApp failed][0x%08x]"), hr)); | |
| 235 return hr; | |
| 236 } | |
| 237 | |
| 238 app->Downloading(); | |
| 239 | |
| 240 CString message; | |
| 241 hr = S_OK; | |
| 242 | |
| 243 for (size_t i = 0; i < num_packages; ++i) { | |
| 244 Package* package(app_version->GetPackage(i)); | |
| 245 hr = DoDownloadPackage(package, state); | |
| 246 if (FAILED(hr)) { | |
| 247 CORE_LOG(LE, (_T("[DoDownloadPackage failed][%s][%s][0x%08x][%Iu]"), | |
| 248 app->display_name(), package->filename(), hr, i)); | |
| 249 message = GetMessageForError(ErrorContext(hr, error_extra_code1()), | |
| 250 app->app_bundle()->display_language()); | |
| 251 break; | |
| 252 } | |
| 253 } | |
| 254 | |
| 255 if (SUCCEEDED(hr)) { | |
| 256 app->DownloadComplete(); | |
| 257 | |
| 258 // TODO(omaha3): Extract and apply differential update if necessary. | |
| 259 | |
| 260 app->MarkReadyToInstall(); | |
| 261 } else { | |
| 262 app->Error(ErrorContext(hr, error_extra_code1()), message); | |
| 263 } | |
| 264 | |
| 265 if (SUCCEEDED(hr)) { | |
| 266 ++metric_worker_download_succeeded; | |
| 267 } | |
| 268 | |
| 269 VERIFY1(SUCCEEDED(DeleteStateForApp(app))); | |
| 270 | |
| 271 return hr; | |
| 272 } | |
| 273 | |
| 274 HRESULT DownloadManager::DownloadPackage(Package* package) { | |
| 275 CORE_LOG(L3, (_T("[DownloadManager::DownloadPackage][0x%p]"), package)); | |
| 276 ASSERT1(package); | |
| 277 | |
| 278 UNREFERENCED_PARAMETER(package); | |
| 279 | |
| 280 // TODO(omaha): implement in terms of DoDownloadPackage. | |
| 281 | |
| 282 return E_NOTIMPL; | |
| 283 } | |
| 284 | |
| 285 HRESULT DownloadManager::GetPackage(const Package* package, | |
| 286 const CString& dir) const { | |
| 287 const CString app_id(package->app_version()->app()->app_guid_string()); | |
| 288 const CString version(package->app_version()->version()); | |
| 289 const CString package_name(package->filename()); | |
| 290 | |
| 291 const PackageCache::Key key(app_id, version, package_name); | |
| 292 | |
| 293 CORE_LOG(L3, (_T("[DownloadManager::GetPackage][%s]"), key.ToString())); | |
| 294 | |
| 295 const CString dest_file(ConcatenatePath(dir, package_name)); | |
| 296 CORE_LOG(L3, (_T("[destination file is '%s']"), dest_file)); | |
| 297 | |
| 298 const CString hash(package->expected_hash()); | |
| 299 HRESULT hr = package_cache()->Get(key, dest_file, hash); | |
| 300 if (FAILED(hr)) { | |
| 301 CORE_LOG(LE, (_T("[failed to get from cache][0x%08x]"), hr)); | |
| 302 return hr; | |
| 303 } | |
| 304 | |
| 305 return S_OK; | |
| 306 } | |
| 307 | |
| 308 bool DownloadManager::IsPackageAvailable(const Package* package) const { | |
| 309 const CString app_id(package->app_version()->app()->app_guid_string()); | |
| 310 const CString version(package->app_version()->version()); | |
| 311 const CString package_name(package->filename()); | |
| 312 | |
| 313 const PackageCache::Key key(app_id, version, package_name); | |
| 314 | |
| 315 CORE_LOG(L3, (_T("[DownloadManager::IsPackageAvailable][%s]"), | |
| 316 key.ToString())); | |
| 317 | |
| 318 const CString hash(package->expected_hash()); | |
| 319 return package_cache()->IsCached(key, hash); | |
| 320 } | |
| 321 | |
| 322 // Attempts a package download by trying the fallback urls. It does not | |
| 323 // retry the download if the file validation fails. | |
| 324 // Assumes the packages are not created or destroyed while method is running. | |
| 325 HRESULT DownloadManager::DoDownloadPackage(Package* package, State* state) { | |
| 326 ASSERT1(package); | |
| 327 ASSERT1(state); | |
| 328 | |
| 329 App* app = package->app_version()->app(); | |
| 330 const CString app_id(app->app_guid_string()); | |
| 331 const CString version(package->app_version()->version()); | |
| 332 const CString package_name(package->filename()); | |
| 333 | |
| 334 const ConfigManager& cm = *ConfigManager::Instance(); | |
| 335 // TODO(omaha): Since we don't currently have is_manual, check the least | |
| 336 // restrictive case of true. It would be nice if we had is_manual. We'll see. | |
| 337 ASSERT(SUCCEEDED(app->CheckGroupPolicy()), | |
| 338 (_T("Downloading package app for disallowed app."))); | |
| 339 | |
| 340 if (app_id.IsEmpty() || package_name.IsEmpty()) { | |
| 341 return E_INVALIDARG; | |
| 342 } | |
| 343 | |
| 344 PackageCache::Key key(app_id, version, package_name); | |
| 345 | |
| 346 CORE_LOG(L3, (_T("[DownloadManager::DoDownloadPackage][%s]"), | |
| 347 key.ToString())); | |
| 348 | |
| 349 const CString hash(package->expected_hash()); | |
| 350 | |
| 351 if (!package_cache()->IsCached(key, hash)) { | |
| 352 CORE_LOG(L3, (_T("[The package is not cached]"))); | |
| 353 | |
| 354 // TODO(omaha3): May need to consider the DownloadPackage case. Also, we may | |
| 355 // want a error code that does not include "UPDATE". If this is a valid | |
| 356 // case, need to add message for | |
| 357 // GOOPDATE_E_APP_UPDATE_DISABLED_EULA_NOT_ACCEPTED to GetMessageForError(). | |
| 358 // As of 9/7/2010, the offline case does not allow downloading if the | |
| 359 // package cannot be found, so offline scenarios should never get here. | |
| 360 if (!app->is_eula_accepted()) { | |
| 361 ASSERT(false, (_T("Can't download because app EULA is not accepted."))); | |
| 362 return GOOPDATE_E_APP_UPDATE_DISABLED_EULA_NOT_ACCEPTED; | |
| 363 } | |
| 364 | |
| 365 if (!ConfigManager::Instance()->CanUseNetwork(is_machine_)) { | |
| 366 CORE_LOG(LE, (_T("[DoDownloadPackage][network use prohibited]"))); | |
| 367 return GOOPDATE_E_CANNOT_USE_NETWORK; | |
| 368 } | |
| 369 | |
| 370 CString unique_filename_path; | |
| 371 HRESULT hr = BuildUniqueFileName(package_name, &unique_filename_path); | |
| 372 if (FAILED(hr)) { | |
| 373 CORE_LOG(LE, (_T("[BuildUniqueFileName failed][0x%08x]"), hr)); | |
| 374 return hr; | |
| 375 } | |
| 376 | |
| 377 NetworkRequest* network_request = state->network_request(); | |
| 378 | |
| 379 network_request->set_callback(package); | |
| 380 | |
| 381 const std::vector<CString> download_base_urls( | |
| 382 package->app_version()->download_base_urls()); | |
| 383 | |
| 384 hr = E_FAIL; | |
| 385 for (size_t i = 0; i < download_base_urls.size() && FAILED(hr); ++i) { | |
| 386 // TODO(omaha3): Append nicely. | |
| 387 const CString url = download_base_urls[i] + package_name; | |
| 388 | |
| 389 if (i > 0) { | |
| 390 CORE_LOG(L3, (_T("[retrying download with fallback base url][%s]"), | |
| 391 url)); | |
| 392 } | |
| 393 // TODO(omaha3): Increment a usage stat for the ith url being used. | |
| 394 // Supporting 3 or 4 should be enough. | |
| 395 | |
| 396 CORE_LOG(L3, (_T("[starting file download][from '%s'][to '%s']"), | |
| 397 url, unique_filename_path)); | |
| 398 | |
| 399 // Downloading a file is a blocking call. It assumes the model is not | |
| 400 // locked by the calling thread, otherwise other threads won't be able to | |
| 401 // to access the model until the file download is complete. | |
| 402 ASSERT1(!package->model()->IsLockedByCaller()); | |
| 403 | |
| 404 hr = network_request->DownloadFile(url, unique_filename_path); | |
| 405 if (FAILED(hr)) { | |
| 406 CORE_LOG(LW, (_T("[DownloadFile failed from url][0x%08x]['%s']['%s']"), | |
| 407 hr, package_name, download_base_urls[i])); | |
| 408 worker_utils::AddHttpRequestDataToEventLog( | |
| 409 hr, | |
| 410 network_request->http_status_code(), | |
| 411 network_request->trace(), | |
| 412 is_machine_); | |
| 413 continue; | |
| 414 } | |
| 415 | |
| 416 // A file has been successfully downloaded from current url. Validate | |
| 417 // and cache it. | |
| 418 hr = CallAsSelfAndImpersonate2( | |
| 419 this, | |
| 420 &DownloadManager::CachePackage, | |
| 421 static_cast<const Package*>(package), | |
| 422 static_cast<const CString*>(&unique_filename_path)); | |
| 423 if (SUCCEEDED(hr)) { | |
| 424 break; | |
| 425 } | |
| 426 | |
| 427 CORE_LOG(LE, (_T("[failed to cache package][0x%08x]"), hr)); | |
| 428 } | |
| 429 VERIFY1(SUCCEEDED(network_request->Close())); | |
| 430 DeleteBeforeOrAfterReboot(unique_filename_path); | |
| 431 | |
| 432 if (FAILED(hr)) { | |
| 433 CORE_LOG(LE, (_T("[DownloadFile/caching failed from all urls][0x%08x]"), | |
| 434 hr)); | |
| 435 return hr; | |
| 436 } | |
| 437 | |
| 438 // Assumes that downloaded bytes equal to the expected package size. | |
| 439 app->UpdateNumBytesDownloaded(package->expected_size()); | |
| 440 } else { | |
| 441 CORE_LOG(L3, (_T("[package is cached]"))); | |
| 442 | |
| 443 // TODO(omaha3): We probably need to update the download stats that | |
| 444 // Package::OnProgress would set. It may be misleading to set | |
| 445 // bytes_downloaded to anything other than zero, but App uses this to | |
| 446 // calculate progress. I suppose we could add an is_complete field instead. | |
| 447 // There is a related issue with the callback not being called with the | |
| 448 // final size. See the TODO in the unit tests. | |
| 449 } | |
| 450 | |
| 451 ASSERT1(package_cache()->IsCached(key, hash)); | |
| 452 return S_OK; | |
| 453 } | |
| 454 | |
| 455 void DownloadManager::Cancel(App* app) { | |
| 456 CORE_LOG(L3, (_T("[DownloadManager::Cancel][0x%p]"), app)); | |
| 457 ASSERT1(app); | |
| 458 | |
| 459 __mutexScope(lock()); | |
| 460 | |
| 461 for (size_t i = 0; i != download_state_.size(); ++i) { | |
| 462 if (app == download_state_[i]->app()) { | |
| 463 VERIFY1(SUCCEEDED(download_state_[i]->CancelNetworkRequest())); | |
| 464 } | |
| 465 } | |
| 466 } | |
| 467 | |
| 468 void DownloadManager::CancelAll() { | |
| 469 CORE_LOG(L3, (_T("[DownloadManager::CancelAll]"))); | |
| 470 | |
| 471 __mutexScope(lock()); | |
| 472 | |
| 473 for (size_t i = 0; i != download_state_.size(); ++i) { | |
| 474 VERIFY1(SUCCEEDED(download_state_[i]->CancelNetworkRequest())); | |
| 475 } | |
| 476 } | |
| 477 | |
| 478 bool DownloadManager::IsBusy() const { | |
| 479 __mutexScope(lock()); | |
| 480 return !download_state_.empty(); | |
| 481 } | |
| 482 | |
| 483 HRESULT DownloadManager::PurgeAppLowerVersions(const CString& app_id, | |
| 484 const CString& version) { | |
| 485 return package_cache()->PurgeAppLowerVersions(app_id, version); | |
| 486 } | |
| 487 | |
| 488 HRESULT DownloadManager::CachePackage(const Package* package, | |
| 489 const CString* filename_path) { | |
| 490 ASSERT1(package); | |
| 491 ASSERT1(filename_path); | |
| 492 | |
| 493 const CString app_id(package->app_version()->app()->app_guid_string()); | |
| 494 const CString version(package->app_version()->version()); | |
| 495 const CString package_name(package->filename()); | |
| 496 PackageCache::Key key(app_id, version, package_name); | |
| 497 | |
| 498 const CString hash(package->expected_hash()); | |
| 499 | |
| 500 HRESULT hr = package_cache()->Put(key, *filename_path, hash); | |
| 501 if (hr != SIGS_E_INVALID_SIGNATURE) { | |
| 502 if (FAILED(hr)) { | |
| 503 set_error_extra_code1(static_cast<int>(hr)); | |
| 504 return GOOPDATEDOWNLOAD_E_CACHING_FAILED; | |
| 505 } | |
| 506 return hr; | |
| 507 } | |
| 508 | |
| 509 // Get a more specific error if possible. | |
| 510 // TODO(omaha): It would be nice to detect that we downloaded a proxy | |
| 511 // page and tell the user this. It would be even better if we could | |
| 512 // display it; that would require a lot more plumbing. | |
| 513 HRESULT size_hr = ValidateSize(*filename_path, package->expected_size()); | |
| 514 if (FAILED(size_hr)) { | |
| 515 hr = size_hr; | |
| 516 } | |
| 517 | |
| 518 return hr; | |
| 519 } | |
| 520 | |
| 521 // The file is initially downloaded to a temporary unique name, to account | |
| 522 // for the case where the same file is downloaded by multiple callers. | |
| 523 HRESULT DownloadManager::BuildUniqueFileName(const CString& filename, | |
| 524 CString* unique_filename) { | |
| 525 ASSERT1(unique_filename); | |
| 526 | |
| 527 GUID guid(GUID_NULL); | |
| 528 HRESULT hr = ::CoCreateGuid(&guid); | |
| 529 if (FAILED(hr)) { | |
| 530 CORE_LOG(L3, (_T("[CoCreateGuid failed][0x%08x]"), hr)); | |
| 531 return hr; | |
| 532 } | |
| 533 | |
| 534 // Format of the unique file name is: <temp_download_dir>/<guid>-<filename>. | |
| 535 const CString temp_dir(ConfigManager::Instance()->GetTempDownloadDir()); | |
| 536 CString temp_filename; | |
| 537 SafeCStringFormat(&temp_filename, _T("%s-%s"), GuidToString(guid), filename); | |
| 538 *unique_filename = ConcatenatePath(temp_dir, temp_filename); | |
| 539 | |
| 540 return unique_filename->IsEmpty() ? | |
| 541 GOOPDATEDOWNLOAD_E_UNIQUE_FILE_PATH_EMPTY : S_OK; | |
| 542 } | |
| 543 | |
| 544 HRESULT DownloadManager::CreateStateForApp(App* app, State** state) { | |
| 545 ASSERT1(app); | |
| 546 ASSERT1(state); | |
| 547 | |
| 548 *state = NULL; | |
| 549 | |
| 550 NetworkRequest* network_request = NULL; | |
| 551 HRESULT hr = CreateNetworkRequest(&network_request); | |
| 552 if (FAILED(hr)) { | |
| 553 return hr; | |
| 554 } | |
| 555 | |
| 556 ASSERT1(network_request); | |
| 557 | |
| 558 const bool use_background_priority = | |
| 559 (app->app_bundle()->priority() < INSTALL_PRIORITY_HIGH); | |
| 560 network_request->set_low_priority(use_background_priority); | |
| 561 | |
| 562 network_request->set_proxy_auth_config( | |
| 563 app->app_bundle()->GetProxyAuthConfig()); | |
| 564 | |
| 565 scoped_ptr<State> state_ptr(new State(app, network_request)); | |
| 566 | |
| 567 __mutexBlock(lock()) { | |
| 568 download_state_.push_back(state_ptr.release()); | |
| 569 *state = download_state_.back(); | |
| 570 } | |
| 571 | |
| 572 return S_OK; | |
| 573 } | |
| 574 | |
| 575 HRESULT DownloadManager::DeleteStateForApp(App* app) { | |
| 576 ASSERT1(app); | |
| 577 | |
| 578 __mutexScope(lock()); | |
| 579 | |
| 580 typedef std::vector<State*>::iterator Iter; | |
| 581 for (Iter it(download_state_.begin()); it != download_state_.end(); ++it) { | |
| 582 if (app == (*it)->app()) { | |
| 583 delete *it; | |
| 584 download_state_.erase(it); | |
| 585 return S_OK; | |
| 586 } | |
| 587 } | |
| 588 | |
| 589 ASSERT1(false); | |
| 590 | |
| 591 return E_UNEXPECTED; | |
| 592 } | |
| 593 | |
| 594 DownloadManager::State::State(App* app, NetworkRequest* network_request) | |
| 595 : app_(app), network_request_(network_request) { | |
| 596 ASSERT1(app); | |
| 597 ASSERT1(network_request); | |
| 598 } | |
| 599 | |
| 600 DownloadManager::State::~State() { | |
| 601 } | |
| 602 | |
| 603 NetworkRequest* DownloadManager::State::network_request() const { | |
| 604 ASSERT1(ConfigManager::Instance()->CanUseNetwork( | |
| 605 app_->app_bundle()->is_machine())); | |
| 606 | |
| 607 return network_request_.get(); | |
| 608 } | |
| 609 | |
| 610 HRESULT DownloadManager::State::CancelNetworkRequest() { | |
| 611 return network_request_->Cancel(); | |
| 612 } | |
| 613 | |
| 614 } // namespace omaha | |
| OLD | NEW |