remote_to_local_syncer.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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_to_local_syncer.h"
6
7#include "base/bind.h"
8#include "base/callback.h"
9#include "base/format_macros.h"
10#include "base/location.h"
11#include "base/logging.h"
12#include "base/message_loop/message_loop_proxy.h"
13#include "base/task_runner_util.h"
14#include "chrome/browser/drive/drive_api_util.h"
15#include "chrome/browser/drive/drive_service_interface.h"
16#include "chrome/browser/sync_file_system/drive_backend/drive_backend_util.h"
17#include "chrome/browser/sync_file_system/drive_backend/metadata_database.h"
18#include "chrome/browser/sync_file_system/drive_backend/sync_engine_context.h"
19#include "chrome/browser/sync_file_system/logger.h"
20#include "chrome/browser/sync_file_system/syncable_file_system_util.h"
21#include "extensions/common/extension.h"
22#include "google_apis/drive/drive_api_parser.h"
23#include "google_apis/drive/gdata_wapi_parser.h"
24#include "webkit/common/fileapi/file_system_util.h"
25
26namespace sync_file_system {
27namespace drive_backend {
28
29namespace {
30
31bool BuildFileSystemURL(
32    MetadataDatabase* metadata_database,
33    const FileTracker& tracker,
34    fileapi::FileSystemURL* url) {
35  base::FilePath path;
36  if (!metadata_database->BuildPathForTracker(
37          tracker.tracker_id(), &path))
38    return false;
39
40  GURL origin =
41      extensions::Extension::GetBaseURLFromExtensionId(tracker.app_id());
42  *url = sync_file_system::CreateSyncableFileSystemURL(origin, path);
43
44  return true;
45}
46
47bool HasFolderAsParent(const FileDetails& details,
48                       const std::string& folder_id) {
49  for (int i = 0; i < details.parent_folder_ids_size(); ++i) {
50    if (details.parent_folder_ids(i) == folder_id)
51      return true;
52  }
53  return false;
54}
55
56bool HasDisabledAppRoot(MetadataDatabase* database,
57                        const FileTracker& tracker) {
58  DCHECK(tracker.active());
59  FileTracker app_root_tracker;
60  if (database->FindAppRootTracker(tracker.app_id(), &app_root_tracker)) {
61    DCHECK(app_root_tracker.tracker_kind() == TRACKER_KIND_APP_ROOT ||
62           app_root_tracker.tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT);
63    return app_root_tracker.tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT;
64  }
65  return false;
66}
67
68scoped_ptr<FileMetadata> GetFileMetadata(MetadataDatabase* database,
69                                         const std::string& file_id) {
70  scoped_ptr<FileMetadata> metadata(new FileMetadata);
71  if (!database->FindFileByFileID(file_id, metadata.get()))
72    metadata.reset();
73  return metadata.Pass();
74}
75
76}  // namespace
77
78RemoteToLocalSyncer::RemoteToLocalSyncer(SyncEngineContext* sync_context)
79    : sync_context_(sync_context),
80      sync_action_(SYNC_ACTION_NONE),
81      prepared_(false),
82      sync_root_deletion_(false),
83      weak_ptr_factory_(this) {
84}
85
86RemoteToLocalSyncer::~RemoteToLocalSyncer() {
87}
88
89void RemoteToLocalSyncer::Run(const SyncStatusCallback& callback) {
90  if (!drive_service() || !metadata_database() || !remote_change_processor()) {
91    util::Log(logging::LOG_VERBOSE, FROM_HERE,
92              "[Remote -> Local] Context not ready.");
93    NOTREACHED();
94    callback.Run(SYNC_STATUS_FAILED);
95    return;
96  }
97
98  SyncStatusCallback wrapped_callback = base::Bind(
99      &RemoteToLocalSyncer::SyncCompleted, weak_ptr_factory_.GetWeakPtr(),
100      base::Bind(&RemoteToLocalSyncer::FinalizeSync,
101                 weak_ptr_factory_.GetWeakPtr(),
102                 callback));
103
104  dirty_tracker_ = make_scoped_ptr(new FileTracker);
105  if (metadata_database()->GetNormalPriorityDirtyTracker(
106          dirty_tracker_.get())) {
107    util::Log(logging::LOG_VERBOSE, FROM_HERE,
108              "[Remote -> Local] Start: tracker_id=%" PRId64,
109              dirty_tracker_->tracker_id());
110    ResolveRemoteChange(wrapped_callback);
111    return;
112  }
113
114  util::Log(logging::LOG_VERBOSE, FROM_HERE,
115            "[Remote -> Local] Nothing to do.");
116  base::MessageLoopProxy::current()->PostTask(
117      FROM_HERE,
118      base::Bind(callback, SYNC_STATUS_NO_CHANGE_TO_SYNC));
119}
120
121void RemoteToLocalSyncer::ResolveRemoteChange(
122    const SyncStatusCallback& callback) {
123  DCHECK(dirty_tracker_);
124  remote_metadata_ = GetFileMetadata(
125      metadata_database(), dirty_tracker_->file_id());
126
127  if (!remote_metadata_ || !remote_metadata_->has_details()) {
128    if (remote_metadata_ && !remote_metadata_->has_details()) {
129      LOG(ERROR) << "Missing details of a remote file: "
130                 << remote_metadata_->file_id();
131      NOTREACHED();
132    }
133    util::Log(logging::LOG_VERBOSE, FROM_HERE,
134              "[Remote -> Local]: Missing remote file case.");
135    HandleMissingRemoteMetadata(callback);
136    return;
137  }
138
139  DCHECK(remote_metadata_);
140  DCHECK(remote_metadata_->has_details());
141  const FileDetails& remote_details = remote_metadata_->details();
142
143  if (!dirty_tracker_->active() ||
144      HasDisabledAppRoot(metadata_database(), *dirty_tracker_)) {
145    // Handle inactive tracker in SyncCompleted.
146    util::Log(logging::LOG_VERBOSE, FROM_HERE,
147              "[Remote -> Local]: Inactive tracker case.");
148    callback.Run(SYNC_STATUS_OK);
149    return;
150  }
151
152  DCHECK(dirty_tracker_->active());
153  DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_));
154
155  if (!dirty_tracker_->has_synced_details()) {
156    LOG(ERROR) << "Missing synced_details of an active tracker: "
157               << dirty_tracker_->tracker_id();
158    NOTREACHED();
159    callback.Run(SYNC_STATUS_FAILED);
160    return;
161  }
162
163  DCHECK(dirty_tracker_->has_synced_details());
164  const FileDetails& synced_details = dirty_tracker_->synced_details();
165
166  if (dirty_tracker_->tracker_id() ==
167      metadata_database()->GetSyncRootTrackerID()) {
168    if (remote_details.missing() ||
169        synced_details.title() != remote_details.title() ||
170        remote_details.parent_folder_ids_size()) {
171      util::Log(logging::LOG_VERBOSE, FROM_HERE,
172                "[Remote -> Local]: Sync-root deletion.");
173      HandleSyncRootDeletion(callback);
174      return;
175    }
176    util::Log(logging::LOG_VERBOSE, FROM_HERE,
177              "[Remote -> Local]: Trivial sync-root change.");
178    callback.Run(SYNC_STATUS_OK);
179    return;
180  }
181
182  DCHECK_NE(dirty_tracker_->tracker_id(),
183            metadata_database()->GetSyncRootTrackerID());
184
185  if (remote_details.missing()) {
186    if (!synced_details.missing()) {
187      util::Log(logging::LOG_VERBOSE, FROM_HERE,
188                "[Remote -> Local]: Remote file deletion.");
189      HandleDeletion(callback);
190      return;
191    }
192
193    DCHECK(synced_details.missing());
194    LOG(ERROR) << "Found a stray missing tracker: "
195               << dirty_tracker_->file_id();
196    NOTREACHED();
197    callback.Run(SYNC_STATUS_OK);
198    return;
199  }
200
201  // Most of remote_details field is valid from here.
202  DCHECK(!remote_details.missing());
203
204  if (synced_details.file_kind() != remote_details.file_kind()) {
205    LOG(ERROR) << "Found type mismatch between remote and local file: "
206               << dirty_tracker_->file_id()
207               << " type: (local) " << synced_details.file_kind()
208               << " vs (remote) " << remote_details.file_kind();
209    NOTREACHED();
210    callback.Run(SYNC_STATUS_FAILED);
211    return;
212  }
213  DCHECK_EQ(synced_details.file_kind(), remote_details.file_kind());
214
215  if (synced_details.file_kind() == FILE_KIND_UNSUPPORTED) {
216    LOG(ERROR) << "Found an unsupported active file: "
217               << remote_metadata_->file_id();
218    NOTREACHED();
219    callback.Run(SYNC_STATUS_FAILED);
220    return;
221  }
222  DCHECK(remote_details.file_kind() == FILE_KIND_FILE ||
223         remote_details.file_kind() == FILE_KIND_FOLDER);
224
225  if (synced_details.title() != remote_details.title()) {
226    // Handle rename as deletion + addition.
227    util::Log(logging::LOG_VERBOSE, FROM_HERE,
228              "[Remote -> Local]: Detected file rename.");
229    Prepare(base::Bind(&RemoteToLocalSyncer::DidPrepareForDeletion,
230                       weak_ptr_factory_.GetWeakPtr(), callback));
231    return;
232  }
233  DCHECK_EQ(synced_details.title(), remote_details.title());
234
235  FileTracker parent_tracker;
236  if (!metadata_database()->FindTrackerByTrackerID(
237          dirty_tracker_->parent_tracker_id(), &parent_tracker)) {
238    LOG(ERROR) << "Missing parent tracker for a non sync-root tracker: "
239               << dirty_tracker_->file_id();
240    NOTREACHED();
241    callback.Run(SYNC_STATUS_FAILED);
242    return;
243  }
244
245  if (!HasFolderAsParent(remote_details, parent_tracker.file_id())) {
246    // Handle reorganize as deletion + addition.
247    util::Log(logging::LOG_VERBOSE, FROM_HERE,
248              "[Remote -> Local]: Detected file reorganize.");
249    Prepare(base::Bind(&RemoteToLocalSyncer::DidPrepareForDeletion,
250                       weak_ptr_factory_.GetWeakPtr(), callback));
251    return;
252  }
253
254  if (synced_details.file_kind() == FILE_KIND_FILE) {
255    if (synced_details.md5() != remote_details.md5()) {
256      util::Log(logging::LOG_VERBOSE, FROM_HERE,
257                "[Remote -> Local]: Detected file content update.");
258      HandleContentUpdate(callback);
259      return;
260    }
261  } else {
262    DCHECK_EQ(FILE_KIND_FOLDER, synced_details.file_kind());
263    if (synced_details.missing()) {
264      util::Log(logging::LOG_VERBOSE, FROM_HERE,
265                "[Remote -> Local]: Detected folder update.");
266      HandleFolderUpdate(callback);
267      return;
268    }
269    if (dirty_tracker_->needs_folder_listing()) {
270      util::Log(logging::LOG_VERBOSE, FROM_HERE,
271                "[Remote -> Local]: Needs listing folder.");
272      ListFolderContent(callback);
273      return;
274    }
275    callback.Run(SYNC_STATUS_OK);
276    return;
277  }
278
279  util::Log(logging::LOG_VERBOSE, FROM_HERE,
280            "[Remote -> Local]: Trivial file change.");
281  callback.Run(SYNC_STATUS_OK);
282}
283
284void RemoteToLocalSyncer::HandleMissingRemoteMetadata(
285    const SyncStatusCallback& callback) {
286  DCHECK(dirty_tracker_);
287
288  drive_service()->GetResourceEntry(
289      dirty_tracker_->file_id(),
290      base::Bind(&RemoteToLocalSyncer::DidGetRemoteMetadata,
291                 weak_ptr_factory_.GetWeakPtr(),
292                 callback));
293}
294
295void RemoteToLocalSyncer::DidGetRemoteMetadata(
296    const SyncStatusCallback& callback,
297    google_apis::GDataErrorCode error,
298    scoped_ptr<google_apis::ResourceEntry> entry) {
299  SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
300  if (status != SYNC_STATUS_OK &&
301      error != google_apis::HTTP_NOT_FOUND) {
302    callback.Run(status);
303    return;
304  }
305
306  if (error == google_apis::HTTP_NOT_FOUND) {
307    metadata_database()->UpdateByDeletedRemoteFile(
308        dirty_tracker_->file_id(), callback);
309    return;
310  }
311
312  if (!entry) {
313    NOTREACHED();
314    callback.Run(SYNC_STATUS_FAILED);
315    return;
316  }
317
318  metadata_database()->UpdateByFileResource(
319      *drive::util::ConvertResourceEntryToFileResource(*entry),
320      base::Bind(&RemoteToLocalSyncer::DidUpdateDatabaseForRemoteMetadata,
321                 weak_ptr_factory_.GetWeakPtr(), callback));
322}
323
324void RemoteToLocalSyncer::DidUpdateDatabaseForRemoteMetadata(
325    const SyncStatusCallback& callback,
326    SyncStatusCode status) {
327  if (status != SYNC_STATUS_OK) {
328    callback.Run(status);
329    return;
330  }
331
332  callback.Run(SYNC_STATUS_RETRY);  // Do not update |dirty_tracker_|.
333}
334
335void RemoteToLocalSyncer::DidPrepareForAddOrUpdateFile(
336    const SyncStatusCallback& callback,
337    SyncStatusCode status) {
338  if (status != SYNC_STATUS_OK) {
339    callback.Run(status);
340    return;
341  }
342
343  DCHECK(url_.is_valid());
344  DCHECK(local_metadata_);
345  DCHECK(local_changes_);
346
347  // Check if the local file exists.
348  if (local_metadata_->file_type == SYNC_FILE_TYPE_UNKNOWN ||
349      (!local_changes_->empty() && local_changes_->back().IsDelete())) {
350    sync_action_ = SYNC_ACTION_ADDED;
351    // Missing local file case.
352    // Download the file and add it to local as a new file.
353    DownloadFile(callback);
354    return;
355  }
356
357  DCHECK(local_changes_->empty() || local_changes_->back().IsAddOrUpdate());
358  if (local_changes_->empty()) {
359    if (local_metadata_->file_type == SYNC_FILE_TYPE_FILE) {
360      sync_action_ = SYNC_ACTION_UPDATED;
361      // Download the file and overwrite the existing local file.
362      DownloadFile(callback);
363      return;
364    }
365
366    DCHECK_EQ(SYNC_FILE_TYPE_DIRECTORY, local_metadata_->file_type);
367
368    // Got a remote regular file modification for existing local folder.
369    // Our policy prioritize folders in this case.
370    // Lower the priority of the tracker to prevent repeated remote sync to the
371    // same tracker, and let local-to-remote sync phase process this change.
372    metadata_database()->LowerTrackerPriority(dirty_tracker_->tracker_id());
373    remote_change_processor()->RecordFakeLocalChange(
374        url_,
375        FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
376                   local_metadata_->file_type),
377        callback);
378    return;
379  }
380
381  DCHECK(local_changes_->back().IsAddOrUpdate());
382  // Conflict case.
383  // Do nothing for the change now, and handle this in LocalToRemoteSync phase.
384
385  // Lower the priority of the tracker to prevent repeated remote sync to the
386  // same tracker.
387  metadata_database()->LowerTrackerPriority(dirty_tracker_->tracker_id());
388  callback.Run(SYNC_STATUS_RETRY);
389}
390
391void RemoteToLocalSyncer::HandleFolderUpdate(
392    const SyncStatusCallback& callback) {
393  DCHECK(dirty_tracker_);
394  DCHECK(dirty_tracker_->active());
395  DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_));
396
397  DCHECK(remote_metadata_);
398  DCHECK(remote_metadata_->has_details());
399  DCHECK(!remote_metadata_->details().missing());
400  DCHECK_EQ(FILE_KIND_FOLDER, remote_metadata_->details().file_kind());
401
402  Prepare(base::Bind(&RemoteToLocalSyncer::DidPrepareForFolderUpdate,
403                     weak_ptr_factory_.GetWeakPtr(), callback));
404}
405
406void RemoteToLocalSyncer::DidPrepareForFolderUpdate(
407    const SyncStatusCallback& callback,
408    SyncStatusCode status) {
409  if (status != SYNC_STATUS_OK) {
410    callback.Run(status);
411    return;
412  }
413
414  DCHECK(url_.is_valid());
415  DCHECK(local_metadata_);
416  DCHECK(local_changes_);
417
418  // Check if the local file exists.
419  if (local_metadata_->file_type == SYNC_FILE_TYPE_UNKNOWN ||
420      (!local_changes_->empty() && local_changes_->back().IsDelete())) {
421    sync_action_ = SYNC_ACTION_ADDED;
422    // No local file exists at the path.
423    CreateFolder(callback);
424    return;
425  }
426
427  if (local_metadata_->file_type == SYNC_FILE_TYPE_DIRECTORY) {
428    // There already exists a folder, nothing left to do.
429    if (dirty_tracker_->needs_folder_listing() &&
430        !dirty_tracker_->synced_details().missing()) {
431      ListFolderContent(callback);
432    } else {
433      callback.Run(SYNC_STATUS_OK);
434    }
435    return;
436  }
437
438  DCHECK_EQ(SYNC_FILE_TYPE_FILE, local_metadata_->file_type);
439  sync_action_ = SYNC_ACTION_ADDED;
440  // Got a remote folder for existing local file.
441  // Our policy prioritize folders in this case.
442  CreateFolder(callback);
443}
444
445void RemoteToLocalSyncer::HandleSyncRootDeletion(
446    const SyncStatusCallback& callback) {
447  sync_root_deletion_ = true;
448  callback.Run(SYNC_STATUS_OK);
449}
450
451void RemoteToLocalSyncer::HandleDeletion(
452    const SyncStatusCallback& callback) {
453  DCHECK(dirty_tracker_);
454  DCHECK(dirty_tracker_->active());
455  DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_));
456  DCHECK(dirty_tracker_->has_synced_details());
457  DCHECK(!dirty_tracker_->synced_details().missing());
458
459  DCHECK(remote_metadata_);
460  DCHECK(remote_metadata_->has_details());
461  DCHECK(remote_metadata_->details().missing());
462
463  Prepare(base::Bind(&RemoteToLocalSyncer::DidPrepareForDeletion,
464                     weak_ptr_factory_.GetWeakPtr(), callback));
465}
466
467void RemoteToLocalSyncer::DidPrepareForDeletion(
468    const SyncStatusCallback& callback,
469    SyncStatusCode status) {
470  if (status != SYNC_STATUS_OK) {
471    callback.Run(status);
472    return;
473  }
474
475  DCHECK(url_.is_valid());
476  DCHECK(local_metadata_);
477  DCHECK(local_changes_);
478
479  // Check if the local file exists.
480  if (local_metadata_->file_type == SYNC_FILE_TYPE_UNKNOWN ||
481      (!local_changes_->empty() && local_changes_->back().IsDelete())) {
482    // No local file exists at the path.
483    callback.Run(SYNC_STATUS_OK);
484    return;
485  }
486
487  DCHECK(local_changes_->empty() || local_changes_->back().IsAddOrUpdate());
488  if (local_changes_->empty()) {
489    sync_action_ = SYNC_ACTION_DELETED;
490    DeleteLocalFile(callback);
491    return;
492  }
493
494  DCHECK(local_changes_->back().IsAddOrUpdate());
495  // File is remotely deleted and locally updated.
496  // Ignore the remote deletion and handle it as if applied successfully.
497  callback.Run(SYNC_STATUS_OK);
498}
499
500void RemoteToLocalSyncer::HandleContentUpdate(
501    const SyncStatusCallback& callback) {
502  DCHECK(dirty_tracker_);
503  DCHECK(dirty_tracker_->active());
504  DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_));
505  DCHECK(dirty_tracker_->has_synced_details());
506  DCHECK_EQ(FILE_KIND_FILE, dirty_tracker_->synced_details().file_kind());
507
508  DCHECK(remote_metadata_);
509  DCHECK(remote_metadata_->has_details());
510  DCHECK(!remote_metadata_->details().missing());
511
512  DCHECK_NE(dirty_tracker_->synced_details().md5(),
513            remote_metadata_->details().md5());
514
515  Prepare(base::Bind(&RemoteToLocalSyncer::DidPrepareForAddOrUpdateFile,
516                     weak_ptr_factory_.GetWeakPtr(), callback));
517}
518
519void RemoteToLocalSyncer::ListFolderContent(
520    const SyncStatusCallback& callback) {
521  DCHECK(dirty_tracker_);
522  DCHECK(dirty_tracker_->active());
523  DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_));
524  DCHECK(dirty_tracker_->has_synced_details());
525  DCHECK(!dirty_tracker_->synced_details().missing());
526  DCHECK_EQ(FILE_KIND_FOLDER, dirty_tracker_->synced_details().file_kind());
527  DCHECK(dirty_tracker_->needs_folder_listing());
528
529  DCHECK(remote_metadata_);
530  DCHECK(remote_metadata_->has_details());
531  DCHECK(!remote_metadata_->details().missing());
532
533  // TODO(tzik): Replace this call with ChildList version.
534  drive_service()->GetResourceListInDirectory(
535      dirty_tracker_->file_id(),
536      base::Bind(&RemoteToLocalSyncer::DidListFolderContent,
537                 weak_ptr_factory_.GetWeakPtr(),
538                 callback,
539                 base::Passed(make_scoped_ptr(new FileIDList))));
540}
541
542void RemoteToLocalSyncer::DidListFolderContent(
543    const SyncStatusCallback& callback,
544    scoped_ptr<FileIDList> children,
545    google_apis::GDataErrorCode error,
546    scoped_ptr<google_apis::ResourceList> resource_list) {
547  SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
548  if (status != SYNC_STATUS_OK) {
549    callback.Run(status);
550    return;
551  }
552
553  if (!resource_list) {
554    NOTREACHED();
555    callback.Run(SYNC_STATUS_FAILED);
556    return;
557  }
558
559  children->reserve(children->size() + resource_list->entries().size());
560  for (ScopedVector<google_apis::ResourceEntry>::const_iterator itr =
561           resource_list->entries().begin();
562       itr != resource_list->entries().end();
563       ++itr) {
564    children->push_back((*itr)->resource_id());
565  }
566
567  GURL next_feed;
568  if (resource_list->GetNextFeedURL(&next_feed)) {
569    drive_service()->GetRemainingFileList(
570        next_feed,
571        base::Bind(&RemoteToLocalSyncer::DidListFolderContent,
572                   weak_ptr_factory_.GetWeakPtr(),
573                   callback, base::Passed(&children)));
574    return;
575  }
576
577  metadata_database()->PopulateFolderByChildList(
578      dirty_tracker_->file_id(), *children, callback);
579}
580
581void RemoteToLocalSyncer::SyncCompleted(const SyncStatusCallback& callback,
582                                        SyncStatusCode status) {
583  util::Log(logging::LOG_VERBOSE, FROM_HERE,
584            "[Remote -> Local]: Finished: action=%s, tracker=%" PRId64
585            " status=%s",
586            SyncActionToString(sync_action_), dirty_tracker_->tracker_id(),
587            SyncStatusCodeToString(status));
588
589  if (sync_root_deletion_) {
590    callback.Run(SYNC_STATUS_OK);
591    return;
592  }
593
594  if (status == SYNC_STATUS_RETRY) {
595    callback.Run(SYNC_STATUS_OK);
596    return;
597  }
598
599  if (status != SYNC_STATUS_OK) {
600    callback.Run(status);
601    return;
602  }
603
604  DCHECK(dirty_tracker_);
605  DCHECK(remote_metadata_);
606  DCHECK(remote_metadata_->has_details());
607
608  FileDetails updated_details = remote_metadata_->details();
609  if (!dirty_tracker_->active() ||
610      HasDisabledAppRoot(metadata_database(), *dirty_tracker_)) {
611    // Operations for an inactive tracker don't update file content.
612    if (dirty_tracker_->has_synced_details())
613      updated_details.set_md5(dirty_tracker_->synced_details().md5());
614    if (!dirty_tracker_->active()) {
615      // Keep missing true, as the change hasn't been synced to local.
616      updated_details.clear_md5();
617      updated_details.set_missing(true);
618    }
619  }
620  metadata_database()->UpdateTracker(dirty_tracker_->tracker_id(),
621                                     updated_details,
622                                     callback);
623}
624
625void RemoteToLocalSyncer::FinalizeSync(const SyncStatusCallback& callback,
626                                       SyncStatusCode status) {
627  if (prepared_) {
628    remote_change_processor()->FinalizeRemoteSync(
629        url_, false /* clear_local_change */, base::Bind(callback, status));
630    return;
631  }
632
633  callback.Run(status);
634}
635
636void RemoteToLocalSyncer::Prepare(const SyncStatusCallback& callback) {
637  bool should_success = BuildFileSystemURL(
638      metadata_database(), *dirty_tracker_, &url_);
639  DCHECK(should_success);
640  DCHECK(url_.is_valid());
641  remote_change_processor()->PrepareForProcessRemoteChange(
642      url_,
643      base::Bind(&RemoteToLocalSyncer::DidPrepare,
644                 weak_ptr_factory_.GetWeakPtr(),
645                 callback));
646}
647
648void RemoteToLocalSyncer::DidPrepare(const SyncStatusCallback& callback,
649                                     SyncStatusCode status,
650                                     const SyncFileMetadata& local_metadata,
651                                     const FileChangeList& local_changes) {
652  if (status != SYNC_STATUS_OK) {
653    callback.Run(status);
654    return;
655  }
656  prepared_ = true;
657
658  local_metadata_.reset(new SyncFileMetadata(local_metadata));
659  local_changes_.reset(new FileChangeList(local_changes));
660
661  callback.Run(status);
662}
663
664void RemoteToLocalSyncer::DeleteLocalFile(const SyncStatusCallback& callback) {
665  remote_change_processor()->ApplyRemoteChange(
666      FileChange(FileChange::FILE_CHANGE_DELETE, SYNC_FILE_TYPE_UNKNOWN),
667      base::FilePath(),
668      url_,
669      callback);
670}
671
672void RemoteToLocalSyncer::DownloadFile(const SyncStatusCallback& callback) {
673  base::PostTaskAndReplyWithResult(
674      sync_context_->GetBlockingTaskRunner(), FROM_HERE,
675      base::Bind(&sync_file_system::drive_backend::CreateTemporaryFile,
676                 make_scoped_refptr(sync_context_->GetBlockingTaskRunner())),
677      base::Bind(&RemoteToLocalSyncer::DidCreateTemporaryFileForDownload,
678                 weak_ptr_factory_.GetWeakPtr(), callback));
679}
680
681void RemoteToLocalSyncer::DidCreateTemporaryFileForDownload(
682    const SyncStatusCallback& callback,
683    webkit_blob::ScopedFile file) {
684  base::FilePath path = file.path();
685  drive_service()->DownloadFile(
686      path, remote_metadata_->file_id(),
687      base::Bind(&RemoteToLocalSyncer::DidDownloadFile,
688                 weak_ptr_factory_.GetWeakPtr(),
689                 callback, base::Passed(&file)),
690      google_apis::GetContentCallback(),
691      google_apis::ProgressCallback());
692}
693
694void RemoteToLocalSyncer::DidDownloadFile(const SyncStatusCallback& callback,
695                                          webkit_blob::ScopedFile file,
696                                          google_apis::GDataErrorCode error,
697                                          const base::FilePath&) {
698  SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
699  if (status != SYNC_STATUS_OK) {
700    callback.Run(status);
701    return;
702  }
703
704  base::FilePath path = file.path();
705  base::PostTaskAndReplyWithResult(
706      sync_context_->GetBlockingTaskRunner(), FROM_HERE,
707      base::Bind(&drive::util::GetMd5Digest, path),
708      base::Bind(&RemoteToLocalSyncer::DidCalculateMD5ForDownload,
709                 weak_ptr_factory_.GetWeakPtr(),
710                 callback, base::Passed(&file)));
711}
712
713void RemoteToLocalSyncer::DidCalculateMD5ForDownload(
714    const SyncStatusCallback& callback,
715    webkit_blob::ScopedFile file,
716    const std::string& md5) {
717  if (md5.empty()) {
718    callback.Run(SYNC_FILE_ERROR_NOT_FOUND);
719    return;
720  }
721
722  if (md5 != remote_metadata_->details().md5()) {
723    // File has been modified since last metadata retrieval.
724
725    // Lower the priority of the tracker to prevent repeated remote sync to the
726    // same tracker.
727    metadata_database()->LowerTrackerPriority(dirty_tracker_->tracker_id());
728    callback.Run(SYNC_STATUS_RETRY);
729    return;
730  }
731
732  base::FilePath path = file.path();
733  remote_change_processor()->ApplyRemoteChange(
734      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, SYNC_FILE_TYPE_FILE),
735      path, url_,
736      base::Bind(&RemoteToLocalSyncer::DidApplyDownload,
737                 weak_ptr_factory_.GetWeakPtr(),
738                 callback, base::Passed(&file)));
739}
740
741void RemoteToLocalSyncer::DidApplyDownload(const SyncStatusCallback& callback,
742                                           webkit_blob::ScopedFile,
743                                           SyncStatusCode status) {
744  if (status != SYNC_STATUS_OK)
745    metadata_database()->LowerTrackerPriority(dirty_tracker_->tracker_id());
746  callback.Run(status);
747}
748
749void RemoteToLocalSyncer::CreateFolder(const SyncStatusCallback& callback) {
750  remote_change_processor()->ApplyRemoteChange(
751      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
752                 SYNC_FILE_TYPE_DIRECTORY),
753      base::FilePath(), url_,
754      callback);
755}
756
757drive::DriveServiceInterface* RemoteToLocalSyncer::drive_service() {
758  return sync_context_->GetDriveService();
759}
760
761MetadataDatabase* RemoteToLocalSyncer::metadata_database() {
762  return sync_context_->GetMetadataDatabase();
763}
764
765RemoteChangeProcessor* RemoteToLocalSyncer::remote_change_processor() {
766  DCHECK(sync_context_->GetRemoteChangeProcessor());
767  return sync_context_->GetRemoteChangeProcessor();
768}
769
770}  // namespace drive_backend
771}  // namespace sync_file_system
772