Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(163)

Side by Side Diff: goopdate/download_manager.cc

Issue 624713003: Keep only base/extractor.[cc|h]. (Closed) Base URL: https://chromium.googlesource.com/external/omaha.git@master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « goopdate/download_manager.h ('k') | goopdate/download_manager_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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
OLDNEW
« no previous file with comments | « goopdate/download_manager.h ('k') | goopdate/download_manager_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698