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