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.
4
5 #include "chrome/browser/sync_file_system/drive_backend_v1/api_util.h"
6
7 #include <algorithm>
8 #include <functional>
9 #include <sstream>
10 #include <string>
11
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"
36
37 namespace sync_file_system {
38 namespace drive_backend {
39
40 namespace {
41
42 enum ParentType {
43 PARENT_TYPE_ROOT_OR_EMPTY,
44 PARENT_TYPE_DIRECTORY,
45 };
46
47 const char kFakeAccountId[] = "test_user@gmail.com";
48
EmptyGDataErrorCodeCallback(google_apis::GDataErrorCode error)49 void EmptyGDataErrorCodeCallback(google_apis::GDataErrorCode error) {}
50
HasParentLinkTo(const ScopedVector<google_apis::Link> & links,const std::string & parent_resource_id,ParentType parent_type)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;
55
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;
63 }
64 }
65
66 return parent_type == PARENT_TYPE_ROOT_OR_EMPTY && !has_parent;
67 }
68
69 struct TitleAndParentQuery
70 : std::unary_function<const google_apis::ResourceEntry*, bool> {
TitleAndParentQuerysync_file_system::drive_backend::__anone4477a5f0111::TitleAndParentQuery71 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) {}
77
operator ()sync_file_system::drive_backend::__anone4477a5f0111::TitleAndParentQuery78 bool operator()(const google_apis::ResourceEntry* entry) const {
79 return entry->title() == title &&
80 HasParentLinkTo(entry->links(), parent_resource_id, parent_type);
81 }
82
83 const std::string& title;
84 const std::string& parent_resource_id;
85 ParentType parent_type;
86 };
87
FilterEntriesByTitleAndParent(ScopedVector<google_apis::ResourceEntry> * entries,const std::string & title,const std::string & parent_resource_id,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());
100 }
101
GetDocumentByTitleAndParent(const ScopedVector<google_apis::ResourceEntry> & entries,const std::string & title,const std::string & parent_resource_id,ParentType parent_type)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;
115 }
116
EntryAdapterForEnsureTitleUniqueness(scoped_ptr<google_apis::ResourceEntry> entry,const APIUtil::EnsureUniquenessCallback & callback,APIUtil::EnsureUniquenessStatus status,google_apis::GDataErrorCode error)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());
123 }
124
UploadResultAdapter(const APIUtil::ResourceEntryCallback & callback,google_apis::GDataErrorCode error,const GURL & upload_location,scoped_ptr<google_apis::FileResource> entry)125 void UploadResultAdapter(const APIUtil::ResourceEntryCallback& callback,
126 google_apis::GDataErrorCode error,
127 const GURL& upload_location,
128 scoped_ptr<google_apis::FileResource> entry) {
129 callback.Run(error, entry ?
130 drive::util::ConvertFileResourceToResourceEntry(*entry) :
131 scoped_ptr<google_apis::ResourceEntry>());
132 }
133
GetMimeTypeFromTitle(const std::string & title)134 std::string GetMimeTypeFromTitle(const std::string& title) {
135 base::FilePath::StringType extension =
136 base::FilePath::FromUTF8Unsafe(title).Extension();
137 std::string mime_type;
138 if (extension.empty() ||
139 !net::GetWellKnownMimeTypeFromExtension(extension.substr(1), &mime_type))
140 return kMimeTypeOctetStream;
141 return mime_type;
142 }
143
CreateTemporaryFile(const base::FilePath & dir_path,webkit_blob::ScopedFile * temp_file)144 bool CreateTemporaryFile(const base::FilePath& dir_path,
145 webkit_blob::ScopedFile* temp_file) {
146 base::FilePath temp_file_path;
147 const bool success = base::CreateDirectory(dir_path) &&
148 base::CreateTemporaryFileInDir(dir_path, &temp_file_path);
149 if (!success)
150 return success;
151 *temp_file =
152 webkit_blob::ScopedFile(temp_file_path,
153 webkit_blob::ScopedFile::DELETE_ON_SCOPE_OUT,
154 base::MessageLoopProxy::current().get());
155 return success;
156 }
157
158 } // namespace
159
APIUtil(Profile * profile,const base::FilePath & temp_dir_path)160 APIUtil::APIUtil(Profile* profile,
161 const base::FilePath& temp_dir_path)
162 : oauth_service_(ProfileOAuth2TokenServiceFactory::GetForProfile(profile)),
163 signin_manager_(SigninManagerFactory::GetForProfile(profile)),
164 upload_next_key_(0),
165 temp_dir_path_(temp_dir_path),
166 has_initialized_token_(false) {
167 base::SequencedWorkerPool* blocking_pool =
168 content::BrowserThread::GetBlockingPool();
169 scoped_refptr<base::SequencedTaskRunner> task_runner(
170 blocking_pool->GetSequencedTaskRunner(blocking_pool->GetSequenceToken()));
171 DCHECK(!IsDriveAPIDisabled());
172 drive_service_.reset(new drive::DriveAPIService(
173 oauth_service_,
174 profile->GetRequestContext(),
175 task_runner.get(),
176 GURL(google_apis::DriveApiUrlGenerator::kBaseUrlForProduction),
177 GURL(google_apis::DriveApiUrlGenerator::kBaseDownloadUrlForProduction),
178 GURL(google_apis::GDataWapiUrlGenerator::kBaseUrlForProduction),
179 std::string() /* custom_user_agent */));
180 drive_service_->Initialize(signin_manager_->GetAuthenticatedAccountId());
181 drive_service_->AddObserver(this);
182 has_initialized_token_ = drive_service_->HasRefreshToken();
183
184 net::NetworkChangeNotifier::AddConnectionTypeObserver(this);
185
186 drive_uploader_.reset(new drive::DriveUploader(
187 drive_service_.get(), content::BrowserThread::GetBlockingPool()));
188 }
189
CreateForTesting(const base::FilePath & temp_dir_path,scoped_ptr<drive::DriveServiceInterface> drive_service,scoped_ptr<drive::DriveUploaderInterface> drive_uploader)190 scoped_ptr<APIUtil> APIUtil::CreateForTesting(
191 const base::FilePath& temp_dir_path,
192 scoped_ptr<drive::DriveServiceInterface> drive_service,
193 scoped_ptr<drive::DriveUploaderInterface> drive_uploader) {
194 return make_scoped_ptr(new APIUtil(
195 temp_dir_path,
196 drive_service.Pass(),
197 drive_uploader.Pass(),
198 kFakeAccountId));
199 }
200
APIUtil(const base::FilePath & temp_dir_path,scoped_ptr<drive::DriveServiceInterface> drive_service,scoped_ptr<drive::DriveUploaderInterface> drive_uploader,const std::string & account_id)201 APIUtil::APIUtil(const base::FilePath& temp_dir_path,
202 scoped_ptr<drive::DriveServiceInterface> drive_service,
203 scoped_ptr<drive::DriveUploaderInterface> drive_uploader,
204 const std::string& account_id)
205 : upload_next_key_(0),
206 temp_dir_path_(temp_dir_path) {
207 drive_service_ = drive_service.Pass();
208 drive_service_->Initialize(account_id);
209 drive_service_->AddObserver(this);
210 net::NetworkChangeNotifier::AddConnectionTypeObserver(this);
211
212 drive_uploader_ = drive_uploader.Pass();
213 }
214
~APIUtil()215 APIUtil::~APIUtil() {
216 DCHECK(CalledOnValidThread());
217 net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
218 drive_service_->RemoveObserver(this);
219 }
220
AddObserver(APIUtilObserver * observer)221 void APIUtil::AddObserver(APIUtilObserver* observer) {
222 DCHECK(CalledOnValidThread());
223 observers_.AddObserver(observer);
224 }
225
RemoveObserver(APIUtilObserver * observer)226 void APIUtil::RemoveObserver(APIUtilObserver* observer) {
227 DCHECK(CalledOnValidThread());
228 observers_.RemoveObserver(observer);
229 }
230
GetDriveRootResourceId(const GDataErrorCallback & callback)231 void APIUtil::GetDriveRootResourceId(const GDataErrorCallback& callback) {
232 DCHECK(CalledOnValidThread());
233 DCHECK(!IsDriveAPIDisabled());
234 DVLOG(2) << "Getting resource id for Drive root";
235
236 drive_service_->GetAboutResource(
237 base::Bind(&APIUtil::DidGetDriveRootResourceId, AsWeakPtr(), callback));
238 }
239
DidGetDriveRootResourceId(const GDataErrorCallback & callback,google_apis::GDataErrorCode error,scoped_ptr<google_apis::AboutResource> about_resource)240 void APIUtil::DidGetDriveRootResourceId(
241 const GDataErrorCallback& callback,
242 google_apis::GDataErrorCode error,
243 scoped_ptr<google_apis::AboutResource> about_resource) {
244 DCHECK(CalledOnValidThread());
245
246 if (error != google_apis::HTTP_SUCCESS) {
247 DVLOG(2) << "Error on getting resource id for Drive root: " << error;
248 callback.Run(error);
249 return;
250 }
251
252 DCHECK(about_resource);
253 root_resource_id_ = about_resource->root_folder_id();
254 DCHECK(!root_resource_id_.empty());
255 DVLOG(2) << "Got resource id for Drive root: " << root_resource_id_;
256 callback.Run(error);
257 }
258
GetDriveDirectoryForSyncRoot(const ResourceIdCallback & callback)259 void APIUtil::GetDriveDirectoryForSyncRoot(const ResourceIdCallback& callback) {
260 DCHECK(CalledOnValidThread());
261
262 if (GetRootResourceId().empty()) {
263 GetDriveRootResourceId(
264 base::Bind(&APIUtil::DidGetDriveRootResourceIdForGetSyncRoot,
265 AsWeakPtr(), callback));
266 return;
267 }
268
269 DVLOG(2) << "Getting Drive directory for SyncRoot";
270 std::string directory_name(GetSyncRootDirectoryName());
271 SearchByTitle(directory_name,
272 std::string(),
273 base::Bind(&APIUtil::DidGetDirectory,
274 AsWeakPtr(),
275 std::string(),
276 directory_name,
277 callback));
278 }
279
DidGetDriveRootResourceIdForGetSyncRoot(const ResourceIdCallback & callback,google_apis::GDataErrorCode error)280 void APIUtil::DidGetDriveRootResourceIdForGetSyncRoot(
281 const ResourceIdCallback& callback,
282 google_apis::GDataErrorCode error) {
283 DCHECK(CalledOnValidThread());
284 if (error != google_apis::HTTP_SUCCESS) {
285 DVLOG(2) << "Error on getting Drive directory for SyncRoot: " << error;
286 callback.Run(error, std::string());
287 return;
288 }
289 GetDriveDirectoryForSyncRoot(callback);
290 }
291
GetDriveDirectoryForOrigin(const std::string & sync_root_resource_id,const GURL & origin,const ResourceIdCallback & callback)292 void APIUtil::GetDriveDirectoryForOrigin(
293 const std::string& sync_root_resource_id,
294 const GURL& origin,
295 const ResourceIdCallback& callback) {
296 DCHECK(CalledOnValidThread());
297 DVLOG(2) << "Getting Drive directory for Origin: " << origin;
298
299 std::string directory_name(OriginToDirectoryTitle(origin));
300 SearchByTitle(directory_name,
301 sync_root_resource_id,
302 base::Bind(&APIUtil::DidGetDirectory,
303 AsWeakPtr(),
304 sync_root_resource_id,
305 directory_name,
306 callback));
307 }
308
DidGetDirectory(const std::string & parent_resource_id,const std::string & directory_name,const ResourceIdCallback & callback,google_apis::GDataErrorCode error,scoped_ptr<google_apis::ResourceList> feed)309 void APIUtil::DidGetDirectory(const std::string& parent_resource_id,
310 const std::string& directory_name,
311 const ResourceIdCallback& callback,
312 google_apis::GDataErrorCode error,
313 scoped_ptr<google_apis::ResourceList> feed) {
314 DCHECK(CalledOnValidThread());
315 DCHECK(base::IsStringASCII(directory_name));
316
317 if (error != google_apis::HTTP_SUCCESS) {
318 DVLOG(2) << "Error on getting Drive directory: " << error;
319 callback.Run(error, std::string());
320 return;
321 }
322
323 std::string resource_id;
324 ParentType parent_type = PARENT_TYPE_DIRECTORY;
325 if (parent_resource_id.empty()) {
326 resource_id = GetRootResourceId();
327 DCHECK(!resource_id.empty());
328 parent_type = PARENT_TYPE_ROOT_OR_EMPTY;
329 } else {
330 resource_id = parent_resource_id;
331 }
332 std::string title(directory_name);
333 google_apis::ResourceEntry* entry = GetDocumentByTitleAndParent(
334 feed->entries(), title, resource_id, parent_type);
335 if (!entry) {
336 DVLOG(2) << "Directory not found. Creating: " << directory_name;
337 drive_service_->AddNewDirectory(
338 resource_id,
339 directory_name,
340 drive::DriveServiceInterface::AddNewDirectoryOptions(),
341 base::Bind(&APIUtil::DidCreateDirectory,
342 AsWeakPtr(),
343 parent_resource_id,
344 title,
345 callback));
346 return;
347 }
348 DVLOG(2) << "Found Drive directory.";
349
350 // TODO(tzik): Handle error.
351 DCHECK_EQ(google_apis::ENTRY_KIND_FOLDER, entry->kind());
352 DCHECK_EQ(directory_name, entry->title());
353
354 if (entry->title() == GetSyncRootDirectoryName())
355 EnsureSyncRootIsNotInMyDrive(entry->resource_id());
356
357 callback.Run(error, entry->resource_id());
358 }
359
DidCreateDirectory(const std::string & parent_resource_id,const std::string & title,const ResourceIdCallback & callback,google_apis::GDataErrorCode error,scoped_ptr<google_apis::FileResource> entry)360 void APIUtil::DidCreateDirectory(const std::string& parent_resource_id,
361 const std::string& title,
362 const ResourceIdCallback& callback,
363 google_apis::GDataErrorCode error,
364 scoped_ptr<google_apis::FileResource> entry) {
365 DCHECK(CalledOnValidThread());
366
367 if (error != google_apis::HTTP_SUCCESS &&
368 error != google_apis::HTTP_CREATED) {
369 DVLOG(2) << "Error on creating Drive directory: " << error;
370 callback.Run(error, std::string());
371 return;
372 }
373 DVLOG(2) << "Created Drive directory.";
374
375 DCHECK(entry);
376 // Check if any other client creates a directory with same title.
377 EnsureTitleUniqueness(
378 parent_resource_id,
379 title,
380 base::Bind(&APIUtil::DidEnsureUniquenessForCreateDirectory,
381 AsWeakPtr(),
382 callback));
383 }
384
DidEnsureUniquenessForCreateDirectory(const ResourceIdCallback & callback,google_apis::GDataErrorCode error,EnsureUniquenessStatus status,scoped_ptr<google_apis::ResourceEntry> entry)385 void APIUtil::DidEnsureUniquenessForCreateDirectory(
386 const ResourceIdCallback& callback,
387 google_apis::GDataErrorCode error,
388 EnsureUniquenessStatus status,
389 scoped_ptr<google_apis::ResourceEntry> entry) {
390 DCHECK(CalledOnValidThread());
391
392 if (error != google_apis::HTTP_SUCCESS) {
393 callback.Run(error, std::string());
394 return;
395 }
396
397 if (status == NO_DUPLICATES_FOUND)
398 error = google_apis::HTTP_CREATED;
399
400 DCHECK(entry) << "No entry: " << error;
401
402 if (!entry->is_folder()) {
403 // TODO(kinuko): Fix this. http://crbug.com/237090
404 util::Log(
405 logging::LOG_ERROR,
406 FROM_HERE,
407 "A file is left for CreateDirectory due to file-folder conflict!");
408 callback.Run(google_apis::HTTP_CONFLICT, std::string());
409 return;
410 }
411
412 if (entry->title() == GetSyncRootDirectoryName())
413 EnsureSyncRootIsNotInMyDrive(entry->resource_id());
414
415 callback.Run(error, entry->resource_id());
416 }
417
GetLargestChangeStamp(const ChangeStampCallback & callback)418 void APIUtil::GetLargestChangeStamp(const ChangeStampCallback& callback) {
419 DCHECK(CalledOnValidThread());
420 DVLOG(2) << "Getting largest change id";
421
422 drive_service_->GetAboutResource(
423 base::Bind(&APIUtil::DidGetLargestChangeStamp, AsWeakPtr(), callback));
424 }
425
GetResourceEntry(const std::string & resource_id,const ResourceEntryCallback & callback)426 void APIUtil::GetResourceEntry(const std::string& resource_id,
427 const ResourceEntryCallback& callback) {
428 DCHECK(CalledOnValidThread());
429 DVLOG(2) << "Getting ResourceEntry for: " << resource_id;
430
431 drive_service_->GetFileResource(
432 resource_id,
433 base::Bind(&APIUtil::DidGetFileResource, AsWeakPtr(), callback));
434 }
435
DidGetLargestChangeStamp(const ChangeStampCallback & callback,google_apis::GDataErrorCode error,scoped_ptr<google_apis::AboutResource> about_resource)436 void APIUtil::DidGetLargestChangeStamp(
437 const ChangeStampCallback& callback,
438 google_apis::GDataErrorCode error,
439 scoped_ptr<google_apis::AboutResource> about_resource) {
440 DCHECK(CalledOnValidThread());
441
442 int64 largest_change_id = 0;
443 if (error == google_apis::HTTP_SUCCESS) {
444 DCHECK(about_resource);
445 largest_change_id = about_resource->largest_change_id();
446 root_resource_id_ = about_resource->root_folder_id();
447 DVLOG(2) << "Got largest change id: " << largest_change_id;
448 } else {
449 DVLOG(2) << "Error on getting largest change id: " << error;
450 }
451
452 callback.Run(error, largest_change_id);
453 }
454
SearchByTitle(const std::string & title,const std::string & directory_resource_id,const ResourceListCallback & callback)455 void APIUtil::SearchByTitle(const std::string& title,
456 const std::string& directory_resource_id,
457 const ResourceListCallback& callback) {
458 DCHECK(CalledOnValidThread());
459 DCHECK(!title.empty());
460 DVLOG(2) << "Searching resources in the directory [" << directory_resource_id
461 << "] with title [" << title << "]";
462
463 drive_service_->SearchByTitle(
464 title,
465 directory_resource_id,
466 base::Bind(&APIUtil::DidGetFileList, AsWeakPtr(), callback));
467 }
468
ListFiles(const std::string & directory_resource_id,const ResourceListCallback & callback)469 void APIUtil::ListFiles(const std::string& directory_resource_id,
470 const ResourceListCallback& callback) {
471 DCHECK(CalledOnValidThread());
472 DVLOG(2) << "Listing resources in the directory [" << directory_resource_id
473 << "]";
474
475 drive_service_->GetFileListInDirectory(
476 directory_resource_id,
477 base::Bind(&APIUtil::DidGetFileList, AsWeakPtr(), callback));
478 }
479
ListChanges(int64 start_changestamp,const ResourceListCallback & callback)480 void APIUtil::ListChanges(int64 start_changestamp,
481 const ResourceListCallback& callback) {
482 DCHECK(CalledOnValidThread());
483 DVLOG(2) << "Listing changes since: " << start_changestamp;
484
485 drive_service_->GetChangeList(
486 start_changestamp,
487 base::Bind(&APIUtil::DidGetChangeList, AsWeakPtr(), callback));
488 }
489
ContinueListing(const GURL & next_link,const ResourceListCallback & callback)490 void APIUtil::ContinueListing(const GURL& next_link,
491 const ResourceListCallback& callback) {
492 DCHECK(CalledOnValidThread());
493 DVLOG(2) << "Continue listing on feed: " << next_link.spec();
494
495 drive_service_->GetRemainingFileList(
496 next_link,
497 base::Bind(&APIUtil::DidGetFileList, AsWeakPtr(), callback));
498 }
499
DownloadFile(const std::string & resource_id,const std::string & local_file_md5,const DownloadFileCallback & callback)500 void APIUtil::DownloadFile(const std::string& resource_id,
501 const std::string& local_file_md5,
502 const DownloadFileCallback& callback) {
503 DCHECK(CalledOnValidThread());
504 DCHECK(!temp_dir_path_.empty());
505 DVLOG(2) << "Downloading file [" << resource_id << "]";
506
507 scoped_ptr<webkit_blob::ScopedFile> temp_file(new webkit_blob::ScopedFile);
508 webkit_blob::ScopedFile* temp_file_ptr = temp_file.get();
509 content::BrowserThread::PostTaskAndReplyWithResult(
510 content::BrowserThread::FILE, FROM_HERE,
511 base::Bind(&CreateTemporaryFile, temp_dir_path_, temp_file_ptr),
512 base::Bind(&APIUtil::DidGetTemporaryFileForDownload,
513 AsWeakPtr(), resource_id, local_file_md5,
514 base::Passed(&temp_file), callback));
515 }
516
UploadNewFile(const std::string & directory_resource_id,const base::FilePath & local_file_path,const std::string & title,const UploadFileCallback & callback)517 void APIUtil::UploadNewFile(const std::string& directory_resource_id,
518 const base::FilePath& local_file_path,
519 const std::string& title,
520 const UploadFileCallback& callback) {
521 DCHECK(CalledOnValidThread());
522 DVLOG(2) << "Uploading new file into the directory [" << directory_resource_id
523 << "] with title [" << title << "]";
524
525 std::string mime_type = GetMimeTypeFromTitle(title);
526 UploadKey upload_key = RegisterUploadCallback(callback);
527 ResourceEntryCallback did_upload_callback =
528 base::Bind(&APIUtil::DidUploadNewFile,
529 AsWeakPtr(),
530 directory_resource_id,
531 title,
532 upload_key);
533 drive_uploader_->UploadNewFile(
534 directory_resource_id,
535 local_file_path,
536 title,
537 mime_type,
538 drive::DriveUploader::UploadNewFileOptions(),
539 base::Bind(&UploadResultAdapter, did_upload_callback),
540 google_apis::ProgressCallback());
541 }
542
UploadExistingFile(const std::string & resource_id,const std::string & remote_file_md5,const base::FilePath & local_file_path,const UploadFileCallback & callback)543 void APIUtil::UploadExistingFile(const std::string& resource_id,
544 const std::string& remote_file_md5,
545 const base::FilePath& local_file_path,
546 const UploadFileCallback& callback) {
547 DCHECK(CalledOnValidThread());
548 DVLOG(2) << "Uploading existing file [" << resource_id << "]";
549 drive_service_->GetFileResource(
550 resource_id,
551 base::Bind(&APIUtil::DidGetFileResource,
552 AsWeakPtr(),
553 base::Bind(&APIUtil::UploadExistingFileInternal,
554 AsWeakPtr(),
555 remote_file_md5,
556 local_file_path,
557 callback)));
558 }
559
CreateDirectory(const std::string & parent_resource_id,const std::string & title,const ResourceIdCallback & callback)560 void APIUtil::CreateDirectory(const std::string& parent_resource_id,
561 const std::string& title,
562 const ResourceIdCallback& callback) {
563 DCHECK(CalledOnValidThread());
564 // TODO(kinuko): This will call EnsureTitleUniqueness and will delete
565 // directories if there're duplicated directories. This must be ok
566 // for current design but we'll need to merge directories when we support
567 // 'real' directories.
568 drive_service_->AddNewDirectory(
569 parent_resource_id,
570 title,
571 drive::DriveServiceInterface::AddNewDirectoryOptions(),
572 base::Bind(&APIUtil::DidCreateDirectory,
573 AsWeakPtr(),
574 parent_resource_id,
575 title,
576 callback));
577 }
578
DeleteFile(const std::string & resource_id,const std::string & remote_file_md5,const GDataErrorCallback & callback)579 void APIUtil::DeleteFile(const std::string& resource_id,
580 const std::string& remote_file_md5,
581 const GDataErrorCallback& callback) {
582 DCHECK(CalledOnValidThread());
583 DVLOG(2) << "Deleting file: " << resource_id;
584
585 // Load actual remote_file_md5 to check for conflict before deletion.
586 if (!remote_file_md5.empty()) {
587 drive_service_->GetFileResource(
588 resource_id,
589 base::Bind(&APIUtil::DidGetFileResource,
590 AsWeakPtr(),
591 base::Bind(&APIUtil::DeleteFileInternal,
592 AsWeakPtr(),
593 remote_file_md5,
594 callback)));
595 return;
596 }
597
598 // Expected remote_file_md5 is empty so do a force delete.
599 drive_service_->TrashResource(
600 resource_id,
601 base::Bind(&APIUtil::DidDeleteFile, AsWeakPtr(), callback));
602 return;
603 }
604
EnsureSyncRootIsNotInMyDrive(const std::string & sync_root_resource_id)605 void APIUtil::EnsureSyncRootIsNotInMyDrive(
606 const std::string& sync_root_resource_id) {
607 DCHECK(CalledOnValidThread());
608
609 if (GetRootResourceId().empty()) {
610 GetDriveRootResourceId(
611 base::Bind(&APIUtil::DidGetDriveRootResourceIdForEnsureSyncRoot,
612 AsWeakPtr(), sync_root_resource_id));
613 return;
614 }
615
616 DVLOG(2) << "Ensuring the sync root directory is not in 'My Drive'.";
617 drive_service_->RemoveResourceFromDirectory(
618 GetRootResourceId(),
619 sync_root_resource_id,
620 base::Bind(&EmptyGDataErrorCodeCallback));
621 }
622
DidGetDriveRootResourceIdForEnsureSyncRoot(const std::string & sync_root_resource_id,google_apis::GDataErrorCode error)623 void APIUtil::DidGetDriveRootResourceIdForEnsureSyncRoot(
624 const std::string& sync_root_resource_id,
625 google_apis::GDataErrorCode error) {
626 DCHECK(CalledOnValidThread());
627
628 if (error != google_apis::HTTP_SUCCESS) {
629 DVLOG(2) << "Error on ensuring the sync root directory is not in"
630 << " 'My Drive': " << error;
631 // Give up ensuring the sync root directory is not in 'My Drive'. This will
632 // be retried at some point.
633 return;
634 }
635
636 DCHECK(!GetRootResourceId().empty());
637 EnsureSyncRootIsNotInMyDrive(sync_root_resource_id);
638 }
639
640 // static
641 // TODO(calvinlo): Delete this when Sync Directory Operations are supported by
642 // default.
GetSyncRootDirectoryName()643 std::string APIUtil::GetSyncRootDirectoryName() {
644 return IsSyncFSDirectoryOperationEnabled() ? kSyncRootFolderTitleDev
645 : kSyncRootFolderTitle;
646 }
647
648 // static
OriginToDirectoryTitle(const GURL & origin)649 std::string APIUtil::OriginToDirectoryTitle(const GURL& origin) {
650 DCHECK(origin.SchemeIs(extensions::kExtensionScheme));
651 return origin.host();
652 }
653
654 // static
DirectoryTitleToOrigin(const std::string & title)655 GURL APIUtil::DirectoryTitleToOrigin(const std::string& title) {
656 return extensions::Extension::GetBaseURLFromExtensionId(title);
657 }
658
OnReadyToSendRequests()659 void APIUtil::OnReadyToSendRequests() {
660 DCHECK(CalledOnValidThread());
661 if (!has_initialized_token_) {
662 drive_service_->Initialize(signin_manager_->GetAuthenticatedAccountId());
663 has_initialized_token_ = true;
664 }
665 FOR_EACH_OBSERVER(APIUtilObserver, observers_, OnAuthenticated());
666 }
667
OnConnectionTypeChanged(net::NetworkChangeNotifier::ConnectionType type)668 void APIUtil::OnConnectionTypeChanged(
669 net::NetworkChangeNotifier::ConnectionType type) {
670 DCHECK(CalledOnValidThread());
671 if (type != net::NetworkChangeNotifier::CONNECTION_NONE) {
672 FOR_EACH_OBSERVER(APIUtilObserver, observers_, OnNetworkConnected());
673 return;
674 }
675 // We're now disconnected, reset the drive_uploader_ to force stop
676 // uploading, otherwise the uploader may get stuck.
677 // TODO(kinuko): Check the uploader behavior if it's the expected behavior
678 // (http://crbug.com/223818)
679 CancelAllUploads(google_apis::GDATA_NO_CONNECTION);
680 }
681
DidGetFileList(const ResourceListCallback & callback,google_apis::GDataErrorCode error,scoped_ptr<google_apis::FileList> file_list)682 void APIUtil::DidGetFileList(const ResourceListCallback& callback,
683 google_apis::GDataErrorCode error,
684 scoped_ptr<google_apis::FileList> file_list) {
685 DCHECK(CalledOnValidThread());
686
687 if (error != google_apis::HTTP_SUCCESS) {
688 DVLOG(2) << "Error on listing files: " << error;
689 callback.Run(error, scoped_ptr<google_apis::ResourceList>());
690 return;
691 }
692
693 DVLOG(2) << "Got file list";
694 DCHECK(file_list);
695 callback.Run(error, drive::util::ConvertFileListToResourceList(*file_list));
696 }
697
DidGetChangeList(const ResourceListCallback & callback,google_apis::GDataErrorCode error,scoped_ptr<google_apis::ChangeList> change_list)698 void APIUtil::DidGetChangeList(
699 const ResourceListCallback& callback,
700 google_apis::GDataErrorCode error,
701 scoped_ptr<google_apis::ChangeList> change_list) {
702 DCHECK(CalledOnValidThread());
703
704 if (error != google_apis::HTTP_SUCCESS) {
705 DVLOG(2) << "Error on listing changes: " << error;
706 callback.Run(error, scoped_ptr<google_apis::ResourceList>());
707 return;
708 }
709
710 DVLOG(2) << "Got change list";
711 DCHECK(change_list);
712 callback.Run(error,
713 drive::util::ConvertChangeListToResourceList(*change_list));
714 }
715
DidGetFileResource(const ResourceEntryCallback & callback,google_apis::GDataErrorCode error,scoped_ptr<google_apis::FileResource> entry)716 void APIUtil::DidGetFileResource(
717 const ResourceEntryCallback& callback,
718 google_apis::GDataErrorCode error,
719 scoped_ptr<google_apis::FileResource> entry) {
720 DCHECK(CalledOnValidThread());
721
722 if (error != google_apis::HTTP_SUCCESS) {
723 DVLOG(2) << "Error on getting resource entry:" << error;
724 callback.Run(error, scoped_ptr<google_apis::ResourceEntry>());
725 return;
726 }
727 DCHECK(entry);
728
729 if (entry->labels().is_trashed()) {
730 DVLOG(2) << "Got resource entry, the entry was trashed.";
731 callback.Run(google_apis::HTTP_NOT_FOUND,
732 drive::util::ConvertFileResourceToResourceEntry(*entry));
733 return;
734 }
735
736 DVLOG(2) << "Got resource entry";
737 callback.Run(error, drive::util::ConvertFileResourceToResourceEntry(*entry));
738 }
739
DidGetTemporaryFileForDownload(const std::string & resource_id,const std::string & local_file_md5,scoped_ptr<webkit_blob::ScopedFile> local_file,const DownloadFileCallback & callback,bool success)740 void APIUtil::DidGetTemporaryFileForDownload(
741 const std::string& resource_id,
742 const std::string& local_file_md5,
743 scoped_ptr<webkit_blob::ScopedFile> local_file,
744 const DownloadFileCallback& callback,
745 bool success) {
746 if (!success) {
747 DVLOG(2) << "Error in creating a temp file under "
748 << temp_dir_path_.value();
749 callback.Run(google_apis::GDATA_FILE_ERROR, std::string(), 0, base::Time(),
750 local_file->Pass());
751 return;
752 }
753 drive_service_->GetFileResource(
754 resource_id,
755 base::Bind(&APIUtil::DidGetFileResource,
756 AsWeakPtr(),
757 base::Bind(&APIUtil::DownloadFileInternal,
758 AsWeakPtr(),
759 local_file_md5,
760 base::Passed(&local_file),
761 callback)));
762 }
763
DownloadFileInternal(const std::string & local_file_md5,scoped_ptr<webkit_blob::ScopedFile> local_file,const DownloadFileCallback & callback,google_apis::GDataErrorCode error,scoped_ptr<google_apis::ResourceEntry> entry)764 void APIUtil::DownloadFileInternal(
765 const std::string& local_file_md5,
766 scoped_ptr<webkit_blob::ScopedFile> local_file,
767 const DownloadFileCallback& callback,
768 google_apis::GDataErrorCode error,
769 scoped_ptr<google_apis::ResourceEntry> entry) {
770 DCHECK(CalledOnValidThread());
771
772 if (error != google_apis::HTTP_SUCCESS) {
773 DVLOG(2) << "Error on getting resource entry for download";
774 callback.Run(error, std::string(), 0, base::Time(), local_file->Pass());
775 return;
776 }
777 DCHECK(entry);
778
779 DVLOG(2) << "Got resource entry for download";
780 // If local file and remote file are same, cancel the download.
781 if (local_file_md5 == entry->file_md5()) {
782 callback.Run(google_apis::HTTP_NOT_MODIFIED,
783 local_file_md5,
784 entry->file_size(),
785 entry->updated_time(),
786 local_file->Pass());
787 return;
788 }
789
790 DVLOG(2) << "Downloading file: " << entry->resource_id();
791 const std::string& resource_id = entry->resource_id();
792 const base::FilePath& local_file_path = local_file->path();
793 drive_service_->DownloadFile(local_file_path,
794 resource_id,
795 base::Bind(&APIUtil::DidDownloadFile,
796 AsWeakPtr(),
797 base::Passed(&entry),
798 base::Passed(&local_file),
799 callback),
800 google_apis::GetContentCallback(),
801 google_apis::ProgressCallback());
802 }
803
DidDownloadFile(scoped_ptr<google_apis::ResourceEntry> entry,scoped_ptr<webkit_blob::ScopedFile> local_file,const DownloadFileCallback & callback,google_apis::GDataErrorCode error,const base::FilePath & downloaded_file_path)804 void APIUtil::DidDownloadFile(scoped_ptr<google_apis::ResourceEntry> entry,
805 scoped_ptr<webkit_blob::ScopedFile> local_file,
806 const DownloadFileCallback& callback,
807 google_apis::GDataErrorCode error,
808 const base::FilePath& downloaded_file_path) {
809 DCHECK(CalledOnValidThread());
810 if (error == google_apis::HTTP_SUCCESS)
811 DVLOG(2) << "Download completed";
812 else
813 DVLOG(2) << "Error on downloading file: " << error;
814
815 callback.Run(
816 error, entry->file_md5(), entry->file_size(), entry->updated_time(),
817 local_file->Pass());
818 }
819
DidUploadNewFile(const std::string & parent_resource_id,const std::string & title,UploadKey upload_key,google_apis::GDataErrorCode error,scoped_ptr<google_apis::ResourceEntry> entry)820 void APIUtil::DidUploadNewFile(const std::string& parent_resource_id,
821 const std::string& title,
822 UploadKey upload_key,
823 google_apis::GDataErrorCode error,
824 scoped_ptr<google_apis::ResourceEntry> entry) {
825 UploadFileCallback callback = GetAndUnregisterUploadCallback(upload_key);
826 DCHECK(!callback.is_null());
827 if (error != google_apis::HTTP_SUCCESS &&
828 error != google_apis::HTTP_CREATED) {
829 DVLOG(2) << "Error on uploading new file: " << error;
830 callback.Run(error, std::string(), std::string());
831 return;
832 }
833
834 DVLOG(2) << "Upload completed";
835 EnsureTitleUniqueness(parent_resource_id,
836 title,
837 base::Bind(&APIUtil::DidEnsureUniquenessForCreateFile,
838 AsWeakPtr(),
839 entry->resource_id(),
840 callback));
841 }
842
DidEnsureUniquenessForCreateFile(const std::string & expected_resource_id,const UploadFileCallback & callback,google_apis::GDataErrorCode error,EnsureUniquenessStatus status,scoped_ptr<google_apis::ResourceEntry> entry)843 void APIUtil::DidEnsureUniquenessForCreateFile(
844 const std::string& expected_resource_id,
845 const UploadFileCallback& callback,
846 google_apis::GDataErrorCode error,
847 EnsureUniquenessStatus status,
848 scoped_ptr<google_apis::ResourceEntry> entry) {
849 if (error != google_apis::HTTP_SUCCESS) {
850 DVLOG(2) << "Error on uploading new file: " << error;
851 callback.Run(error, std::string(), std::string());
852 return;
853 }
854
855 switch (status) {
856 case NO_DUPLICATES_FOUND:
857 // The file was uploaded successfully and no conflict was detected.
858 DCHECK(entry);
859 DVLOG(2) << "No conflict detected on uploading new file";
860 callback.Run(
861 google_apis::HTTP_CREATED, entry->resource_id(), entry->file_md5());
862 return;
863
864 case RESOLVED_DUPLICATES:
865 // The file was uploaded successfully but a conflict was detected.
866 // The duplicated file was deleted successfully.
867 DCHECK(entry);
868 if (entry->resource_id() != expected_resource_id) {
869 // TODO(kinuko): We should check local vs remote md5 here.
870 DVLOG(2) << "Conflict detected on uploading new file";
871 callback.Run(google_apis::HTTP_CONFLICT,
872 entry->resource_id(),
873 entry->file_md5());
874 return;
875 }
876
877 DVLOG(2) << "Conflict detected on uploading new file and resolved";
878 callback.Run(
879 google_apis::HTTP_CREATED, entry->resource_id(), entry->file_md5());
880 return;
881
882 default:
883 NOTREACHED() << "Unknown status from EnsureTitleUniqueness:" << status
884 << " for " << expected_resource_id;
885 }
886 }
887
UploadExistingFileInternal(const std::string & remote_file_md5,const base::FilePath & local_file_path,const UploadFileCallback & callback,google_apis::GDataErrorCode error,scoped_ptr<google_apis::ResourceEntry> entry)888 void APIUtil::UploadExistingFileInternal(
889 const std::string& remote_file_md5,
890 const base::FilePath& local_file_path,
891 const UploadFileCallback& callback,
892 google_apis::GDataErrorCode error,
893 scoped_ptr<google_apis::ResourceEntry> entry) {
894 DCHECK(CalledOnValidThread());
895
896 if (error != google_apis::HTTP_SUCCESS) {
897 DVLOG(2) << "Error on uploading existing file: " << error;
898 callback.Run(error, std::string(), std::string());
899 return;
900 }
901 DCHECK(entry);
902
903 // If remote file's hash value is different from the expected one, conflict
904 // might have occurred.
905 if (!remote_file_md5.empty() && remote_file_md5 != entry->file_md5()) {
906 DVLOG(2) << "Conflict detected before uploading existing file";
907 callback.Run(google_apis::HTTP_CONFLICT, std::string(), std::string());
908 return;
909 }
910
911 drive::DriveUploader::UploadExistingFileOptions options;
912 options.etag = entry->etag();
913 std::string mime_type = GetMimeTypeFromTitle(entry->title());
914 UploadKey upload_key = RegisterUploadCallback(callback);
915 ResourceEntryCallback did_upload_callback =
916 base::Bind(&APIUtil::DidUploadExistingFile, AsWeakPtr(), upload_key);
917 drive_uploader_->UploadExistingFile(
918 entry->resource_id(),
919 local_file_path,
920 mime_type,
921 options,
922 base::Bind(&UploadResultAdapter, did_upload_callback),
923 google_apis::ProgressCallback());
924 }
925
IsAuthenticated() const926 bool APIUtil::IsAuthenticated() const {
927 return drive_service_->HasRefreshToken();
928 }
929
DidUploadExistingFile(UploadKey upload_key,google_apis::GDataErrorCode error,scoped_ptr<google_apis::ResourceEntry> entry)930 void APIUtil::DidUploadExistingFile(
931 UploadKey upload_key,
932 google_apis::GDataErrorCode error,
933 scoped_ptr<google_apis::ResourceEntry> entry) {
934 DCHECK(CalledOnValidThread());
935 UploadFileCallback callback = GetAndUnregisterUploadCallback(upload_key);
936 DCHECK(!callback.is_null());
937 if (error != google_apis::HTTP_SUCCESS) {
938 DVLOG(2) << "Error on uploading existing file: " << error;
939 callback.Run(error, std::string(), std::string());
940 return;
941 }
942
943 DCHECK(entry);
944 DVLOG(2) << "Upload completed";
945 callback.Run(error, entry->resource_id(), entry->file_md5());
946 }
947
DeleteFileInternal(const std::string & remote_file_md5,const GDataErrorCallback & callback,google_apis::GDataErrorCode error,scoped_ptr<google_apis::ResourceEntry> entry)948 void APIUtil::DeleteFileInternal(const std::string& remote_file_md5,
949 const GDataErrorCallback& callback,
950 google_apis::GDataErrorCode error,
951 scoped_ptr<google_apis::ResourceEntry> entry) {
952 DCHECK(CalledOnValidThread());
953
954 if (error != google_apis::HTTP_SUCCESS) {
955 DVLOG(2) << "Error on getting resource entry for deleting file: " << error;
956 callback.Run(error);
957 return;
958 }
959 DCHECK(entry);
960
961 // If remote file's hash value is different from the expected one, conflict
962 // might have occurred.
963 if (!remote_file_md5.empty() && remote_file_md5 != entry->file_md5()) {
964 DVLOG(2) << "Conflict detected before deleting file";
965 callback.Run(google_apis::HTTP_CONFLICT);
966 return;
967 }
968 DVLOG(2) << "Got resource entry for deleting file";
969
970 // Move the file to trash (don't delete it completely).
971 drive_service_->TrashResource(
972 entry->resource_id(),
973 base::Bind(&APIUtil::DidDeleteFile, AsWeakPtr(), callback));
974 }
975
DidDeleteFile(const GDataErrorCallback & callback,google_apis::GDataErrorCode error)976 void APIUtil::DidDeleteFile(const GDataErrorCallback& callback,
977 google_apis::GDataErrorCode error) {
978 DCHECK(CalledOnValidThread());
979 if (error == google_apis::HTTP_SUCCESS)
980 DVLOG(2) << "Deletion completed";
981 else
982 DVLOG(2) << "Error on deleting file: " << error;
983
984 callback.Run(error);
985 }
986
EnsureTitleUniqueness(const std::string & parent_resource_id,const std::string & expected_title,const EnsureUniquenessCallback & callback)987 void APIUtil::EnsureTitleUniqueness(const std::string& parent_resource_id,
988 const std::string& expected_title,
989 const EnsureUniquenessCallback& callback) {
990 DCHECK(CalledOnValidThread());
991 DVLOG(2) << "Checking if there's no conflict on entry creation";
992
993 const google_apis::GetResourceListCallback& bound_callback =
994 base::Bind(&APIUtil::DidListEntriesToEnsureUniqueness,
995 AsWeakPtr(),
996 parent_resource_id,
997 expected_title,
998 callback);
999
1000 SearchByTitle(expected_title, parent_resource_id, bound_callback);
1001 }
1002
DidListEntriesToEnsureUniqueness(const std::string & parent_resource_id,const std::string & expected_title,const EnsureUniquenessCallback & callback,google_apis::GDataErrorCode error,scoped_ptr<google_apis::ResourceList> feed)1003 void APIUtil::DidListEntriesToEnsureUniqueness(
1004 const std::string& parent_resource_id,
1005 const std::string& expected_title,
1006 const EnsureUniquenessCallback& callback,
1007 google_apis::GDataErrorCode error,
1008 scoped_ptr<google_apis::ResourceList> feed) {
1009 DCHECK(CalledOnValidThread());
1010
1011 if (error != google_apis::HTTP_SUCCESS) {
1012 DVLOG(2) << "Error on listing resource for ensuring title uniqueness";
1013 callback.Run(
1014 error, NO_DUPLICATES_FOUND, scoped_ptr<google_apis::ResourceEntry>());
1015 return;
1016 }
1017 DVLOG(2) << "Got resource list for ensuring title uniqueness";
1018
1019 // This filtering is needed only on WAPI. Once we move to Drive API we can
1020 // drop this.
1021 std::string resource_id;
1022 ParentType parent_type = PARENT_TYPE_DIRECTORY;
1023 if (parent_resource_id.empty()) {
1024 resource_id = GetRootResourceId();
1025 DCHECK(!resource_id.empty());
1026 parent_type = PARENT_TYPE_ROOT_OR_EMPTY;
1027 } else {
1028 resource_id = parent_resource_id;
1029 }
1030 ScopedVector<google_apis::ResourceEntry> entries;
1031 entries.swap(*feed->mutable_entries());
1032 FilterEntriesByTitleAndParent(
1033 &entries, expected_title, resource_id, parent_type);
1034
1035 if (entries.empty()) {
1036 DVLOG(2) << "Uploaded file is not found";
1037 callback.Run(google_apis::HTTP_NOT_FOUND,
1038 NO_DUPLICATES_FOUND,
1039 scoped_ptr<google_apis::ResourceEntry>());
1040 return;
1041 }
1042
1043 if (entries.size() >= 2) {
1044 DVLOG(2) << "Conflict detected on creating entry";
1045 for (size_t i = 0; i < entries.size() - 1; ++i) {
1046 // TODO(tzik): Replace published_time with creation time after we move to
1047 // Drive API.
1048 if (entries[i]->published_time() < entries.back()->published_time())
1049 std::swap(entries[i], entries.back());
1050 }
1051
1052 scoped_ptr<google_apis::ResourceEntry> earliest_entry(entries.back());
1053 entries.back() = NULL;
1054 entries.get().pop_back();
1055
1056 DeleteEntriesForEnsuringTitleUniqueness(
1057 entries.Pass(),
1058 base::Bind(&EntryAdapterForEnsureTitleUniqueness,
1059 base::Passed(&earliest_entry),
1060 callback,
1061 RESOLVED_DUPLICATES));
1062 return;
1063 }
1064
1065 DVLOG(2) << "no conflict detected";
1066 DCHECK_EQ(1u, entries.size());
1067 scoped_ptr<google_apis::ResourceEntry> entry(entries.front());
1068 entries.weak_clear();
1069
1070 callback.Run(google_apis::HTTP_SUCCESS, NO_DUPLICATES_FOUND, entry.Pass());
1071 }
1072
DeleteEntriesForEnsuringTitleUniqueness(ScopedVector<google_apis::ResourceEntry> entries,const GDataErrorCallback & callback)1073 void APIUtil::DeleteEntriesForEnsuringTitleUniqueness(
1074 ScopedVector<google_apis::ResourceEntry> entries,
1075 const GDataErrorCallback& callback) {
1076 DCHECK(CalledOnValidThread());
1077 DVLOG(2) << "Cleaning up conflict on entry creation";
1078
1079 if (entries.empty()) {
1080 callback.Run(google_apis::HTTP_SUCCESS);
1081 return;
1082 }
1083
1084 scoped_ptr<google_apis::ResourceEntry> entry(entries.back());
1085 entries.back() = NULL;
1086 entries.get().pop_back();
1087
1088 // We don't care conflicts here as other clients may be also deleting this
1089 // file, so passing an empty etag.
1090 drive_service_->TrashResource(
1091 entry->resource_id(),
1092 base::Bind(&APIUtil::DidDeleteEntriesForEnsuringTitleUniqueness,
1093 AsWeakPtr(),
1094 base::Passed(&entries),
1095 callback));
1096 }
1097
DidDeleteEntriesForEnsuringTitleUniqueness(ScopedVector<google_apis::ResourceEntry> entries,const GDataErrorCallback & callback,google_apis::GDataErrorCode error)1098 void APIUtil::DidDeleteEntriesForEnsuringTitleUniqueness(
1099 ScopedVector<google_apis::ResourceEntry> entries,
1100 const GDataErrorCallback& callback,
1101 google_apis::GDataErrorCode error) {
1102 DCHECK(CalledOnValidThread());
1103
1104 if (error != google_apis::HTTP_SUCCESS &&
1105 error != google_apis::HTTP_NOT_FOUND) {
1106 DVLOG(2) << "Error on deleting file: " << error;
1107 callback.Run(error);
1108 return;
1109 }
1110
1111 DVLOG(2) << "Deletion completed";
1112 DeleteEntriesForEnsuringTitleUniqueness(entries.Pass(), callback);
1113 }
1114
RegisterUploadCallback(const UploadFileCallback & callback)1115 APIUtil::UploadKey APIUtil::RegisterUploadCallback(
1116 const UploadFileCallback& callback) {
1117 const bool inserted = upload_callback_map_.insert(
1118 std::make_pair(upload_next_key_, callback)).second;
1119 CHECK(inserted);
1120 return upload_next_key_++;
1121 }
1122
GetAndUnregisterUploadCallback(UploadKey key)1123 APIUtil::UploadFileCallback APIUtil::GetAndUnregisterUploadCallback(
1124 UploadKey key) {
1125 UploadFileCallback callback;
1126 UploadCallbackMap::iterator found = upload_callback_map_.find(key);
1127 if (found == upload_callback_map_.end())
1128 return callback;
1129 callback = found->second;
1130 upload_callback_map_.erase(found);
1131 return callback;
1132 }
1133
CancelAllUploads(google_apis::GDataErrorCode error)1134 void APIUtil::CancelAllUploads(google_apis::GDataErrorCode error) {
1135 if (upload_callback_map_.empty())
1136 return;
1137 for (UploadCallbackMap::iterator iter = upload_callback_map_.begin();
1138 iter != upload_callback_map_.end();
1139 ++iter) {
1140 iter->second.Run(error, std::string(), std::string());
1141 }
1142 upload_callback_map_.clear();
1143 drive_uploader_.reset(new drive::DriveUploader(
1144 drive_service_.get(), content::BrowserThread::GetBlockingPool()));
1145 }
1146
GetRootResourceId() const1147 std::string APIUtil::GetRootResourceId() const {
1148 if (IsDriveAPIDisabled())
1149 return drive_service_->GetRootResourceId();
1150 return root_resource_id_;
1151 }
1152
1153 } // namespace drive_backend
1154 } // namespace sync_file_system
1155