• 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 "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:
DriveUserData(const base::FilePath & path)34   explicit DriveUserData(const base::FilePath& path) : file_path_(path),
35                                                        is_complete_(false) {}
~DriveUserData()36   virtual ~DriveUserData() {}
37 
file_path() const38   const base::FilePath& file_path() const { return file_path_; }
cache_file_path() const39   const base::FilePath& cache_file_path() const { return cache_file_path_; }
set_cache_file_path(const base::FilePath & path)40   void set_cache_file_path(const base::FilePath& path) {
41     cache_file_path_ = path;
42   }
is_complete() const43   bool is_complete() const { return is_complete_; }
set_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|.
GetDriveUserData(const DownloadItem * download)53 const DriveUserData* GetDriveUserData(const DownloadItem* download) {
54   return static_cast<const DriveUserData*>(
55       download->GetUserData(&kDrivePathKey));
56 }
57 
GetDriveUserData(DownloadItem * download)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.
GetDriveTempDownloadPath(const base::FilePath & drive_tmp_download_dir)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.
MoveDownloadedFile(const base::FilePath & downloaded_file,base::FilePath * cache_file_path,FileError error,const base::FilePath & dest_path)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().
ContinueCheckingForFileExistence(const content::CheckForFileExistenceCallback & callback,FileError error,scoped_ptr<ResourceEntry> entry)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.
IsPersistedDriveDownload(const base::FilePath & drive_tmp_download_path,DownloadItem * download)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 
DownloadHandler(FileSystemInterface * file_system)113 DownloadHandler::DownloadHandler(FileSystemInterface* file_system)
114     : file_system_(file_system),
115       weak_ptr_factory_(this) {
116 }
117 
~DownloadHandler()118 DownloadHandler::~DownloadHandler() {
119 }
120 
121 // static
GetForProfile(Profile * profile)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 
Initialize(DownloadManager * download_manager,const base::FilePath & drive_tmp_download_path)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 
ObserveIncognitoDownloadManager(DownloadManager * download_manager)149 void DownloadHandler::ObserveIncognitoDownloadManager(
150     DownloadManager* download_manager) {
151   notifier_incognito_.reset(new AllDownloadItemNotifier(download_manager,
152                                                         this));
153 }
154 
SubstituteDriveDownloadPath(const base::FilePath & drive_path,content::DownloadItem * download,const SubstituteDriveDownloadPathCallback & callback)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 
SetDownloadParams(const base::FilePath & drive_path,DownloadItem * download)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 
GetTargetPath(const DownloadItem * download)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 
GetCacheFilePath(const DownloadItem * download)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 
IsDriveDownload(const DownloadItem * download)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 
CheckForFileExistence(const DownloadItem * download,const content::CheckForFileExistenceCallback & callback)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 
OnDownloadCreated(DownloadManager * manager,DownloadItem * download)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 
RemoveDownload(void * manager_id,int id)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 
OnDownloadUpdated(DownloadManager * manager,DownloadItem * download)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 
OnCreateDirectory(const SubstituteDriveDownloadPathCallback & callback,FileError error)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 
UploadDownloadItem(DownloadManager * manager,DownloadItem * download)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 
SetCacheFilePath(void * manager_id,int id,const base::FilePath * cache_file_path,FileError error)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 
GetDownloadManager(void * manager_id)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