Cast: Stop logging kVideoFrameSentToEncoder and rename a couple events.
[chromium-blink-merge.git] / chrome / browser / sync_file_system / drive_backend_v1 / api_util.cc
blobd3d5835e2a59b37e2cb208b94ef9ac69998b5577
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"
7 #include <algorithm>
8 #include <functional>
9 #include <sstream>
10 #include <string>
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 {
40 namespace {
42 enum ParentType {
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) {
59 has_parent = true;
60 if (drive::util::ExtractResourceIdFromUrl((*itr)->href()) ==
61 parent_resource_id)
62 return true;
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)
74 : title(title),
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(),
95 entries->end(),
96 TitleAndParentQuery(title,
97 parent_resource_id,
98 parent_type));
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;
108 iterator found =
109 std::find_if(entries.begin(),
110 entries.end(),
111 TitleAndParentQuery(title, parent_resource_id, parent_type));
112 if (found != entries.end())
113 return *found;
114 return NULL;
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;
139 return mime_type;
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);
147 if (!success)
148 return success;
149 *temp_file =
150 webkit_blob::ScopedFile(temp_file_path,
151 webkit_blob::ScopedFile::DELETE_ON_SCOPE_OUT,
152 base::MessageLoopProxy::current().get());
153 return success;
156 } // namespace
158 APIUtil::APIUtil(Profile* profile,
159 const base::FilePath& temp_dir_path)
160 : oauth_service_(ProfileOAuth2TokenServiceFactory::GetForProfile(profile)),
161 signin_manager_(SigninManagerFactory::GetForProfile(profile)),
162 upload_next_key_(0),
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(
171 oauth_service_,
172 profile->GetRequestContext(),
173 task_runner.get(),
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(
193 temp_dir_path,
194 drive_service.Pass(),
195 drive_uploader.Pass(),
196 kFakeAccountId));
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;
246 callback.Run(error);
247 return;
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_;
254 callback.Run(error);
257 void APIUtil::GetDriveDirectoryForSyncRoot(const ResourceIdCallback& callback) {
258 DCHECK(CalledOnValidThread());
260 if (GetRootResourceId().empty()) {
261 GetDriveRootResourceId(
262 base::Bind(&APIUtil::DidGetDriveRootResourceIdForGetSyncRoot,
263 AsWeakPtr(), callback));
264 return;
267 DVLOG(2) << "Getting Drive directory for SyncRoot";
268 std::string directory_name(GetSyncRootDirectoryName());
269 SearchByTitle(directory_name,
270 std::string(),
271 base::Bind(&APIUtil::DidGetDirectory,
272 AsWeakPtr(),
273 std::string(),
274 directory_name,
275 callback));
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());
285 return;
287 GetDriveDirectoryForSyncRoot(callback);
290 void APIUtil::GetDriveDirectoryForOrigin(
291 const std::string& sync_root_resource_id,
292 const GURL& origin,
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,
301 AsWeakPtr(),
302 sync_root_resource_id,
303 directory_name,
304 callback));
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());
318 return;
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;
327 } else {
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);
333 if (!entry) {
334 DVLOG(2) << "Directory not found. Creating: " << directory_name;
335 drive_service_->AddNewDirectory(
336 resource_id,
337 directory_name,
338 drive::DriveServiceInterface::AddNewDirectoryOptions(),
339 base::Bind(&APIUtil::DidCreateDirectory,
340 AsWeakPtr(),
341 parent_resource_id,
342 title,
343 callback));
344 return;
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());
369 return;
371 DVLOG(2) << "Created Drive directory.";
373 DCHECK(entry);
374 // Check if any other client creates a directory with same title.
375 EnsureTitleUniqueness(
376 parent_resource_id,
377 title,
378 base::Bind(&APIUtil::DidEnsureUniquenessForCreateDirectory,
379 AsWeakPtr(),
380 callback));
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());
392 return;
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
402 util::Log(
403 logging::LOG_ERROR,
404 FROM_HERE,
405 "A file is left for CreateDirectory due to file-folder conflict!");
406 callback.Run(google_apis::HTTP_CONFLICT, std::string());
407 return;
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(
430 resource_id,
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;
446 } else {
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(
462 title,
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
471 << "]";
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(
482 start_changestamp,
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(
492 next_link,
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,
525 AsWeakPtr(),
526 directory_resource_id,
527 title,
528 upload_key);
529 drive_uploader_->UploadNewFile(
530 directory_resource_id,
531 local_file_path,
532 title,
533 mime_type,
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(
546 resource_id,
547 base::Bind(&APIUtil::DidGetResourceEntry,
548 AsWeakPtr(),
549 base::Bind(&APIUtil::UploadExistingFileInternal,
550 AsWeakPtr(),
551 remote_file_md5,
552 local_file_path,
553 callback)));
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(
565 parent_resource_id,
566 title,
567 drive::DriveServiceInterface::AddNewDirectoryOptions(),
568 base::Bind(&APIUtil::DidCreateDirectory,
569 AsWeakPtr(),
570 parent_resource_id,
571 title,
572 callback));
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(
584 resource_id,
585 base::Bind(&APIUtil::DidGetResourceEntry,
586 AsWeakPtr(),
587 base::Bind(&APIUtil::DeleteFileInternal,
588 AsWeakPtr(),
589 remote_file_md5,
590 callback)));
591 return;
594 // Expected remote_file_md5 is empty so do a force delete.
595 drive_service_->TrashResource(
596 resource_id,
597 base::Bind(&APIUtil::DidDeleteFile, AsWeakPtr(), callback));
598 return;
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));
609 return;
612 DVLOG(2) << "Ensuring the sync root directory is not in 'My Drive'.";
613 drive_service_->RemoveResourceFromDirectory(
614 GetRootResourceId(),
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.
629 return;
632 DCHECK(!GetRootResourceId().empty());
633 EnsureSyncRootIsNotInMyDrive(sync_root_resource_id);
636 // static
637 // TODO(calvinlo): Delete this when Sync Directory Operations are supported by
638 // default.
639 std::string APIUtil::GetSyncRootDirectoryName() {
640 return IsSyncFSDirectoryOperationEnabled() ? kSyncRootFolderTitleDev
641 : kSyncRootFolderTitle;
644 // static
645 std::string APIUtil::OriginToDirectoryTitle(const GURL& origin) {
646 DCHECK(origin.SchemeIs(extensions::kExtensionScheme));
647 return origin.host();
650 // static
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());
669 return;
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>());
687 return;
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>());
704 return;
707 if (entry->deleted()) {
708 DVLOG(2) << "Got resource entry, the entry was trashed.";
709 callback.Run(google_apis::HTTP_NOT_FOUND, entry.Pass());
710 return;
713 DVLOG(2) << "Got resource entry";
714 DCHECK(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,
723 bool success) {
724 if (!success) {
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(),
728 local_file->Pass());
729 return;
731 drive_service_->GetResourceEntry(
732 resource_id,
733 base::Bind(&APIUtil::DidGetResourceEntry,
734 AsWeakPtr(),
735 base::Bind(&APIUtil::DownloadFileInternal,
736 AsWeakPtr(),
737 local_file_md5,
738 base::Passed(&local_file),
739 callback)));
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());
753 return;
755 DCHECK(entry);
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,
761 local_file_md5,
762 entry->file_size(),
763 entry->updated_time(),
764 local_file->Pass());
765 return;
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,
772 resource_id,
773 base::Bind(&APIUtil::DidDownloadFile,
774 AsWeakPtr(),
775 base::Passed(&entry),
776 base::Passed(&local_file),
777 callback),
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";
790 else
791 DVLOG(2) << "Error on downloading file: " << error;
793 callback.Run(
794 error, entry->file_md5(), entry->file_size(), entry->updated_time(),
795 local_file->Pass());
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());
809 return;
812 DVLOG(2) << "Upload completed";
813 EnsureTitleUniqueness(parent_resource_id,
814 title,
815 base::Bind(&APIUtil::DidEnsureUniquenessForCreateFile,
816 AsWeakPtr(),
817 entry->resource_id(),
818 callback));
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());
830 return;
833 switch (status) {
834 case NO_DUPLICATES_FOUND:
835 // The file was uploaded successfully and no conflict was detected.
836 DCHECK(entry);
837 DVLOG(2) << "No conflict detected on uploading new file";
838 callback.Run(
839 google_apis::HTTP_CREATED, entry->resource_id(), entry->file_md5());
840 return;
842 case RESOLVED_DUPLICATES:
843 // The file was uploaded successfully but a conflict was detected.
844 // The duplicated file was deleted successfully.
845 DCHECK(entry);
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(),
851 entry->file_md5());
852 return;
855 DVLOG(2) << "Conflict detected on uploading new file and resolved";
856 callback.Run(
857 google_apis::HTTP_CREATED, entry->resource_id(), entry->file_md5());
858 return;
860 default:
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());
877 return;
879 DCHECK(entry);
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());
886 return;
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(),
897 local_file_path,
898 mime_type,
899 options,
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());
918 return;
921 DCHECK(entry);
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;
934 callback.Run(error);
935 return;
937 DCHECK(entry);
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);
944 return;
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";
959 else
960 DVLOG(2) << "Error on deleting file: " << error;
962 callback.Run(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,
973 AsWeakPtr(),
974 parent_resource_id,
975 expected_title,
976 callback);
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";
991 callback.Run(
992 error, NO_DUPLICATES_FOUND, scoped_ptr<google_apis::ResourceEntry>());
993 return;
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
998 // drop this.
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;
1005 } else {
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>());
1018 return;
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
1025 // Drive API.
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(
1035 entries.Pass(),
1036 base::Bind(&EntryAdapterForEnsureTitleUniqueness,
1037 base::Passed(&earliest_entry),
1038 callback,
1039 RESOLVED_DUPLICATES));
1040 return;
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);
1059 return;
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,
1071 AsWeakPtr(),
1072 base::Passed(&entries),
1073 callback));
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);
1086 return;
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;
1097 CHECK(inserted);
1098 return upload_next_key_++;
1101 APIUtil::UploadFileCallback APIUtil::GetAndUnregisterUploadCallback(
1102 UploadKey key) {
1103 UploadFileCallback callback;
1104 UploadCallbackMap::iterator found = upload_callback_map_.find(key);
1105 if (found == upload_callback_map_.end())
1106 return callback;
1107 callback = found->second;
1108 upload_callback_map_.erase(found);
1109 return callback;
1112 void APIUtil::CancelAllUploads(google_apis::GDataErrorCode error) {
1113 if (upload_callback_map_.empty())
1114 return;
1115 for (UploadCallbackMap::iterator iter = upload_callback_map_.begin();
1116 iter != upload_callback_map_.end();
1117 ++iter) {
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