• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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