| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/sync/engine/syncer_util.h" | 5 #include "chrome/browser/sync/engine/syncer_util.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <set> | 8 #include <set> |
| 9 #include <string> | 9 #include <string> |
| 10 #include <vector> | 10 #include <vector> |
| 11 | 11 |
| 12 #include "base/location.h" | 12 #include "base/location.h" |
| 13 #include "base/metrics/histogram.h" |
| 13 #include "chrome/browser/sync/engine/conflict_resolver.h" | 14 #include "chrome/browser/sync/engine/conflict_resolver.h" |
| 14 #include "chrome/browser/sync/engine/nigori_util.h" | 15 #include "chrome/browser/sync/engine/nigori_util.h" |
| 15 #include "chrome/browser/sync/engine/syncer_proto_util.h" | 16 #include "chrome/browser/sync/engine/syncer_proto_util.h" |
| 16 #include "chrome/browser/sync/engine/syncer_types.h" | 17 #include "chrome/browser/sync/engine/syncer_types.h" |
| 17 #include "chrome/browser/sync/engine/syncproto.h" | 18 #include "chrome/browser/sync/engine/syncproto.h" |
| 18 #include "chrome/browser/sync/protocol/bookmark_specifics.pb.h" | 19 #include "chrome/browser/sync/protocol/bookmark_specifics.pb.h" |
| 19 #include "chrome/browser/sync/protocol/nigori_specifics.pb.h" | 20 #include "chrome/browser/sync/protocol/nigori_specifics.pb.h" |
| 20 #include "chrome/browser/sync/protocol/password_specifics.pb.h" | 21 #include "chrome/browser/sync/protocol/password_specifics.pb.h" |
| 21 #include "chrome/browser/sync/protocol/sync.pb.h" | 22 #include "chrome/browser/sync/protocol/sync.pb.h" |
| 22 #include "chrome/browser/sync/syncable/directory_manager.h" | 23 #include "chrome/browser/sync/syncable/directory_manager.h" |
| 23 #include "chrome/browser/sync/syncable/model_type.h" | 24 #include "chrome/browser/sync/syncable/model_type.h" |
| 24 #include "chrome/browser/sync/syncable/syncable.h" | 25 #include "chrome/browser/sync/syncable/syncable.h" |
| 25 #include "chrome/browser/sync/syncable/syncable_changes_version.h" | 26 #include "chrome/browser/sync/syncable/syncable_changes_version.h" |
| 27 #include "chrome/browser/sync/util/cryptographer.h" |
| 26 #include "chrome/browser/sync/util/time.h" | 28 #include "chrome/browser/sync/util/time.h" |
| 27 | 29 |
| 28 using syncable::BASE_VERSION; | 30 using syncable::BASE_VERSION; |
| 29 using syncable::Blob; | 31 using syncable::Blob; |
| 30 using syncable::CHANGES_VERSION; | 32 using syncable::CHANGES_VERSION; |
| 31 using syncable::CREATE; | 33 using syncable::CREATE; |
| 32 using syncable::CREATE_NEW_UPDATE_ITEM; | 34 using syncable::CREATE_NEW_UPDATE_ITEM; |
| 33 using syncable::CTIME; | 35 using syncable::CTIME; |
| 34 using syncable::Directory; | 36 using syncable::Directory; |
| 35 using syncable::Entry; | 37 using syncable::Entry; |
| 38 using syncable::GetModelTypeFromSpecifics; |
| 36 using syncable::GET_BY_HANDLE; | 39 using syncable::GET_BY_HANDLE; |
| 37 using syncable::GET_BY_ID; | 40 using syncable::GET_BY_ID; |
| 38 using syncable::ID; | 41 using syncable::ID; |
| 39 using syncable::IS_DEL; | 42 using syncable::IS_DEL; |
| 40 using syncable::IS_DIR; | 43 using syncable::IS_DIR; |
| 41 using syncable::IS_UNAPPLIED_UPDATE; | 44 using syncable::IS_UNAPPLIED_UPDATE; |
| 42 using syncable::IS_UNSYNCED; | 45 using syncable::IS_UNSYNCED; |
| 43 using syncable::Id; | 46 using syncable::Id; |
| 47 using syncable::IsRealDataType; |
| 44 using syncable::META_HANDLE; | 48 using syncable::META_HANDLE; |
| 45 using syncable::MTIME; | 49 using syncable::MTIME; |
| 46 using syncable::MutableEntry; | 50 using syncable::MutableEntry; |
| 47 using syncable::NEXT_ID; | 51 using syncable::NEXT_ID; |
| 48 using syncable::NON_UNIQUE_NAME; | 52 using syncable::NON_UNIQUE_NAME; |
| 53 using syncable::BASE_SERVER_SPECIFICS; |
| 49 using syncable::PARENT_ID; | 54 using syncable::PARENT_ID; |
| 50 using syncable::PREV_ID; | 55 using syncable::PREV_ID; |
| 51 using syncable::ReadTransaction; | 56 using syncable::ReadTransaction; |
| 52 using syncable::SERVER_CTIME; | 57 using syncable::SERVER_CTIME; |
| 53 using syncable::SERVER_IS_DEL; | 58 using syncable::SERVER_IS_DEL; |
| 54 using syncable::SERVER_IS_DIR; | 59 using syncable::SERVER_IS_DIR; |
| 55 using syncable::SERVER_MTIME; | 60 using syncable::SERVER_MTIME; |
| 56 using syncable::SERVER_NON_UNIQUE_NAME; | 61 using syncable::SERVER_NON_UNIQUE_NAME; |
| 57 using syncable::SERVER_PARENT_ID; | 62 using syncable::SERVER_PARENT_ID; |
| 58 using syncable::SERVER_POSITION_IN_PARENT; | 63 using syncable::SERVER_POSITION_IN_PARENT; |
| (...skipping 235 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 294 // as well, so as soon as a client is restarted with this datatype marked | 299 // as well, so as soon as a client is restarted with this datatype marked |
| 295 // for encryption, all the data should be updated as necessary. | 300 // for encryption, all the data should be updated as necessary. |
| 296 | 301 |
| 297 // If this fails, something is wrong with the cryptographer, but there's | 302 // If this fails, something is wrong with the cryptographer, but there's |
| 298 // nothing we can do about it here. | 303 // nothing we can do about it here. |
| 299 syncable::ProcessUnsyncedChangesForEncryption(trans, | 304 syncable::ProcessUnsyncedChangesForEncryption(trans, |
| 300 cryptographer); | 305 cryptographer); |
| 301 } | 306 } |
| 302 } | 307 } |
| 303 | 308 |
| 309 // Only apply updates that we can decrypt. If we can't decrypt the update, it |
| 310 // is likely because the passphrase has not arrived yet. Because the |
| 311 // passphrase may not arrive within this GetUpdates, we can't just return |
| 312 // conflict, else we try to perform normal conflict resolution prematurely or |
| 313 // the syncer may get stuck. As such, we return CONFLICT_ENCRYPTION, which is |
| 314 // treated as a non-blocking conflict. See the description in syncer_types.h. |
| 315 // This prevents any unsynced changes from commiting and postpones conflict |
| 316 // resolution until all data can be decrypted. |
| 317 if (specifics.has_encrypted() && |
| 318 !cryptographer->CanDecrypt(specifics.encrypted())) { |
| 319 // We can't decrypt this node yet. |
| 320 DVLOG(1) << "Received an undecryptable " |
| 321 << syncable::ModelTypeToString(entry->GetServerModelType()) |
| 322 << " update, returning encryption_conflict."; |
| 323 return CONFLICT_ENCRYPTION; |
| 324 } else if (specifics.HasExtension(sync_pb::password) && |
| 325 entry->Get(UNIQUE_SERVER_TAG).empty()) { |
| 326 // Passwords use their own legacy encryption scheme. |
| 327 const sync_pb::PasswordSpecifics& password = |
| 328 specifics.GetExtension(sync_pb::password); |
| 329 if (!cryptographer->CanDecrypt(password.encrypted())) { |
| 330 DVLOG(1) << "Received an undecryptable password update, returning " |
| 331 << "encryption_conflict."; |
| 332 return CONFLICT_ENCRYPTION; |
| 333 } |
| 334 } |
| 335 |
| 304 if (entry->Get(IS_UNSYNCED)) { | 336 if (entry->Get(IS_UNSYNCED)) { |
| 305 DVLOG(1) << "Skipping update, returning conflict for: " << id | 337 DVLOG(1) << "Skipping update, returning conflict for: " << id |
| 306 << " ; it's unsynced."; | 338 << " ; it's unsynced."; |
| 307 return CONFLICT; | 339 return CONFLICT; |
| 308 } | 340 } |
| 309 if (!entry->Get(SERVER_IS_DEL)) { | 341 if (!entry->Get(SERVER_IS_DEL)) { |
| 310 syncable::Id new_parent = entry->Get(SERVER_PARENT_ID); | 342 syncable::Id new_parent = entry->Get(SERVER_PARENT_ID); |
| 311 Entry parent(trans, GET_BY_ID, new_parent); | 343 Entry parent(trans, GET_BY_ID, new_parent); |
| 312 // A note on non-directory parents: | 344 // A note on non-directory parents: |
| 313 // We catch most unfixable tree invariant errors at update receipt time, | 345 // We catch most unfixable tree invariant errors at update receipt time, |
| (...skipping 15 matching lines...) Expand all Loading... |
| 329 Directory::ChildHandles handles; | 361 Directory::ChildHandles handles; |
| 330 trans->directory()->GetChildHandlesById(trans, id, &handles); | 362 trans->directory()->GetChildHandlesById(trans, id, &handles); |
| 331 if (!handles.empty()) { | 363 if (!handles.empty()) { |
| 332 // If we have still-existing children, then we need to deal with | 364 // If we have still-existing children, then we need to deal with |
| 333 // them before we can process this change. | 365 // them before we can process this change. |
| 334 DVLOG(1) << "Not deleting directory; it's not empty " << *entry; | 366 DVLOG(1) << "Not deleting directory; it's not empty " << *entry; |
| 335 return CONFLICT; | 367 return CONFLICT; |
| 336 } | 368 } |
| 337 } | 369 } |
| 338 | 370 |
| 339 // Only apply updates that we can decrypt. If we can't decrypt the update, it | 371 if (specifics.has_encrypted()) { |
| 340 // is likely because the passphrase has not arrived yet. Because the | 372 DVLOG(2) << "Received a decryptable " |
| 341 // passphrase may not arrive within this GetUpdates, we can't just return | |
| 342 // conflict, else the syncer gets stuck. As such, we return | |
| 343 // CONFLICT_ENCRYPTION, which is treated as a non-blocking conflict. See the | |
| 344 // description in syncer_types.h. | |
| 345 if (specifics.has_encrypted() && | |
| 346 !cryptographer->CanDecrypt(specifics.encrypted())) { | |
| 347 // We can't decrypt this node yet. | |
| 348 DVLOG(1) << "Received an undecryptable " | |
| 349 << syncable::ModelTypeToString(entry->GetServerModelType()) | 373 << syncable::ModelTypeToString(entry->GetServerModelType()) |
| 350 << " update, returning encryption_conflict."; | 374 << " update, applying normally."; |
| 351 return CONFLICT_ENCRYPTION; | |
| 352 } else if (specifics.HasExtension(sync_pb::password) && | |
| 353 entry->Get(UNIQUE_SERVER_TAG).empty()) { | |
| 354 // Passwords use their own legacy encryption scheme. | |
| 355 const sync_pb::PasswordSpecifics& password = | |
| 356 specifics.GetExtension(sync_pb::password); | |
| 357 if (!cryptographer->CanDecrypt(password.encrypted())) { | |
| 358 DVLOG(1) << "Received an undecryptable password update, returning " | |
| 359 << "encryption_conflict."; | |
| 360 return CONFLICT_ENCRYPTION; | |
| 361 } | |
| 362 } else { | 375 } else { |
| 363 if (specifics.has_encrypted()) { | 376 DVLOG(2) << "Received an unencrypted " |
| 364 DVLOG(2) << "Received a decryptable " | 377 << syncable::ModelTypeToString(entry->GetServerModelType()) |
| 365 << syncable::ModelTypeToString(entry->GetServerModelType()) | 378 << " update, applying normally."; |
| 366 << " update, applying normally."; | |
| 367 } else { | |
| 368 DVLOG(2) << "Received an unencrypted " | |
| 369 << syncable::ModelTypeToString(entry->GetServerModelType()) | |
| 370 << " update, applying normally."; | |
| 371 } | |
| 372 } | 379 } |
| 373 | 380 |
| 374 SyncerUtil::UpdateLocalDataFromServerData(trans, entry); | 381 SyncerUtil::UpdateLocalDataFromServerData(trans, entry); |
| 375 | 382 |
| 376 return SUCCESS; | 383 return SUCCESS; |
| 377 } | 384 } |
| 378 | 385 |
| 379 namespace { | 386 namespace { |
| 380 // Helper to synthesize a new-style sync_pb::EntitySpecifics for use locally, | 387 // Helper to synthesize a new-style sync_pb::EntitySpecifics for use locally, |
| 381 // when the server speaks only the old sync_pb::SyncEntity_BookmarkData-based | 388 // when the server speaks only the old sync_pb::SyncEntity_BookmarkData-based |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 468 // commit we don't apply it as time differences may occur. | 475 // commit we don't apply it as time differences may occur. |
| 469 if (update.version() > target->Get(BASE_VERSION)) { | 476 if (update.version() > target->Get(BASE_VERSION)) { |
| 470 target->Put(IS_UNAPPLIED_UPDATE, true); | 477 target->Put(IS_UNAPPLIED_UPDATE, true); |
| 471 } | 478 } |
| 472 } | 479 } |
| 473 | 480 |
| 474 // Creates a new Entry iff no Entry exists with the given id. | 481 // Creates a new Entry iff no Entry exists with the given id. |
| 475 // static | 482 // static |
| 476 void SyncerUtil::CreateNewEntry(syncable::WriteTransaction *trans, | 483 void SyncerUtil::CreateNewEntry(syncable::WriteTransaction *trans, |
| 477 const syncable::Id& id) { | 484 const syncable::Id& id) { |
| 478 syncable::MutableEntry entry(trans, syncable::GET_BY_ID, id); | 485 syncable::MutableEntry entry(trans, GET_BY_ID, id); |
| 479 if (!entry.good()) { | 486 if (!entry.good()) { |
| 480 syncable::MutableEntry new_entry(trans, syncable::CREATE_NEW_UPDATE_ITEM, | 487 syncable::MutableEntry new_entry(trans, syncable::CREATE_NEW_UPDATE_ITEM, |
| 481 id); | 488 id); |
| 482 } | 489 } |
| 483 } | 490 } |
| 484 | 491 |
| 485 // static | 492 // static |
| 486 void SyncerUtil::SplitServerInformationIntoNewEntry( | 493 void SyncerUtil::SplitServerInformationIntoNewEntry( |
| 487 syncable::WriteTransaction* trans, | 494 syncable::WriteTransaction* trans, |
| 488 syncable::MutableEntry* entry) { | 495 syncable::MutableEntry* entry) { |
| (...skipping 14 matching lines...) Expand all Loading... |
| 503 // static | 510 // static |
| 504 void SyncerUtil::UpdateLocalDataFromServerData( | 511 void SyncerUtil::UpdateLocalDataFromServerData( |
| 505 syncable::WriteTransaction* trans, | 512 syncable::WriteTransaction* trans, |
| 506 syncable::MutableEntry* entry) { | 513 syncable::MutableEntry* entry) { |
| 507 DCHECK(!entry->Get(IS_UNSYNCED)); | 514 DCHECK(!entry->Get(IS_UNSYNCED)); |
| 508 DCHECK(entry->Get(IS_UNAPPLIED_UPDATE)); | 515 DCHECK(entry->Get(IS_UNAPPLIED_UPDATE)); |
| 509 | 516 |
| 510 DVLOG(2) << "Updating entry : " << *entry; | 517 DVLOG(2) << "Updating entry : " << *entry; |
| 511 // Start by setting the properties that determine the model_type. | 518 // Start by setting the properties that determine the model_type. |
| 512 entry->Put(SPECIFICS, entry->Get(SERVER_SPECIFICS)); | 519 entry->Put(SPECIFICS, entry->Get(SERVER_SPECIFICS)); |
| 520 // Clear the previous server specifics now that we're applying successfully. |
| 521 entry->Put(BASE_SERVER_SPECIFICS, sync_pb::EntitySpecifics()); |
| 513 entry->Put(IS_DIR, entry->Get(SERVER_IS_DIR)); | 522 entry->Put(IS_DIR, entry->Get(SERVER_IS_DIR)); |
| 514 // This strange dance around the IS_DEL flag avoids problems when setting | 523 // This strange dance around the IS_DEL flag avoids problems when setting |
| 515 // the name. | 524 // the name. |
| 516 // TODO(chron): Is this still an issue? Unit test this codepath. | 525 // TODO(chron): Is this still an issue? Unit test this codepath. |
| 517 if (entry->Get(SERVER_IS_DEL)) { | 526 if (entry->Get(SERVER_IS_DEL)) { |
| 518 entry->Put(IS_DEL, true); | 527 entry->Put(IS_DEL, true); |
| 519 } else { | 528 } else { |
| 520 entry->Put(NON_UNIQUE_NAME, entry->Get(SERVER_NON_UNIQUE_NAME)); | 529 entry->Put(NON_UNIQUE_NAME, entry->Get(SERVER_NON_UNIQUE_NAME)); |
| 521 entry->Put(PARENT_ID, entry->Get(SERVER_PARENT_ID)); | 530 entry->Put(PARENT_ID, entry->Get(SERVER_PARENT_ID)); |
| 522 CHECK(entry->Put(IS_DEL, false)); | 531 CHECK(entry->Put(IS_DEL, false)); |
| (...skipping 239 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 762 if (update.version() < target->Get(SERVER_VERSION)) { | 771 if (update.version() < target->Get(SERVER_VERSION)) { |
| 763 LOG(WARNING) << "Update older than current server version for " | 772 LOG(WARNING) << "Update older than current server version for " |
| 764 << *target << " Update:" | 773 << *target << " Update:" |
| 765 << SyncerProtoUtil::SyncEntityDebugString(update); | 774 << SyncerProtoUtil::SyncEntityDebugString(update); |
| 766 return VERIFY_SUCCESS; // Expected in new sync protocol. | 775 return VERIFY_SUCCESS; // Expected in new sync protocol. |
| 767 } | 776 } |
| 768 return VERIFY_UNDECIDED; | 777 return VERIFY_UNDECIDED; |
| 769 } | 778 } |
| 770 | 779 |
| 771 } // namespace browser_sync | 780 } // namespace browser_sync |
| OLD | NEW |