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