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