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