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