• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
DriveUserData(const base::FilePath & path)31   explicit DriveUserData(const base::FilePath& path) : file_path_(path),
32                                                        is_complete_(false) {}
~DriveUserData()33   virtual ~DriveUserData() {}
34 
file_path() const35   const base::FilePath& file_path() const { return file_path_; }
cache_file_path() const36   const base::FilePath& cache_file_path() const { return cache_file_path_; }
set_cache_file_path(const base::FilePath & path)37   void set_cache_file_path(const base::FilePath& path) {
38     cache_file_path_ = path;
39   }
is_complete() const40   bool is_complete() const { return is_complete_; }
set_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|.
GetDriveUserData(const DownloadItem * download)50 const DriveUserData* GetDriveUserData(const DownloadItem* download) {
51   return static_cast<const DriveUserData*>(
52       download->GetUserData(&kDrivePathKey));
53 }
54 
GetDriveUserData(DownloadItem * download)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.
GetDriveTempDownloadPath(const base::FilePath & drive_tmp_download_dir)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.
MoveDownloadedFile(const base::FilePath & downloaded_file,base::FilePath * cache_file_path,FileError error,const base::FilePath & dest_path)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().
ContinueCheckingForFileExistence(const content::CheckForFileExistenceCallback & callback,FileError error,scoped_ptr<ResourceEntry> entry)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.
IsPersistedDriveDownload(const base::FilePath & drive_tmp_download_path,DownloadItem * download)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 
DownloadHandler(FileSystemInterface * file_system)105 DownloadHandler::DownloadHandler(FileSystemInterface* file_system)
106     : file_system_(file_system),
107       weak_ptr_factory_(this) {
108 }
109 
~DownloadHandler()110 DownloadHandler::~DownloadHandler() {
111 }
112 
113 // static
GetForProfile(Profile * profile)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 
Initialize(DownloadManager * download_manager,const base::FilePath & drive_tmp_download_path)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 
SubstituteDriveDownloadPath(const base::FilePath & drive_path,content::DownloadItem * download,const SubstituteDriveDownloadPathCallback & callback)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 
SetDownloadParams(const base::FilePath & drive_path,DownloadItem * download)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 
GetTargetPath(const DownloadItem * download)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 
GetCacheFilePath(const DownloadItem * download)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 
IsDriveDownload(const DownloadItem * download)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 
CheckForFileExistence(const DownloadItem * download,const content::CheckForFileExistenceCallback & callback)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 
OnDownloadCreated(DownloadManager * manager,DownloadItem * download)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 
RemoveDownload(int id)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 
OnDownloadUpdated(DownloadManager * manager,DownloadItem * download)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 
OnCreateDirectory(const SubstituteDriveDownloadPathCallback & callback,FileError error)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 
UploadDownloadItem(DownloadItem * download)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 
SetCacheFilePath(int id,const base::FilePath * cache_file_path,FileError error)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