root/chrome/browser/sync_file_system/drive_backend_v1/remote_sync_delegate.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. EmptyStatusCallback
  2. clear_local_changes_
  3. Run
  4. DidPrepareForProcessRemoteChange
  5. ApplyRemoteChange
  6. DidApplyRemoteChange
  7. DeleteMetadata
  8. DownloadFile
  9. DidDownloadFile
  10. HandleConflict
  11. HandleLocalWin
  12. HandleRemoteWin
  13. HandleManualResolutionCase
  14. DidGetEntryForConflictResolution
  15. ResolveToLocal
  16. DidResolveToLocal
  17. ResolveToRemote
  18. DidResolveToRemote
  19. StartOver
  20. CompleteSync
  21. AbortSync
  22. DidFinish
  23. DispatchCallbackAfterDidFinish
  24. GDataErrorCodeToSyncStatusCodeWrapper
  25. metadata_store
  26. api_util
  27. remote_change_handler
  28. remote_change_processor
  29. conflict_resolution_resolver

// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/sync_file_system/drive_backend_v1/remote_sync_delegate.h"

#include "base/file_util.h"
#include "chrome/browser/sync_file_system/drive_backend_v1/remote_sync_operation_resolver.h"
#include "chrome/browser/sync_file_system/logger.h"
#include "chrome/browser/sync_file_system/remote_change_processor.h"
#include "chrome/browser/sync_file_system/syncable_file_system_util.h"

using fileapi::FileSystemURL;

namespace {

void EmptyStatusCallback(sync_file_system::SyncStatusCode status) {}

}  // namespace

namespace sync_file_system {
namespace drive_backend {

RemoteSyncDelegate::RemoteSyncDelegate(
    DriveFileSyncService* sync_service,
    const RemoteChange& remote_change)
    : sync_service_(sync_service),
      remote_change_(remote_change),
      sync_action_(SYNC_ACTION_NONE),
      metadata_updated_(false),
      clear_local_changes_(true) {
}

RemoteSyncDelegate::~RemoteSyncDelegate() {}

void RemoteSyncDelegate::Run(const SyncStatusCallback& callback) {
  util::Log(logging::LOG_VERBOSE, FROM_HERE,
            "ProcessRemoteChange for %s change:%s",
            url().DebugString().c_str(),
            remote_file_change().DebugString().c_str());

  remote_change_processor()->PrepareForProcessRemoteChange(
      url(),
      base::Bind(&RemoteSyncDelegate::DidPrepareForProcessRemoteChange,
                 AsWeakPtr(), callback));
}

void RemoteSyncDelegate::DidPrepareForProcessRemoteChange(
    const SyncStatusCallback& callback,
    SyncStatusCode status,
    const SyncFileMetadata& metadata,
    const FileChangeList& local_changes) {
  if (status != SYNC_STATUS_OK) {
    AbortSync(callback, status);
    return;
  }

  local_metadata_ = metadata;
  status = metadata_store()->ReadEntry(url(), &drive_metadata_);
  DCHECK(status == SYNC_STATUS_OK || status == SYNC_DATABASE_ERROR_NOT_FOUND);

  bool missing_db_entry = (status != SYNC_STATUS_OK);
  if (missing_db_entry) {
    drive_metadata_.set_resource_id(remote_change_.resource_id);
    drive_metadata_.set_md5_checksum(std::string());
    drive_metadata_.set_conflicted(false);
    drive_metadata_.set_to_be_fetched(false);
  }
  bool missing_local_file = (metadata.file_type == SYNC_FILE_TYPE_UNKNOWN);

  if (drive_metadata_.resource_id().empty()) {
    // This (missing_db_entry is false but resource_id is empty) could
    // happen when the remote file gets deleted (this clears resource_id
    // in drive_metadata) but then a file is added with the same name.
    drive_metadata_.set_resource_id(remote_change_.resource_id);
  }

  SyncOperationType operation =
      RemoteSyncOperationResolver::Resolve(remote_file_change(),
                                           local_changes,
                                           local_metadata_.file_type,
                                           drive_metadata_.conflicted());

  util::Log(logging::LOG_VERBOSE, FROM_HERE,
            "ProcessRemoteChange for %s %s%sremote_change: %s ==> %s",
            url().DebugString().c_str(),
            drive_metadata_.conflicted() ? " (conflicted)" : " ",
            missing_local_file ? " (missing local file)" : " ",
            remote_file_change().DebugString().c_str(),
            SyncOperationTypeToString(operation));
  DCHECK_NE(SYNC_OPERATION_FAIL, operation);

  switch (operation) {
    case SYNC_OPERATION_ADD_FILE:
    case SYNC_OPERATION_ADD_DIRECTORY:
      sync_action_ = SYNC_ACTION_ADDED;
      break;
    case SYNC_OPERATION_UPDATE_FILE:
      sync_action_ = SYNC_ACTION_UPDATED;
      break;
    case SYNC_OPERATION_DELETE:
      sync_action_ = SYNC_ACTION_DELETED;
      break;
    case SYNC_OPERATION_NONE:
    case SYNC_OPERATION_DELETE_METADATA:
      sync_action_ = SYNC_ACTION_NONE;
      break;
    default:
      break;
  }

  switch (operation) {
    case SYNC_OPERATION_ADD_FILE:
    case SYNC_OPERATION_UPDATE_FILE:
      DownloadFile(callback);
      return;
    case SYNC_OPERATION_ADD_DIRECTORY:
    case SYNC_OPERATION_DELETE:
      ApplyRemoteChange(callback);
      return;
    case SYNC_OPERATION_NONE:
      CompleteSync(callback, SYNC_STATUS_OK);
      return;
    case SYNC_OPERATION_CONFLICT:
      HandleConflict(callback, remote_file_change().file_type());
      return;
    case SYNC_OPERATION_RESOLVE_TO_LOCAL:
      ResolveToLocal(callback);
      return;
    case SYNC_OPERATION_RESOLVE_TO_REMOTE:
      ResolveToRemote(callback);
      return;
    case SYNC_OPERATION_DELETE_METADATA:
      if (missing_db_entry)
        CompleteSync(callback, SYNC_STATUS_OK);
      else
        DeleteMetadata(callback);
      return;
    case SYNC_OPERATION_FAIL:
      AbortSync(callback, SYNC_STATUS_FAILED);
      return;
  }
  NOTREACHED();
  AbortSync(callback, SYNC_STATUS_FAILED);
}

void RemoteSyncDelegate::ApplyRemoteChange(const SyncStatusCallback& callback) {
  remote_change_processor()->ApplyRemoteChange(
      remote_file_change(), temporary_file_.path(), url(),
      base::Bind(&RemoteSyncDelegate::DidApplyRemoteChange, AsWeakPtr(),
                 callback));
}

void RemoteSyncDelegate::DidApplyRemoteChange(
    const SyncStatusCallback& callback,
    SyncStatusCode status) {
  if (status != SYNC_STATUS_OK) {
    AbortSync(callback, status);
    return;
  }

  if (remote_file_change().IsDelete()) {
    DeleteMetadata(callback);
    return;
  }

  drive_metadata_.set_resource_id(remote_change_.resource_id);
  drive_metadata_.set_conflicted(false);
  if (remote_file_change().IsFile()) {
    drive_metadata_.set_type(DriveMetadata::RESOURCE_TYPE_FILE);
  } else {
    DCHECK(IsSyncFSDirectoryOperationEnabled());
    drive_metadata_.set_type(DriveMetadata::RESOURCE_TYPE_FOLDER);
  }

  metadata_store()->UpdateEntry(
      url(), drive_metadata_,
      base::Bind(&RemoteSyncDelegate::CompleteSync,
                 AsWeakPtr(), callback));
}

void RemoteSyncDelegate::DeleteMetadata(const SyncStatusCallback& callback) {
  metadata_store()->DeleteEntry(
      url(),
      base::Bind(&RemoteSyncDelegate::CompleteSync, AsWeakPtr(), callback));
}

void RemoteSyncDelegate::DownloadFile(const SyncStatusCallback& callback) {
  // We should not use the md5 in metadata for FETCH type to avoid the download
  // finishes due to NOT_MODIFIED.
  std::string md5_checksum;
  if (!drive_metadata_.to_be_fetched())
    md5_checksum = drive_metadata_.md5_checksum();

  api_util()->DownloadFile(
      remote_change_.resource_id,
      md5_checksum,
      base::Bind(&RemoteSyncDelegate::DidDownloadFile,
                 AsWeakPtr(),
                 callback));
}

void RemoteSyncDelegate::DidDownloadFile(
    const SyncStatusCallback& callback,
    google_apis::GDataErrorCode error,
    const std::string& md5_checksum,
    int64 file_size,
    const base::Time& updated_time,
    webkit_blob::ScopedFile downloaded_file) {
  if (error == google_apis::HTTP_NOT_MODIFIED) {
    sync_action_ = SYNC_ACTION_NONE;
    DidApplyRemoteChange(callback, SYNC_STATUS_OK);
    return;
  }

  // File may be deleted. If this was for new file it's ok, if this was
  // for existing file we'll process the delete change later.
  if (error == google_apis::HTTP_NOT_FOUND) {
    sync_action_ = SYNC_ACTION_NONE;
    DidApplyRemoteChange(callback, SYNC_STATUS_OK);
    return;
  }

  SyncStatusCode status = GDataErrorCodeToSyncStatusCodeWrapper(error);
  if (status != SYNC_STATUS_OK) {
    AbortSync(callback, status);
    return;
  }

  temporary_file_ = downloaded_file.Pass();
  drive_metadata_.set_md5_checksum(md5_checksum);
  remote_change_processor()->ApplyRemoteChange(
      remote_file_change(), temporary_file_.path(), url(),
      base::Bind(&RemoteSyncDelegate::DidApplyRemoteChange,
                 AsWeakPtr(), callback));
}

void RemoteSyncDelegate::HandleConflict(
    const SyncStatusCallback& callback,
    SyncFileType remote_file_type) {
  ConflictResolution resolution = conflict_resolution_resolver()->Resolve(
      local_metadata_.file_type,
      local_metadata_.last_modified,
      remote_file_type,
      remote_change_.updated_time);

  switch (resolution) {
    case CONFLICT_RESOLUTION_LOCAL_WIN:
      HandleLocalWin(callback);
      return;
    case CONFLICT_RESOLUTION_REMOTE_WIN:
      HandleRemoteWin(callback, remote_file_type);
      return;
    case CONFLICT_RESOLUTION_MARK_CONFLICT:
      HandleManualResolutionCase(callback);
      return;
    case CONFLICT_RESOLUTION_UNKNOWN:
      // Get remote file time and call this method again.
      api_util()->GetResourceEntry(
          remote_change_.resource_id,
          base::Bind(
              &RemoteSyncDelegate::DidGetEntryForConflictResolution,
              AsWeakPtr(), callback));
      return;
  }
  NOTREACHED();
  AbortSync(callback, SYNC_STATUS_FAILED);
}

void RemoteSyncDelegate::HandleLocalWin(
    const SyncStatusCallback& callback) {
  util::Log(logging::LOG_VERBOSE, FROM_HERE,
            "Resolving conflict for remote sync: %s: LOCAL WIN",
            url().DebugString().c_str());
  ResolveToLocal(callback);
}

void RemoteSyncDelegate::HandleRemoteWin(
    const SyncStatusCallback& callback,
    SyncFileType remote_file_type) {
  // Make sure we reset the conflict flag and start over the remote sync
  // with empty local changes.
  util::Log(logging::LOG_VERBOSE, FROM_HERE,
            "Resolving conflict for remote sync: %s: REMOTE WIN",
            url().DebugString().c_str());

  drive_metadata_.set_conflicted(false);
  drive_metadata_.set_to_be_fetched(false);
  drive_metadata_.set_type(
      DriveFileSyncService::SyncFileTypeToDriveMetadataResourceType(
          remote_file_type));
  metadata_store()->UpdateEntry(
      url(), drive_metadata_,
      base::Bind(&RemoteSyncDelegate::StartOver, AsWeakPtr(), callback));
}

void RemoteSyncDelegate::HandleManualResolutionCase(
    const SyncStatusCallback& callback) {
  sync_action_ = SYNC_ACTION_NONE;
  sync_service_->MarkConflict(
      url(), &drive_metadata_,
      base::Bind(&RemoteSyncDelegate::CompleteSync, AsWeakPtr(), callback));
}

void RemoteSyncDelegate::DidGetEntryForConflictResolution(
    const SyncStatusCallback& callback,
    google_apis::GDataErrorCode error,
    scoped_ptr<google_apis::ResourceEntry> entry) {
  SyncStatusCode status = GDataErrorCodeToSyncStatusCodeWrapper(error);
  if (status != SYNC_STATUS_OK || entry->updated_time().is_null()) {
    HandleLocalWin(callback);
    return;
  }

  SyncFileType file_type = SYNC_FILE_TYPE_UNKNOWN;
  if (entry->is_file())
    file_type = SYNC_FILE_TYPE_FILE;
  if (entry->is_folder())
    file_type = SYNC_FILE_TYPE_DIRECTORY;

  remote_change_.updated_time = entry->updated_time();
  HandleConflict(callback, file_type);
}

void RemoteSyncDelegate::ResolveToLocal(
    const SyncStatusCallback& callback) {
  sync_action_ = SYNC_ACTION_NONE;
  clear_local_changes_ = false;

  // Re-add a fake local change to resolve it later in next LocalSync.
  remote_change_processor()->RecordFakeLocalChange(
      url(),
      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
                 local_metadata_.file_type),
      base::Bind(&RemoteSyncDelegate::DidResolveToLocal,
                 AsWeakPtr(), callback));
}

void RemoteSyncDelegate::DidResolveToLocal(
    const SyncStatusCallback& callback,
    SyncStatusCode status) {
  if (status != SYNC_STATUS_OK) {
    DCHECK_NE(SYNC_STATUS_HAS_CONFLICT, status);
    AbortSync(callback, status);
    return;
  }

  if (remote_file_change().IsDelete()) {
    metadata_store()->DeleteEntry(
        url(),
        base::Bind(&RemoteSyncDelegate::CompleteSync,
                   AsWeakPtr(), callback));
  } else {
    DCHECK(!remote_change_.resource_id.empty());
    drive_metadata_.set_resource_id(remote_change_.resource_id);
    drive_metadata_.set_conflicted(false);
    drive_metadata_.set_to_be_fetched(false);
    drive_metadata_.set_md5_checksum(std::string());
    metadata_store()->UpdateEntry(
        url(), drive_metadata_,
        base::Bind(&RemoteSyncDelegate::CompleteSync,
                   AsWeakPtr(), callback));
  }
}

void RemoteSyncDelegate::ResolveToRemote(
    const SyncStatusCallback& callback) {
  drive_metadata_.set_conflicted(false);
  drive_metadata_.set_to_be_fetched(true);
  metadata_store()->UpdateEntry(
      url(), drive_metadata_,
      base::Bind(&RemoteSyncDelegate::DidResolveToRemote,
                 AsWeakPtr(), callback));
}

void RemoteSyncDelegate::DidResolveToRemote(
    const SyncStatusCallback& callback,
    SyncStatusCode status) {
  if (status != SYNC_STATUS_OK) {
    AbortSync(callback, status);
    return;
  }

  sync_action_ = SYNC_ACTION_ADDED;
  if (remote_file_change().file_type() == SYNC_FILE_TYPE_FILE) {
    DownloadFile(callback);
    return;
  }

  // ApplyRemoteChange should replace any existing local file or
  // directory with remote_change_.
  ApplyRemoteChange(callback);
}

void RemoteSyncDelegate::StartOver(
    const SyncStatusCallback& callback,
    SyncStatusCode status) {
  DidPrepareForProcessRemoteChange(
      callback, status, local_metadata_, FileChangeList());
}

void RemoteSyncDelegate::CompleteSync(
    const SyncStatusCallback& callback,
    SyncStatusCode status) {
  if (status != SYNC_STATUS_OK) {
    AbortSync(callback, status);
    return;
  }

  sync_service_->RemoveRemoteChange(url());

  if (drive_metadata_.to_be_fetched()) {
    // Clear |to_be_fetched| flag since we completed fetching the remote change
    // and applying it to the local file.
    DCHECK(!drive_metadata_.conflicted());
    drive_metadata_.set_conflicted(false);
    drive_metadata_.set_to_be_fetched(false);
    metadata_store()->UpdateEntry(url(), drive_metadata_,
                                  base::Bind(&EmptyStatusCallback));
  }

  if (remote_change_.changestamp > 0) {
    DCHECK(metadata_store()->IsIncrementalSyncOrigin(url().origin()));
    metadata_store()->SetLargestChangeStamp(
        remote_change_.changestamp,
        base::Bind(&RemoteSyncDelegate::DidFinish, AsWeakPtr(), callback));
    return;
  }

  if (drive_metadata_.conflicted())
    status = SYNC_STATUS_HAS_CONFLICT;

  DidFinish(callback, status);
}

void RemoteSyncDelegate::AbortSync(
    const SyncStatusCallback& callback,
    SyncStatusCode status) {
  clear_local_changes_ = false;
  DidFinish(callback, status);
}

void RemoteSyncDelegate::DidFinish(
    const SyncStatusCallback& callback,
    SyncStatusCode status) {
  remote_change_processor()->FinalizeRemoteSync(
      url(), clear_local_changes_,
      base::Bind(&RemoteSyncDelegate::DispatchCallbackAfterDidFinish,
                 AsWeakPtr(), callback, status));
}

void RemoteSyncDelegate::DispatchCallbackAfterDidFinish(
    const SyncStatusCallback& callback,
    SyncStatusCode status) {
  if (status == SYNC_STATUS_OK && sync_action_ != SYNC_ACTION_NONE) {
    sync_service_->NotifyObserversFileStatusChanged(
        url(),
        SYNC_FILE_STATUS_SYNCED,
        sync_action_,
        SYNC_DIRECTION_REMOTE_TO_LOCAL);
  }

  callback.Run(status);
}

SyncStatusCode RemoteSyncDelegate::GDataErrorCodeToSyncStatusCodeWrapper(
    google_apis::GDataErrorCode error) {
  return sync_service_->GDataErrorCodeToSyncStatusCodeWrapper(error);
}

DriveMetadataStore* RemoteSyncDelegate::metadata_store() {
  return sync_service_->metadata_store_.get();
}

APIUtilInterface* RemoteSyncDelegate::api_util() {
  return sync_service_->api_util_.get();
}

RemoteChangeHandler* RemoteSyncDelegate::remote_change_handler() {
  return &sync_service_->remote_change_handler_;
}

RemoteChangeProcessor* RemoteSyncDelegate::remote_change_processor() {
  return sync_service_->remote_change_processor_;
}

ConflictResolutionResolver* RemoteSyncDelegate::conflict_resolution_resolver() {
  return &sync_service_->conflict_resolution_resolver_;
}

}  // namespace drive_backend
}  // namespace sync_file_system

/* [<][>][^][v][top][bottom][index][help] */