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 3 // found in the LICENSE file. 4 5 #include "chrome/browser/chromeos/drive/download_handler.h" 6 7 #include "base/bind.h" 8 #include "base/file_util.h" 9 #include "base/supports_user_data.h" 10 #include "base/threading/sequenced_worker_pool.h" 11 #include "chrome/browser/chromeos/drive/drive.pb.h" 12 #include "chrome/browser/chromeos/drive/drive_integration_service.h" 13 #include "chrome/browser/chromeos/drive/file_system_interface.h" 14 #include "chrome/browser/chromeos/drive/file_system_util.h" 15 #include "chrome/browser/chromeos/drive/write_on_cache_file.h" 16 #include "content/public/browser/browser_thread.h" 17 18 using content::BrowserThread; 19 using content::DownloadManager; 20 using content::DownloadItem; 21 22 namespace drive { 23 namespace { 24 25 // Key for base::SupportsUserData::Data. 26 const char kDrivePathKey[] = "DrivePath"; 27 28 // User Data stored in DownloadItem for drive path. 29 class DriveUserData : public base::SupportsUserData::Data { 30 public: 31 explicit DriveUserData(const base::FilePath& path) : file_path_(path), 32 is_complete_(false) {} 33 virtual ~DriveUserData() {} 34 35 const base::FilePath& file_path() const { return file_path_; } 36 const base::FilePath& cache_file_path() const { return cache_file_path_; } 37 void set_cache_file_path(const base::FilePath& path) { 38 cache_file_path_ = path; 39 } 40 bool is_complete() const { return is_complete_; } 41 void set_complete() { is_complete_ = true; } 42 43 private: 44 const base::FilePath file_path_; 45 base::FilePath cache_file_path_; 46 bool is_complete_; 47 }; 48 49 // Extracts DriveUserData* from |download|. 50 const DriveUserData* GetDriveUserData(const DownloadItem* download) { 51 return static_cast<const DriveUserData*>( 52 download->GetUserData(&kDrivePathKey)); 53 } 54 55 DriveUserData* GetDriveUserData(DownloadItem* download) { 56 return static_cast<DriveUserData*>(download->GetUserData(&kDrivePathKey)); 57 } 58 59 // Creates a temporary file |drive_tmp_download_path| in 60 // |drive_tmp_download_dir|. Must be called on a thread that allows file 61 // operations. 62 base::FilePath GetDriveTempDownloadPath( 63 const base::FilePath& drive_tmp_download_dir) { 64 bool created = base::CreateDirectory(drive_tmp_download_dir); 65 DCHECK(created) << "Can not create temp download directory at " 66 << drive_tmp_download_dir.value(); 67 base::FilePath drive_tmp_download_path; 68 created = base::CreateTemporaryFileInDir(drive_tmp_download_dir, 69 &drive_tmp_download_path); 70 DCHECK(created) << "Temporary download file creation failed"; 71 return drive_tmp_download_path; 72 } 73 74 // Moves downloaded file to Drive. 75 void MoveDownloadedFile(const base::FilePath& downloaded_file, 76 base::FilePath* cache_file_path, 77 FileError error, 78 const base::FilePath& dest_path) { 79 if (error != FILE_ERROR_OK || 80 !base::Move(downloaded_file, dest_path)) 81 return; 82 *cache_file_path = dest_path; 83 } 84 85 // Used to implement CheckForFileExistence(). 86 void ContinueCheckingForFileExistence( 87 const content::CheckForFileExistenceCallback& callback, 88 FileError error, 89 scoped_ptr<ResourceEntry> entry) { 90 callback.Run(error == FILE_ERROR_OK); 91 } 92 93 // Returns true if |download| is a Drive download created from data persisted 94 // on the download history DB. 95 bool IsPersistedDriveDownload(const base::FilePath& drive_tmp_download_path, 96 DownloadItem* download) { 97 // Persisted downloads are not in IN_PROGRESS state when created, while newly 98 // created downloads are. 99 return drive_tmp_download_path.IsParent(download->GetTargetFilePath()) && 100 download->GetState() != DownloadItem::IN_PROGRESS; 101 } 102 103 } // namespace 104 105 DownloadHandler::DownloadHandler(FileSystemInterface* file_system) 106 : file_system_(file_system), 107 weak_ptr_factory_(this) { 108 } 109 110 DownloadHandler::~DownloadHandler() { 111 } 112 113 // static 114 DownloadHandler* DownloadHandler::GetForProfile(Profile* profile) { 115 DriveIntegrationService* service = 116 DriveIntegrationServiceFactory::FindForProfile(profile); 117 if (!service || !service->IsMounted()) 118 return NULL; 119 return service->download_handler(); 120 } 121 122 void DownloadHandler::Initialize( 123 DownloadManager* download_manager, 124 const base::FilePath& drive_tmp_download_path) { 125 DCHECK(!drive_tmp_download_path.empty()); 126 127 drive_tmp_download_path_ = drive_tmp_download_path; 128 129 if (download_manager) { 130 notifier_.reset(new AllDownloadItemNotifier(download_manager, this)); 131 // Remove any persisted Drive DownloadItem. crbug.com/171384 132 content::DownloadManager::DownloadVector downloads; 133 download_manager->GetAllDownloads(&downloads); 134 for (size_t i = 0; i < downloads.size(); ++i) { 135 if (IsPersistedDriveDownload(drive_tmp_download_path_, downloads[i])) 136 RemoveDownload(downloads[i]->GetId()); 137 } 138 } 139 } 140 141 void DownloadHandler::SubstituteDriveDownloadPath( 142 const base::FilePath& drive_path, 143 content::DownloadItem* download, 144 const SubstituteDriveDownloadPathCallback& callback) { 145 DVLOG(1) << "SubstituteDriveDownloadPath " << drive_path.value(); 146 147 SetDownloadParams(drive_path, download); 148 149 if (util::IsUnderDriveMountPoint(drive_path)) { 150 // Prepare the destination directory. 151 const bool is_exclusive = false, is_recursive = true; 152 file_system_->CreateDirectory( 153 util::ExtractDrivePath(drive_path.DirName()), 154 is_exclusive, is_recursive, 155 base::Bind(&DownloadHandler::OnCreateDirectory, 156 weak_ptr_factory_.GetWeakPtr(), 157 callback)); 158 } else { 159 callback.Run(drive_path); 160 } 161 } 162 163 void DownloadHandler::SetDownloadParams(const base::FilePath& drive_path, 164 DownloadItem* download) { 165 if (!download || (download->GetState() != DownloadItem::IN_PROGRESS)) 166 return; 167 168 if (util::IsUnderDriveMountPoint(drive_path)) { 169 download->SetUserData(&kDrivePathKey, new DriveUserData(drive_path)); 170 download->SetDisplayName(drive_path.BaseName()); 171 } else if (IsDriveDownload(download)) { 172 // This may have been previously set if the default download folder is 173 // /drive, and the user has now changed the download target to a local 174 // folder. 175 download->SetUserData(&kDrivePathKey, NULL); 176 download->SetDisplayName(base::FilePath()); 177 } 178 } 179 180 base::FilePath DownloadHandler::GetTargetPath( 181 const DownloadItem* download) { 182 const DriveUserData* data = GetDriveUserData(download); 183 // If data is NULL, we've somehow lost the drive path selected by the file 184 // picker. 185 DCHECK(data); 186 return data ? data->file_path() : base::FilePath(); 187 } 188 189 base::FilePath DownloadHandler::GetCacheFilePath(const DownloadItem* download) { 190 const DriveUserData* data = GetDriveUserData(download); 191 return data ? data->cache_file_path() : base::FilePath(); 192 } 193 194 bool DownloadHandler::IsDriveDownload(const DownloadItem* download) { 195 // We use the existence of the DriveUserData object in download as a 196 // signal that this is a download to Drive. 197 return GetDriveUserData(download) != NULL; 198 } 199 200 void DownloadHandler::CheckForFileExistence( 201 const DownloadItem* download, 202 const content::CheckForFileExistenceCallback& callback) { 203 file_system_->GetResourceEntry( 204 util::ExtractDrivePath(GetTargetPath(download)), 205 base::Bind(&ContinueCheckingForFileExistence, 206 callback)); 207 } 208 209 void DownloadHandler::OnDownloadCreated(DownloadManager* manager, 210 DownloadItem* download) { 211 // Remove any persisted Drive DownloadItem. crbug.com/171384 212 if (IsPersistedDriveDownload(drive_tmp_download_path_, download)) { 213 // Remove download later, since doing it here results in a crash. 214 BrowserThread::PostTask(BrowserThread::UI, 215 FROM_HERE, 216 base::Bind(&DownloadHandler::RemoveDownload, 217 weak_ptr_factory_.GetWeakPtr(), 218 download->GetId())); 219 } 220 } 221 222 void DownloadHandler::RemoveDownload(int id) { 223 DownloadManager* manager = notifier_->GetManager(); 224 if (!manager) 225 return; 226 DownloadItem* download = manager->GetDownload(id); 227 if (!download) 228 return; 229 download->Remove(); 230 } 231 232 void DownloadHandler::OnDownloadUpdated( 233 DownloadManager* manager, DownloadItem* download) { 234 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 235 236 // Only accept downloads that have the Drive meta data associated with them. 237 DriveUserData* data = GetDriveUserData(download); 238 if (!drive_tmp_download_path_.IsParent(download->GetTargetFilePath()) || 239 !data || 240 data->is_complete()) 241 return; 242 243 switch (download->GetState()) { 244 case DownloadItem::IN_PROGRESS: 245 break; 246 247 case DownloadItem::COMPLETE: 248 UploadDownloadItem(download); 249 data->set_complete(); 250 break; 251 252 case DownloadItem::CANCELLED: 253 download->SetUserData(&kDrivePathKey, NULL); 254 break; 255 256 case DownloadItem::INTERRUPTED: 257 // Interrupted downloads can be resumed. Keep the Drive user data around 258 // so that it can be used when the download resumes. The download is truly 259 // done when it's complete, is cancelled or is removed. 260 break; 261 262 default: 263 NOTREACHED(); 264 } 265 } 266 267 void DownloadHandler::OnCreateDirectory( 268 const SubstituteDriveDownloadPathCallback& callback, 269 FileError error) { 270 DVLOG(1) << "OnCreateDirectory " << FileErrorToString(error); 271 if (error == FILE_ERROR_OK) { 272 base::PostTaskAndReplyWithResult( 273 BrowserThread::GetBlockingPool(), 274 FROM_HERE, 275 base::Bind(&GetDriveTempDownloadPath, drive_tmp_download_path_), 276 callback); 277 } else { 278 LOG(WARNING) << "Failed to create directory, error = " 279 << FileErrorToString(error); 280 callback.Run(base::FilePath()); 281 } 282 } 283 284 void DownloadHandler::UploadDownloadItem(DownloadItem* download) { 285 DCHECK_EQ(DownloadItem::COMPLETE, download->GetState()); 286 base::FilePath* cache_file_path = new base::FilePath; 287 WriteOnCacheFileAndReply( 288 file_system_, 289 util::ExtractDrivePath(GetTargetPath(download)), 290 download->GetMimeType(), 291 base::Bind(&MoveDownloadedFile, download->GetTargetFilePath(), 292 cache_file_path), 293 base::Bind(&DownloadHandler::SetCacheFilePath, 294 weak_ptr_factory_.GetWeakPtr(), 295 download->GetId(), 296 base::Owned(cache_file_path))); 297 } 298 299 void DownloadHandler::SetCacheFilePath(int id, 300 const base::FilePath* cache_file_path, 301 FileError error) { 302 if (error != FILE_ERROR_OK) 303 return; 304 DownloadManager* manager = notifier_->GetManager(); 305 if (!manager) 306 return; 307 DownloadItem* download = manager->GetDownload(id); 308 if (!download) 309 return; 310 DriveUserData* data = GetDriveUserData(download); 311 if (!data) 312 return; 313 data->set_cache_file_path(*cache_file_path); 314 } 315 316 317 } // namespace drive 318