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/remote_sync_delegate.h"
      6 
      7 #include "base/file_util.h"
      8 #include "chrome/browser/sync_file_system/drive_backend_v1/remote_sync_operation_resolver.h"
      9 #include "chrome/browser/sync_file_system/logger.h"
     10 #include "chrome/browser/sync_file_system/remote_change_processor.h"
     11 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
     12 
     13 using fileapi::FileSystemURL;
     14 
     15 namespace {
     16 
     17 void EmptyStatusCallback(sync_file_system::SyncStatusCode status) {}
     18 
     19 }  // namespace
     20 
     21 namespace sync_file_system {
     22 namespace drive_backend {
     23 
     24 RemoteSyncDelegate::RemoteSyncDelegate(
     25     DriveFileSyncService* sync_service,
     26     const RemoteChange& remote_change)
     27     : sync_service_(sync_service),
     28       remote_change_(remote_change),
     29       sync_action_(SYNC_ACTION_NONE),
     30       metadata_updated_(false),
     31       clear_local_changes_(true) {
     32 }
     33 
     34 RemoteSyncDelegate::~RemoteSyncDelegate() {}
     35 
     36 void RemoteSyncDelegate::Run(const SyncStatusCallback& callback) {
     37   util::Log(logging::LOG_VERBOSE, FROM_HERE,
     38             "ProcessRemoteChange for %s change:%s",
     39             url().DebugString().c_str(),
     40             remote_file_change().DebugString().c_str());
     41 
     42   remote_change_processor()->PrepareForProcessRemoteChange(
     43       url(),
     44       base::Bind(&RemoteSyncDelegate::DidPrepareForProcessRemoteChange,
     45                  AsWeakPtr(), callback));
     46 }
     47 
     48 void RemoteSyncDelegate::DidPrepareForProcessRemoteChange(
     49     const SyncStatusCallback& callback,
     50     SyncStatusCode status,
     51     const SyncFileMetadata& metadata,
     52     const FileChangeList& local_changes) {
     53   if (status != SYNC_STATUS_OK) {
     54     AbortSync(callback, status);
     55     return;
     56   }
     57 
     58   local_metadata_ = metadata;
     59   status = metadata_store()->ReadEntry(url(), &drive_metadata_);
     60   DCHECK(status == SYNC_STATUS_OK || status == SYNC_DATABASE_ERROR_NOT_FOUND);
     61 
     62   bool missing_db_entry = (status != SYNC_STATUS_OK);
     63   if (missing_db_entry) {
     64     drive_metadata_.set_resource_id(remote_change_.resource_id);
     65     drive_metadata_.set_md5_checksum(std::string());
     66     drive_metadata_.set_conflicted(false);
     67     drive_metadata_.set_to_be_fetched(false);
     68   }
     69   bool missing_local_file = (metadata.file_type == SYNC_FILE_TYPE_UNKNOWN);
     70 
     71   if (drive_metadata_.resource_id().empty()) {
     72     // This (missing_db_entry is false but resource_id is empty) could
     73     // happen when the remote file gets deleted (this clears resource_id
     74     // in drive_metadata) but then a file is added with the same name.
     75     drive_metadata_.set_resource_id(remote_change_.resource_id);
     76   }
     77 
     78   SyncOperationType operation =
     79       RemoteSyncOperationResolver::Resolve(remote_file_change(),
     80                                            local_changes,
     81                                            local_metadata_.file_type,
     82                                            drive_metadata_.conflicted());
     83 
     84   util::Log(logging::LOG_VERBOSE, FROM_HERE,
     85             "ProcessRemoteChange for %s %s%sremote_change: %s ==> %s",
     86             url().DebugString().c_str(),
     87             drive_metadata_.conflicted() ? " (conflicted)" : " ",
     88             missing_local_file ? " (missing local file)" : " ",
     89             remote_file_change().DebugString().c_str(),
     90             SyncOperationTypeToString(operation));
     91   DCHECK_NE(SYNC_OPERATION_FAIL, operation);
     92 
     93   switch (operation) {
     94     case SYNC_OPERATION_ADD_FILE:
     95     case SYNC_OPERATION_ADD_DIRECTORY:
     96       sync_action_ = SYNC_ACTION_ADDED;
     97       break;
     98     case SYNC_OPERATION_UPDATE_FILE:
     99       sync_action_ = SYNC_ACTION_UPDATED;
    100       break;
    101     case SYNC_OPERATION_DELETE:
    102       sync_action_ = SYNC_ACTION_DELETED;
    103       break;
    104     case SYNC_OPERATION_NONE:
    105     case SYNC_OPERATION_DELETE_METADATA:
    106       sync_action_ = SYNC_ACTION_NONE;
    107       break;
    108     default:
    109       break;
    110   }
    111 
    112   switch (operation) {
    113     case SYNC_OPERATION_ADD_FILE:
    114     case SYNC_OPERATION_UPDATE_FILE:
    115       DownloadFile(callback);
    116       return;
    117     case SYNC_OPERATION_ADD_DIRECTORY:
    118     case SYNC_OPERATION_DELETE:
    119       ApplyRemoteChange(callback);
    120       return;
    121     case SYNC_OPERATION_NONE:
    122       CompleteSync(callback, SYNC_STATUS_OK);
    123       return;
    124     case SYNC_OPERATION_CONFLICT:
    125       HandleConflict(callback, remote_file_change().file_type());
    126       return;
    127     case SYNC_OPERATION_RESOLVE_TO_LOCAL:
    128       ResolveToLocal(callback);
    129       return;
    130     case SYNC_OPERATION_RESOLVE_TO_REMOTE:
    131       ResolveToRemote(callback);
    132       return;
    133     case SYNC_OPERATION_DELETE_METADATA:
    134       if (missing_db_entry)
    135         CompleteSync(callback, SYNC_STATUS_OK);
    136       else
    137         DeleteMetadata(callback);
    138       return;
    139     case SYNC_OPERATION_FAIL:
    140       AbortSync(callback, SYNC_STATUS_FAILED);
    141       return;
    142   }
    143   NOTREACHED();
    144   AbortSync(callback, SYNC_STATUS_FAILED);
    145 }
    146 
    147 void RemoteSyncDelegate::ApplyRemoteChange(const SyncStatusCallback& callback) {
    148   remote_change_processor()->ApplyRemoteChange(
    149       remote_file_change(), temporary_file_.path(), url(),
    150       base::Bind(&RemoteSyncDelegate::DidApplyRemoteChange, AsWeakPtr(),
    151                  callback));
    152 }
    153 
    154 void RemoteSyncDelegate::DidApplyRemoteChange(
    155     const SyncStatusCallback& callback,
    156     SyncStatusCode status) {
    157   if (status != SYNC_STATUS_OK) {
    158     AbortSync(callback, status);
    159     return;
    160   }
    161 
    162   if (remote_file_change().IsDelete()) {
    163     DeleteMetadata(callback);
    164     return;
    165   }
    166 
    167   drive_metadata_.set_resource_id(remote_change_.resource_id);
    168   drive_metadata_.set_conflicted(false);
    169   if (remote_file_change().IsFile()) {
    170     drive_metadata_.set_type(DriveMetadata::RESOURCE_TYPE_FILE);
    171   } else {
    172     DCHECK(IsSyncFSDirectoryOperationEnabled());
    173     drive_metadata_.set_type(DriveMetadata::RESOURCE_TYPE_FOLDER);
    174   }
    175 
    176   metadata_store()->UpdateEntry(
    177       url(), drive_metadata_,
    178       base::Bind(&RemoteSyncDelegate::CompleteSync,
    179                  AsWeakPtr(), callback));
    180 }
    181 
    182 void RemoteSyncDelegate::DeleteMetadata(const SyncStatusCallback& callback) {
    183   metadata_store()->DeleteEntry(
    184       url(),
    185       base::Bind(&RemoteSyncDelegate::CompleteSync, AsWeakPtr(), callback));
    186 }
    187 
    188 void RemoteSyncDelegate::DownloadFile(const SyncStatusCallback& callback) {
    189   // We should not use the md5 in metadata for FETCH type to avoid the download
    190   // finishes due to NOT_MODIFIED.
    191   std::string md5_checksum;
    192   if (!drive_metadata_.to_be_fetched())
    193     md5_checksum = drive_metadata_.md5_checksum();
    194 
    195   api_util()->DownloadFile(
    196       remote_change_.resource_id,
    197       md5_checksum,
    198       base::Bind(&RemoteSyncDelegate::DidDownloadFile,
    199                  AsWeakPtr(),
    200                  callback));
    201 }
    202 
    203 void RemoteSyncDelegate::DidDownloadFile(
    204     const SyncStatusCallback& callback,
    205     google_apis::GDataErrorCode error,
    206     const std::string& md5_checksum,
    207     int64 file_size,
    208     const base::Time& updated_time,
    209     webkit_blob::ScopedFile downloaded_file) {
    210   if (error == google_apis::HTTP_NOT_MODIFIED) {
    211     sync_action_ = SYNC_ACTION_NONE;
    212     DidApplyRemoteChange(callback, SYNC_STATUS_OK);
    213     return;
    214   }
    215 
    216   // File may be deleted. If this was for new file it's ok, if this was
    217   // for existing file we'll process the delete change later.
    218   if (error == google_apis::HTTP_NOT_FOUND) {
    219     sync_action_ = SYNC_ACTION_NONE;
    220     DidApplyRemoteChange(callback, SYNC_STATUS_OK);
    221     return;
    222   }
    223 
    224   SyncStatusCode status = GDataErrorCodeToSyncStatusCodeWrapper(error);
    225   if (status != SYNC_STATUS_OK) {
    226     AbortSync(callback, status);
    227     return;
    228   }
    229 
    230   temporary_file_ = downloaded_file.Pass();
    231   drive_metadata_.set_md5_checksum(md5_checksum);
    232   remote_change_processor()->ApplyRemoteChange(
    233       remote_file_change(), temporary_file_.path(), url(),
    234       base::Bind(&RemoteSyncDelegate::DidApplyRemoteChange,
    235                  AsWeakPtr(), callback));
    236 }
    237 
    238 void RemoteSyncDelegate::HandleConflict(
    239     const SyncStatusCallback& callback,
    240     SyncFileType remote_file_type) {
    241   ConflictResolution resolution = conflict_resolution_resolver()->Resolve(
    242       local_metadata_.file_type,
    243       local_metadata_.last_modified,
    244       remote_file_type,
    245       remote_change_.updated_time);
    246 
    247   switch (resolution) {
    248     case CONFLICT_RESOLUTION_LOCAL_WIN:
    249       HandleLocalWin(callback);
    250       return;
    251     case CONFLICT_RESOLUTION_REMOTE_WIN:
    252       HandleRemoteWin(callback, remote_file_type);
    253       return;
    254     case CONFLICT_RESOLUTION_MARK_CONFLICT:
    255       HandleManualResolutionCase(callback);
    256       return;
    257     case CONFLICT_RESOLUTION_UNKNOWN:
    258       // Get remote file time and call this method again.
    259       api_util()->GetResourceEntry(
    260           remote_change_.resource_id,
    261           base::Bind(
    262               &RemoteSyncDelegate::DidGetEntryForConflictResolution,
    263               AsWeakPtr(), callback));
    264       return;
    265   }
    266   NOTREACHED();
    267   AbortSync(callback, SYNC_STATUS_FAILED);
    268 }
    269 
    270 void RemoteSyncDelegate::HandleLocalWin(
    271     const SyncStatusCallback& callback) {
    272   util::Log(logging::LOG_VERBOSE, FROM_HERE,
    273             "Resolving conflict for remote sync: %s: LOCAL WIN",
    274             url().DebugString().c_str());
    275   ResolveToLocal(callback);
    276 }
    277 
    278 void RemoteSyncDelegate::HandleRemoteWin(
    279     const SyncStatusCallback& callback,
    280     SyncFileType remote_file_type) {
    281   // Make sure we reset the conflict flag and start over the remote sync
    282   // with empty local changes.
    283   util::Log(logging::LOG_VERBOSE, FROM_HERE,
    284             "Resolving conflict for remote sync: %s: REMOTE WIN",
    285             url().DebugString().c_str());
    286 
    287   drive_metadata_.set_conflicted(false);
    288   drive_metadata_.set_to_be_fetched(false);
    289   drive_metadata_.set_type(
    290       DriveFileSyncService::SyncFileTypeToDriveMetadataResourceType(
    291           remote_file_type));
    292   metadata_store()->UpdateEntry(
    293       url(), drive_metadata_,
    294       base::Bind(&RemoteSyncDelegate::StartOver, AsWeakPtr(), callback));
    295 }
    296 
    297 void RemoteSyncDelegate::HandleManualResolutionCase(
    298     const SyncStatusCallback& callback) {
    299   sync_action_ = SYNC_ACTION_NONE;
    300   sync_service_->MarkConflict(
    301       url(), &drive_metadata_,
    302       base::Bind(&RemoteSyncDelegate::CompleteSync, AsWeakPtr(), callback));
    303 }
    304 
    305 void RemoteSyncDelegate::DidGetEntryForConflictResolution(
    306     const SyncStatusCallback& callback,
    307     google_apis::GDataErrorCode error,
    308     scoped_ptr<google_apis::ResourceEntry> entry) {
    309   SyncStatusCode status = GDataErrorCodeToSyncStatusCodeWrapper(error);
    310   if (status != SYNC_STATUS_OK || entry->updated_time().is_null()) {
    311     HandleLocalWin(callback);
    312     return;
    313   }
    314 
    315   SyncFileType file_type = SYNC_FILE_TYPE_UNKNOWN;
    316   if (entry->is_file())
    317     file_type = SYNC_FILE_TYPE_FILE;
    318   if (entry->is_folder())
    319     file_type = SYNC_FILE_TYPE_DIRECTORY;
    320 
    321   remote_change_.updated_time = entry->updated_time();
    322   HandleConflict(callback, file_type);
    323 }
    324 
    325 void RemoteSyncDelegate::ResolveToLocal(
    326     const SyncStatusCallback& callback) {
    327   sync_action_ = SYNC_ACTION_NONE;
    328   clear_local_changes_ = false;
    329 
    330   // Re-add a fake local change to resolve it later in next LocalSync.
    331   remote_change_processor()->RecordFakeLocalChange(
    332       url(),
    333       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    334                  local_metadata_.file_type),
    335       base::Bind(&RemoteSyncDelegate::DidResolveToLocal,
    336                  AsWeakPtr(), callback));
    337 }
    338 
    339 void RemoteSyncDelegate::DidResolveToLocal(
    340     const SyncStatusCallback& callback,
    341     SyncStatusCode status) {
    342   if (status != SYNC_STATUS_OK) {
    343     DCHECK_NE(SYNC_STATUS_HAS_CONFLICT, status);
    344     AbortSync(callback, status);
    345     return;
    346   }
    347 
    348   if (remote_file_change().IsDelete()) {
    349     metadata_store()->DeleteEntry(
    350         url(),
    351         base::Bind(&RemoteSyncDelegate::CompleteSync,
    352                    AsWeakPtr(), callback));
    353   } else {
    354     DCHECK(!remote_change_.resource_id.empty());
    355     drive_metadata_.set_resource_id(remote_change_.resource_id);
    356     drive_metadata_.set_conflicted(false);
    357     drive_metadata_.set_to_be_fetched(false);
    358     drive_metadata_.set_md5_checksum(std::string());
    359     metadata_store()->UpdateEntry(
    360         url(), drive_metadata_,
    361         base::Bind(&RemoteSyncDelegate::CompleteSync,
    362                    AsWeakPtr(), callback));
    363   }
    364 }
    365 
    366 void RemoteSyncDelegate::ResolveToRemote(
    367     const SyncStatusCallback& callback) {
    368   drive_metadata_.set_conflicted(false);
    369   drive_metadata_.set_to_be_fetched(true);
    370   metadata_store()->UpdateEntry(
    371       url(), drive_metadata_,
    372       base::Bind(&RemoteSyncDelegate::DidResolveToRemote,
    373                  AsWeakPtr(), callback));
    374 }
    375 
    376 void RemoteSyncDelegate::DidResolveToRemote(
    377     const SyncStatusCallback& callback,
    378     SyncStatusCode status) {
    379   if (status != SYNC_STATUS_OK) {
    380     AbortSync(callback, status);
    381     return;
    382   }
    383 
    384   sync_action_ = SYNC_ACTION_ADDED;
    385   if (remote_file_change().file_type() == SYNC_FILE_TYPE_FILE) {
    386     DownloadFile(callback);
    387     return;
    388   }
    389 
    390   // ApplyRemoteChange should replace any existing local file or
    391   // directory with remote_change_.
    392   ApplyRemoteChange(callback);
    393 }
    394 
    395 void RemoteSyncDelegate::StartOver(
    396     const SyncStatusCallback& callback,
    397     SyncStatusCode status) {
    398   DidPrepareForProcessRemoteChange(
    399       callback, status, local_metadata_, FileChangeList());
    400 }
    401 
    402 void RemoteSyncDelegate::CompleteSync(
    403     const SyncStatusCallback& callback,
    404     SyncStatusCode status) {
    405   if (status != SYNC_STATUS_OK) {
    406     AbortSync(callback, status);
    407     return;
    408   }
    409 
    410   sync_service_->RemoveRemoteChange(url());
    411 
    412   if (drive_metadata_.to_be_fetched()) {
    413     // Clear |to_be_fetched| flag since we completed fetching the remote change
    414     // and applying it to the local file.
    415     DCHECK(!drive_metadata_.conflicted());
    416     drive_metadata_.set_conflicted(false);
    417     drive_metadata_.set_to_be_fetched(false);
    418     metadata_store()->UpdateEntry(url(), drive_metadata_,
    419                                   base::Bind(&EmptyStatusCallback));
    420   }
    421 
    422   if (remote_change_.changestamp > 0) {
    423     DCHECK(metadata_store()->IsIncrementalSyncOrigin(url().origin()));
    424     metadata_store()->SetLargestChangeStamp(
    425         remote_change_.changestamp,
    426         base::Bind(&RemoteSyncDelegate::DidFinish, AsWeakPtr(), callback));
    427     return;
    428   }
    429 
    430   if (drive_metadata_.conflicted())
    431     status = SYNC_STATUS_HAS_CONFLICT;
    432 
    433   DidFinish(callback, status);
    434 }
    435 
    436 void RemoteSyncDelegate::AbortSync(
    437     const SyncStatusCallback& callback,
    438     SyncStatusCode status) {
    439   clear_local_changes_ = false;
    440   DidFinish(callback, status);
    441 }
    442 
    443 void RemoteSyncDelegate::DidFinish(
    444     const SyncStatusCallback& callback,
    445     SyncStatusCode status) {
    446   remote_change_processor()->FinalizeRemoteSync(
    447       url(), clear_local_changes_,
    448       base::Bind(&RemoteSyncDelegate::DispatchCallbackAfterDidFinish,
    449                  AsWeakPtr(), callback, status));
    450 }
    451 
    452 void RemoteSyncDelegate::DispatchCallbackAfterDidFinish(
    453     const SyncStatusCallback& callback,
    454     SyncStatusCode status) {
    455   if (status == SYNC_STATUS_OK && sync_action_ != SYNC_ACTION_NONE) {
    456     sync_service_->NotifyObserversFileStatusChanged(
    457         url(),
    458         SYNC_FILE_STATUS_SYNCED,
    459         sync_action_,
    460         SYNC_DIRECTION_REMOTE_TO_LOCAL);
    461   }
    462 
    463   callback.Run(status);
    464 }
    465 
    466 SyncStatusCode RemoteSyncDelegate::GDataErrorCodeToSyncStatusCodeWrapper(
    467     google_apis::GDataErrorCode error) {
    468   return sync_service_->GDataErrorCodeToSyncStatusCodeWrapper(error);
    469 }
    470 
    471 DriveMetadataStore* RemoteSyncDelegate::metadata_store() {
    472   return sync_service_->metadata_store_.get();
    473 }
    474 
    475 APIUtilInterface* RemoteSyncDelegate::api_util() {
    476   return sync_service_->api_util_.get();
    477 }
    478 
    479 RemoteChangeHandler* RemoteSyncDelegate::remote_change_handler() {
    480   return &sync_service_->remote_change_handler_;
    481 }
    482 
    483 RemoteChangeProcessor* RemoteSyncDelegate::remote_change_processor() {
    484   return sync_service_->remote_change_processor_;
    485 }
    486 
    487 ConflictResolutionResolver* RemoteSyncDelegate::conflict_resolution_resolver() {
    488   return &sync_service_->conflict_resolution_resolver_;
    489 }
    490 
    491 }  // namespace drive_backend
    492 }  // namespace sync_file_system
    493