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 "chrome/browser/sync/glue/generic_change_processor.h"
7 #include "base/location.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "content/public/browser/browser_thread.h"
11 #include "sync/api/sync_change.h"
12 #include "sync/api/sync_error.h"
13 #include "sync/api/syncable_service.h"
14 #include "sync/internal_api/public/base_node.h"
15 #include "sync/internal_api/public/change_record.h"
16 #include "sync/internal_api/public/read_node.h"
17 #include "sync/internal_api/public/read_transaction.h"
18 #include "sync/internal_api/public/util/unrecoverable_error_handler.h"
19 #include "sync/internal_api/public/write_node.h"
20 #include "sync/internal_api/public/write_transaction.h"
21 #include "sync/syncable/entry.h" // TODO(tim): Bug 123674.
23 using content::BrowserThread;
25 namespace browser_sync {
29 void SetNodeSpecifics(const sync_pb::EntitySpecifics& entity_specifics,
30 syncer::WriteNode* write_node) {
31 if (syncer::GetModelTypeFromSpecifics(entity_specifics) ==
33 write_node->SetPasswordSpecifics(
34 entity_specifics.password().client_only_encrypted_data());
36 write_node->SetEntitySpecifics(entity_specifics);
40 syncer::SyncData BuildRemoteSyncData(
42 const syncer::BaseNode& read_node) {
43 // Use the specifics of non-password datatypes directly (encryption has
44 // already been handled).
45 if (read_node.GetModelType() != syncer::PASSWORDS) {
46 return syncer::SyncData::CreateRemoteData(sync_id,
47 read_node.GetEntitySpecifics(),
48 read_node.GetModificationTime());
51 // Passwords must be accessed differently, to account for their encryption,
52 // and stored into a temporary EntitySpecifics.
53 sync_pb::EntitySpecifics password_holder;
54 password_holder.mutable_password()->mutable_client_only_encrypted_data()->
55 CopyFrom(read_node.GetPasswordSpecifics());
56 return syncer::SyncData::CreateRemoteData(sync_id,
58 read_node.GetModificationTime());
63 GenericChangeProcessor::GenericChangeProcessor(
64 DataTypeErrorHandler* error_handler,
65 const base::WeakPtr<syncer::SyncableService>& local_service,
66 const base::WeakPtr<syncer::SyncMergeResult>& merge_result,
67 syncer::UserShare* user_share)
68 : ChangeProcessor(error_handler),
69 local_service_(local_service),
70 merge_result_(merge_result),
71 share_handle_(user_share) {
72 DCHECK(CalledOnValidThread());
75 GenericChangeProcessor::~GenericChangeProcessor() {
76 DCHECK(CalledOnValidThread());
79 void GenericChangeProcessor::ApplyChangesFromSyncModel(
80 const syncer::BaseTransaction* trans,
82 const syncer::ImmutableChangeRecordList& changes) {
83 DCHECK(CalledOnValidThread());
84 DCHECK(syncer_changes_.empty());
85 for (syncer::ChangeRecordList::const_iterator it =
86 changes.Get().begin(); it != changes.Get().end(); ++it) {
87 if (it->action == syncer::ChangeRecord::ACTION_DELETE) {
88 scoped_ptr<sync_pb::EntitySpecifics> specifics;
89 if (it->specifics.has_password()) {
90 DCHECK(it->extra.get());
91 specifics.reset(new sync_pb::EntitySpecifics(it->specifics));
92 specifics->mutable_password()->mutable_client_only_encrypted_data()->
93 CopyFrom(it->extra->unencrypted());
95 syncer_changes_.push_back(syncer::SyncChange(
97 syncer::SyncChange::ACTION_DELETE,
98 syncer::SyncData::CreateRemoteData(
99 it->id, specifics ? *specifics : it->specifics, base::Time())));
101 syncer::SyncChange::SyncChangeType action =
102 (it->action == syncer::ChangeRecord::ACTION_ADD) ?
103 syncer::SyncChange::ACTION_ADD : syncer::SyncChange::ACTION_UPDATE;
104 // Need to load specifics from node.
105 syncer::ReadNode read_node(trans);
106 if (read_node.InitByIdLookup(it->id) != syncer::BaseNode::INIT_OK) {
107 error_handler()->OnSingleDatatypeUnrecoverableError(
109 "Failed to look up data for received change with id " +
110 base::Int64ToString(it->id));
113 syncer_changes_.push_back(
117 BuildRemoteSyncData(it->id, read_node)));
122 void GenericChangeProcessor::CommitChangesFromSyncModel() {
123 DCHECK(CalledOnValidThread());
124 if (syncer_changes_.empty())
126 if (!local_service_.get()) {
127 syncer::ModelType type = syncer_changes_[0].sync_data().GetDataType();
128 syncer::SyncError error(FROM_HERE,
129 syncer::SyncError::DATATYPE_ERROR,
130 "Local service destroyed.",
132 error_handler()->OnSingleDatatypeUnrecoverableError(error.location(),
136 syncer::SyncError error = local_service_->ProcessSyncChanges(FROM_HERE,
138 syncer_changes_.clear();
140 error_handler()->OnSingleDatatypeUnrecoverableError(
141 error.location(), error.message());
145 syncer::SyncDataList GenericChangeProcessor::GetAllSyncData(
146 syncer::ModelType type) const {
147 // This is slow / memory intensive. Should be used sparingly by datatypes.
148 syncer::SyncDataList data;
149 GetAllSyncDataReturnError(type, &data);
153 syncer::SyncError GenericChangeProcessor::GetAllSyncDataReturnError(
154 syncer::ModelType type,
155 syncer::SyncDataList* current_sync_data) const {
156 DCHECK(CalledOnValidThread());
157 std::string type_name = syncer::ModelTypeToString(type);
158 syncer::ReadTransaction trans(FROM_HERE, share_handle());
159 syncer::ReadNode root(&trans);
160 if (root.InitByTagLookup(syncer::ModelTypeToRootTag(type)) !=
161 syncer::BaseNode::INIT_OK) {
162 syncer::SyncError error(FROM_HERE,
163 syncer::SyncError::DATATYPE_ERROR,
164 "Server did not create the top-level " + type_name +
165 " node. We might be running against an out-of-"
171 // TODO(akalin): We'll have to do a tree traversal for bookmarks.
172 DCHECK_NE(type, syncer::BOOKMARKS);
174 std::vector<int64> child_ids;
175 root.GetChildIds(&child_ids);
177 for (std::vector<int64>::iterator it = child_ids.begin();
178 it != child_ids.end(); ++it) {
179 syncer::ReadNode sync_child_node(&trans);
180 if (sync_child_node.InitByIdLookup(*it) !=
181 syncer::BaseNode::INIT_OK) {
182 syncer::SyncError error(FROM_HERE,
183 syncer::SyncError::DATATYPE_ERROR,
184 "Failed to fetch child node for type " +
189 current_sync_data->push_back(BuildRemoteSyncData(sync_child_node.GetId(),
192 return syncer::SyncError();
195 int GenericChangeProcessor::GetSyncCountForType(syncer::ModelType type) {
196 syncer::ReadTransaction trans(FROM_HERE, share_handle());
197 syncer::ReadNode root(&trans);
198 if (root.InitByTagLookup(syncer::ModelTypeToRootTag(type)) !=
199 syncer::BaseNode::INIT_OK)
202 // Subtract one to account for type's root node.
203 return root.GetTotalNodeCount() - 1;
208 // TODO(isherman): Investigating http://crbug.com/121592
209 // WARNING: this code is sensitive to compiler optimizations. Be careful
210 // modifying any code around an OnSingleDatatypeUnrecoverableError call, else
211 // the compiler attempts to merge it with other calls, losing useful information
212 // in breakpad uploads.
213 syncer::SyncError LogLookupFailure(
214 syncer::BaseNode::InitByLookupResult lookup_result,
215 const tracked_objects::Location& from_here,
216 const std::string& error_prefix,
217 syncer::ModelType type,
218 DataTypeErrorHandler* error_handler) {
219 switch (lookup_result) {
220 case syncer::BaseNode::INIT_FAILED_ENTRY_NOT_GOOD: {
221 syncer::SyncError error;
222 error.Reset(from_here,
224 "could not find entry matching the lookup criteria.",
226 error_handler->OnSingleDatatypeUnrecoverableError(FROM_HERE,
228 LOG(ERROR) << "Delete: Bad entry.";
231 case syncer::BaseNode::INIT_FAILED_ENTRY_IS_DEL: {
232 syncer::SyncError error;
233 error.Reset(from_here, error_prefix + "entry is already deleted.", type);
234 error_handler->OnSingleDatatypeUnrecoverableError(FROM_HERE,
236 LOG(ERROR) << "Delete: Deleted entry.";
239 case syncer::BaseNode::INIT_FAILED_DECRYPT_IF_NECESSARY: {
240 syncer::SyncError error;
241 error.Reset(from_here, error_prefix + "unable to decrypt", type);
242 error_handler->OnSingleDatatypeUnrecoverableError(FROM_HERE,
244 LOG(ERROR) << "Delete: Undecryptable entry.";
247 case syncer::BaseNode::INIT_FAILED_PRECONDITION: {
248 syncer::SyncError error;
249 error.Reset(from_here,
250 error_prefix + "a precondition was not met for calling init.",
252 error_handler->OnSingleDatatypeUnrecoverableError(FROM_HERE,
254 LOG(ERROR) << "Delete: Failed precondition.";
258 syncer::SyncError error;
259 // Should have listed all the possible error cases above.
260 error.Reset(from_here, error_prefix + "unknown error", type);
261 error_handler->OnSingleDatatypeUnrecoverableError(FROM_HERE,
263 LOG(ERROR) << "Delete: Unknown error.";
269 syncer::SyncError AttemptDelete(
270 const syncer::SyncChange& change,
271 syncer::ModelType type,
272 const std::string& type_str,
273 syncer::WriteNode* node,
274 DataTypeErrorHandler* error_handler) {
275 DCHECK_EQ(change.change_type(), syncer::SyncChange::ACTION_DELETE);
276 if (change.sync_data().IsLocal()) {
277 const std::string& tag = change.sync_data().GetTag();
279 syncer::SyncError error(
281 syncer::SyncError::DATATYPE_ERROR,
282 "Failed to delete " + type_str + " node. Local data, empty tag. " +
283 change.location().ToString(),
285 error_handler->OnSingleDatatypeUnrecoverableError(error.location(),
291 syncer::BaseNode::InitByLookupResult result =
292 node->InitByClientTagLookup(change.sync_data().GetDataType(), tag);
293 if (result != syncer::BaseNode::INIT_OK) {
294 return LogLookupFailure(
296 "Failed to delete " + type_str + " node. Local data. " +
297 change.location().ToString(),
298 type, error_handler);
301 syncer::BaseNode::InitByLookupResult result =
302 node->InitByIdLookup(change.sync_data().GetRemoteId());
303 if (result != syncer::BaseNode::INIT_OK) {
304 return LogLookupFailure(
306 "Failed to delete " + type_str + " node. Non-local data. " +
307 change.location().ToString(),
308 type, error_handler);
311 if (IsActOnceDataType(type))
315 return syncer::SyncError();
320 syncer::SyncError GenericChangeProcessor::ProcessSyncChanges(
321 const tracked_objects::Location& from_here,
322 const syncer::SyncChangeList& list_of_changes) {
323 DCHECK(CalledOnValidThread());
324 syncer::WriteTransaction trans(from_here, share_handle());
326 for (syncer::SyncChangeList::const_iterator iter = list_of_changes.begin();
327 iter != list_of_changes.end();
329 const syncer::SyncChange& change = *iter;
330 DCHECK_NE(change.sync_data().GetDataType(), syncer::UNSPECIFIED);
331 syncer::ModelType type = change.sync_data().GetDataType();
332 std::string type_str = syncer::ModelTypeToString(type);
333 syncer::WriteNode sync_node(&trans);
334 if (change.change_type() == syncer::SyncChange::ACTION_DELETE) {
335 syncer::SyncError error =
336 AttemptDelete(change, type, type_str, &sync_node, error_handler());
341 if (merge_result_.get()) {
342 merge_result_->set_num_items_deleted(
343 merge_result_->num_items_deleted() + 1);
345 } else if (change.change_type() == syncer::SyncChange::ACTION_ADD) {
346 syncer::SyncError error =
347 HandleActionAdd(change, type_str, type, trans, &sync_node);
351 } else if (change.change_type() == syncer::SyncChange::ACTION_UPDATE) {
352 syncer::SyncError error =
353 HandleActionUpdate(change, type_str, type, trans, &sync_node);
358 syncer::SyncError error(
360 syncer::SyncError::DATATYPE_ERROR,
361 "Received unset SyncChange in the change processor, " +
362 change.location().ToString(),
364 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
367 LOG(ERROR) << "Unset sync change.";
371 return syncer::SyncError();
374 // WARNING: this code is sensitive to compiler optimizations. Be careful
375 // modifying any code around an OnSingleDatatypeUnrecoverableError call, else
376 // the compiler attempts to merge it with other calls, losing useful information
377 // in breakpad uploads.
378 syncer::SyncError GenericChangeProcessor::HandleActionAdd(
379 const syncer::SyncChange& change,
380 const std::string& type_str,
381 const syncer::ModelType& type,
382 const syncer::WriteTransaction& trans,
383 syncer::WriteNode* sync_node) {
384 // TODO(sync): Handle other types of creation (custom parents, folders,
386 syncer::ReadNode root_node(&trans);
387 if (root_node.InitByTagLookup(syncer::ModelTypeToRootTag(
388 change.sync_data().GetDataType())) != syncer::BaseNode::INIT_OK) {
389 syncer::SyncError error(FROM_HERE,
390 syncer::SyncError::DATATYPE_ERROR,
391 "Failed to look up root node for type " + type_str,
393 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
396 LOG(ERROR) << "Create: no root node.";
399 syncer::WriteNode::InitUniqueByCreationResult result =
400 sync_node->InitUniqueByCreation(change.sync_data().GetDataType(),
402 change.sync_data().GetTag());
403 if (result != syncer::WriteNode::INIT_SUCCESS) {
404 std::string error_prefix = "Failed to create " + type_str + " node: " +
405 change.location().ToString() + ", ";
407 case syncer::WriteNode::INIT_FAILED_EMPTY_TAG: {
408 syncer::SyncError error;
409 error.Reset(FROM_HERE, error_prefix + "empty tag", type);
410 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
412 LOG(ERROR) << "Create: Empty tag.";
415 case syncer::WriteNode::INIT_FAILED_ENTRY_ALREADY_EXISTS: {
416 syncer::SyncError error;
417 error.Reset(FROM_HERE, error_prefix + "entry already exists", type);
418 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
420 LOG(ERROR) << "Create: Entry exists.";
423 case syncer::WriteNode::INIT_FAILED_COULD_NOT_CREATE_ENTRY: {
424 syncer::SyncError error;
425 error.Reset(FROM_HERE, error_prefix + "failed to create entry", type);
426 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
428 LOG(ERROR) << "Create: Could not create entry.";
431 case syncer::WriteNode::INIT_FAILED_SET_PREDECESSOR: {
432 syncer::SyncError error;
434 FROM_HERE, error_prefix + "failed to set predecessor", type);
435 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
437 LOG(ERROR) << "Create: Bad predecessor.";
441 syncer::SyncError error;
442 error.Reset(FROM_HERE, error_prefix + "unknown error", type);
443 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
445 LOG(ERROR) << "Create: Unknown error.";
450 sync_node->SetTitle(base::UTF8ToWide(change.sync_data().GetTitle()));
451 SetNodeSpecifics(change.sync_data().GetSpecifics(), sync_node);
452 if (merge_result_.get()) {
453 merge_result_->set_num_items_added(merge_result_->num_items_added() + 1);
455 return syncer::SyncError();
457 // WARNING: this code is sensitive to compiler optimizations. Be careful
458 // modifying any code around an OnSingleDatatypeUnrecoverableError call, else
459 // the compiler attempts to merge it with other calls, losing useful information
460 // in breakpad uploads.
461 syncer::SyncError GenericChangeProcessor::HandleActionUpdate(
462 const syncer::SyncChange& change,
463 const std::string& type_str,
464 const syncer::ModelType& type,
465 const syncer::WriteTransaction& trans,
466 syncer::WriteNode* sync_node) {
467 // TODO(zea): consider having this logic for all possible changes?
468 syncer::BaseNode::InitByLookupResult result =
469 sync_node->InitByClientTagLookup(change.sync_data().GetDataType(),
470 change.sync_data().GetTag());
471 if (result != syncer::BaseNode::INIT_OK) {
472 std::string error_prefix = "Failed to load " + type_str + " node. " +
473 change.location().ToString() + ", ";
474 if (result == syncer::BaseNode::INIT_FAILED_PRECONDITION) {
475 syncer::SyncError error;
476 error.Reset(FROM_HERE, error_prefix + "empty tag", type);
477 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
479 LOG(ERROR) << "Update: Empty tag.";
481 } else if (result == syncer::BaseNode::INIT_FAILED_ENTRY_NOT_GOOD) {
482 syncer::SyncError error;
483 error.Reset(FROM_HERE, error_prefix + "bad entry", type);
484 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
486 LOG(ERROR) << "Update: bad entry.";
488 } else if (result == syncer::BaseNode::INIT_FAILED_ENTRY_IS_DEL) {
489 syncer::SyncError error;
490 error.Reset(FROM_HERE, error_prefix + "deleted entry", type);
491 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
493 LOG(ERROR) << "Update: deleted entry.";
496 syncer::Cryptographer* crypto = trans.GetCryptographer();
497 syncer::ModelTypeSet encrypted_types(trans.GetEncryptedTypes());
498 const sync_pb::EntitySpecifics& specifics =
499 sync_node->GetEntry()->GetSpecifics();
500 CHECK(specifics.has_encrypted());
501 const bool can_decrypt = crypto->CanDecrypt(specifics.encrypted());
502 const bool agreement = encrypted_types.Has(type);
503 if (!agreement && !can_decrypt) {
504 syncer::SyncError error;
505 error.Reset(FROM_HERE,
506 "Failed to load encrypted entry, missing key and "
507 "nigori mismatch for " +
510 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
512 LOG(ERROR) << "Update: encr case 1.";
514 } else if (agreement && can_decrypt) {
515 syncer::SyncError error;
516 error.Reset(FROM_HERE,
517 "Failed to load encrypted entry, we have the key "
518 "and the nigori matches (?!) for " +
521 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
523 LOG(ERROR) << "Update: encr case 2.";
525 } else if (agreement) {
526 syncer::SyncError error;
527 error.Reset(FROM_HERE,
528 "Failed to load encrypted entry, missing key and "
529 "the nigori matches for " +
532 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
534 LOG(ERROR) << "Update: encr case 3.";
537 syncer::SyncError error;
538 error.Reset(FROM_HERE,
539 "Failed to load encrypted entry, we have the key"
540 "(?!) and nigori mismatch for " +
543 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
545 LOG(ERROR) << "Update: encr case 4.";
551 sync_node->SetTitle(base::UTF8ToWide(change.sync_data().GetTitle()));
552 SetNodeSpecifics(change.sync_data().GetSpecifics(), sync_node);
553 if (merge_result_.get()) {
554 merge_result_->set_num_items_modified(merge_result_->num_items_modified() +
557 // TODO(sync): Support updating other parts of the sync node (title,
558 // successor, parent, etc.).
559 return syncer::SyncError();
562 bool GenericChangeProcessor::SyncModelHasUserCreatedNodes(
563 syncer::ModelType type,
565 DCHECK(CalledOnValidThread());
567 DCHECK_NE(type, syncer::UNSPECIFIED);
568 std::string type_name = syncer::ModelTypeToString(type);
569 std::string err_str = "Server did not create the top-level " + type_name +
570 " node. We might be running against an out-of-date server.";
572 syncer::ReadTransaction trans(FROM_HERE, share_handle());
573 syncer::ReadNode type_root_node(&trans);
574 if (type_root_node.InitByTagLookup(syncer::ModelTypeToRootTag(type)) !=
575 syncer::BaseNode::INIT_OK) {
576 LOG(ERROR) << err_str;
580 // The sync model has user created nodes if the type's root node has any
582 *has_nodes = type_root_node.HasChildren();
586 bool GenericChangeProcessor::CryptoReadyIfNecessary(syncer::ModelType type) {
587 DCHECK(CalledOnValidThread());
588 DCHECK_NE(type, syncer::UNSPECIFIED);
589 // We only access the cryptographer while holding a transaction.
590 syncer::ReadTransaction trans(FROM_HERE, share_handle());
591 const syncer::ModelTypeSet encrypted_types = trans.GetEncryptedTypes();
592 return !encrypted_types.Has(type) ||
593 trans.GetCryptographer()->is_ready();
596 void GenericChangeProcessor::StartImpl(Profile* profile) {
597 DCHECK(CalledOnValidThread());
600 syncer::UserShare* GenericChangeProcessor::share_handle() const {
601 DCHECK(CalledOnValidThread());
602 return share_handle_;
605 } // namespace browser_sync