1 // Copyright 2013 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.
5 #include "chrome/browser/sync_file_system/drive_backend_v1/api_util.h"
12 #include "base/file_util.h"
13 #include "base/sequenced_task_runner.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/threading/sequenced_worker_pool.h"
17 #include "base/values.h"
18 #include "chrome/browser/drive/drive_api_service.h"
19 #include "chrome/browser/drive/drive_api_util.h"
20 #include "chrome/browser/drive/drive_uploader.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
23 #include "chrome/browser/signin/signin_manager_factory.h"
24 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h"
25 #include "chrome/browser/sync_file_system/drive_backend_v1/drive_file_sync_util.h"
26 #include "chrome/browser/sync_file_system/logger.h"
27 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
28 #include "components/signin/core/browser/profile_oauth2_token_service.h"
29 #include "components/signin/core/browser/signin_manager.h"
30 #include "content/public/browser/browser_thread.h"
31 #include "extensions/common/constants.h"
32 #include "extensions/common/extension.h"
33 #include "google_apis/drive/drive_api_parser.h"
34 #include "google_apis/drive/drive_api_url_generator.h"
35 #include "google_apis/drive/gdata_wapi_url_generator.h"
37 namespace sync_file_system
{
38 namespace drive_backend
{
43 PARENT_TYPE_ROOT_OR_EMPTY
,
44 PARENT_TYPE_DIRECTORY
,
47 const char kFakeAccountId
[] = "test_user@gmail.com";
49 void EmptyGDataErrorCodeCallback(google_apis::GDataErrorCode error
) {}
51 bool HasParentLinkTo(const ScopedVector
<google_apis::Link
>& links
,
52 const std::string
& parent_resource_id
,
53 ParentType parent_type
) {
54 bool has_parent
= false;
56 for (ScopedVector
<google_apis::Link
>::const_iterator itr
= links
.begin();
57 itr
!= links
.end(); ++itr
) {
58 if ((*itr
)->type() == google_apis::Link::LINK_PARENT
) {
60 if (drive::util::ExtractResourceIdFromUrl((*itr
)->href()) ==
66 return parent_type
== PARENT_TYPE_ROOT_OR_EMPTY
&& !has_parent
;
69 struct TitleAndParentQuery
70 : std::unary_function
<const google_apis::ResourceEntry
*, bool> {
71 TitleAndParentQuery(const std::string
& title
,
72 const std::string
& parent_resource_id
,
73 ParentType parent_type
)
75 parent_resource_id(parent_resource_id
),
76 parent_type(parent_type
) {}
78 bool operator()(const google_apis::ResourceEntry
* entry
) const {
79 return entry
->title() == title
&&
80 HasParentLinkTo(entry
->links(), parent_resource_id
, parent_type
);
83 const std::string
& title
;
84 const std::string
& parent_resource_id
;
85 ParentType parent_type
;
88 void FilterEntriesByTitleAndParent(
89 ScopedVector
<google_apis::ResourceEntry
>* entries
,
90 const std::string
& title
,
91 const std::string
& parent_resource_id
,
92 ParentType parent_type
) {
93 typedef ScopedVector
<google_apis::ResourceEntry
>::iterator iterator
;
94 iterator itr
= std::partition(entries
->begin(),
96 TitleAndParentQuery(title
,
99 entries
->erase(itr
, entries
->end());
102 google_apis::ResourceEntry
* GetDocumentByTitleAndParent(
103 const ScopedVector
<google_apis::ResourceEntry
>& entries
,
104 const std::string
& title
,
105 const std::string
& parent_resource_id
,
106 ParentType parent_type
) {
107 typedef ScopedVector
<google_apis::ResourceEntry
>::const_iterator iterator
;
109 std::find_if(entries
.begin(),
111 TitleAndParentQuery(title
, parent_resource_id
, parent_type
));
112 if (found
!= entries
.end())
117 void EntryAdapterForEnsureTitleUniqueness(
118 scoped_ptr
<google_apis::ResourceEntry
> entry
,
119 const APIUtil::EnsureUniquenessCallback
& callback
,
120 APIUtil::EnsureUniquenessStatus status
,
121 google_apis::GDataErrorCode error
) {
122 callback
.Run(error
, status
, entry
.Pass());
125 void UploadResultAdapter(const APIUtil::ResourceEntryCallback
& callback
,
126 google_apis::GDataErrorCode error
,
127 const GURL
& upload_location
,
128 scoped_ptr
<google_apis::ResourceEntry
> entry
) {
129 callback
.Run(error
, entry
.Pass());
132 std::string
GetMimeTypeFromTitle(const std::string
& title
) {
133 base::FilePath::StringType extension
=
134 base::FilePath::FromUTF8Unsafe(title
).Extension();
135 std::string mime_type
;
136 if (extension
.empty() ||
137 !net::GetWellKnownMimeTypeFromExtension(extension
.substr(1), &mime_type
))
138 return kMimeTypeOctetStream
;
142 bool CreateTemporaryFile(const base::FilePath
& dir_path
,
143 webkit_blob::ScopedFile
* temp_file
) {
144 base::FilePath temp_file_path
;
145 const bool success
= base::CreateDirectory(dir_path
) &&
146 base::CreateTemporaryFileInDir(dir_path
, &temp_file_path
);
150 webkit_blob::ScopedFile(temp_file_path
,
151 webkit_blob::ScopedFile::DELETE_ON_SCOPE_OUT
,
152 base::MessageLoopProxy::current().get());
158 APIUtil::APIUtil(Profile
* profile
,
159 const base::FilePath
& temp_dir_path
)
160 : oauth_service_(ProfileOAuth2TokenServiceFactory::GetForProfile(profile
)),
161 signin_manager_(SigninManagerFactory::GetForProfile(profile
)),
163 temp_dir_path_(temp_dir_path
),
164 has_initialized_token_(false) {
165 base::SequencedWorkerPool
* blocking_pool
=
166 content::BrowserThread::GetBlockingPool();
167 scoped_refptr
<base::SequencedTaskRunner
> task_runner(
168 blocking_pool
->GetSequencedTaskRunner(blocking_pool
->GetSequenceToken()));
169 DCHECK(!IsDriveAPIDisabled());
170 drive_service_
.reset(new drive::DriveAPIService(
172 profile
->GetRequestContext(),
174 GURL(google_apis::DriveApiUrlGenerator::kBaseUrlForProduction
),
175 GURL(google_apis::DriveApiUrlGenerator::kBaseDownloadUrlForProduction
),
176 GURL(google_apis::GDataWapiUrlGenerator::kBaseUrlForProduction
),
177 std::string() /* custom_user_agent */));
178 drive_service_
->Initialize(signin_manager_
->GetAuthenticatedAccountId());
179 drive_service_
->AddObserver(this);
180 has_initialized_token_
= drive_service_
->HasRefreshToken();
182 net::NetworkChangeNotifier::AddConnectionTypeObserver(this);
184 drive_uploader_
.reset(new drive::DriveUploader(
185 drive_service_
.get(), content::BrowserThread::GetBlockingPool()));
188 scoped_ptr
<APIUtil
> APIUtil::CreateForTesting(
189 const base::FilePath
& temp_dir_path
,
190 scoped_ptr
<drive::DriveServiceInterface
> drive_service
,
191 scoped_ptr
<drive::DriveUploaderInterface
> drive_uploader
) {
192 return make_scoped_ptr(new APIUtil(
194 drive_service
.Pass(),
195 drive_uploader
.Pass(),
199 APIUtil::APIUtil(const base::FilePath
& temp_dir_path
,
200 scoped_ptr
<drive::DriveServiceInterface
> drive_service
,
201 scoped_ptr
<drive::DriveUploaderInterface
> drive_uploader
,
202 const std::string
& account_id
)
203 : upload_next_key_(0),
204 temp_dir_path_(temp_dir_path
) {
205 drive_service_
= drive_service
.Pass();
206 drive_service_
->Initialize(account_id
);
207 drive_service_
->AddObserver(this);
208 net::NetworkChangeNotifier::AddConnectionTypeObserver(this);
210 drive_uploader_
= drive_uploader
.Pass();
213 APIUtil::~APIUtil() {
214 DCHECK(CalledOnValidThread());
215 net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
216 drive_service_
->RemoveObserver(this);
219 void APIUtil::AddObserver(APIUtilObserver
* observer
) {
220 DCHECK(CalledOnValidThread());
221 observers_
.AddObserver(observer
);
224 void APIUtil::RemoveObserver(APIUtilObserver
* observer
) {
225 DCHECK(CalledOnValidThread());
226 observers_
.RemoveObserver(observer
);
229 void APIUtil::GetDriveRootResourceId(const GDataErrorCallback
& callback
) {
230 DCHECK(CalledOnValidThread());
231 DCHECK(!IsDriveAPIDisabled());
232 DVLOG(2) << "Getting resource id for Drive root";
234 drive_service_
->GetAboutResource(
235 base::Bind(&APIUtil::DidGetDriveRootResourceId
, AsWeakPtr(), callback
));
238 void APIUtil::DidGetDriveRootResourceId(
239 const GDataErrorCallback
& callback
,
240 google_apis::GDataErrorCode error
,
241 scoped_ptr
<google_apis::AboutResource
> about_resource
) {
242 DCHECK(CalledOnValidThread());
244 if (error
!= google_apis::HTTP_SUCCESS
) {
245 DVLOG(2) << "Error on getting resource id for Drive root: " << error
;
250 DCHECK(about_resource
);
251 root_resource_id_
= about_resource
->root_folder_id();
252 DCHECK(!root_resource_id_
.empty());
253 DVLOG(2) << "Got resource id for Drive root: " << root_resource_id_
;
257 void APIUtil::GetDriveDirectoryForSyncRoot(const ResourceIdCallback
& callback
) {
258 DCHECK(CalledOnValidThread());
260 if (GetRootResourceId().empty()) {
261 GetDriveRootResourceId(
262 base::Bind(&APIUtil::DidGetDriveRootResourceIdForGetSyncRoot
,
263 AsWeakPtr(), callback
));
267 DVLOG(2) << "Getting Drive directory for SyncRoot";
268 std::string
directory_name(GetSyncRootDirectoryName());
269 SearchByTitle(directory_name
,
271 base::Bind(&APIUtil::DidGetDirectory
,
278 void APIUtil::DidGetDriveRootResourceIdForGetSyncRoot(
279 const ResourceIdCallback
& callback
,
280 google_apis::GDataErrorCode error
) {
281 DCHECK(CalledOnValidThread());
282 if (error
!= google_apis::HTTP_SUCCESS
) {
283 DVLOG(2) << "Error on getting Drive directory for SyncRoot: " << error
;
284 callback
.Run(error
, std::string());
287 GetDriveDirectoryForSyncRoot(callback
);
290 void APIUtil::GetDriveDirectoryForOrigin(
291 const std::string
& sync_root_resource_id
,
293 const ResourceIdCallback
& callback
) {
294 DCHECK(CalledOnValidThread());
295 DVLOG(2) << "Getting Drive directory for Origin: " << origin
;
297 std::string
directory_name(OriginToDirectoryTitle(origin
));
298 SearchByTitle(directory_name
,
299 sync_root_resource_id
,
300 base::Bind(&APIUtil::DidGetDirectory
,
302 sync_root_resource_id
,
307 void APIUtil::DidGetDirectory(const std::string
& parent_resource_id
,
308 const std::string
& directory_name
,
309 const ResourceIdCallback
& callback
,
310 google_apis::GDataErrorCode error
,
311 scoped_ptr
<google_apis::ResourceList
> feed
) {
312 DCHECK(CalledOnValidThread());
313 DCHECK(IsStringASCII(directory_name
));
315 if (error
!= google_apis::HTTP_SUCCESS
) {
316 DVLOG(2) << "Error on getting Drive directory: " << error
;
317 callback
.Run(error
, std::string());
321 std::string resource_id
;
322 ParentType parent_type
= PARENT_TYPE_DIRECTORY
;
323 if (parent_resource_id
.empty()) {
324 resource_id
= GetRootResourceId();
325 DCHECK(!resource_id
.empty());
326 parent_type
= PARENT_TYPE_ROOT_OR_EMPTY
;
328 resource_id
= parent_resource_id
;
330 std::string
title(directory_name
);
331 google_apis::ResourceEntry
* entry
= GetDocumentByTitleAndParent(
332 feed
->entries(), title
, resource_id
, parent_type
);
334 DVLOG(2) << "Directory not found. Creating: " << directory_name
;
335 drive_service_
->AddNewDirectory(
338 drive::DriveServiceInterface::AddNewDirectoryOptions(),
339 base::Bind(&APIUtil::DidCreateDirectory
,
346 DVLOG(2) << "Found Drive directory.";
348 // TODO(tzik): Handle error.
349 DCHECK_EQ(google_apis::ENTRY_KIND_FOLDER
, entry
->kind());
350 DCHECK_EQ(directory_name
, entry
->title());
352 if (entry
->title() == GetSyncRootDirectoryName())
353 EnsureSyncRootIsNotInMyDrive(entry
->resource_id());
355 callback
.Run(error
, entry
->resource_id());
358 void APIUtil::DidCreateDirectory(const std::string
& parent_resource_id
,
359 const std::string
& title
,
360 const ResourceIdCallback
& callback
,
361 google_apis::GDataErrorCode error
,
362 scoped_ptr
<google_apis::ResourceEntry
> entry
) {
363 DCHECK(CalledOnValidThread());
365 if (error
!= google_apis::HTTP_SUCCESS
&&
366 error
!= google_apis::HTTP_CREATED
) {
367 DVLOG(2) << "Error on creating Drive directory: " << error
;
368 callback
.Run(error
, std::string());
371 DVLOG(2) << "Created Drive directory.";
374 // Check if any other client creates a directory with same title.
375 EnsureTitleUniqueness(
378 base::Bind(&APIUtil::DidEnsureUniquenessForCreateDirectory
,
383 void APIUtil::DidEnsureUniquenessForCreateDirectory(
384 const ResourceIdCallback
& callback
,
385 google_apis::GDataErrorCode error
,
386 EnsureUniquenessStatus status
,
387 scoped_ptr
<google_apis::ResourceEntry
> entry
) {
388 DCHECK(CalledOnValidThread());
390 if (error
!= google_apis::HTTP_SUCCESS
) {
391 callback
.Run(error
, std::string());
395 if (status
== NO_DUPLICATES_FOUND
)
396 error
= google_apis::HTTP_CREATED
;
398 DCHECK(entry
) << "No entry: " << error
;
400 if (!entry
->is_folder()) {
401 // TODO(kinuko): Fix this. http://crbug.com/237090
405 "A file is left for CreateDirectory due to file-folder conflict!");
406 callback
.Run(google_apis::HTTP_CONFLICT
, std::string());
410 if (entry
->title() == GetSyncRootDirectoryName())
411 EnsureSyncRootIsNotInMyDrive(entry
->resource_id());
413 callback
.Run(error
, entry
->resource_id());
416 void APIUtil::GetLargestChangeStamp(const ChangeStampCallback
& callback
) {
417 DCHECK(CalledOnValidThread());
418 DVLOG(2) << "Getting largest change id";
420 drive_service_
->GetAboutResource(
421 base::Bind(&APIUtil::DidGetLargestChangeStamp
, AsWeakPtr(), callback
));
424 void APIUtil::GetResourceEntry(const std::string
& resource_id
,
425 const ResourceEntryCallback
& callback
) {
426 DCHECK(CalledOnValidThread());
427 DVLOG(2) << "Getting ResourceEntry for: " << resource_id
;
429 drive_service_
->GetResourceEntry(
431 base::Bind(&APIUtil::DidGetResourceEntry
, AsWeakPtr(), callback
));
434 void APIUtil::DidGetLargestChangeStamp(
435 const ChangeStampCallback
& callback
,
436 google_apis::GDataErrorCode error
,
437 scoped_ptr
<google_apis::AboutResource
> about_resource
) {
438 DCHECK(CalledOnValidThread());
440 int64 largest_change_id
= 0;
441 if (error
== google_apis::HTTP_SUCCESS
) {
442 DCHECK(about_resource
);
443 largest_change_id
= about_resource
->largest_change_id();
444 root_resource_id_
= about_resource
->root_folder_id();
445 DVLOG(2) << "Got largest change id: " << largest_change_id
;
447 DVLOG(2) << "Error on getting largest change id: " << error
;
450 callback
.Run(error
, largest_change_id
);
453 void APIUtil::SearchByTitle(const std::string
& title
,
454 const std::string
& directory_resource_id
,
455 const ResourceListCallback
& callback
) {
456 DCHECK(CalledOnValidThread());
457 DCHECK(!title
.empty());
458 DVLOG(2) << "Searching resources in the directory [" << directory_resource_id
459 << "] with title [" << title
<< "]";
461 drive_service_
->SearchByTitle(
463 directory_resource_id
,
464 base::Bind(&APIUtil::DidGetResourceList
, AsWeakPtr(), callback
));
467 void APIUtil::ListFiles(const std::string
& directory_resource_id
,
468 const ResourceListCallback
& callback
) {
469 DCHECK(CalledOnValidThread());
470 DVLOG(2) << "Listing resources in the directory [" << directory_resource_id
473 drive_service_
->GetResourceListInDirectory(directory_resource_id
, callback
);
476 void APIUtil::ListChanges(int64 start_changestamp
,
477 const ResourceListCallback
& callback
) {
478 DCHECK(CalledOnValidThread());
479 DVLOG(2) << "Listing changes since: " << start_changestamp
;
481 drive_service_
->GetChangeList(
483 base::Bind(&APIUtil::DidGetResourceList
, AsWeakPtr(), callback
));
486 void APIUtil::ContinueListing(const GURL
& next_link
,
487 const ResourceListCallback
& callback
) {
488 DCHECK(CalledOnValidThread());
489 DVLOG(2) << "Continue listing on feed: " << next_link
.spec();
491 drive_service_
->GetRemainingFileList(
493 base::Bind(&APIUtil::DidGetResourceList
, AsWeakPtr(), callback
));
496 void APIUtil::DownloadFile(const std::string
& resource_id
,
497 const std::string
& local_file_md5
,
498 const DownloadFileCallback
& callback
) {
499 DCHECK(CalledOnValidThread());
500 DCHECK(!temp_dir_path_
.empty());
501 DVLOG(2) << "Downloading file [" << resource_id
<< "]";
503 scoped_ptr
<webkit_blob::ScopedFile
> temp_file(new webkit_blob::ScopedFile
);
504 webkit_blob::ScopedFile
* temp_file_ptr
= temp_file
.get();
505 content::BrowserThread::PostTaskAndReplyWithResult(
506 content::BrowserThread::FILE, FROM_HERE
,
507 base::Bind(&CreateTemporaryFile
, temp_dir_path_
, temp_file_ptr
),
508 base::Bind(&APIUtil::DidGetTemporaryFileForDownload
,
509 AsWeakPtr(), resource_id
, local_file_md5
,
510 base::Passed(&temp_file
), callback
));
513 void APIUtil::UploadNewFile(const std::string
& directory_resource_id
,
514 const base::FilePath
& local_file_path
,
515 const std::string
& title
,
516 const UploadFileCallback
& callback
) {
517 DCHECK(CalledOnValidThread());
518 DVLOG(2) << "Uploading new file into the directory [" << directory_resource_id
519 << "] with title [" << title
<< "]";
521 std::string mime_type
= GetMimeTypeFromTitle(title
);
522 UploadKey upload_key
= RegisterUploadCallback(callback
);
523 ResourceEntryCallback did_upload_callback
=
524 base::Bind(&APIUtil::DidUploadNewFile
,
526 directory_resource_id
,
529 drive_uploader_
->UploadNewFile(
530 directory_resource_id
,
534 drive::DriveUploader::UploadNewFileOptions(),
535 base::Bind(&UploadResultAdapter
, did_upload_callback
),
536 google_apis::ProgressCallback());
539 void APIUtil::UploadExistingFile(const std::string
& resource_id
,
540 const std::string
& remote_file_md5
,
541 const base::FilePath
& local_file_path
,
542 const UploadFileCallback
& callback
) {
543 DCHECK(CalledOnValidThread());
544 DVLOG(2) << "Uploading existing file [" << resource_id
<< "]";
545 drive_service_
->GetResourceEntry(
547 base::Bind(&APIUtil::DidGetResourceEntry
,
549 base::Bind(&APIUtil::UploadExistingFileInternal
,
556 void APIUtil::CreateDirectory(const std::string
& parent_resource_id
,
557 const std::string
& title
,
558 const ResourceIdCallback
& callback
) {
559 DCHECK(CalledOnValidThread());
560 // TODO(kinuko): This will call EnsureTitleUniqueness and will delete
561 // directories if there're duplicated directories. This must be ok
562 // for current design but we'll need to merge directories when we support
563 // 'real' directories.
564 drive_service_
->AddNewDirectory(
567 drive::DriveServiceInterface::AddNewDirectoryOptions(),
568 base::Bind(&APIUtil::DidCreateDirectory
,
575 void APIUtil::DeleteFile(const std::string
& resource_id
,
576 const std::string
& remote_file_md5
,
577 const GDataErrorCallback
& callback
) {
578 DCHECK(CalledOnValidThread());
579 DVLOG(2) << "Deleting file: " << resource_id
;
581 // Load actual remote_file_md5 to check for conflict before deletion.
582 if (!remote_file_md5
.empty()) {
583 drive_service_
->GetResourceEntry(
585 base::Bind(&APIUtil::DidGetResourceEntry
,
587 base::Bind(&APIUtil::DeleteFileInternal
,
594 // Expected remote_file_md5 is empty so do a force delete.
595 drive_service_
->TrashResource(
597 base::Bind(&APIUtil::DidDeleteFile
, AsWeakPtr(), callback
));
601 void APIUtil::EnsureSyncRootIsNotInMyDrive(
602 const std::string
& sync_root_resource_id
) {
603 DCHECK(CalledOnValidThread());
605 if (GetRootResourceId().empty()) {
606 GetDriveRootResourceId(
607 base::Bind(&APIUtil::DidGetDriveRootResourceIdForEnsureSyncRoot
,
608 AsWeakPtr(), sync_root_resource_id
));
612 DVLOG(2) << "Ensuring the sync root directory is not in 'My Drive'.";
613 drive_service_
->RemoveResourceFromDirectory(
615 sync_root_resource_id
,
616 base::Bind(&EmptyGDataErrorCodeCallback
));
619 void APIUtil::DidGetDriveRootResourceIdForEnsureSyncRoot(
620 const std::string
& sync_root_resource_id
,
621 google_apis::GDataErrorCode error
) {
622 DCHECK(CalledOnValidThread());
624 if (error
!= google_apis::HTTP_SUCCESS
) {
625 DVLOG(2) << "Error on ensuring the sync root directory is not in"
626 << " 'My Drive': " << error
;
627 // Give up ensuring the sync root directory is not in 'My Drive'. This will
628 // be retried at some point.
632 DCHECK(!GetRootResourceId().empty());
633 EnsureSyncRootIsNotInMyDrive(sync_root_resource_id
);
637 // TODO(calvinlo): Delete this when Sync Directory Operations are supported by
639 std::string
APIUtil::GetSyncRootDirectoryName() {
640 return IsSyncFSDirectoryOperationEnabled() ? kSyncRootFolderTitleDev
641 : kSyncRootFolderTitle
;
645 std::string
APIUtil::OriginToDirectoryTitle(const GURL
& origin
) {
646 DCHECK(origin
.SchemeIs(extensions::kExtensionScheme
));
647 return origin
.host();
651 GURL
APIUtil::DirectoryTitleToOrigin(const std::string
& title
) {
652 return extensions::Extension::GetBaseURLFromExtensionId(title
);
655 void APIUtil::OnReadyToSendRequests() {
656 DCHECK(CalledOnValidThread());
657 if (!has_initialized_token_
) {
658 drive_service_
->Initialize(signin_manager_
->GetAuthenticatedAccountId());
659 has_initialized_token_
= true;
661 FOR_EACH_OBSERVER(APIUtilObserver
, observers_
, OnAuthenticated());
664 void APIUtil::OnConnectionTypeChanged(
665 net::NetworkChangeNotifier::ConnectionType type
) {
666 DCHECK(CalledOnValidThread());
667 if (type
!= net::NetworkChangeNotifier::CONNECTION_NONE
) {
668 FOR_EACH_OBSERVER(APIUtilObserver
, observers_
, OnNetworkConnected());
671 // We're now disconnected, reset the drive_uploader_ to force stop
672 // uploading, otherwise the uploader may get stuck.
673 // TODO(kinuko): Check the uploader behavior if it's the expected behavior
674 // (http://crbug.com/223818)
675 CancelAllUploads(google_apis::GDATA_NO_CONNECTION
);
678 void APIUtil::DidGetResourceList(
679 const ResourceListCallback
& callback
,
680 google_apis::GDataErrorCode error
,
681 scoped_ptr
<google_apis::ResourceList
> resource_list
) {
682 DCHECK(CalledOnValidThread());
684 if (error
!= google_apis::HTTP_SUCCESS
) {
685 DVLOG(2) << "Error on listing resource: " << error
;
686 callback
.Run(error
, scoped_ptr
<google_apis::ResourceList
>());
690 DVLOG(2) << "Got resource list";
691 DCHECK(resource_list
);
692 callback
.Run(error
, resource_list
.Pass());
695 void APIUtil::DidGetResourceEntry(
696 const ResourceEntryCallback
& callback
,
697 google_apis::GDataErrorCode error
,
698 scoped_ptr
<google_apis::ResourceEntry
> entry
) {
699 DCHECK(CalledOnValidThread());
701 if (error
!= google_apis::HTTP_SUCCESS
) {
702 DVLOG(2) << "Error on getting resource entry:" << error
;
703 callback
.Run(error
, scoped_ptr
<google_apis::ResourceEntry
>());
707 if (entry
->deleted()) {
708 DVLOG(2) << "Got resource entry, the entry was trashed.";
709 callback
.Run(google_apis::HTTP_NOT_FOUND
, entry
.Pass());
713 DVLOG(2) << "Got resource entry";
715 callback
.Run(error
, entry
.Pass());
718 void APIUtil::DidGetTemporaryFileForDownload(
719 const std::string
& resource_id
,
720 const std::string
& local_file_md5
,
721 scoped_ptr
<webkit_blob::ScopedFile
> local_file
,
722 const DownloadFileCallback
& callback
,
725 DVLOG(2) << "Error in creating a temp file under "
726 << temp_dir_path_
.value();
727 callback
.Run(google_apis::GDATA_FILE_ERROR
, std::string(), 0, base::Time(),
731 drive_service_
->GetResourceEntry(
733 base::Bind(&APIUtil::DidGetResourceEntry
,
735 base::Bind(&APIUtil::DownloadFileInternal
,
738 base::Passed(&local_file
),
742 void APIUtil::DownloadFileInternal(
743 const std::string
& local_file_md5
,
744 scoped_ptr
<webkit_blob::ScopedFile
> local_file
,
745 const DownloadFileCallback
& callback
,
746 google_apis::GDataErrorCode error
,
747 scoped_ptr
<google_apis::ResourceEntry
> entry
) {
748 DCHECK(CalledOnValidThread());
750 if (error
!= google_apis::HTTP_SUCCESS
) {
751 DVLOG(2) << "Error on getting resource entry for download";
752 callback
.Run(error
, std::string(), 0, base::Time(), local_file
->Pass());
757 DVLOG(2) << "Got resource entry for download";
758 // If local file and remote file are same, cancel the download.
759 if (local_file_md5
== entry
->file_md5()) {
760 callback
.Run(google_apis::HTTP_NOT_MODIFIED
,
763 entry
->updated_time(),
768 DVLOG(2) << "Downloading file: " << entry
->resource_id();
769 const std::string
& resource_id
= entry
->resource_id();
770 const base::FilePath
& local_file_path
= local_file
->path();
771 drive_service_
->DownloadFile(local_file_path
,
773 base::Bind(&APIUtil::DidDownloadFile
,
775 base::Passed(&entry
),
776 base::Passed(&local_file
),
778 google_apis::GetContentCallback(),
779 google_apis::ProgressCallback());
782 void APIUtil::DidDownloadFile(scoped_ptr
<google_apis::ResourceEntry
> entry
,
783 scoped_ptr
<webkit_blob::ScopedFile
> local_file
,
784 const DownloadFileCallback
& callback
,
785 google_apis::GDataErrorCode error
,
786 const base::FilePath
& downloaded_file_path
) {
787 DCHECK(CalledOnValidThread());
788 if (error
== google_apis::HTTP_SUCCESS
)
789 DVLOG(2) << "Download completed";
791 DVLOG(2) << "Error on downloading file: " << error
;
794 error
, entry
->file_md5(), entry
->file_size(), entry
->updated_time(),
798 void APIUtil::DidUploadNewFile(const std::string
& parent_resource_id
,
799 const std::string
& title
,
800 UploadKey upload_key
,
801 google_apis::GDataErrorCode error
,
802 scoped_ptr
<google_apis::ResourceEntry
> entry
) {
803 UploadFileCallback callback
= GetAndUnregisterUploadCallback(upload_key
);
804 DCHECK(!callback
.is_null());
805 if (error
!= google_apis::HTTP_SUCCESS
&&
806 error
!= google_apis::HTTP_CREATED
) {
807 DVLOG(2) << "Error on uploading new file: " << error
;
808 callback
.Run(error
, std::string(), std::string());
812 DVLOG(2) << "Upload completed";
813 EnsureTitleUniqueness(parent_resource_id
,
815 base::Bind(&APIUtil::DidEnsureUniquenessForCreateFile
,
817 entry
->resource_id(),
821 void APIUtil::DidEnsureUniquenessForCreateFile(
822 const std::string
& expected_resource_id
,
823 const UploadFileCallback
& callback
,
824 google_apis::GDataErrorCode error
,
825 EnsureUniquenessStatus status
,
826 scoped_ptr
<google_apis::ResourceEntry
> entry
) {
827 if (error
!= google_apis::HTTP_SUCCESS
) {
828 DVLOG(2) << "Error on uploading new file: " << error
;
829 callback
.Run(error
, std::string(), std::string());
834 case NO_DUPLICATES_FOUND
:
835 // The file was uploaded successfully and no conflict was detected.
837 DVLOG(2) << "No conflict detected on uploading new file";
839 google_apis::HTTP_CREATED
, entry
->resource_id(), entry
->file_md5());
842 case RESOLVED_DUPLICATES
:
843 // The file was uploaded successfully but a conflict was detected.
844 // The duplicated file was deleted successfully.
846 if (entry
->resource_id() != expected_resource_id
) {
847 // TODO(kinuko): We should check local vs remote md5 here.
848 DVLOG(2) << "Conflict detected on uploading new file";
849 callback
.Run(google_apis::HTTP_CONFLICT
,
850 entry
->resource_id(),
855 DVLOG(2) << "Conflict detected on uploading new file and resolved";
857 google_apis::HTTP_CREATED
, entry
->resource_id(), entry
->file_md5());
861 NOTREACHED() << "Unknown status from EnsureTitleUniqueness:" << status
862 << " for " << expected_resource_id
;
866 void APIUtil::UploadExistingFileInternal(
867 const std::string
& remote_file_md5
,
868 const base::FilePath
& local_file_path
,
869 const UploadFileCallback
& callback
,
870 google_apis::GDataErrorCode error
,
871 scoped_ptr
<google_apis::ResourceEntry
> entry
) {
872 DCHECK(CalledOnValidThread());
874 if (error
!= google_apis::HTTP_SUCCESS
) {
875 DVLOG(2) << "Error on uploading existing file: " << error
;
876 callback
.Run(error
, std::string(), std::string());
881 // If remote file's hash value is different from the expected one, conflict
882 // might have occurred.
883 if (!remote_file_md5
.empty() && remote_file_md5
!= entry
->file_md5()) {
884 DVLOG(2) << "Conflict detected before uploading existing file";
885 callback
.Run(google_apis::HTTP_CONFLICT
, std::string(), std::string());
889 drive::DriveUploader::UploadExistingFileOptions options
;
890 options
.etag
= entry
->etag();
891 std::string mime_type
= GetMimeTypeFromTitle(entry
->title());
892 UploadKey upload_key
= RegisterUploadCallback(callback
);
893 ResourceEntryCallback did_upload_callback
=
894 base::Bind(&APIUtil::DidUploadExistingFile
, AsWeakPtr(), upload_key
);
895 drive_uploader_
->UploadExistingFile(
896 entry
->resource_id(),
900 base::Bind(&UploadResultAdapter
, did_upload_callback
),
901 google_apis::ProgressCallback());
904 bool APIUtil::IsAuthenticated() const {
905 return drive_service_
->HasRefreshToken();
908 void APIUtil::DidUploadExistingFile(
909 UploadKey upload_key
,
910 google_apis::GDataErrorCode error
,
911 scoped_ptr
<google_apis::ResourceEntry
> entry
) {
912 DCHECK(CalledOnValidThread());
913 UploadFileCallback callback
= GetAndUnregisterUploadCallback(upload_key
);
914 DCHECK(!callback
.is_null());
915 if (error
!= google_apis::HTTP_SUCCESS
) {
916 DVLOG(2) << "Error on uploading existing file: " << error
;
917 callback
.Run(error
, std::string(), std::string());
922 DVLOG(2) << "Upload completed";
923 callback
.Run(error
, entry
->resource_id(), entry
->file_md5());
926 void APIUtil::DeleteFileInternal(const std::string
& remote_file_md5
,
927 const GDataErrorCallback
& callback
,
928 google_apis::GDataErrorCode error
,
929 scoped_ptr
<google_apis::ResourceEntry
> entry
) {
930 DCHECK(CalledOnValidThread());
932 if (error
!= google_apis::HTTP_SUCCESS
) {
933 DVLOG(2) << "Error on getting resource entry for deleting file: " << error
;
939 // If remote file's hash value is different from the expected one, conflict
940 // might have occurred.
941 if (!remote_file_md5
.empty() && remote_file_md5
!= entry
->file_md5()) {
942 DVLOG(2) << "Conflict detected before deleting file";
943 callback
.Run(google_apis::HTTP_CONFLICT
);
946 DVLOG(2) << "Got resource entry for deleting file";
948 // Move the file to trash (don't delete it completely).
949 drive_service_
->TrashResource(
950 entry
->resource_id(),
951 base::Bind(&APIUtil::DidDeleteFile
, AsWeakPtr(), callback
));
954 void APIUtil::DidDeleteFile(const GDataErrorCallback
& callback
,
955 google_apis::GDataErrorCode error
) {
956 DCHECK(CalledOnValidThread());
957 if (error
== google_apis::HTTP_SUCCESS
)
958 DVLOG(2) << "Deletion completed";
960 DVLOG(2) << "Error on deleting file: " << error
;
965 void APIUtil::EnsureTitleUniqueness(const std::string
& parent_resource_id
,
966 const std::string
& expected_title
,
967 const EnsureUniquenessCallback
& callback
) {
968 DCHECK(CalledOnValidThread());
969 DVLOG(2) << "Checking if there's no conflict on entry creation";
971 const google_apis::GetResourceListCallback
& bound_callback
=
972 base::Bind(&APIUtil::DidListEntriesToEnsureUniqueness
,
978 SearchByTitle(expected_title
, parent_resource_id
, bound_callback
);
981 void APIUtil::DidListEntriesToEnsureUniqueness(
982 const std::string
& parent_resource_id
,
983 const std::string
& expected_title
,
984 const EnsureUniquenessCallback
& callback
,
985 google_apis::GDataErrorCode error
,
986 scoped_ptr
<google_apis::ResourceList
> feed
) {
987 DCHECK(CalledOnValidThread());
989 if (error
!= google_apis::HTTP_SUCCESS
) {
990 DVLOG(2) << "Error on listing resource for ensuring title uniqueness";
992 error
, NO_DUPLICATES_FOUND
, scoped_ptr
<google_apis::ResourceEntry
>());
995 DVLOG(2) << "Got resource list for ensuring title uniqueness";
997 // This filtering is needed only on WAPI. Once we move to Drive API we can
999 std::string resource_id
;
1000 ParentType parent_type
= PARENT_TYPE_DIRECTORY
;
1001 if (parent_resource_id
.empty()) {
1002 resource_id
= GetRootResourceId();
1003 DCHECK(!resource_id
.empty());
1004 parent_type
= PARENT_TYPE_ROOT_OR_EMPTY
;
1006 resource_id
= parent_resource_id
;
1008 ScopedVector
<google_apis::ResourceEntry
> entries
;
1009 entries
.swap(*feed
->mutable_entries());
1010 FilterEntriesByTitleAndParent(
1011 &entries
, expected_title
, resource_id
, parent_type
);
1013 if (entries
.empty()) {
1014 DVLOG(2) << "Uploaded file is not found";
1015 callback
.Run(google_apis::HTTP_NOT_FOUND
,
1016 NO_DUPLICATES_FOUND
,
1017 scoped_ptr
<google_apis::ResourceEntry
>());
1021 if (entries
.size() >= 2) {
1022 DVLOG(2) << "Conflict detected on creating entry";
1023 for (size_t i
= 0; i
< entries
.size() - 1; ++i
) {
1024 // TODO(tzik): Replace published_time with creation time after we move to
1026 if (entries
[i
]->published_time() < entries
.back()->published_time())
1027 std::swap(entries
[i
], entries
.back());
1030 scoped_ptr
<google_apis::ResourceEntry
> earliest_entry(entries
.back());
1031 entries
.back() = NULL
;
1032 entries
.get().pop_back();
1034 DeleteEntriesForEnsuringTitleUniqueness(
1036 base::Bind(&EntryAdapterForEnsureTitleUniqueness
,
1037 base::Passed(&earliest_entry
),
1039 RESOLVED_DUPLICATES
));
1043 DVLOG(2) << "no conflict detected";
1044 DCHECK_EQ(1u, entries
.size());
1045 scoped_ptr
<google_apis::ResourceEntry
> entry(entries
.front());
1046 entries
.weak_clear();
1048 callback
.Run(google_apis::HTTP_SUCCESS
, NO_DUPLICATES_FOUND
, entry
.Pass());
1051 void APIUtil::DeleteEntriesForEnsuringTitleUniqueness(
1052 ScopedVector
<google_apis::ResourceEntry
> entries
,
1053 const GDataErrorCallback
& callback
) {
1054 DCHECK(CalledOnValidThread());
1055 DVLOG(2) << "Cleaning up conflict on entry creation";
1057 if (entries
.empty()) {
1058 callback
.Run(google_apis::HTTP_SUCCESS
);
1062 scoped_ptr
<google_apis::ResourceEntry
> entry(entries
.back());
1063 entries
.back() = NULL
;
1064 entries
.get().pop_back();
1066 // We don't care conflicts here as other clients may be also deleting this
1067 // file, so passing an empty etag.
1068 drive_service_
->TrashResource(
1069 entry
->resource_id(),
1070 base::Bind(&APIUtil::DidDeleteEntriesForEnsuringTitleUniqueness
,
1072 base::Passed(&entries
),
1076 void APIUtil::DidDeleteEntriesForEnsuringTitleUniqueness(
1077 ScopedVector
<google_apis::ResourceEntry
> entries
,
1078 const GDataErrorCallback
& callback
,
1079 google_apis::GDataErrorCode error
) {
1080 DCHECK(CalledOnValidThread());
1082 if (error
!= google_apis::HTTP_SUCCESS
&&
1083 error
!= google_apis::HTTP_NOT_FOUND
) {
1084 DVLOG(2) << "Error on deleting file: " << error
;
1085 callback
.Run(error
);
1089 DVLOG(2) << "Deletion completed";
1090 DeleteEntriesForEnsuringTitleUniqueness(entries
.Pass(), callback
);
1093 APIUtil::UploadKey
APIUtil::RegisterUploadCallback(
1094 const UploadFileCallback
& callback
) {
1095 const bool inserted
= upload_callback_map_
.insert(
1096 std::make_pair(upload_next_key_
, callback
)).second
;
1098 return upload_next_key_
++;
1101 APIUtil::UploadFileCallback
APIUtil::GetAndUnregisterUploadCallback(
1103 UploadFileCallback callback
;
1104 UploadCallbackMap::iterator found
= upload_callback_map_
.find(key
);
1105 if (found
== upload_callback_map_
.end())
1107 callback
= found
->second
;
1108 upload_callback_map_
.erase(found
);
1112 void APIUtil::CancelAllUploads(google_apis::GDataErrorCode error
) {
1113 if (upload_callback_map_
.empty())
1115 for (UploadCallbackMap::iterator iter
= upload_callback_map_
.begin();
1116 iter
!= upload_callback_map_
.end();
1118 iter
->second
.Run(error
, std::string(), std::string());
1120 upload_callback_map_
.clear();
1121 drive_uploader_
.reset(new drive::DriveUploader(
1122 drive_service_
.get(), content::BrowserThread::GetBlockingPool()));
1125 std::string
APIUtil::GetRootResourceId() const {
1126 if (IsDriveAPIDisabled())
1127 return drive_service_
->GetRootResourceId();
1128 return root_resource_id_
;
1131 } // namespace drive_backend
1132 } // namespace sync_file_system