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