| 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::PREV_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 233 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 292 // so as soon as a client is restarted with this datatype encrypted, all | 297 // so as soon as a client is restarted with this datatype encrypted, all |
| 293 // the data should be updated as necessary. | 298 // the data should be updated as necessary. |
| 294 | 299 |
| 295 // If this fails, something is wrong with the cryptographer, but there's | 300 // If this fails, something is wrong with the cryptographer, but there's |
| 296 // nothing we can do about it here. | 301 // nothing we can do about it here. |
| 297 syncable::ProcessUnsyncedChangesForEncryption(trans, | 302 syncable::ProcessUnsyncedChangesForEncryption(trans, |
| 298 cryptographer); | 303 cryptographer); |
| 299 } | 304 } |
| 300 } | 305 } |
| 301 | 306 |
| 307 // Only apply updates that we can decrypt. If we can't decrypt the update, it |
| 308 // is likely because the passphrase has not arrived yet. Because the |
| 309 // passphrase may not arrive within this GetUpdates, we can't just return |
| 310 // conflict, else we try to perform normal conflict resolution prematurely or |
| 311 // the syncer may get stuck. As such, we return CONFLICT_ENCRYPTION, which is |
| 312 // treated as a non-blocking conflict. See the description in syncer_types.h. |
| 313 // This prevents any unsynced changes from commiting and postpones conflict |
| 314 // resolution until all data can be decrypted. |
| 315 if (specifics.has_encrypted() && |
| 316 !cryptographer->CanDecrypt(specifics.encrypted())) { |
| 317 // We can't decrypt this node yet. |
| 318 DVLOG(1) << "Received an undecryptable " |
| 319 << syncable::ModelTypeToString(entry->GetServerModelType()) |
| 320 << " update, returning encryption_conflict."; |
| 321 return CONFLICT_ENCRYPTION; |
| 322 } else if (specifics.HasExtension(sync_pb::password) && |
| 323 entry->Get(UNIQUE_SERVER_TAG).empty()) { |
| 324 // Passwords use their own legacy encryption scheme. |
| 325 const sync_pb::PasswordSpecifics& password = |
| 326 specifics.GetExtension(sync_pb::password); |
| 327 if (!cryptographer->CanDecrypt(password.encrypted())) { |
| 328 DVLOG(1) << "Received an undecryptable password update, returning " |
| 329 << "encryption_conflict."; |
| 330 return CONFLICT_ENCRYPTION; |
| 331 } |
| 332 } |
| 333 |
| 302 if (entry->Get(IS_UNSYNCED)) { | 334 if (entry->Get(IS_UNSYNCED)) { |
| 303 DVLOG(1) << "Skipping update, returning conflict for: " << id | 335 DVLOG(1) << "Skipping update, returning conflict for: " << id |
| 304 << " ; it's unsynced."; | 336 << " ; it's unsynced."; |
| 305 return CONFLICT; | 337 return CONFLICT; |
| 306 } | 338 } |
| 307 if (!entry->Get(SERVER_IS_DEL)) { | 339 if (!entry->Get(SERVER_IS_DEL)) { |
| 308 syncable::Id new_parent = entry->Get(SERVER_PARENT_ID); | 340 syncable::Id new_parent = entry->Get(SERVER_PARENT_ID); |
| 309 Entry parent(trans, GET_BY_ID, new_parent); | 341 Entry parent(trans, GET_BY_ID, new_parent); |
| 310 // A note on non-directory parents: | 342 // A note on non-directory parents: |
| 311 // We catch most unfixable tree invariant errors at update receipt time, | 343 // We catch most unfixable tree invariant errors at update receipt time, |
| (...skipping 15 matching lines...) Expand all Loading... |
| 327 Directory::ChildHandles handles; | 359 Directory::ChildHandles handles; |
| 328 trans->directory()->GetChildHandlesById(trans, id, &handles); | 360 trans->directory()->GetChildHandlesById(trans, id, &handles); |
| 329 if (!handles.empty()) { | 361 if (!handles.empty()) { |
| 330 // If we have still-existing children, then we need to deal with | 362 // If we have still-existing children, then we need to deal with |
| 331 // them before we can process this change. | 363 // them before we can process this change. |
| 332 DVLOG(1) << "Not deleting directory; it's not empty " << *entry; | 364 DVLOG(1) << "Not deleting directory; it's not empty " << *entry; |
| 333 return CONFLICT; | 365 return CONFLICT; |
| 334 } | 366 } |
| 335 } | 367 } |
| 336 | 368 |
| 337 // Only apply updates that we can decrypt. If we can't decrypt the update, it | 369 if (specifics.has_encrypted()) { |
| 338 // is likely because the passphrase has not arrived yet. Because the | 370 DVLOG(2) << "Received a decryptable " |
| 339 // passphrase may not arrive within this GetUpdates, we can't just return | |
| 340 // conflict, else the syncer gets stuck. As such, we return | |
| 341 // CONFLICT_ENCRYPTION, which is treated as a non-blocking conflict. See the | |
| 342 // description in syncer_types.h. | |
| 343 if (specifics.has_encrypted() && | |
| 344 !cryptographer->CanDecrypt(specifics.encrypted())) { | |
| 345 // We can't decrypt this node yet. | |
| 346 DVLOG(1) << "Received an undecryptable " | |
| 347 << syncable::ModelTypeToString(entry->GetServerModelType()) | 371 << syncable::ModelTypeToString(entry->GetServerModelType()) |
| 348 << " update, returning encryption_conflict."; | 372 << " update, applying normally."; |
| 349 return CONFLICT_ENCRYPTION; | |
| 350 } else if (specifics.HasExtension(sync_pb::password) && | |
| 351 entry->Get(UNIQUE_SERVER_TAG).empty()) { | |
| 352 // Passwords use their own legacy encryption scheme. | |
| 353 const sync_pb::PasswordSpecifics& password = | |
| 354 specifics.GetExtension(sync_pb::password); | |
| 355 if (!cryptographer->CanDecrypt(password.encrypted())) { | |
| 356 DVLOG(1) << "Received an undecryptable password update, returning " | |
| 357 << "encryption_conflict."; | |
| 358 return CONFLICT_ENCRYPTION; | |
| 359 } | |
| 360 } else { | 373 } else { |
| 361 if (specifics.has_encrypted()) { | 374 DVLOG(2) << "Received an unencrypted " |
| 362 DVLOG(2) << "Received a decryptable " | 375 << syncable::ModelTypeToString(entry->GetServerModelType()) |
| 363 << syncable::ModelTypeToString(entry->GetServerModelType()) | 376 << " update, applying normally."; |
| 364 << " update, applying normally."; | |
| 365 } else { | |
| 366 DVLOG(2) << "Received an unencrypted " | |
| 367 << syncable::ModelTypeToString(entry->GetServerModelType()) | |
| 368 << " update, applying normally."; | |
| 369 } | |
| 370 } | 377 } |
| 371 | 378 |
| 372 SyncerUtil::UpdateLocalDataFromServerData(trans, entry); | 379 SyncerUtil::UpdateLocalDataFromServerData(trans, entry); |
| 373 | 380 |
| 374 return SUCCESS; | 381 return SUCCESS; |
| 375 } | 382 } |
| 376 | 383 |
| 377 namespace { | 384 namespace { |
| 378 // Helper to synthesize a new-style sync_pb::EntitySpecifics for use locally, | 385 // Helper to synthesize a new-style sync_pb::EntitySpecifics for use locally, |
| 379 // when the server speaks only the old sync_pb::SyncEntity_BookmarkData-based | 386 // 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... |
| 466 // commit we don't apply it as time differences may occur. | 473 // commit we don't apply it as time differences may occur. |
| 467 if (update.version() > target->Get(BASE_VERSION)) { | 474 if (update.version() > target->Get(BASE_VERSION)) { |
| 468 target->Put(IS_UNAPPLIED_UPDATE, true); | 475 target->Put(IS_UNAPPLIED_UPDATE, true); |
| 469 } | 476 } |
| 470 } | 477 } |
| 471 | 478 |
| 472 // Creates a new Entry iff no Entry exists with the given id. | 479 // Creates a new Entry iff no Entry exists with the given id. |
| 473 // static | 480 // static |
| 474 void SyncerUtil::CreateNewEntry(syncable::WriteTransaction *trans, | 481 void SyncerUtil::CreateNewEntry(syncable::WriteTransaction *trans, |
| 475 const syncable::Id& id) { | 482 const syncable::Id& id) { |
| 476 syncable::MutableEntry entry(trans, syncable::GET_BY_ID, id); | 483 syncable::MutableEntry entry(trans, GET_BY_ID, id); |
| 477 if (!entry.good()) { | 484 if (!entry.good()) { |
| 478 syncable::MutableEntry new_entry(trans, syncable::CREATE_NEW_UPDATE_ITEM, | 485 syncable::MutableEntry new_entry(trans, syncable::CREATE_NEW_UPDATE_ITEM, |
| 479 id); | 486 id); |
| 480 } | 487 } |
| 481 } | 488 } |
| 482 | 489 |
| 483 // static | 490 // static |
| 484 void SyncerUtil::SplitServerInformationIntoNewEntry( | 491 void SyncerUtil::SplitServerInformationIntoNewEntry( |
| 485 syncable::WriteTransaction* trans, | 492 syncable::WriteTransaction* trans, |
| 486 syncable::MutableEntry* entry) { | 493 syncable::MutableEntry* entry) { |
| (...skipping 14 matching lines...) Expand all Loading... |
| 501 // static | 508 // static |
| 502 void SyncerUtil::UpdateLocalDataFromServerData( | 509 void SyncerUtil::UpdateLocalDataFromServerData( |
| 503 syncable::WriteTransaction* trans, | 510 syncable::WriteTransaction* trans, |
| 504 syncable::MutableEntry* entry) { | 511 syncable::MutableEntry* entry) { |
| 505 DCHECK(!entry->Get(IS_UNSYNCED)); | 512 DCHECK(!entry->Get(IS_UNSYNCED)); |
| 506 DCHECK(entry->Get(IS_UNAPPLIED_UPDATE)); | 513 DCHECK(entry->Get(IS_UNAPPLIED_UPDATE)); |
| 507 | 514 |
| 508 DVLOG(2) << "Updating entry : " << *entry; | 515 DVLOG(2) << "Updating entry : " << *entry; |
| 509 // Start by setting the properties that determine the model_type. | 516 // Start by setting the properties that determine the model_type. |
| 510 entry->Put(SPECIFICS, entry->Get(SERVER_SPECIFICS)); | 517 entry->Put(SPECIFICS, entry->Get(SERVER_SPECIFICS)); |
| 518 // Clear the previous server specifics now that we're applying successfully. |
| 519 entry->Put(PREV_SERVER_SPECIFICS, sync_pb::EntitySpecifics()); |
| 511 entry->Put(IS_DIR, entry->Get(SERVER_IS_DIR)); | 520 entry->Put(IS_DIR, entry->Get(SERVER_IS_DIR)); |
| 512 // This strange dance around the IS_DEL flag avoids problems when setting | 521 // This strange dance around the IS_DEL flag avoids problems when setting |
| 513 // the name. | 522 // the name. |
| 514 // TODO(chron): Is this still an issue? Unit test this codepath. | 523 // TODO(chron): Is this still an issue? Unit test this codepath. |
| 515 if (entry->Get(SERVER_IS_DEL)) { | 524 if (entry->Get(SERVER_IS_DEL)) { |
| 516 entry->Put(IS_DEL, true); | 525 entry->Put(IS_DEL, true); |
| 517 } else { | 526 } else { |
| 518 entry->Put(NON_UNIQUE_NAME, entry->Get(SERVER_NON_UNIQUE_NAME)); | 527 entry->Put(NON_UNIQUE_NAME, entry->Get(SERVER_NON_UNIQUE_NAME)); |
| 519 entry->Put(PARENT_ID, entry->Get(SERVER_PARENT_ID)); | 528 entry->Put(PARENT_ID, entry->Get(SERVER_PARENT_ID)); |
| 520 CHECK(entry->Put(IS_DEL, false)); | 529 CHECK(entry->Put(IS_DEL, false)); |
| (...skipping 239 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 760 if (update.version() < target->Get(SERVER_VERSION)) { | 769 if (update.version() < target->Get(SERVER_VERSION)) { |
| 761 LOG(WARNING) << "Update older than current server version for " | 770 LOG(WARNING) << "Update older than current server version for " |
| 762 << *target << " Update:" | 771 << *target << " Update:" |
| 763 << SyncerProtoUtil::SyncEntityDebugString(update); | 772 << SyncerProtoUtil::SyncEntityDebugString(update); |
| 764 return VERIFY_SUCCESS; // Expected in new sync protocol. | 773 return VERIFY_SUCCESS; // Expected in new sync protocol. |
| 765 } | 774 } |
| 766 return VERIFY_UNDECIDED; | 775 return VERIFY_UNDECIDED; |
| 767 } | 776 } |
| 768 | 777 |
| 769 } // namespace browser_sync | 778 } // namespace browser_sync |
| OLD | NEW |