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