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

Side by Side Diff: chrome/browser/download/chrome_download_manager_delegate.cc

Issue 10704052: Download filename determination refactor (3/3) (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Merge with r148594 to and resolve conflicts with r148576 Created 8 years, 5 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/download/chrome_download_manager_delegate.h" 5 #include "chrome/browser/download/chrome_download_manager_delegate.h"
6 6
7 #include <string> 7 #include <string>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/bind_helpers.h" 10 #include "base/bind_helpers.h"
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after
147 } 147 }
148 148
149 DownloadId ChromeDownloadManagerDelegate::GetNextId() { 149 DownloadId ChromeDownloadManagerDelegate::GetNextId() {
150 if (!profile_->IsOffTheRecord()) 150 if (!profile_->IsOffTheRecord())
151 return DownloadId(this, next_download_id_++); 151 return DownloadId(this, next_download_id_++);
152 152
153 return BrowserContext::GetDownloadManager(profile_->GetOriginalProfile())-> 153 return BrowserContext::GetDownloadManager(profile_->GetOriginalProfile())->
154 GetDelegate()->GetNextId(); 154 GetDelegate()->GetNextId();
155 } 155 }
156 156
157 bool ChromeDownloadManagerDelegate::ShouldStartDownload(int32 download_id) { 157 bool ChromeDownloadManagerDelegate::DetermineDownloadTarget(
158 // We create a download item and store it in our download map, and inform the 158 DownloadItem* download,
159 // history system of a new download. Since this method can be called while the 159 const content::DownloadTargetCallback& callback) {
160 // history service thread is still reading the persistent state, we do not
161 // insert the new DownloadItem into 'history_downloads_' or inform our
162 // observers at this point. OnCreateDownloadEntryComplete() handles that
163 // finalization of the the download creation as a callback from the history
164 // thread.
165 DownloadItem* download =
166 download_manager_->GetActiveDownloadItem(download_id);
167 if (!download)
168 return false;
169
170 #if defined(ENABLE_SAFE_BROWSING) 160 #if defined(ENABLE_SAFE_BROWSING)
171 DownloadProtectionService* service = GetDownloadProtectionService(); 161 DownloadProtectionService* service = GetDownloadProtectionService();
172 if (service) { 162 if (service) {
173 VLOG(2) << __FUNCTION__ << "() Start SB URL check for download = " 163 VLOG(2) << __FUNCTION__ << "() Start SB URL check for download = "
174 << download->DebugString(false); 164 << download->DebugString(false);
175 service->CheckDownloadUrl( 165 service->CheckDownloadUrl(
176 DownloadProtectionService::DownloadInfo::FromDownloadItem(*download), 166 DownloadProtectionService::DownloadInfo::FromDownloadItem(*download),
177 base::Bind( 167 base::Bind(
178 &ChromeDownloadManagerDelegate::CheckDownloadUrlDone, 168 &ChromeDownloadManagerDelegate::CheckDownloadUrlDone,
179 this, 169 this,
180 download->GetId())); 170 download->GetId(),
181 return false; 171 callback));
172 return true;
182 } 173 }
183 #endif 174 #endif
184 CheckDownloadUrlDone(download_id, DownloadProtectionService::SAFE); 175 CheckDownloadUrlDone(download->GetId(), callback,
185 return false; 176 DownloadProtectionService::SAFE);
177 return true;
186 } 178 }
187 179
188 void ChromeDownloadManagerDelegate::ChooseDownloadPath(DownloadItem* item) { 180 void ChromeDownloadManagerDelegate::ChooseDownloadPath(
181 DownloadItem* item,
182 const FilePath& suggested_path,
183 const FileSelectedCallback& file_selected_callback) {
189 // Deletes itself. 184 // Deletes itself.
190 DownloadFilePicker* file_picker = 185 DownloadFilePicker* file_picker =
191 #if defined(OS_CHROMEOS) 186 #if defined(OS_CHROMEOS)
192 new DownloadFilePickerChromeOS(); 187 new DownloadFilePickerChromeOS();
193 #else 188 #else
194 new DownloadFilePicker(); 189 new DownloadFilePicker();
195 #endif 190 #endif
196 file_picker->Init(download_manager_, item); 191 file_picker->Init(download_manager_, item, suggested_path,
192 file_selected_callback);
197 } 193 }
198 194
199 FilePath ChromeDownloadManagerDelegate::GetIntermediatePath( 195 FilePath ChromeDownloadManagerDelegate::GetIntermediatePath(
200 const DownloadItem& download) { 196 const FilePath& target_path,
197 content::DownloadDangerType danger_type) {
201 // If the download is not dangerous, just append .crdownload to the target 198 // If the download is not dangerous, just append .crdownload to the target
202 // path. 199 // path.
203 if (download.GetDangerType() == content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS) 200 if (danger_type == content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS)
204 return download_util::GetCrDownloadPath(download.GetTargetFilePath()); 201 return download_util::GetCrDownloadPath(target_path);
205 202
206 // If the download is potentially dangerous we create a filename of the form 203 // If the download is potentially dangerous we create a filename of the form
207 // 'Unconfirmed <random>.crdownload'. 204 // 'Unconfirmed <random>.crdownload'.
208 FilePath::StringType file_name; 205 FilePath::StringType file_name;
209 FilePath dir = download.GetTargetFilePath().DirName(); 206 FilePath dir = target_path.DirName();
210 #if defined(OS_WIN) 207 #if defined(OS_WIN)
211 string16 unconfirmed_prefix = 208 string16 unconfirmed_prefix =
212 l10n_util::GetStringUTF16(IDS_DOWNLOAD_UNCONFIRMED_PREFIX); 209 l10n_util::GetStringUTF16(IDS_DOWNLOAD_UNCONFIRMED_PREFIX);
213 #else 210 #else
214 std::string unconfirmed_prefix = 211 std::string unconfirmed_prefix =
215 l10n_util::GetStringUTF8(IDS_DOWNLOAD_UNCONFIRMED_PREFIX); 212 l10n_util::GetStringUTF8(IDS_DOWNLOAD_UNCONFIRMED_PREFIX);
216 #endif 213 #endif
217 base::SStringPrintf( 214 base::SStringPrintf(
218 &file_name, 215 &file_name,
219 unconfirmed_prefix.append( 216 unconfirmed_prefix.append(
(...skipping 270 matching lines...) Expand 10 before | Expand all | Expand 10 after
490 const content::SavePackagePathPickedCallback& callback) { 487 const content::SavePackagePathPickedCallback& callback) {
491 // Deletes itself. 488 // Deletes itself.
492 #if defined(OS_CHROMEOS) 489 #if defined(OS_CHROMEOS)
493 new SavePackageFilePickerChromeOS(web_contents, suggested_path, callback); 490 new SavePackageFilePickerChromeOS(web_contents, suggested_path, callback);
494 #else 491 #else
495 new SavePackageFilePicker(web_contents, suggested_path, default_extension, 492 new SavePackageFilePicker(web_contents, suggested_path, default_extension,
496 can_save_as_complete, download_prefs_.get(), callback); 493 can_save_as_complete, download_prefs_.get(), callback);
497 #endif 494 #endif
498 } 495 }
499 496
497 void ChromeDownloadManagerDelegate::ClearLastDownloadPath() {
498 last_download_path_.clear();
499 }
500
500 DownloadProtectionService* 501 DownloadProtectionService*
501 ChromeDownloadManagerDelegate::GetDownloadProtectionService() { 502 ChromeDownloadManagerDelegate::GetDownloadProtectionService() {
502 #if defined(ENABLE_SAFE_BROWSING) 503 #if defined(ENABLE_SAFE_BROWSING)
503 SafeBrowsingService* sb_service = g_browser_process->safe_browsing_service(); 504 SafeBrowsingService* sb_service = g_browser_process->safe_browsing_service();
504 if (sb_service && sb_service->download_protection_service() && 505 if (sb_service && sb_service->download_protection_service() &&
505 profile_->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled)) { 506 profile_->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled)) {
506 return sb_service->download_protection_service(); 507 return sb_service->download_protection_service();
507 } 508 }
508 #endif 509 #endif
509 return NULL; 510 return NULL;
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
542 return !download.HasUserGesture() || !visited_referrer_before; 543 return !download.HasUserGesture() || !visited_referrer_before;
543 544
544 return danger_level == download_util::Dangerous; 545 return danger_level == download_util::Dangerous;
545 } 546 }
546 547
547 void ChromeDownloadManagerDelegate::GetReservedPath( 548 void ChromeDownloadManagerDelegate::GetReservedPath(
548 DownloadItem& download, 549 DownloadItem& download,
549 const FilePath& target_path, 550 const FilePath& target_path,
550 const FilePath& default_download_path, 551 const FilePath& default_download_path,
551 bool should_uniquify_path, 552 bool should_uniquify_path,
552 const DownloadPathReservationTracker::ReservedPathCallback callback) { 553 const DownloadPathReservationTracker::ReservedPathCallback& callback) {
553 DownloadPathReservationTracker::GetReservedPath( 554 DownloadPathReservationTracker::GetReservedPath(
554 download, target_path, default_download_path, should_uniquify_path, 555 download, target_path, default_download_path, should_uniquify_path,
555 callback); 556 callback);
556 } 557 }
557 558
558 void ChromeDownloadManagerDelegate::CheckDownloadUrlDone( 559 void ChromeDownloadManagerDelegate::CheckDownloadUrlDone(
559 int32 download_id, 560 int32 download_id,
561 const content::DownloadTargetCallback& callback,
560 DownloadProtectionService::DownloadCheckResult result) { 562 DownloadProtectionService::DownloadCheckResult result) {
561 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 563 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
562 DownloadItem* download = 564 DownloadItem* download =
563 download_manager_->GetActiveDownloadItem(download_id); 565 download_manager_->GetActiveDownloadItem(download_id);
564 if (!download) 566 if (!download)
565 return; 567 return;
566 568
567 VLOG(2) << __FUNCTION__ << "() download = " << download->DebugString(false) 569 VLOG(2) << __FUNCTION__ << "() download = " << download->DebugString(false)
568 << " verdict = " << result; 570 << " verdict = " << result;
569 content::DownloadDangerType danger_type = download->GetDangerType(); 571 content::DownloadDangerType danger_type = download->GetDangerType();
570 if (result != DownloadProtectionService::SAFE) 572 if (result != DownloadProtectionService::SAFE)
571 danger_type = content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL; 573 danger_type = content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL;
572 574
573 download_history_->CheckVisitedReferrerBefore( 575 download_history_->CheckVisitedReferrerBefore(
574 download_id, download->GetReferrerUrl(), 576 download_id, download->GetReferrerUrl(),
575 base::Bind(&ChromeDownloadManagerDelegate::CheckVisitedReferrerBeforeDone, 577 base::Bind(&ChromeDownloadManagerDelegate::CheckVisitedReferrerBeforeDone,
576 base::Unretained(this), download_id, danger_type)); 578 base::Unretained(this), download_id, callback, danger_type));
577 } 579 }
578 580
579 void ChromeDownloadManagerDelegate::CheckClientDownloadDone( 581 void ChromeDownloadManagerDelegate::CheckClientDownloadDone(
580 int32 download_id, 582 int32 download_id,
581 DownloadProtectionService::DownloadCheckResult result) { 583 DownloadProtectionService::DownloadCheckResult result) {
582 DownloadItem* item = download_manager_->GetActiveDownloadItem(download_id); 584 DownloadItem* item = download_manager_->GetActiveDownloadItem(download_id);
583 if (!item) 585 if (!item)
584 return; 586 return;
585 587
586 VLOG(2) << __FUNCTION__ << "() download = " << item->DebugString(false) 588 VLOG(2) << __FUNCTION__ << "() download = " << item->DebugString(false)
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
624 int download_id = crx_installers_[installer]; 626 int download_id = crx_installers_[installer];
625 crx_installers_.erase(installer.get()); 627 crx_installers_.erase(installer.get());
626 628
627 DownloadItem* item = download_manager_->GetActiveDownloadItem(download_id); 629 DownloadItem* item = download_manager_->GetActiveDownloadItem(download_id);
628 if (item) 630 if (item)
629 item->DelayedDownloadOpened(installer->did_handle_successfully()); 631 item->DelayedDownloadOpened(installer->did_handle_successfully());
630 } 632 }
631 633
632 void ChromeDownloadManagerDelegate::CheckVisitedReferrerBeforeDone( 634 void ChromeDownloadManagerDelegate::CheckVisitedReferrerBeforeDone(
633 int32 download_id, 635 int32 download_id,
636 const content::DownloadTargetCallback& callback,
634 content::DownloadDangerType danger_type, 637 content::DownloadDangerType danger_type,
635 bool visited_referrer_before) { 638 bool visited_referrer_before) {
636 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 639 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
637 640
638 DownloadItem* download = 641 DownloadItem* download =
639 download_manager_->GetActiveDownloadItem(download_id); 642 download_manager_->GetActiveDownloadItem(download_id);
640 if (!download) 643 if (!download)
641 return; 644 return;
642 645
643 bool should_prompt = (download->GetTargetDisposition() == 646 bool should_prompt = (download->GetTargetDisposition() ==
(...skipping 21 matching lines...) Expand all
665 !ShouldOpenFileBasedOnExtension(generated_name)) 668 !ShouldOpenFileBasedOnExtension(generated_name))
666 should_prompt = true; 669 should_prompt = true;
667 } 670 }
668 if (download_prefs_->IsDownloadPathManaged()) 671 if (download_prefs_->IsDownloadPathManaged())
669 should_prompt = false; 672 should_prompt = false;
670 673
671 // Determine the proper path for a download, by either one of the following: 674 // Determine the proper path for a download, by either one of the following:
672 // 1) using the default download directory. 675 // 1) using the default download directory.
673 // 2) prompting the user. 676 // 2) prompting the user.
674 FilePath target_directory; 677 FilePath target_directory;
675 if (should_prompt && !download_manager_->LastDownloadPath().empty()) 678 if (should_prompt && !last_download_path_.empty())
676 target_directory = download_manager_->LastDownloadPath(); 679 target_directory = last_download_path_;
677 else 680 else
678 target_directory = download_prefs_->DownloadPath(); 681 target_directory = download_prefs_->DownloadPath();
679 suggested_path = target_directory.Append(generated_name); 682 suggested_path = target_directory.Append(generated_name);
680 } else { 683 } else {
681 DCHECK(!should_prompt); 684 DCHECK(!should_prompt);
682 suggested_path = download->GetForcedFilePath(); 685 suggested_path = download->GetForcedFilePath();
683 } 686 }
684 687
685 // If the download hasn't already been marked dangerous (could be 688 // If the download hasn't already been marked dangerous (could be
686 // DANGEROUS_URL), check if it is a dangerous file. 689 // DANGEROUS_URL), check if it is a dangerous file.
(...skipping 22 matching lines...) Expand all
709 } else { 712 } else {
710 // Currently we only expect this case. 713 // Currently we only expect this case.
711 DCHECK_EQ(content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL, danger_type); 714 DCHECK_EQ(content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL, danger_type);
712 } 715 }
713 716
714 #if defined (OS_CHROMEOS) 717 #if defined (OS_CHROMEOS)
715 gdata::GDataDownloadObserver::SubstituteGDataDownloadPath( 718 gdata::GDataDownloadObserver::SubstituteGDataDownloadPath(
716 profile_, suggested_path, download, 719 profile_, suggested_path, download,
717 base::Bind( 720 base::Bind(
718 &ChromeDownloadManagerDelegate::SubstituteGDataDownloadPathCallback, 721 &ChromeDownloadManagerDelegate::SubstituteGDataDownloadPathCallback,
719 this, download->GetId(), should_prompt, is_forced_path, danger_type)); 722 this, download->GetId(), callback, should_prompt, is_forced_path,
723 danger_type));
720 #else 724 #else
721 GetReservedPath( 725 GetReservedPath(
722 *download, suggested_path, download_prefs_->DownloadPath(), 726 *download, suggested_path, download_prefs_->DownloadPath(),
723 !is_forced_path, 727 !is_forced_path,
724 base::Bind(&ChromeDownloadManagerDelegate::OnPathReservationAvailable, 728 base::Bind(&ChromeDownloadManagerDelegate::OnPathReservationAvailable,
725 this, download->GetId(), should_prompt, danger_type)); 729 this, download->GetId(), callback, should_prompt,
730 danger_type));
726 #endif 731 #endif
727 } 732 }
728 733
729 #if defined (OS_CHROMEOS) 734 #if defined (OS_CHROMEOS)
735 // TODO(asanka): Merge this logic with the logic in DownloadFilePickerChromeOS.
730 void ChromeDownloadManagerDelegate::SubstituteGDataDownloadPathCallback( 736 void ChromeDownloadManagerDelegate::SubstituteGDataDownloadPathCallback(
731 int32 download_id, 737 int32 download_id,
738 const content::DownloadTargetCallback& callback,
732 bool should_prompt, 739 bool should_prompt,
733 bool is_forced_path, 740 bool is_forced_path,
734 content::DownloadDangerType danger_type, 741 content::DownloadDangerType danger_type,
735 const FilePath& suggested_path) { 742 const FilePath& suggested_path) {
736 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 743 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
737 DownloadItem* download = 744 DownloadItem* download =
738 download_manager_->GetActiveDownloadItem(download_id); 745 download_manager_->GetActiveDownloadItem(download_id);
739 if (!download) 746 if (!download)
740 return; 747 return;
741 748
742 GetReservedPath( 749 GetReservedPath(
743 *download, suggested_path, download_prefs_->DownloadPath(), 750 *download, suggested_path, download_prefs_->DownloadPath(),
744 !is_forced_path, 751 !is_forced_path,
745 base::Bind(&ChromeDownloadManagerDelegate::OnPathReservationAvailable, 752 base::Bind(&ChromeDownloadManagerDelegate::OnPathReservationAvailable,
746 this, download->GetId(), should_prompt, danger_type)); 753 this, download->GetId(), callback, should_prompt,
754 danger_type));
747 } 755 }
748 #endif 756 #endif
749 757
750 void ChromeDownloadManagerDelegate::OnPathReservationAvailable( 758 void ChromeDownloadManagerDelegate::OnPathReservationAvailable(
751 int32 download_id, 759 int32 download_id,
760 const content::DownloadTargetCallback& callback,
752 bool should_prompt, 761 bool should_prompt,
753 content::DownloadDangerType danger_type, 762 content::DownloadDangerType danger_type,
754 const FilePath& target_path, 763 const FilePath& reserved_path,
755 bool target_path_verified) { 764 bool reserved_path_verified) {
756 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 765 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
757 DownloadItem* download = 766 DownloadItem* download =
758 download_manager_->GetActiveDownloadItem(download_id); 767 download_manager_->GetActiveDownloadItem(download_id);
759 if (!download) 768 if (!download)
760 return; 769 return;
761 DownloadItem::TargetDisposition disposition; 770 if (should_prompt || !reserved_path_verified) {
762 // If the target path could not be verified then the path was non-existant, 771 // If the target path could not be verified then the path was non-existant,
763 // non writeable or could not be uniquified. Prompt the user. 772 // non writeable or could not be uniquified. Prompt the user.
764 if (should_prompt || !target_path_verified) 773 ChooseDownloadPath(
765 disposition = DownloadItem::TARGET_DISPOSITION_PROMPT; 774 download, reserved_path,
766 else 775 base::Bind(&ChromeDownloadManagerDelegate::OnTargetPathDetermined,
767 disposition = DownloadItem::TARGET_DISPOSITION_OVERWRITE; 776 this, download_id, callback,
768 download->OnTargetPathDetermined(target_path, disposition, danger_type); 777 DownloadItem::TARGET_DISPOSITION_PROMPT, danger_type));
769 download_manager_->RestartDownload(download_id); 778 } else {
779 OnTargetPathDetermined(download_id, callback,
780 DownloadItem::TARGET_DISPOSITION_OVERWRITE,
781 danger_type, reserved_path);
782 }
783 }
784
785 void ChromeDownloadManagerDelegate::OnTargetPathDetermined(
786 int32 download_id,
787 const content::DownloadTargetCallback& callback,
788 DownloadItem::TargetDisposition disposition,
789 content::DownloadDangerType danger_type,
790 const FilePath& target_path) {
791 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
792 FilePath intermediate_path;
793 DownloadItem* download =
794 download_manager_->GetActiveDownloadItem(download_id);
795 if (!download)
796 return;
797
798 // If |target_path| is empty, then that means that the user wants to cancel
799 // the download.
800 if (!target_path.empty()) {
801 intermediate_path = GetIntermediatePath(target_path, danger_type);
802
803 // Retain the last directory. Exclude temporary downloads since the path
804 // likely points at the location of a temporary file.
805 // TODO(asanka): This logic is a hack. DownloadFilePicker should give us a
806 // directory to persist. Or perhaps, if the GData path
807 // substitution logic is moved here, then we would have a
808 // persistable path after the DownloadFilePicker is done.
809 if (disposition == DownloadItem::TARGET_DISPOSITION_PROMPT &&
810 !download->IsTemporary())
811 last_download_path_ = target_path.DirName();
812 }
813 callback.Run(target_path, disposition, danger_type, intermediate_path);
770 } 814 }
771 815
772 void ChromeDownloadManagerDelegate::OnItemAddedToPersistentStore( 816 void ChromeDownloadManagerDelegate::OnItemAddedToPersistentStore(
773 int32 download_id, int64 db_handle) { 817 int32 download_id, int64 db_handle) {
774 // It's not immediately obvious, but HistoryBackend::CreateDownload() can 818 // It's not immediately obvious, but HistoryBackend::CreateDownload() can
775 // call this function with an invalid |db_handle|. For instance, this can 819 // call this function with an invalid |db_handle|. For instance, this can
776 // happen when the history database is offline. We cannot have multiple 820 // happen when the history database is offline. We cannot have multiple
777 // DownloadItems with the same invalid db_handle, so we need to assign a 821 // DownloadItems with the same invalid db_handle, so we need to assign a
778 // unique |db_handle| here. 822 // unique |db_handle| here.
779 if (db_handle == DownloadItem::kUninitializedHandle) 823 if (db_handle == DownloadItem::kUninitializedHandle)
780 db_handle = download_history_->GetNextFakeDbHandle(); 824 db_handle = download_history_->GetNextFakeDbHandle();
781 download_manager_->OnItemAddedToPersistentStore(download_id, db_handle); 825 download_manager_->OnItemAddedToPersistentStore(download_id, db_handle);
782 } 826 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698