More bring up of ui code on iOS.
[chromium-blink-merge.git] / sync / engine / process_updates_command.cc
blobde6be0f64a5aa3ae925e014d4449a326c6fa4231
1 // Copyright (c) 2012 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.
5 #include "sync/engine/process_updates_command.h"
7 #include <vector>
9 #include "base/basictypes.h"
10 #include "base/location.h"
11 #include "sync/engine/syncer.h"
12 #include "sync/engine/syncer_proto_util.h"
13 #include "sync/engine/syncer_util.h"
14 #include "sync/sessions/sync_session.h"
15 #include "sync/syncable/directory.h"
16 #include "sync/syncable/mutable_entry.h"
17 #include "sync/syncable/syncable_proto_util.h"
18 #include "sync/syncable/syncable_util.h"
19 #include "sync/syncable/write_transaction.h"
20 #include "sync/util/cryptographer.h"
22 using std::vector;
24 namespace syncer {
26 using sessions::SyncSession;
27 using sessions::StatusController;
28 using sessions::UpdateProgress;
30 ProcessUpdatesCommand::ProcessUpdatesCommand() {}
31 ProcessUpdatesCommand::~ProcessUpdatesCommand() {}
33 std::set<ModelSafeGroup> ProcessUpdatesCommand::GetGroupsToChange(
34 const sessions::SyncSession& session) const {
35 return session.GetEnabledGroupsWithVerifiedUpdates();
38 SyncerError ProcessUpdatesCommand::ModelChangingExecuteImpl(
39 SyncSession* session) {
40 syncable::Directory* dir = session->context()->directory();
42 const sessions::UpdateProgress* progress =
43 session->status_controller().update_progress();
44 if (!progress)
45 return SYNCER_OK; // Nothing to do.
47 syncable::WriteTransaction trans(FROM_HERE, syncable::SYNCER, dir);
48 vector<sessions::VerifiedUpdate>::const_iterator it;
49 for (it = progress->VerifiedUpdatesBegin();
50 it != progress->VerifiedUpdatesEnd();
51 ++it) {
52 const sync_pb::SyncEntity& update = it->second;
54 if (it->first != VERIFY_SUCCESS && it->first != VERIFY_UNDELETE)
55 continue;
56 switch (ProcessUpdate(update,
57 dir->GetCryptographer(&trans),
58 &trans)) {
59 case SUCCESS_PROCESSED:
60 case SUCCESS_STORED:
61 break;
62 default:
63 NOTREACHED();
64 break;
68 StatusController* status = session->mutable_status_controller();
69 status->mutable_update_progress()->ClearVerifiedUpdates();
70 return SYNCER_OK;
73 namespace {
74 // Returns true if the entry is still ok to process.
75 bool ReverifyEntry(syncable::WriteTransaction* trans,
76 const sync_pb::SyncEntity& entry,
77 syncable::MutableEntry* same_id) {
79 const bool deleted = entry.has_deleted() && entry.deleted();
80 const bool is_directory = IsFolder(entry);
81 const ModelType model_type = GetModelType(entry);
83 return VERIFY_SUCCESS == VerifyUpdateConsistency(trans,
84 entry,
85 same_id,
86 deleted,
87 is_directory,
88 model_type);
90 } // namespace
92 // Process a single update. Will avoid touching global state.
93 ServerUpdateProcessingResult ProcessUpdatesCommand::ProcessUpdate(
94 const sync_pb::SyncEntity& update,
95 const Cryptographer* cryptographer,
96 syncable::WriteTransaction* const trans) {
97 const syncable::Id& server_id = SyncableIdFromProto(update.id_string());
98 const std::string name = SyncerProtoUtil::NameFromSyncEntity(update);
100 // Look to see if there's a local item that should recieve this update,
101 // maybe due to a duplicate client tag or a lost commit response.
102 syncable::Id local_id = FindLocalIdToUpdate(trans, update);
104 // FindLocalEntryToUpdate has veto power.
105 if (local_id.IsNull()) {
106 return SUCCESS_PROCESSED; // The entry has become irrelevant.
109 CreateNewEntry(trans, local_id);
111 // We take a two step approach. First we store the entries data in the
112 // server fields of a local entry and then move the data to the local fields
113 syncable::MutableEntry target_entry(trans, syncable::GET_BY_ID, local_id);
115 // We need to run the Verify checks again; the world could have changed
116 // since VerifyUpdatesCommand.
117 if (!ReverifyEntry(trans, update, &target_entry)) {
118 return SUCCESS_PROCESSED; // The entry has become irrelevant.
121 // If we're repurposing an existing local entry with a new server ID,
122 // change the ID now, after we're sure that the update can succeed.
123 if (local_id != server_id) {
124 DCHECK(!update.deleted());
125 ChangeEntryIDAndUpdateChildren(trans, &target_entry, server_id);
126 // When IDs change, versions become irrelevant. Forcing BASE_VERSION
127 // to zero would ensure that this update gets applied, but would indicate
128 // creation or undeletion if it were committed that way. Instead, prefer
129 // forcing BASE_VERSION to entry.version() while also forcing
130 // IS_UNAPPLIED_UPDATE to true. If the item is UNSYNCED, it's committable
131 // from the new state; it may commit before the conflict resolver gets
132 // a crack at it.
133 if (target_entry.Get(syncable::IS_UNSYNCED) ||
134 target_entry.Get(syncable::BASE_VERSION) > 0) {
135 // If either of these conditions are met, then we can expect valid client
136 // fields for this entry. When BASE_VERSION is positive, consistency is
137 // enforced on the client fields at update-application time. Otherwise,
138 // we leave the BASE_VERSION field alone; it'll get updated the first time
139 // we successfully apply this update.
140 target_entry.Put(syncable::BASE_VERSION, update.version());
142 // Force application of this update, no matter what.
143 target_entry.Put(syncable::IS_UNAPPLIED_UPDATE, true);
146 // If this is a newly received undecryptable update, and the only thing that
147 // has changed are the specifics, store the original decryptable specifics,
148 // (on which any current or future local changes are based) before we
149 // overwrite SERVER_SPECIFICS.
150 // MTIME, CTIME, and NON_UNIQUE_NAME are not enforced.
151 if (!update.deleted() && !target_entry.Get(syncable::SERVER_IS_DEL) &&
152 (SyncableIdFromProto(update.parent_id_string()) ==
153 target_entry.Get(syncable::SERVER_PARENT_ID)) &&
154 (update.position_in_parent() ==
155 target_entry.Get(syncable::SERVER_POSITION_IN_PARENT)) &&
156 update.has_specifics() && update.specifics().has_encrypted() &&
157 !cryptographer->CanDecrypt(update.specifics().encrypted())) {
158 sync_pb::EntitySpecifics prev_specifics =
159 target_entry.Get(syncable::SERVER_SPECIFICS);
160 // We only store the old specifics if they were decryptable and applied and
161 // there is no BASE_SERVER_SPECIFICS already. Else do nothing.
162 if (!target_entry.Get(syncable::IS_UNAPPLIED_UPDATE) &&
163 !IsRealDataType(GetModelTypeFromSpecifics(
164 target_entry.Get(syncable::BASE_SERVER_SPECIFICS))) &&
165 (!prev_specifics.has_encrypted() ||
166 cryptographer->CanDecrypt(prev_specifics.encrypted()))) {
167 DVLOG(2) << "Storing previous server specifcs: "
168 << prev_specifics.SerializeAsString();
169 target_entry.Put(syncable::BASE_SERVER_SPECIFICS, prev_specifics);
171 } else if (IsRealDataType(GetModelTypeFromSpecifics(
172 target_entry.Get(syncable::BASE_SERVER_SPECIFICS)))) {
173 // We have a BASE_SERVER_SPECIFICS, but a subsequent non-specifics-only
174 // change arrived. As a result, we can't use the specifics alone to detect
175 // changes, so we clear BASE_SERVER_SPECIFICS.
176 target_entry.Put(syncable::BASE_SERVER_SPECIFICS,
177 sync_pb::EntitySpecifics());
180 UpdateServerFieldsFromUpdate(&target_entry, update, name);
182 return SUCCESS_PROCESSED;
185 } // namespace syncer