Home | History | Annotate | Download | only in drive
      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