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. 4 5 #include "chrome/browser/sync/glue/generic_change_processor.h" 6 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. 22 23 using content::BrowserThread; 24 25 namespace browser_sync { 26 27 namespace { 28 29 void SetNodeSpecifics(const sync_pb::EntitySpecifics& entity_specifics, 30 syncer::WriteNode* write_node) { 31 if (syncer::GetModelTypeFromSpecifics(entity_specifics) == 32 syncer::PASSWORDS) { 33 write_node->SetPasswordSpecifics( 34 entity_specifics.password().client_only_encrypted_data()); 35 } else { 36 write_node->SetEntitySpecifics(entity_specifics); 37 } 38 } 39 40 syncer::SyncData BuildRemoteSyncData( 41 int64 sync_id, 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()); 49 } 50 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, 57 password_holder, 58 read_node.GetModificationTime()); 59 } 60 61 } // namespace 62 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()); 73 } 74 75 GenericChangeProcessor::~GenericChangeProcessor() { 76 DCHECK(CalledOnValidThread()); 77 } 78 79 void GenericChangeProcessor::ApplyChangesFromSyncModel( 80 const syncer::BaseTransaction* trans, 81 int64 model_version, 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 syncer_changes_.push_back( 89 syncer::SyncChange( 90 FROM_HERE, 91 syncer::SyncChange::ACTION_DELETE, 92 syncer::SyncData::CreateRemoteData( 93 it->id, it->specifics, base::Time()))); 94 } else { 95 syncer::SyncChange::SyncChangeType action = 96 (it->action == syncer::ChangeRecord::ACTION_ADD) ? 97 syncer::SyncChange::ACTION_ADD : syncer::SyncChange::ACTION_UPDATE; 98 // Need to load specifics from node. 99 syncer::ReadNode read_node(trans); 100 if (read_node.InitByIdLookup(it->id) != syncer::BaseNode::INIT_OK) { 101 error_handler()->OnSingleDatatypeUnrecoverableError( 102 FROM_HERE, 103 "Failed to look up data for received change with id " + 104 base::Int64ToString(it->id)); 105 return; 106 } 107 syncer_changes_.push_back( 108 syncer::SyncChange( 109 FROM_HERE, 110 action, 111 BuildRemoteSyncData(it->id, read_node))); 112 } 113 } 114 } 115 116 void GenericChangeProcessor::CommitChangesFromSyncModel() { 117 DCHECK(CalledOnValidThread()); 118 if (syncer_changes_.empty()) 119 return; 120 if (!local_service_.get()) { 121 syncer::ModelType type = syncer_changes_[0].sync_data().GetDataType(); 122 syncer::SyncError error(FROM_HERE, 123 syncer::SyncError::DATATYPE_ERROR, 124 "Local service destroyed.", 125 type); 126 error_handler()->OnSingleDatatypeUnrecoverableError(error.location(), 127 error.message()); 128 return; 129 } 130 syncer::SyncError error = local_service_->ProcessSyncChanges(FROM_HERE, 131 syncer_changes_); 132 syncer_changes_.clear(); 133 if (error.IsSet()) { 134 error_handler()->OnSingleDatatypeUnrecoverableError( 135 error.location(), error.message()); 136 } 137 } 138 139 syncer::SyncError GenericChangeProcessor::GetSyncDataForType( 140 syncer::ModelType type, 141 syncer::SyncDataList* current_sync_data) { 142 DCHECK(CalledOnValidThread()); 143 std::string type_name = syncer::ModelTypeToString(type); 144 syncer::ReadTransaction trans(FROM_HERE, share_handle()); 145 syncer::ReadNode root(&trans); 146 if (root.InitByTagLookup(syncer::ModelTypeToRootTag(type)) != 147 syncer::BaseNode::INIT_OK) { 148 syncer::SyncError error(FROM_HERE, 149 syncer::SyncError::DATATYPE_ERROR, 150 "Server did not create the top-level " + type_name + 151 " node. We might be running against an out-of-" 152 "date server.", 153 type); 154 return error; 155 } 156 157 // TODO(akalin): We'll have to do a tree traversal for bookmarks. 158 DCHECK_NE(type, syncer::BOOKMARKS); 159 160 std::vector<int64> child_ids; 161 root.GetChildIds(&child_ids); 162 163 for (std::vector<int64>::iterator it = child_ids.begin(); 164 it != child_ids.end(); ++it) { 165 syncer::ReadNode sync_child_node(&trans); 166 if (sync_child_node.InitByIdLookup(*it) != 167 syncer::BaseNode::INIT_OK) { 168 syncer::SyncError error(FROM_HERE, 169 syncer::SyncError::DATATYPE_ERROR, 170 "Failed to fetch child node for type " + 171 type_name + ".", 172 type); 173 return error; 174 } 175 current_sync_data->push_back(BuildRemoteSyncData(sync_child_node.GetId(), 176 sync_child_node)); 177 } 178 return syncer::SyncError(); 179 } 180 181 int GenericChangeProcessor::GetSyncCountForType(syncer::ModelType type) { 182 syncer::ReadTransaction trans(FROM_HERE, share_handle()); 183 syncer::ReadNode root(&trans); 184 if (root.InitByTagLookup(syncer::ModelTypeToRootTag(type)) != 185 syncer::BaseNode::INIT_OK) 186 return 0; 187 188 // Subtract one to account for type's root node. 189 return root.GetTotalNodeCount() - 1; 190 } 191 192 namespace { 193 194 // TODO(isherman): Investigating http://crbug.com/121592 195 // WARNING: this code is sensitive to compiler optimizations. Be careful 196 // modifying any code around an OnSingleDatatypeUnrecoverableError call, else 197 // the compiler attempts to merge it with other calls, losing useful information 198 // in breakpad uploads. 199 syncer::SyncError LogLookupFailure( 200 syncer::BaseNode::InitByLookupResult lookup_result, 201 const tracked_objects::Location& from_here, 202 const std::string& error_prefix, 203 syncer::ModelType type, 204 DataTypeErrorHandler* error_handler) { 205 switch (lookup_result) { 206 case syncer::BaseNode::INIT_FAILED_ENTRY_NOT_GOOD: { 207 syncer::SyncError error; 208 error.Reset(from_here, 209 error_prefix + 210 "could not find entry matching the lookup criteria.", 211 type); 212 error_handler->OnSingleDatatypeUnrecoverableError(FROM_HERE, 213 error.message()); 214 LOG(ERROR) << "Delete: Bad entry."; 215 return error; 216 } 217 case syncer::BaseNode::INIT_FAILED_ENTRY_IS_DEL: { 218 syncer::SyncError error; 219 error.Reset(from_here, error_prefix + "entry is already deleted.", type); 220 error_handler->OnSingleDatatypeUnrecoverableError(FROM_HERE, 221 error.message()); 222 LOG(ERROR) << "Delete: Deleted entry."; 223 return error; 224 } 225 case syncer::BaseNode::INIT_FAILED_DECRYPT_IF_NECESSARY: { 226 syncer::SyncError error; 227 error.Reset(from_here, error_prefix + "unable to decrypt", type); 228 error_handler->OnSingleDatatypeUnrecoverableError(FROM_HERE, 229 error.message()); 230 LOG(ERROR) << "Delete: Undecryptable entry."; 231 return error; 232 } 233 case syncer::BaseNode::INIT_FAILED_PRECONDITION: { 234 syncer::SyncError error; 235 error.Reset(from_here, 236 error_prefix + "a precondition was not met for calling init.", 237 type); 238 error_handler->OnSingleDatatypeUnrecoverableError(FROM_HERE, 239 error.message()); 240 LOG(ERROR) << "Delete: Failed precondition."; 241 return error; 242 } 243 default: { 244 syncer::SyncError error; 245 // Should have listed all the possible error cases above. 246 error.Reset(from_here, error_prefix + "unknown error", type); 247 error_handler->OnSingleDatatypeUnrecoverableError(FROM_HERE, 248 error.message()); 249 LOG(ERROR) << "Delete: Unknown error."; 250 return error; 251 } 252 } 253 } 254 255 syncer::SyncError AttemptDelete( 256 const syncer::SyncChange& change, 257 syncer::ModelType type, 258 const std::string& type_str, 259 syncer::WriteNode* node, 260 DataTypeErrorHandler* error_handler) { 261 DCHECK_EQ(change.change_type(), syncer::SyncChange::ACTION_DELETE); 262 if (change.sync_data().IsLocal()) { 263 const std::string& tag = change.sync_data().GetTag(); 264 if (tag.empty()) { 265 syncer::SyncError error( 266 FROM_HERE, 267 syncer::SyncError::DATATYPE_ERROR, 268 "Failed to delete " + type_str + " node. Local data, empty tag. " + 269 change.location().ToString(), 270 type); 271 error_handler->OnSingleDatatypeUnrecoverableError(error.location(), 272 error.message()); 273 NOTREACHED(); 274 return error; 275 } 276 277 syncer::BaseNode::InitByLookupResult result = 278 node->InitByClientTagLookup(change.sync_data().GetDataType(), tag); 279 if (result != syncer::BaseNode::INIT_OK) { 280 return LogLookupFailure( 281 result, FROM_HERE, 282 "Failed to delete " + type_str + " node. Local data. " + 283 change.location().ToString(), 284 type, error_handler); 285 } 286 } else { 287 syncer::BaseNode::InitByLookupResult result = 288 node->InitByIdLookup(change.sync_data().GetRemoteId()); 289 if (result != syncer::BaseNode::INIT_OK) { 290 return LogLookupFailure( 291 result, FROM_HERE, 292 "Failed to delete " + type_str + " node. Non-local data. " + 293 change.location().ToString(), 294 type, error_handler); 295 } 296 } 297 if (IsActOnceDataType(type)) 298 node->Drop(); 299 else 300 node->Tombstone(); 301 return syncer::SyncError(); 302 } 303 304 } // namespace 305 306 // WARNING: this code is sensitive to compiler optimizations. Be careful 307 // modifying any code around an OnSingleDatatypeUnrecoverableError call, else 308 // the compiler attempts to merge it with other calls, losing useful information 309 // in breakpad uploads. 310 syncer::SyncError GenericChangeProcessor::ProcessSyncChanges( 311 const tracked_objects::Location& from_here, 312 const syncer::SyncChangeList& list_of_changes) { 313 DCHECK(CalledOnValidThread()); 314 syncer::WriteTransaction trans(from_here, share_handle()); 315 316 for (syncer::SyncChangeList::const_iterator iter = list_of_changes.begin(); 317 iter != list_of_changes.end(); 318 ++iter) { 319 const syncer::SyncChange& change = *iter; 320 DCHECK_NE(change.sync_data().GetDataType(), syncer::UNSPECIFIED); 321 syncer::ModelType type = change.sync_data().GetDataType(); 322 std::string type_str = syncer::ModelTypeToString(type); 323 syncer::WriteNode sync_node(&trans); 324 if (change.change_type() == syncer::SyncChange::ACTION_DELETE) { 325 syncer::SyncError error = 326 AttemptDelete(change, type, type_str, &sync_node, 327 error_handler()); 328 if (error.IsSet()) { 329 NOTREACHED(); 330 return error; 331 } 332 if (merge_result_.get()) { 333 merge_result_->set_num_items_deleted( 334 merge_result_->num_items_deleted() + 1); 335 } 336 } else if (change.change_type() == syncer::SyncChange::ACTION_ADD) { 337 // TODO(sync): Handle other types of creation (custom parents, folders, 338 // etc.). 339 syncer::ReadNode root_node(&trans); 340 if (root_node.InitByTagLookup( 341 syncer::ModelTypeToRootTag(change.sync_data().GetDataType())) != 342 syncer::BaseNode::INIT_OK) { 343 syncer::SyncError error(FROM_HERE, 344 syncer::SyncError::DATATYPE_ERROR, 345 "Failed to look up root node for type " + 346 type_str, 347 type); 348 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE, 349 error.message()); 350 NOTREACHED(); 351 LOG(ERROR) << "Create: no root node."; 352 return error; 353 } 354 syncer::WriteNode::InitUniqueByCreationResult result = 355 sync_node.InitUniqueByCreation(change.sync_data().GetDataType(), 356 root_node, 357 change.sync_data().GetTag()); 358 if (result != syncer::WriteNode::INIT_SUCCESS) { 359 std::string error_prefix = "Failed to create " + type_str + " node: " + 360 change.location().ToString() + ", "; 361 switch (result) { 362 case syncer::WriteNode::INIT_FAILED_EMPTY_TAG: { 363 syncer::SyncError error; 364 error.Reset(FROM_HERE, error_prefix + "empty tag", type); 365 error_handler()->OnSingleDatatypeUnrecoverableError( 366 FROM_HERE, error.message()); 367 LOG(ERROR) << "Create: Empty tag."; 368 return error; 369 } 370 case syncer::WriteNode::INIT_FAILED_ENTRY_ALREADY_EXISTS: { 371 syncer::SyncError error; 372 error.Reset(FROM_HERE, error_prefix + "entry already exists", type); 373 error_handler()->OnSingleDatatypeUnrecoverableError( 374 FROM_HERE, error.message()); 375 LOG(ERROR) << "Create: Entry exists."; 376 return error; 377 } 378 case syncer::WriteNode::INIT_FAILED_COULD_NOT_CREATE_ENTRY: { 379 syncer::SyncError error; 380 error.Reset(FROM_HERE, error_prefix + "failed to create entry", 381 type); 382 error_handler()->OnSingleDatatypeUnrecoverableError( 383 FROM_HERE, error.message()); 384 LOG(ERROR) << "Create: Could not create entry."; 385 return error; 386 } 387 case syncer::WriteNode::INIT_FAILED_SET_PREDECESSOR: { 388 syncer::SyncError error; 389 error.Reset(FROM_HERE, error_prefix + "failed to set predecessor", 390 type); 391 error_handler()->OnSingleDatatypeUnrecoverableError( 392 FROM_HERE, error.message()); 393 LOG(ERROR) << "Create: Bad predecessor."; 394 return error; 395 } 396 default: { 397 syncer::SyncError error; 398 error.Reset(FROM_HERE, error_prefix + "unknown error", type); 399 error_handler()->OnSingleDatatypeUnrecoverableError( 400 FROM_HERE, error.message()); 401 LOG(ERROR) << "Create: Unknown error."; 402 return error; 403 } 404 } 405 } 406 sync_node.SetTitle(UTF8ToWide(change.sync_data().GetTitle())); 407 SetNodeSpecifics(change.sync_data().GetSpecifics(), &sync_node); 408 if (merge_result_.get()) { 409 merge_result_->set_num_items_added(merge_result_->num_items_added() + 410 1); 411 } 412 } else if (change.change_type() == syncer::SyncChange::ACTION_UPDATE) { 413 // TODO(zea): consider having this logic for all possible changes? 414 syncer::BaseNode::InitByLookupResult result = 415 sync_node.InitByClientTagLookup(change.sync_data().GetDataType(), 416 change.sync_data().GetTag()); 417 if (result != syncer::BaseNode::INIT_OK) { 418 std::string error_prefix = "Failed to load " + type_str + " node. " + 419 change.location().ToString() + ", "; 420 if (result == syncer::BaseNode::INIT_FAILED_PRECONDITION) { 421 syncer::SyncError error; 422 error.Reset(FROM_HERE, 423 error_prefix + "empty tag", 424 type); 425 error_handler()->OnSingleDatatypeUnrecoverableError( 426 FROM_HERE, error.message()); 427 LOG(ERROR) << "Update: Empty tag."; 428 return error; 429 } else if (result == syncer::BaseNode::INIT_FAILED_ENTRY_NOT_GOOD) { 430 syncer::SyncError error; 431 error.Reset(FROM_HERE, 432 error_prefix + "bad entry", 433 type); 434 error_handler()->OnSingleDatatypeUnrecoverableError( 435 FROM_HERE, error.message()); 436 LOG(ERROR) << "Update: bad entry."; 437 return error; 438 } else if (result == syncer::BaseNode::INIT_FAILED_ENTRY_IS_DEL) { 439 syncer::SyncError error; 440 error.Reset(FROM_HERE, 441 error_prefix + "deleted entry", 442 type); 443 error_handler()->OnSingleDatatypeUnrecoverableError( 444 FROM_HERE, error.message()); 445 LOG(ERROR) << "Update: deleted entry."; 446 return error; 447 } else { 448 syncer::Cryptographer* crypto = trans.GetCryptographer(); 449 syncer::ModelTypeSet encrypted_types(trans.GetEncryptedTypes()); 450 const sync_pb::EntitySpecifics& specifics = 451 sync_node.GetEntry()->Get(syncer::syncable::SPECIFICS); 452 CHECK(specifics.has_encrypted()); 453 const bool can_decrypt = crypto->CanDecrypt(specifics.encrypted()); 454 const bool agreement = encrypted_types.Has(type); 455 if (!agreement && !can_decrypt) { 456 syncer::SyncError error; 457 error.Reset(FROM_HERE, 458 "Failed to load encrypted entry, missing key and " 459 "nigori mismatch for " + type_str + ".", 460 type); 461 error_handler()->OnSingleDatatypeUnrecoverableError( 462 FROM_HERE, error.message()); 463 LOG(ERROR) << "Update: encr case 1."; 464 return error; 465 } else if (agreement && can_decrypt) { 466 syncer::SyncError error; 467 error.Reset(FROM_HERE, 468 "Failed to load encrypted entry, we have the key " 469 "and the nigori matches (?!) for " + type_str + ".", 470 type); 471 error_handler()->OnSingleDatatypeUnrecoverableError( 472 FROM_HERE, error.message()); 473 LOG(ERROR) << "Update: encr case 2."; 474 return error; 475 } else if (agreement) { 476 syncer::SyncError error; 477 error.Reset(FROM_HERE, 478 "Failed to load encrypted entry, missing key and " 479 "the nigori matches for " + type_str + ".", 480 type); 481 error_handler()->OnSingleDatatypeUnrecoverableError( 482 FROM_HERE, error.message()); 483 LOG(ERROR) << "Update: encr case 3."; 484 return error; 485 } else { 486 syncer::SyncError error; 487 error.Reset(FROM_HERE, 488 "Failed to load encrypted entry, we have the key" 489 "(?!) and nigori mismatch for " + type_str + ".", 490 type); 491 error_handler()->OnSingleDatatypeUnrecoverableError( 492 FROM_HERE, error.message()); 493 LOG(ERROR) << "Update: encr case 4."; 494 return error; 495 } 496 } 497 } 498 499 sync_node.SetTitle(UTF8ToWide(change.sync_data().GetTitle())); 500 SetNodeSpecifics(change.sync_data().GetSpecifics(), &sync_node); 501 if (merge_result_.get()) { 502 merge_result_->set_num_items_modified( 503 merge_result_->num_items_modified() + 1); 504 } 505 // TODO(sync): Support updating other parts of the sync node (title, 506 // successor, parent, etc.). 507 } else { 508 syncer::SyncError error( 509 FROM_HERE, 510 syncer::SyncError::DATATYPE_ERROR, 511 "Received unset SyncChange in the change processor, " + 512 change.location().ToString(), 513 type); 514 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE, 515 error.message()); 516 NOTREACHED(); 517 LOG(ERROR) << "Unset sync change."; 518 return error; 519 } 520 } 521 return syncer::SyncError(); 522 } 523 524 bool GenericChangeProcessor::SyncModelHasUserCreatedNodes( 525 syncer::ModelType type, 526 bool* has_nodes) { 527 DCHECK(CalledOnValidThread()); 528 DCHECK(has_nodes); 529 DCHECK_NE(type, syncer::UNSPECIFIED); 530 std::string type_name = syncer::ModelTypeToString(type); 531 std::string err_str = "Server did not create the top-level " + type_name + 532 " node. We might be running against an out-of-date server."; 533 *has_nodes = false; 534 syncer::ReadTransaction trans(FROM_HERE, share_handle()); 535 syncer::ReadNode type_root_node(&trans); 536 if (type_root_node.InitByTagLookup(syncer::ModelTypeToRootTag(type)) != 537 syncer::BaseNode::INIT_OK) { 538 LOG(ERROR) << err_str; 539 return false; 540 } 541 542 // The sync model has user created nodes if the type's root node has any 543 // children. 544 *has_nodes = type_root_node.HasChildren(); 545 return true; 546 } 547 548 bool GenericChangeProcessor::CryptoReadyIfNecessary(syncer::ModelType type) { 549 DCHECK(CalledOnValidThread()); 550 DCHECK_NE(type, syncer::UNSPECIFIED); 551 // We only access the cryptographer while holding a transaction. 552 syncer::ReadTransaction trans(FROM_HERE, share_handle()); 553 const syncer::ModelTypeSet encrypted_types = trans.GetEncryptedTypes(); 554 return !encrypted_types.Has(type) || 555 trans.GetCryptographer()->is_ready(); 556 } 557 558 void GenericChangeProcessor::StartImpl(Profile* profile) { 559 DCHECK(CalledOnValidThread()); 560 } 561 562 syncer::UserShare* GenericChangeProcessor::share_handle() const { 563 DCHECK(CalledOnValidThread()); 564 return share_handle_; 565 } 566 567 } // namespace browser_sync 568