1// Copyright (c) 2010 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
6#include "chrome/browser/sync/engine/verify_updates_command.h"
7
8#include "chrome/browser/sync/engine/syncer.h"
9#include "chrome/browser/sync/engine/syncer_proto_util.h"
10#include "chrome/browser/sync/engine/syncer_types.h"
11#include "chrome/browser/sync/engine/syncer_util.h"
12#include "chrome/browser/sync/engine/syncproto.h"
13#include "chrome/browser/sync/protocol/bookmark_specifics.pb.h"
14#include "chrome/browser/sync/syncable/directory_manager.h"
15#include "chrome/browser/sync/syncable/syncable.h"
16
17namespace browser_sync {
18
19using syncable::ScopedDirLookup;
20using syncable::SyncName;
21using syncable::WriteTransaction;
22
23using syncable::GET_BY_ID;
24using syncable::SYNCER;
25
26VerifyUpdatesCommand::VerifyUpdatesCommand() {}
27VerifyUpdatesCommand::~VerifyUpdatesCommand() {}
28
29void VerifyUpdatesCommand::ModelChangingExecuteImpl(
30    sessions::SyncSession* session) {
31  VLOG(1) << "Beginning Update Verification";
32  ScopedDirLookup dir(session->context()->directory_manager(),
33                      session->context()->account_name());
34  if (!dir.good()) {
35    LOG(ERROR) << "Scoped dir lookup failed!";
36    return;
37  }
38  WriteTransaction trans(dir, SYNCER, __FILE__, __LINE__);
39  sessions::StatusController* status = session->status_controller();
40  const GetUpdatesResponse& updates = status->updates_response().get_updates();
41  int update_count = updates.entries().size();
42
43  VLOG(1) << update_count << " entries to verify";
44  for (int i = 0; i < update_count; i++) {
45    const SyncEntity& update =
46        *reinterpret_cast<const SyncEntity *>(&(updates.entries(i)));
47    ModelSafeGroup g = GetGroupForModelType(update.GetModelType(),
48                                            session->routing_info());
49    if (g != status->group_restriction())
50      continue;
51
52    VerifyUpdateResult result = VerifyUpdate(&trans, update,
53                                             session->routing_info());
54    status->mutable_update_progress()->AddVerifyResult(result.value, update);
55    status->increment_num_updates_downloaded_by(1);
56    if (update.deleted())
57      status->increment_num_tombstone_updates_downloaded_by(1);
58  }
59}
60
61namespace {
62// In the event that IDs match, but tags differ AttemptReuniteClient tag
63// will have refused to unify the update.
64// We should not attempt to apply it at all since it violates consistency
65// rules.
66VerifyResult VerifyTagConsistency(const SyncEntity& entry,
67                                  const syncable::MutableEntry& same_id) {
68  if (entry.has_client_defined_unique_tag() &&
69      entry.client_defined_unique_tag() !=
70          same_id.Get(syncable::UNIQUE_CLIENT_TAG)) {
71    return VERIFY_FAIL;
72  }
73  return VERIFY_UNDECIDED;
74}
75}  // namespace
76
77VerifyUpdatesCommand::VerifyUpdateResult VerifyUpdatesCommand::VerifyUpdate(
78    syncable::WriteTransaction* trans, const SyncEntity& entry,
79    const ModelSafeRoutingInfo& routes) {
80  syncable::Id id = entry.id();
81  VerifyUpdateResult result = {VERIFY_FAIL, GROUP_PASSIVE};
82
83  const bool deleted = entry.has_deleted() && entry.deleted();
84  const bool is_directory = entry.IsFolder();
85  const syncable::ModelType model_type = entry.GetModelType();
86
87  if (!id.ServerKnows()) {
88    LOG(ERROR) << "Illegal negative id in received updates";
89    return result;
90  }
91  {
92    const std::string name = SyncerProtoUtil::NameFromSyncEntity(entry);
93    if (name.empty() && !deleted) {
94      LOG(ERROR) << "Zero length name in non-deleted update";
95      return result;
96    }
97  }
98
99  syncable::MutableEntry same_id(trans, GET_BY_ID, id);
100  result.value = SyncerUtil::VerifyNewEntry(entry, &same_id, deleted);
101
102  syncable::ModelType placement_type = !deleted ? entry.GetModelType()
103      : same_id.good() ? same_id.GetModelType() : syncable::UNSPECIFIED;
104  result.placement = GetGroupForModelType(placement_type, routes);
105
106  if (VERIFY_UNDECIDED == result.value) {
107    result.value = VerifyTagConsistency(entry, same_id);
108  }
109
110  if (VERIFY_UNDECIDED == result.value) {
111    if (deleted)
112      result.value = VERIFY_SUCCESS;
113  }
114
115  // If we have an existing entry, we check here for updates that break
116  // consistency rules.
117  if (VERIFY_UNDECIDED == result.value) {
118    result.value = SyncerUtil::VerifyUpdateConsistency(trans, entry, &same_id,
119        deleted, is_directory, model_type);
120  }
121
122  if (VERIFY_UNDECIDED == result.value)
123    result.value = VERIFY_SUCCESS;  // No news is good news.
124
125  return result;  // This might be VERIFY_SUCCESS as well
126}
127
128}  // namespace browser_sync
129