remote_to_local_syncer.cc revision 46d4c2bc3267f3f028f39e7e311b0f89aba2e4fd
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::RunExclusive(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  sync_context_->GetWorkerTaskRunner()->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 metadata 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()->GetFileResource(
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::FileResource> entry) {
299  DCHECK(sync_context_->GetWorkerTaskRunner()->RunsTasksOnCurrentThread());
300
301  SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
302  if (status != SYNC_STATUS_OK &&
303      error != google_apis::HTTP_NOT_FOUND) {
304    callback.Run(status);
305    return;
306  }
307
308  if (error == google_apis::HTTP_NOT_FOUND) {
309    metadata_database()->UpdateByDeletedRemoteFile(
310        dirty_tracker_->file_id(), callback);
311    return;
312  }
313
314  if (!entry) {
315    NOTREACHED();
316    callback.Run(SYNC_STATUS_FAILED);
317    return;
318  }
319
320  metadata_database()->UpdateByFileResource(
321      *entry,
322      base::Bind(&RemoteToLocalSyncer::DidUpdateDatabaseForRemoteMetadata,
323                 weak_ptr_factory_.GetWeakPtr(), callback));
324}
325
326void RemoteToLocalSyncer::DidUpdateDatabaseForRemoteMetadata(
327    const SyncStatusCallback& callback,
328    SyncStatusCode status) {
329  if (status != SYNC_STATUS_OK) {
330    callback.Run(status);
331    return;
332  }
333
334  callback.Run(SYNC_STATUS_RETRY);  // Do not update |dirty_tracker_|.
335}
336
337void RemoteToLocalSyncer::DidPrepareForAddOrUpdateFile(
338    const SyncStatusCallback& callback,
339    SyncStatusCode status) {
340  if (status != SYNC_STATUS_OK) {
341    callback.Run(status);
342    return;
343  }
344
345  DCHECK(url_.is_valid());
346  DCHECK(local_metadata_);
347  DCHECK(local_changes_);
348
349  // Check if the local file exists.
350  if (local_metadata_->file_type == SYNC_FILE_TYPE_UNKNOWN ||
351      (!local_changes_->empty() && local_changes_->back().IsDelete())) {
352    sync_action_ = SYNC_ACTION_ADDED;
353    // Missing local file case.
354    // Download the file and add it to local as a new file.
355    DownloadFile(callback);
356    return;
357  }
358
359  DCHECK(local_changes_->empty() || local_changes_->back().IsAddOrUpdate());
360  if (local_changes_->empty()) {
361    if (local_metadata_->file_type == SYNC_FILE_TYPE_FILE) {
362      sync_action_ = SYNC_ACTION_UPDATED;
363      // Download the file and overwrite the existing local file.
364      DownloadFile(callback);
365      return;
366    }
367
368    DCHECK_EQ(SYNC_FILE_TYPE_DIRECTORY, local_metadata_->file_type);
369
370    // Got a remote regular file modification for existing local folder.
371    // Our policy prioritize folders in this case.
372    // Lower the priority of the tracker to prevent repeated remote sync to the
373    // same tracker, and let local-to-remote sync phase process this change.
374    metadata_database()->LowerTrackerPriority(dirty_tracker_->tracker_id());
375    remote_change_processor()->RecordFakeLocalChange(
376        url_,
377        FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
378                   local_metadata_->file_type),
379        callback);
380    return;
381  }
382
383  DCHECK(local_changes_->back().IsAddOrUpdate());
384  // Conflict case.
385  // Do nothing for the change now, and handle this in LocalToRemoteSync phase.
386
387  // Lower the priority of the tracker to prevent repeated remote sync to the
388  // same tracker.
389  metadata_database()->LowerTrackerPriority(dirty_tracker_->tracker_id());
390  callback.Run(SYNC_STATUS_RETRY);
391}
392
393void RemoteToLocalSyncer::HandleFolderUpdate(
394    const SyncStatusCallback& callback) {
395  DCHECK(dirty_tracker_);
396  DCHECK(dirty_tracker_->active());
397  DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_));
398
399  DCHECK(remote_metadata_);
400  DCHECK(remote_metadata_->has_details());
401  DCHECK(!remote_metadata_->details().missing());
402  DCHECK_EQ(FILE_KIND_FOLDER, remote_metadata_->details().file_kind());
403
404  Prepare(base::Bind(&RemoteToLocalSyncer::DidPrepareForFolderUpdate,
405                     weak_ptr_factory_.GetWeakPtr(), callback));
406}
407
408void RemoteToLocalSyncer::DidPrepareForFolderUpdate(
409    const SyncStatusCallback& callback,
410    SyncStatusCode status) {
411  if (status != SYNC_STATUS_OK) {
412    callback.Run(status);
413    return;
414  }
415
416  DCHECK(url_.is_valid());
417  DCHECK(local_metadata_);
418  DCHECK(local_changes_);
419
420  // Check if the local file exists.
421  if (local_metadata_->file_type == SYNC_FILE_TYPE_UNKNOWN ||
422      (!local_changes_->empty() && local_changes_->back().IsDelete())) {
423    sync_action_ = SYNC_ACTION_ADDED;
424    // No local file exists at the path.
425    CreateFolder(callback);
426    return;
427  }
428
429  if (local_metadata_->file_type == SYNC_FILE_TYPE_DIRECTORY) {
430    // There already exists a folder, nothing left to do.
431    if (dirty_tracker_->needs_folder_listing() &&
432        !dirty_tracker_->synced_details().missing()) {
433      ListFolderContent(callback);
434    } else {
435      callback.Run(SYNC_STATUS_OK);
436    }
437    return;
438  }
439
440  DCHECK_EQ(SYNC_FILE_TYPE_FILE, local_metadata_->file_type);
441  sync_action_ = SYNC_ACTION_ADDED;
442  // Got a remote folder for existing local file.
443  // Our policy prioritize folders in this case.
444  CreateFolder(callback);
445}
446
447void RemoteToLocalSyncer::HandleSyncRootDeletion(
448    const SyncStatusCallback& callback) {
449  sync_root_deletion_ = true;
450  callback.Run(SYNC_STATUS_OK);
451}
452
453void RemoteToLocalSyncer::HandleDeletion(
454    const SyncStatusCallback& callback) {
455  DCHECK(dirty_tracker_);
456  DCHECK(dirty_tracker_->active());
457  DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_));
458  DCHECK(dirty_tracker_->has_synced_details());
459  DCHECK(!dirty_tracker_->synced_details().missing());
460
461  DCHECK(remote_metadata_);
462  DCHECK(remote_metadata_->has_details());
463  DCHECK(remote_metadata_->details().missing());
464
465  Prepare(base::Bind(&RemoteToLocalSyncer::DidPrepareForDeletion,
466                     weak_ptr_factory_.GetWeakPtr(), callback));
467}
468
469void RemoteToLocalSyncer::DidPrepareForDeletion(
470    const SyncStatusCallback& callback,
471    SyncStatusCode status) {
472  if (status != SYNC_STATUS_OK) {
473    callback.Run(status);
474    return;
475  }
476
477  DCHECK(url_.is_valid());
478  DCHECK(local_metadata_);
479  DCHECK(local_changes_);
480
481  // Check if the local file exists.
482  if (local_metadata_->file_type == SYNC_FILE_TYPE_UNKNOWN ||
483      (!local_changes_->empty() && local_changes_->back().IsDelete())) {
484    // No local file exists at the path.
485    callback.Run(SYNC_STATUS_OK);
486    return;
487  }
488
489  DCHECK(local_changes_->empty() || local_changes_->back().IsAddOrUpdate());
490  if (local_changes_->empty()) {
491    sync_action_ = SYNC_ACTION_DELETED;
492    DeleteLocalFile(callback);
493    return;
494  }
495
496  DCHECK(local_changes_->back().IsAddOrUpdate());
497  // File is remotely deleted and locally updated.
498  // Ignore the remote deletion and handle it as if applied successfully.
499  callback.Run(SYNC_STATUS_OK);
500}
501
502void RemoteToLocalSyncer::HandleContentUpdate(
503    const SyncStatusCallback& callback) {
504  DCHECK(dirty_tracker_);
505  DCHECK(dirty_tracker_->active());
506  DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_));
507  DCHECK(dirty_tracker_->has_synced_details());
508  DCHECK_EQ(FILE_KIND_FILE, dirty_tracker_->synced_details().file_kind());
509
510  DCHECK(remote_metadata_);
511  DCHECK(remote_metadata_->has_details());
512  DCHECK(!remote_metadata_->details().missing());
513
514  DCHECK_NE(dirty_tracker_->synced_details().md5(),
515            remote_metadata_->details().md5());
516
517  Prepare(base::Bind(&RemoteToLocalSyncer::DidPrepareForAddOrUpdateFile,
518                     weak_ptr_factory_.GetWeakPtr(), callback));
519}
520
521void RemoteToLocalSyncer::ListFolderContent(
522    const SyncStatusCallback& callback) {
523  DCHECK(dirty_tracker_);
524  DCHECK(dirty_tracker_->active());
525  DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_));
526  DCHECK(dirty_tracker_->has_synced_details());
527  DCHECK(!dirty_tracker_->synced_details().missing());
528  DCHECK_EQ(FILE_KIND_FOLDER, dirty_tracker_->synced_details().file_kind());
529  DCHECK(dirty_tracker_->needs_folder_listing());
530
531  DCHECK(remote_metadata_);
532  DCHECK(remote_metadata_->has_details());
533  DCHECK(!remote_metadata_->details().missing());
534
535  // TODO(tzik): Replace this call with ChildList version.
536  drive_service()->GetFileListInDirectory(
537      dirty_tracker_->file_id(),
538      base::Bind(&RemoteToLocalSyncer::DidListFolderContent,
539                 weak_ptr_factory_.GetWeakPtr(),
540                 callback,
541                 base::Passed(make_scoped_ptr(new FileIDList))));
542}
543
544void RemoteToLocalSyncer::DidListFolderContent(
545    const SyncStatusCallback& callback,
546    scoped_ptr<FileIDList> children,
547    google_apis::GDataErrorCode error,
548    scoped_ptr<google_apis::FileList> file_list) {
549  SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
550  if (status != SYNC_STATUS_OK) {
551    callback.Run(status);
552    return;
553  }
554
555  if (!file_list) {
556    NOTREACHED();
557    callback.Run(SYNC_STATUS_FAILED);
558    return;
559  }
560
561  children->reserve(children->size() + file_list->items().size());
562  for (ScopedVector<google_apis::FileResource>::const_iterator itr =
563           file_list->items().begin();
564       itr != file_list->items().end();
565       ++itr) {
566    children->push_back((*itr)->file_id());
567  }
568
569  if (!file_list->next_link().is_empty()) {
570    drive_service()->GetRemainingFileList(
571        file_list->next_link(),
572        base::Bind(&RemoteToLocalSyncer::DidListFolderContent,
573                   weak_ptr_factory_.GetWeakPtr(),
574                   callback, base::Passed(&children)));
575    return;
576  }
577
578  metadata_database()->PopulateFolderByChildList(
579      dirty_tracker_->file_id(), *children, callback);
580}
581
582void RemoteToLocalSyncer::SyncCompleted(const SyncStatusCallback& callback,
583                                        SyncStatusCode status) {
584  util::Log(logging::LOG_VERBOSE, FROM_HERE,
585            "[Remote -> Local]: Finished: action=%s, tracker=%" PRId64
586            " status=%s",
587            SyncActionToString(sync_action_), dirty_tracker_->tracker_id(),
588            SyncStatusCodeToString(status));
589
590  if (sync_root_deletion_) {
591    callback.Run(SYNC_STATUS_OK);
592    return;
593  }
594
595  if (status == SYNC_STATUS_RETRY) {
596    callback.Run(SYNC_STATUS_OK);
597    return;
598  }
599
600  if (status != SYNC_STATUS_OK) {
601    callback.Run(status);
602    return;
603  }
604
605  DCHECK(dirty_tracker_);
606  DCHECK(remote_metadata_);
607  DCHECK(remote_metadata_->has_details());
608
609  FileDetails updated_details = remote_metadata_->details();
610  if (!dirty_tracker_->active() ||
611      HasDisabledAppRoot(metadata_database(), *dirty_tracker_)) {
612    // Operations for an inactive tracker don't update file content.
613    if (dirty_tracker_->has_synced_details())
614      updated_details.set_md5(dirty_tracker_->synced_details().md5());
615    if (!dirty_tracker_->active()) {
616      // Keep missing true, as the change hasn't been synced to local.
617      updated_details.clear_md5();
618      updated_details.set_missing(true);
619    }
620  }
621  metadata_database()->UpdateTracker(dirty_tracker_->tracker_id(),
622                                     updated_details,
623                                     callback);
624}
625
626void RemoteToLocalSyncer::FinalizeSync(const SyncStatusCallback& callback,
627                                       SyncStatusCode status) {
628  if (prepared_) {
629    remote_change_processor()->FinalizeRemoteSync(
630        url_, false /* clear_local_change */, base::Bind(callback, status));
631    return;
632  }
633
634  callback.Run(status);
635}
636
637void RemoteToLocalSyncer::Prepare(const SyncStatusCallback& callback) {
638  bool should_success = BuildFileSystemURL(
639      metadata_database(), *dirty_tracker_, &url_);
640  DCHECK(should_success);
641  DCHECK(url_.is_valid());
642  remote_change_processor()->PrepareForProcessRemoteChange(
643      url_,
644      base::Bind(&RemoteToLocalSyncer::DidPrepare,
645                 weak_ptr_factory_.GetWeakPtr(),
646                 callback));
647}
648
649void RemoteToLocalSyncer::DidPrepare(const SyncStatusCallback& callback,
650                                     SyncStatusCode status,
651                                     const SyncFileMetadata& local_metadata,
652                                     const FileChangeList& local_changes) {
653  if (status != SYNC_STATUS_OK) {
654    callback.Run(status);
655    return;
656  }
657  prepared_ = true;
658
659  local_metadata_.reset(new SyncFileMetadata(local_metadata));
660  local_changes_.reset(new FileChangeList(local_changes));
661
662  callback.Run(status);
663}
664
665void RemoteToLocalSyncer::DeleteLocalFile(const SyncStatusCallback& callback) {
666  remote_change_processor()->ApplyRemoteChange(
667      FileChange(FileChange::FILE_CHANGE_DELETE, SYNC_FILE_TYPE_UNKNOWN),
668      base::FilePath(),
669      url_,
670      callback);
671}
672
673void RemoteToLocalSyncer::DownloadFile(const SyncStatusCallback& callback) {
674  base::PostTaskAndReplyWithResult(
675      sync_context_->GetFileTaskRunner(), FROM_HERE,
676      base::Bind(&sync_file_system::drive_backend::CreateTemporaryFile,
677                 make_scoped_refptr(sync_context_->GetFileTaskRunner())),
678      base::Bind(&RemoteToLocalSyncer::DidCreateTemporaryFileForDownload,
679                 weak_ptr_factory_.GetWeakPtr(), callback));
680}
681
682void RemoteToLocalSyncer::DidCreateTemporaryFileForDownload(
683    const SyncStatusCallback& callback,
684    webkit_blob::ScopedFile file) {
685  base::FilePath path = file.path();
686  drive_service()->DownloadFile(
687      path, remote_metadata_->file_id(),
688      base::Bind(&RemoteToLocalSyncer::DidDownloadFile,
689                 weak_ptr_factory_.GetWeakPtr(),
690                 callback, base::Passed(&file)),
691      google_apis::GetContentCallback(),
692      google_apis::ProgressCallback());
693}
694
695void RemoteToLocalSyncer::DidDownloadFile(const SyncStatusCallback& callback,
696                                          webkit_blob::ScopedFile file,
697                                          google_apis::GDataErrorCode error,
698                                          const base::FilePath&) {
699  SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
700  if (status != SYNC_STATUS_OK) {
701    callback.Run(status);
702    return;
703  }
704
705  base::FilePath path = file.path();
706  base::PostTaskAndReplyWithResult(
707      sync_context_->GetFileTaskRunner(), FROM_HERE,
708      base::Bind(&drive::util::GetMd5Digest, path),
709      base::Bind(&RemoteToLocalSyncer::DidCalculateMD5ForDownload,
710                 weak_ptr_factory_.GetWeakPtr(),
711                 callback, base::Passed(&file)));
712}
713
714void RemoteToLocalSyncer::DidCalculateMD5ForDownload(
715    const SyncStatusCallback& callback,
716    webkit_blob::ScopedFile file,
717    const std::string& md5) {
718  if (md5.empty()) {
719    callback.Run(SYNC_FILE_ERROR_NOT_FOUND);
720    return;
721  }
722
723  if (md5 != remote_metadata_->details().md5()) {
724    // File has been modified since last metadata retrieval.
725
726    // Lower the priority of the tracker to prevent repeated remote sync to the
727    // same tracker.
728    metadata_database()->LowerTrackerPriority(dirty_tracker_->tracker_id());
729    callback.Run(SYNC_STATUS_RETRY);
730    return;
731  }
732
733  base::FilePath path = file.path();
734  remote_change_processor()->ApplyRemoteChange(
735      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, SYNC_FILE_TYPE_FILE),
736      path, url_,
737      base::Bind(&RemoteToLocalSyncer::DidApplyDownload,
738                 weak_ptr_factory_.GetWeakPtr(),
739                 callback, base::Passed(&file)));
740}
741
742void RemoteToLocalSyncer::DidApplyDownload(const SyncStatusCallback& callback,
743                                           webkit_blob::ScopedFile,
744                                           SyncStatusCode status) {
745  if (status != SYNC_STATUS_OK)
746    metadata_database()->LowerTrackerPriority(dirty_tracker_->tracker_id());
747  callback.Run(status);
748}
749
750void RemoteToLocalSyncer::CreateFolder(const SyncStatusCallback& callback) {
751  remote_change_processor()->ApplyRemoteChange(
752      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
753                 SYNC_FILE_TYPE_DIRECTORY),
754      base::FilePath(), url_,
755      callback);
756}
757
758drive::DriveServiceInterface* RemoteToLocalSyncer::drive_service() {
759  return sync_context_->GetDriveService();
760}
761
762MetadataDatabase* RemoteToLocalSyncer::metadata_database() {
763  return sync_context_->GetMetadataDatabase();
764}
765
766RemoteChangeProcessor* RemoteToLocalSyncer::remote_change_processor() {
767  DCHECK(sync_context_->GetRemoteChangeProcessor());
768  return sync_context_->GetRemoteChangeProcessor();
769}
770
771}  // namespace drive_backend
772}  // namespace sync_file_system
773