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/sync_engine_initializer.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/callback.h"
      9 #include "base/logging.h"
     10 #include "chrome/browser/drive/drive_api_service.h"
     11 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h"
     12 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_util.h"
     13 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.h"
     14 #include "chrome/browser/sync_file_system/drive_backend/sync_engine_context.h"
     15 #include "chrome/browser/sync_file_system/drive_backend/sync_task_manager.h"
     16 #include "chrome/browser/sync_file_system/drive_backend/sync_task_token.h"
     17 #include "chrome/browser/sync_file_system/logger.h"
     18 #include "google_apis/drive/drive_api_parser.h"
     19 #include "google_apis/drive/gdata_wapi_parser.h"
     20 
     21 namespace sync_file_system {
     22 namespace drive_backend {
     23 
     24 namespace {
     25 
     26 ////////////////////////////////////////////////////////////////////////////////
     27 // Functions below are for wrapping the access to legacy GData WAPI classes.
     28 
     29 bool HasNoParents(const google_apis::FileResource& entry) {
     30   return entry.parents().empty();
     31 }
     32 
     33 bool HasFolderAsParent(const google_apis::FileResource& entry,
     34                        const std::string& parent_id) {
     35   for (size_t i = 0; i < entry.parents().size(); ++i) {
     36     if (entry.parents()[i].file_id() == parent_id)
     37       return true;
     38   }
     39   return false;
     40 }
     41 
     42 bool LessOnCreationTime(const google_apis::FileResource& left,
     43                         const google_apis::FileResource& right) {
     44   return left.created_date() < right.created_date();
     45 }
     46 
     47 typedef base::Callback<void(scoped_ptr<SyncTaskToken> token,
     48                             google_apis::GDataErrorCode error,
     49                             scoped_ptr<google_apis::ResourceList> resources)>
     50     TokenAndResourceListCallback;
     51 
     52 // Functions above are for wrapping the access to legacy GData WAPI classes.
     53 ////////////////////////////////////////////////////////////////////////////////
     54 
     55 }  // namespace
     56 
     57 SyncEngineInitializer::SyncEngineInitializer(
     58     SyncEngineContext* sync_context,
     59     const base::FilePath& database_path,
     60     leveldb::Env* env_override)
     61     : sync_context_(sync_context),
     62       env_override_(env_override),
     63       database_path_(database_path),
     64       find_sync_root_retry_count_(0),
     65       largest_change_id_(0),
     66       weak_ptr_factory_(this) {
     67   DCHECK(sync_context);
     68 }
     69 
     70 SyncEngineInitializer::~SyncEngineInitializer() {
     71   if (!cancel_callback_.is_null())
     72     cancel_callback_.Run();
     73 }
     74 
     75 void SyncEngineInitializer::RunPreflight(scoped_ptr<SyncTaskToken> token) {
     76   util::Log(logging::LOG_VERBOSE, FROM_HERE, "[Initialize] Start.");
     77   DCHECK(sync_context_);
     78   DCHECK(sync_context_->GetDriveService());
     79 
     80   // The metadata seems to have been already initialized. Just return with OK.
     81   if (sync_context_->GetMetadataDatabase()) {
     82     util::Log(logging::LOG_VERBOSE, FROM_HERE,
     83               "[Initialize] Already initialized.");
     84     SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_OK);
     85     return;
     86   }
     87 
     88   SyncStatusCode status = SYNC_STATUS_FAILED;
     89   scoped_ptr<MetadataDatabase> metadata_database =
     90       MetadataDatabase::Create(database_path_, env_override_, &status);
     91 
     92   if (status != SYNC_STATUS_OK) {
     93     util::Log(logging::LOG_VERBOSE, FROM_HERE,
     94               "[Initialize] Failed to initialize MetadataDatabase.");
     95     SyncTaskManager::NotifyTaskDone(token.Pass(), status);
     96     return;
     97   }
     98 
     99   DCHECK(metadata_database);
    100   metadata_database_ = metadata_database.Pass();
    101   if (metadata_database_->HasSyncRoot()) {
    102     util::Log(logging::LOG_VERBOSE, FROM_HERE,
    103               "[Initialize] Found local cache of sync-root.");
    104     SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_OK);
    105     return;
    106   }
    107 
    108   GetAboutResource(token.Pass());
    109 }
    110 
    111 scoped_ptr<MetadataDatabase> SyncEngineInitializer::PassMetadataDatabase() {
    112   return metadata_database_.Pass();
    113 }
    114 
    115 void SyncEngineInitializer::GetAboutResource(
    116     scoped_ptr<SyncTaskToken> token) {
    117   set_used_network(true);
    118   sync_context_->GetDriveService()->GetAboutResource(
    119       base::Bind(&SyncEngineInitializer::DidGetAboutResource,
    120                  weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)));
    121 }
    122 
    123 void SyncEngineInitializer::DidGetAboutResource(
    124     scoped_ptr<SyncTaskToken> token,
    125     google_apis::GDataErrorCode error,
    126     scoped_ptr<google_apis::AboutResource> about_resource) {
    127   cancel_callback_.Reset();
    128 
    129   SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
    130   if (status != SYNC_STATUS_OK) {
    131     util::Log(logging::LOG_VERBOSE, FROM_HERE,
    132               "[Initialize] Failed to get AboutResource.");
    133     SyncTaskManager::NotifyTaskDone(token.Pass(), status);
    134     return;
    135   }
    136 
    137   DCHECK(about_resource);
    138   root_folder_id_ = about_resource->root_folder_id();
    139   largest_change_id_ = about_resource->largest_change_id();
    140 
    141   DCHECK(!root_folder_id_.empty());
    142   FindSyncRoot(token.Pass());
    143 }
    144 
    145 void SyncEngineInitializer::FindSyncRoot(scoped_ptr<SyncTaskToken> token) {
    146   if (find_sync_root_retry_count_++ >= kMaxRetry) {
    147     util::Log(logging::LOG_VERBOSE, FROM_HERE,
    148               "[Initialize] Reached max retry count.");
    149     SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED);
    150     return;
    151   }
    152 
    153   set_used_network(true);
    154   cancel_callback_ = sync_context_->GetDriveService()->SearchByTitle(
    155       kSyncRootFolderTitle,
    156       std::string(),  // parent_folder_id
    157       base::Bind(&SyncEngineInitializer::DidFindSyncRoot,
    158                  weak_ptr_factory_.GetWeakPtr(),
    159                  base::Passed(&token)));
    160 }
    161 
    162 void SyncEngineInitializer::DidFindSyncRoot(
    163     scoped_ptr<SyncTaskToken> token,
    164     google_apis::GDataErrorCode error,
    165     scoped_ptr<google_apis::FileList> file_list) {
    166   cancel_callback_.Reset();
    167 
    168   SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
    169   if (status != SYNC_STATUS_OK) {
    170     util::Log(logging::LOG_VERBOSE, FROM_HERE,
    171               "[Initialize] Failed to find sync root.");
    172     SyncTaskManager::NotifyTaskDone(token.Pass(), status);
    173     return;
    174   }
    175 
    176   if (!file_list) {
    177     NOTREACHED();
    178     util::Log(logging::LOG_VERBOSE, FROM_HERE,
    179               "[Initialize] Got invalid resource list.");
    180     SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED);
    181     return;
    182   }
    183 
    184   ScopedVector<google_apis::FileResource>* items = file_list->mutable_items();
    185   for (ScopedVector<google_apis::FileResource>::iterator itr = items->begin();
    186        itr != items->end(); ++itr) {
    187     google_apis::FileResource* entry = *itr;
    188 
    189     // Ignore deleted folder.
    190     if (entry->labels().is_trashed())
    191       continue;
    192 
    193     // Pick an orphaned folder or a direct child of the root folder and
    194     // ignore others.
    195     DCHECK(!root_folder_id_.empty());
    196     if (!HasNoParents(*entry) && !HasFolderAsParent(*entry, root_folder_id_))
    197       continue;
    198 
    199     if (!sync_root_folder_ || LessOnCreationTime(*entry, *sync_root_folder_)) {
    200       sync_root_folder_.reset(entry);
    201       *itr = NULL;
    202     }
    203   }
    204 
    205   set_used_network(true);
    206   // If there are more results, retrieve them.
    207   if (!file_list->next_link().is_empty()) {
    208     cancel_callback_ = sync_context_->GetDriveService()->GetRemainingFileList(
    209         file_list->next_link(),
    210         base::Bind(&SyncEngineInitializer::DidFindSyncRoot,
    211                    weak_ptr_factory_.GetWeakPtr(),
    212                    base::Passed(&token)));
    213     return;
    214   }
    215 
    216   if (!sync_root_folder_) {
    217     CreateSyncRoot(token.Pass());
    218     return;
    219   }
    220 
    221   if (!HasNoParents(*sync_root_folder_)) {
    222     DetachSyncRoot(token.Pass());
    223     return;
    224   }
    225 
    226   ListAppRootFolders(token.Pass());
    227 }
    228 
    229 void SyncEngineInitializer::CreateSyncRoot(scoped_ptr<SyncTaskToken> token) {
    230   DCHECK(!sync_root_folder_);
    231   set_used_network(true);
    232   cancel_callback_ = sync_context_->GetDriveService()->AddNewDirectory(
    233       root_folder_id_, kSyncRootFolderTitle,
    234       drive::DriveServiceInterface::AddNewDirectoryOptions(),
    235       base::Bind(&SyncEngineInitializer::DidCreateSyncRoot,
    236                  weak_ptr_factory_.GetWeakPtr(),
    237                  base::Passed(&token)));
    238 }
    239 
    240 void SyncEngineInitializer::DidCreateSyncRoot(
    241     scoped_ptr<SyncTaskToken> token,
    242     google_apis::GDataErrorCode error,
    243     scoped_ptr<google_apis::FileResource> entry) {
    244   DCHECK(!sync_root_folder_);
    245   cancel_callback_.Reset();
    246 
    247   SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
    248   if (status != SYNC_STATUS_OK) {
    249     util::Log(logging::LOG_VERBOSE, FROM_HERE,
    250               "[Initialize] Failed to create sync root.");
    251     SyncTaskManager::NotifyTaskDone(token.Pass(), status);
    252     return;
    253   }
    254 
    255   FindSyncRoot(token.Pass());
    256 }
    257 
    258 void SyncEngineInitializer::DetachSyncRoot(scoped_ptr<SyncTaskToken> token) {
    259   DCHECK(sync_root_folder_);
    260   set_used_network(true);
    261   cancel_callback_ =
    262       sync_context_->GetDriveService()->RemoveResourceFromDirectory(
    263           root_folder_id_,
    264           sync_root_folder_->file_id(),
    265           base::Bind(&SyncEngineInitializer::DidDetachSyncRoot,
    266                      weak_ptr_factory_.GetWeakPtr(),
    267                      base::Passed(&token)));
    268 }
    269 
    270 void SyncEngineInitializer::DidDetachSyncRoot(
    271     scoped_ptr<SyncTaskToken> token,
    272     google_apis::GDataErrorCode error) {
    273   cancel_callback_.Reset();
    274 
    275   SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
    276   if (status != SYNC_STATUS_OK) {
    277     util::Log(logging::LOG_VERBOSE, FROM_HERE,
    278               "[Initialize] Failed to detach sync root.");
    279     SyncTaskManager::NotifyTaskDone(token.Pass(), status);
    280     return;
    281   }
    282 
    283   ListAppRootFolders(token.Pass());
    284 }
    285 
    286 void SyncEngineInitializer::ListAppRootFolders(
    287     scoped_ptr<SyncTaskToken> token) {
    288   DCHECK(sync_root_folder_);
    289   set_used_network(true);
    290   cancel_callback_ =
    291       sync_context_->GetDriveService()->GetFileListInDirectory(
    292           sync_root_folder_->file_id(),
    293           base::Bind(&SyncEngineInitializer::DidListAppRootFolders,
    294                      weak_ptr_factory_.GetWeakPtr(),
    295                      base::Passed(&token)));
    296 }
    297 
    298 void SyncEngineInitializer::DidListAppRootFolders(
    299     scoped_ptr<SyncTaskToken> token,
    300     google_apis::GDataErrorCode error,
    301     scoped_ptr<google_apis::FileList> file_list) {
    302   cancel_callback_.Reset();
    303 
    304   SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
    305   if (status != SYNC_STATUS_OK) {
    306     util::Log(logging::LOG_VERBOSE, FROM_HERE,
    307               "[Initialize] Failed to get initial app-root folders.");
    308     SyncTaskManager::NotifyTaskDone(token.Pass(), status);
    309     return;
    310   }
    311 
    312   if (!file_list) {
    313     NOTREACHED();
    314     util::Log(logging::LOG_VERBOSE, FROM_HERE,
    315               "[Initialize] Got invalid initial app-root list.");
    316     SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED);
    317     return;
    318   }
    319 
    320   ScopedVector<google_apis::FileResource>* new_entries =
    321       file_list->mutable_items();
    322   app_root_folders_.insert(app_root_folders_.end(),
    323                            new_entries->begin(), new_entries->end());
    324   new_entries->weak_clear();
    325 
    326   set_used_network(true);
    327   if (!file_list->next_link().is_empty()) {
    328     cancel_callback_ =
    329         sync_context_->GetDriveService()->GetRemainingFileList(
    330             file_list->next_link(),
    331             base::Bind(&SyncEngineInitializer::DidListAppRootFolders,
    332                        weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)));
    333     return;
    334   }
    335 
    336   PopulateDatabase(token.Pass());
    337 }
    338 
    339 void SyncEngineInitializer::PopulateDatabase(
    340     scoped_ptr<SyncTaskToken> token) {
    341   DCHECK(sync_root_folder_);
    342   SyncStatusCode status = metadata_database_->PopulateInitialData(
    343       largest_change_id_, *sync_root_folder_, app_root_folders_);
    344   if (status != SYNC_STATUS_OK) {
    345     util::Log(logging::LOG_VERBOSE, FROM_HERE,
    346               "[Initialize] Failed to populate initial data"
    347               " to MetadataDatabase.");
    348     SyncTaskManager::NotifyTaskDone(token.Pass(), status);
    349     return;
    350   }
    351 
    352   util::Log(logging::LOG_VERBOSE, FROM_HERE,
    353             "[Initialize] Completed successfully.");
    354   SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_OK);
    355 }
    356 
    357 }  // namespace drive_backend
    358 }  // namespace sync_file_system
    359