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