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 3// found in the LICENSE file. 4 5#include "chrome/browser/sync/engine/syncapi.h" 6 7#include <algorithm> 8#include <bitset> 9#include <iomanip> 10#include <list> 11#include <queue> 12#include <string> 13#include <vector> 14 15#include "base/base64.h" 16#include "base/command_line.h" 17#include "base/logging.h" 18#include "base/memory/scoped_ptr.h" 19#include "base/message_loop.h" 20#include "base/observer_list.h" 21#include "base/sha1.h" 22#include "base/string_number_conversions.h" 23#include "base/string_util.h" 24#include "base/synchronization/lock.h" 25#include "base/task.h" 26#include "base/time.h" 27#include "base/utf_string_conversions.h" 28#include "base/values.h" 29#include "chrome/browser/sync/engine/all_status.h" 30#include "chrome/browser/sync/engine/change_reorder_buffer.h" 31#include "chrome/browser/sync/engine/model_safe_worker.h" 32#include "chrome/browser/sync/engine/nudge_source.h" 33#include "chrome/browser/sync/engine/net/server_connection_manager.h" 34#include "chrome/browser/sync/engine/net/syncapi_server_connection_manager.h" 35#include "chrome/browser/sync/engine/syncer.h" 36#include "chrome/browser/sync/engine/syncer_thread.h" 37#include "chrome/browser/sync/engine/http_post_provider_factory.h" 38#include "chrome/browser/sync/js_arg_list.h" 39#include "chrome/browser/sync/js_backend.h" 40#include "chrome/browser/sync/js_event_router.h" 41#include "chrome/browser/sync/notifier/sync_notifier.h" 42#include "chrome/browser/sync/notifier/sync_notifier_observer.h" 43#include "chrome/browser/sync/protocol/app_specifics.pb.h" 44#include "chrome/browser/sync/protocol/autofill_specifics.pb.h" 45#include "chrome/browser/sync/protocol/bookmark_specifics.pb.h" 46#include "chrome/browser/sync/protocol/extension_specifics.pb.h" 47#include "chrome/browser/sync/protocol/nigori_specifics.pb.h" 48#include "chrome/browser/sync/protocol/preference_specifics.pb.h" 49#include "chrome/browser/sync/protocol/proto_value_conversions.h" 50#include "chrome/browser/sync/protocol/service_constants.h" 51#include "chrome/browser/sync/protocol/session_specifics.pb.h" 52#include "chrome/browser/sync/protocol/sync.pb.h" 53#include "chrome/browser/sync/protocol/theme_specifics.pb.h" 54#include "chrome/browser/sync/protocol/typed_url_specifics.pb.h" 55#include "chrome/browser/sync/sessions/sync_session.h" 56#include "chrome/browser/sync/sessions/sync_session_context.h" 57#include "chrome/browser/sync/syncable/autofill_migration.h" 58#include "chrome/browser/sync/syncable/directory_change_listener.h" 59#include "chrome/browser/sync/syncable/directory_manager.h" 60#include "chrome/browser/sync/syncable/model_type_payload_map.h" 61#include "chrome/browser/sync/syncable/model_type.h" 62#include "chrome/browser/sync/syncable/nigori_util.h" 63#include "chrome/browser/sync/syncable/syncable.h" 64#include "chrome/browser/sync/util/crypto_helpers.h" 65#include "chrome/common/chrome_switches.h" 66#include "chrome/common/deprecated/event_sys.h" 67#include "chrome/common/net/gaia/gaia_authenticator.h" 68#include "content/browser/browser_thread.h" 69#include "net/base/network_change_notifier.h" 70 71using base::TimeDelta; 72using browser_sync::AllStatus; 73using browser_sync::Cryptographer; 74using browser_sync::KeyParams; 75using browser_sync::ModelSafeRoutingInfo; 76using browser_sync::ModelSafeWorker; 77using browser_sync::ModelSafeWorkerRegistrar; 78using browser_sync::ServerConnectionEvent; 79using browser_sync::ServerConnectionEvent2; 80using browser_sync::ServerConnectionEventListener; 81using browser_sync::SyncEngineEvent; 82using browser_sync::SyncEngineEventListener; 83using browser_sync::Syncer; 84using browser_sync::SyncerThread; 85using browser_sync::kNigoriTag; 86using browser_sync::sessions::SyncSessionContext; 87using std::list; 88using std::hex; 89using std::string; 90using std::vector; 91using syncable::Directory; 92using syncable::DirectoryManager; 93using syncable::Entry; 94using syncable::ModelTypeBitSet; 95using syncable::OriginalEntries; 96using syncable::WriterTag; 97using syncable::SPECIFICS; 98using sync_pb::AutofillProfileSpecifics; 99 100typedef GoogleServiceAuthError AuthError; 101 102static const int kThreadExitTimeoutMsec = 60000; 103static const int kSSLPort = 443; 104static const int kSyncerThreadDelayMsec = 250; 105 106#if defined(OS_CHROMEOS) 107static const int kChromeOSNetworkChangeReactionDelayHackMsec = 5000; 108#endif // OS_CHROMEOS 109 110// We manage the lifetime of sync_api::SyncManager::SyncInternal ourselves. 111DISABLE_RUNNABLE_METHOD_REFCOUNT(sync_api::SyncManager::SyncInternal); 112 113namespace sync_api { 114 115static const FilePath::CharType kBookmarkSyncUserSettingsDatabase[] = 116 FILE_PATH_LITERAL("BookmarkSyncSettings.sqlite3"); 117static const char kDefaultNameForNewNodes[] = " "; 118 119// The list of names which are reserved for use by the server. 120static const char* kForbiddenServerNames[] = { "", ".", ".." }; 121 122////////////////////////////////////////////////////////////////////////// 123// Static helper functions. 124 125// Helper function to look up the int64 metahandle of an object given the ID 126// string. 127static int64 IdToMetahandle(syncable::BaseTransaction* trans, 128 const syncable::Id& id) { 129 syncable::Entry entry(trans, syncable::GET_BY_ID, id); 130 if (!entry.good()) 131 return kInvalidId; 132 return entry.Get(syncable::META_HANDLE); 133} 134 135// Checks whether |name| is a server-illegal name followed by zero or more space 136// characters. The three server-illegal names are the empty string, dot, and 137// dot-dot. Very long names (>255 bytes in UTF-8 Normalization Form C) are 138// also illegal, but are not considered here. 139static bool IsNameServerIllegalAfterTrimming(const std::string& name) { 140 size_t untrimmed_count = name.find_last_not_of(' ') + 1; 141 for (size_t i = 0; i < arraysize(kForbiddenServerNames); ++i) { 142 if (name.compare(0, untrimmed_count, kForbiddenServerNames[i]) == 0) 143 return true; 144 } 145 return false; 146} 147 148static bool EndsWithSpace(const std::string& string) { 149 return !string.empty() && *string.rbegin() == ' '; 150} 151 152// When taking a name from the syncapi, append a space if it matches the 153// pattern of a server-illegal name followed by zero or more spaces. 154static void SyncAPINameToServerName(const std::wstring& sync_api_name, 155 std::string* out) { 156 *out = WideToUTF8(sync_api_name); 157 if (IsNameServerIllegalAfterTrimming(*out)) 158 out->append(" "); 159} 160 161// In the reverse direction, if a server name matches the pattern of a 162// server-illegal name followed by one or more spaces, remove the trailing 163// space. 164static void ServerNameToSyncAPIName(const std::string& server_name, 165 std::wstring* out) { 166 int length_to_copy = server_name.length(); 167 if (IsNameServerIllegalAfterTrimming(server_name) && 168 EndsWithSpace(server_name)) 169 --length_to_copy; 170 if (!UTF8ToWide(server_name.c_str(), length_to_copy, out)) { 171 NOTREACHED() << "Could not convert server name from UTF8 to wide"; 172 } 173} 174 175UserShare::UserShare() {} 176 177UserShare::~UserShare() {} 178 179//////////////////////////////////// 180// BaseNode member definitions. 181 182BaseNode::BaseNode() {} 183 184BaseNode::~BaseNode() {} 185 186std::string BaseNode::GenerateSyncableHash( 187 syncable::ModelType model_type, const std::string& client_tag) { 188 // blank PB with just the extension in it has termination symbol, 189 // handy for delimiter 190 sync_pb::EntitySpecifics serialized_type; 191 syncable::AddDefaultExtensionValue(model_type, &serialized_type); 192 std::string hash_input; 193 serialized_type.AppendToString(&hash_input); 194 hash_input.append(client_tag); 195 196 std::string encode_output; 197 CHECK(base::Base64Encode(base::SHA1HashString(hash_input), &encode_output)); 198 return encode_output; 199} 200 201sync_pb::PasswordSpecificsData* DecryptPasswordSpecifics( 202 const sync_pb::EntitySpecifics& specifics, Cryptographer* crypto) { 203 if (!specifics.HasExtension(sync_pb::password)) 204 return NULL; 205 const sync_pb::PasswordSpecifics& password_specifics = 206 specifics.GetExtension(sync_pb::password); 207 if (!password_specifics.has_encrypted()) 208 return NULL; 209 const sync_pb::EncryptedData& encrypted = password_specifics.encrypted(); 210 scoped_ptr<sync_pb::PasswordSpecificsData> data( 211 new sync_pb::PasswordSpecificsData); 212 if (!crypto->Decrypt(encrypted, data.get())) 213 return NULL; 214 return data.release(); 215} 216 217bool BaseNode::DecryptIfNecessary(Entry* entry) { 218 if (GetIsFolder()) return true; // Ignore the top-level datatype folder. 219 const sync_pb::EntitySpecifics& specifics = 220 entry->Get(syncable::SPECIFICS); 221 if (specifics.HasExtension(sync_pb::password)) { 222 // Passwords have their own legacy encryption structure. 223 scoped_ptr<sync_pb::PasswordSpecificsData> data(DecryptPasswordSpecifics( 224 specifics, GetTransaction()->GetCryptographer())); 225 if (!data.get()) 226 return false; 227 password_data_.swap(data); 228 return true; 229 } 230 231 // We assume any node with the encrypted field set has encrypted data. 232 if (!specifics.has_encrypted()) 233 return true; 234 235 const sync_pb::EncryptedData& encrypted = 236 specifics.encrypted(); 237 std::string plaintext_data = GetTransaction()->GetCryptographer()-> 238 DecryptToString(encrypted); 239 if (plaintext_data.length() == 0) 240 return false; 241 if (!unencrypted_data_.ParseFromString(plaintext_data)) { 242 LOG(ERROR) << "Failed to decrypt encrypted node of type " << 243 syncable::ModelTypeToString(entry->GetModelType()) << "."; 244 return false; 245 } 246 return true; 247} 248 249const sync_pb::EntitySpecifics& BaseNode::GetUnencryptedSpecifics( 250 const syncable::Entry* entry) const { 251 const sync_pb::EntitySpecifics& specifics = entry->Get(SPECIFICS); 252 if (specifics.has_encrypted()) { 253 DCHECK(syncable::GetModelTypeFromSpecifics(unencrypted_data_) != 254 syncable::UNSPECIFIED); 255 return unencrypted_data_; 256 } else { 257 DCHECK(syncable::GetModelTypeFromSpecifics(unencrypted_data_) == 258 syncable::UNSPECIFIED); 259 return specifics; 260 } 261} 262 263int64 BaseNode::GetParentId() const { 264 return IdToMetahandle(GetTransaction()->GetWrappedTrans(), 265 GetEntry()->Get(syncable::PARENT_ID)); 266} 267 268int64 BaseNode::GetId() const { 269 return GetEntry()->Get(syncable::META_HANDLE); 270} 271 272int64 BaseNode::GetModificationTime() const { 273 return GetEntry()->Get(syncable::MTIME); 274} 275 276bool BaseNode::GetIsFolder() const { 277 return GetEntry()->Get(syncable::IS_DIR); 278} 279 280std::wstring BaseNode::GetTitle() const { 281 std::wstring result; 282 ServerNameToSyncAPIName(GetEntry()->Get(syncable::NON_UNIQUE_NAME), &result); 283 return result; 284} 285 286GURL BaseNode::GetURL() const { 287 return GURL(GetBookmarkSpecifics().url()); 288} 289 290int64 BaseNode::GetPredecessorId() const { 291 syncable::Id id_string = GetEntry()->Get(syncable::PREV_ID); 292 if (id_string.IsRoot()) 293 return kInvalidId; 294 return IdToMetahandle(GetTransaction()->GetWrappedTrans(), id_string); 295} 296 297int64 BaseNode::GetSuccessorId() const { 298 syncable::Id id_string = GetEntry()->Get(syncable::NEXT_ID); 299 if (id_string.IsRoot()) 300 return kInvalidId; 301 return IdToMetahandle(GetTransaction()->GetWrappedTrans(), id_string); 302} 303 304int64 BaseNode::GetFirstChildId() const { 305 syncable::Directory* dir = GetTransaction()->GetLookup(); 306 syncable::BaseTransaction* trans = GetTransaction()->GetWrappedTrans(); 307 syncable::Id id_string = 308 dir->GetFirstChildId(trans, GetEntry()->Get(syncable::ID)); 309 if (id_string.IsRoot()) 310 return kInvalidId; 311 return IdToMetahandle(GetTransaction()->GetWrappedTrans(), id_string); 312} 313 314DictionaryValue* BaseNode::ToValue() const { 315 DictionaryValue* node_info = new DictionaryValue(); 316 node_info->SetString("id", base::Int64ToString(GetId())); 317 // TODO(akalin): Return time in a better format. 318 node_info->SetString("modificationTime", 319 base::Int64ToString(GetModificationTime())); 320 node_info->SetString("parentId", base::Int64ToString(GetParentId())); 321 node_info->SetBoolean("isFolder", GetIsFolder()); 322 // TODO(akalin): Add a std::string accessor for the title. 323 node_info->SetString("title", WideToUTF8(GetTitle())); 324 node_info->Set("type", ModelTypeToValue(GetModelType())); 325 // Specifics are already in the Entry value, so no need to duplicate 326 // it here. 327 node_info->SetString("externalId", 328 base::Int64ToString(GetExternalId())); 329 node_info->SetString("predecessorId", 330 base::Int64ToString(GetPredecessorId())); 331 node_info->SetString("successorId", 332 base::Int64ToString(GetSuccessorId())); 333 node_info->SetString("firstChildId", 334 base::Int64ToString(GetFirstChildId())); 335 node_info->Set("entry", GetEntry()->ToValue()); 336 return node_info; 337} 338 339void BaseNode::GetFaviconBytes(std::vector<unsigned char>* output) const { 340 if (!output) 341 return; 342 const std::string& favicon = GetBookmarkSpecifics().favicon(); 343 output->assign(reinterpret_cast<const unsigned char*>(favicon.data()), 344 reinterpret_cast<const unsigned char*>(favicon.data() + 345 favicon.length())); 346} 347 348int64 BaseNode::GetExternalId() const { 349 return GetEntry()->Get(syncable::LOCAL_EXTERNAL_ID); 350} 351 352const sync_pb::AppSpecifics& BaseNode::GetAppSpecifics() const { 353 DCHECK_EQ(syncable::APPS, GetModelType()); 354 const sync_pb::EntitySpecifics& unencrypted = 355 GetUnencryptedSpecifics(GetEntry()); 356 return unencrypted.GetExtension(sync_pb::app); 357} 358 359const sync_pb::AutofillSpecifics& BaseNode::GetAutofillSpecifics() const { 360 DCHECK_EQ(syncable::AUTOFILL, GetModelType()); 361 const sync_pb::EntitySpecifics& unencrypted = 362 GetUnencryptedSpecifics(GetEntry()); 363 return unencrypted.GetExtension(sync_pb::autofill); 364} 365 366const AutofillProfileSpecifics& BaseNode::GetAutofillProfileSpecifics() const { 367 DCHECK_EQ(GetModelType(), syncable::AUTOFILL_PROFILE); 368 const sync_pb::EntitySpecifics& unencrypted = 369 GetUnencryptedSpecifics(GetEntry()); 370 return unencrypted.GetExtension(sync_pb::autofill_profile); 371} 372 373const sync_pb::BookmarkSpecifics& BaseNode::GetBookmarkSpecifics() const { 374 DCHECK_EQ(syncable::BOOKMARKS, GetModelType()); 375 const sync_pb::EntitySpecifics& unencrypted = 376 GetUnencryptedSpecifics(GetEntry()); 377 return unencrypted.GetExtension(sync_pb::bookmark); 378} 379 380const sync_pb::NigoriSpecifics& BaseNode::GetNigoriSpecifics() const { 381 DCHECK_EQ(syncable::NIGORI, GetModelType()); 382 const sync_pb::EntitySpecifics& unencrypted = 383 GetUnencryptedSpecifics(GetEntry()); 384 return unencrypted.GetExtension(sync_pb::nigori); 385} 386 387const sync_pb::PasswordSpecificsData& BaseNode::GetPasswordSpecifics() const { 388 DCHECK_EQ(syncable::PASSWORDS, GetModelType()); 389 DCHECK(password_data_.get()); 390 return *password_data_; 391} 392 393const sync_pb::PreferenceSpecifics& BaseNode::GetPreferenceSpecifics() const { 394 DCHECK_EQ(syncable::PREFERENCES, GetModelType()); 395 const sync_pb::EntitySpecifics& unencrypted = 396 GetUnencryptedSpecifics(GetEntry()); 397 return unencrypted.GetExtension(sync_pb::preference); 398} 399 400const sync_pb::ThemeSpecifics& BaseNode::GetThemeSpecifics() const { 401 DCHECK_EQ(syncable::THEMES, GetModelType()); 402 const sync_pb::EntitySpecifics& unencrypted = 403 GetUnencryptedSpecifics(GetEntry()); 404 return unencrypted.GetExtension(sync_pb::theme); 405} 406 407const sync_pb::TypedUrlSpecifics& BaseNode::GetTypedUrlSpecifics() const { 408 DCHECK_EQ(syncable::TYPED_URLS, GetModelType()); 409 const sync_pb::EntitySpecifics& unencrypted = 410 GetUnencryptedSpecifics(GetEntry()); 411 return unencrypted.GetExtension(sync_pb::typed_url); 412} 413 414const sync_pb::ExtensionSpecifics& BaseNode::GetExtensionSpecifics() const { 415 DCHECK_EQ(syncable::EXTENSIONS, GetModelType()); 416 const sync_pb::EntitySpecifics& unencrypted = 417 GetUnencryptedSpecifics(GetEntry()); 418 return unencrypted.GetExtension(sync_pb::extension); 419} 420 421const sync_pb::SessionSpecifics& BaseNode::GetSessionSpecifics() const { 422 DCHECK_EQ(syncable::SESSIONS, GetModelType()); 423 const sync_pb::EntitySpecifics& unencrypted = 424 GetUnencryptedSpecifics(GetEntry()); 425 return unencrypted.GetExtension(sync_pb::session); 426} 427 428syncable::ModelType BaseNode::GetModelType() const { 429 return GetEntry()->GetModelType(); 430} 431 432//////////////////////////////////// 433// WriteNode member definitions 434void WriteNode::EncryptIfNecessary(sync_pb::EntitySpecifics* unencrypted) { 435 syncable::ModelType type = syncable::GetModelTypeFromSpecifics(*unencrypted); 436 DCHECK_NE(type, syncable::UNSPECIFIED); 437 DCHECK_NE(type, syncable::PASSWORDS); // Passwords use their own encryption. 438 DCHECK_NE(type, syncable::NIGORI); // Nigori is encrypted separately. 439 440 syncable::ModelTypeSet encrypted_types = 441 GetEncryptedDataTypes(GetTransaction()->GetWrappedTrans()); 442 if (encrypted_types.count(type) == 0) { 443 // This datatype does not require encryption. 444 return; 445 } 446 447 if (unencrypted->has_encrypted()) { 448 // This specifics is already encrypted, our work is done. 449 LOG(WARNING) << "Attempted to encrypt an already encrypted entity" 450 << " specifics of type " << syncable::ModelTypeToString(type) 451 << ". Dropping."; 452 return; 453 } 454 sync_pb::EntitySpecifics encrypted; 455 syncable::AddDefaultExtensionValue(type, &encrypted); 456 VLOG(2) << "Encrypted specifics of type " << syncable::ModelTypeToString(type) 457 << " with content: " << unencrypted->SerializeAsString() << "\n"; 458 if (!GetTransaction()->GetCryptographer()->Encrypt( 459 *unencrypted, 460 encrypted.mutable_encrypted())) { 461 LOG(ERROR) << "Could not encrypt data for node of type " << 462 syncable::ModelTypeToString(type); 463 NOTREACHED(); 464 } 465 unencrypted->CopyFrom(encrypted); 466} 467 468void WriteNode::SetIsFolder(bool folder) { 469 if (entry_->Get(syncable::IS_DIR) == folder) 470 return; // Skip redundant changes. 471 472 entry_->Put(syncable::IS_DIR, folder); 473 MarkForSyncing(); 474} 475 476void WriteNode::SetTitle(const std::wstring& title) { 477 std::string server_legal_name; 478 SyncAPINameToServerName(title, &server_legal_name); 479 480 string old_name = entry_->Get(syncable::NON_UNIQUE_NAME); 481 482 if (server_legal_name == old_name) 483 return; // Skip redundant changes. 484 485 entry_->Put(syncable::NON_UNIQUE_NAME, server_legal_name); 486 MarkForSyncing(); 487} 488 489void WriteNode::SetURL(const GURL& url) { 490 sync_pb::BookmarkSpecifics new_value = GetBookmarkSpecifics(); 491 new_value.set_url(url.spec()); 492 SetBookmarkSpecifics(new_value); 493} 494 495void WriteNode::SetAppSpecifics( 496 const sync_pb::AppSpecifics& new_value) { 497 DCHECK_EQ(syncable::APPS, GetModelType()); 498 PutAppSpecificsAndMarkForSyncing(new_value); 499} 500 501void WriteNode::SetAutofillSpecifics( 502 const sync_pb::AutofillSpecifics& new_value) { 503 DCHECK_EQ(syncable::AUTOFILL, GetModelType()); 504 PutAutofillSpecificsAndMarkForSyncing(new_value); 505} 506 507void WriteNode::PutAutofillSpecificsAndMarkForSyncing( 508 const sync_pb::AutofillSpecifics& new_value) { 509 sync_pb::EntitySpecifics entity_specifics; 510 entity_specifics.MutableExtension(sync_pb::autofill)->CopyFrom(new_value); 511 EncryptIfNecessary(&entity_specifics); 512 PutSpecificsAndMarkForSyncing(entity_specifics); 513} 514 515void WriteNode::SetAutofillProfileSpecifics( 516 const sync_pb::AutofillProfileSpecifics& new_value) { 517 DCHECK_EQ(GetModelType(), syncable::AUTOFILL_PROFILE); 518 PutAutofillProfileSpecificsAndMarkForSyncing(new_value); 519} 520 521void WriteNode::PutAutofillProfileSpecificsAndMarkForSyncing( 522 const sync_pb::AutofillProfileSpecifics& new_value) { 523 sync_pb::EntitySpecifics entity_specifics; 524 entity_specifics.MutableExtension(sync_pb::autofill_profile)->CopyFrom( 525 new_value); 526 EncryptIfNecessary(&entity_specifics); 527 PutSpecificsAndMarkForSyncing(entity_specifics); 528} 529 530void WriteNode::SetBookmarkSpecifics( 531 const sync_pb::BookmarkSpecifics& new_value) { 532 DCHECK_EQ(syncable::BOOKMARKS, GetModelType()); 533 PutBookmarkSpecificsAndMarkForSyncing(new_value); 534} 535 536void WriteNode::PutBookmarkSpecificsAndMarkForSyncing( 537 const sync_pb::BookmarkSpecifics& new_value) { 538 sync_pb::EntitySpecifics entity_specifics; 539 entity_specifics.MutableExtension(sync_pb::bookmark)->CopyFrom(new_value); 540 EncryptIfNecessary(&entity_specifics); 541 PutSpecificsAndMarkForSyncing(entity_specifics); 542} 543 544void WriteNode::SetNigoriSpecifics( 545 const sync_pb::NigoriSpecifics& new_value) { 546 DCHECK_EQ(syncable::NIGORI, GetModelType()); 547 PutNigoriSpecificsAndMarkForSyncing(new_value); 548} 549 550void WriteNode::PutNigoriSpecificsAndMarkForSyncing( 551 const sync_pb::NigoriSpecifics& new_value) { 552 sync_pb::EntitySpecifics entity_specifics; 553 entity_specifics.MutableExtension(sync_pb::nigori)->CopyFrom(new_value); 554 PutSpecificsAndMarkForSyncing(entity_specifics); 555} 556 557void WriteNode::SetPasswordSpecifics( 558 const sync_pb::PasswordSpecificsData& data) { 559 DCHECK_EQ(syncable::PASSWORDS, GetModelType()); 560 561 Cryptographer* cryptographer = GetTransaction()->GetCryptographer(); 562 563 // Idempotency check to prevent unnecessary syncing: if the plaintexts match 564 // and the old ciphertext is encrypted with the most current key, there's 565 // nothing to do here. Because each encryption is seeded with a different 566 // random value, checking for equivalence post-encryption doesn't suffice. 567 const sync_pb::EncryptedData& old_ciphertext = 568 GetEntry()->Get(SPECIFICS).GetExtension(sync_pb::password).encrypted(); 569 scoped_ptr<sync_pb::PasswordSpecificsData> old_plaintext( 570 DecryptPasswordSpecifics(GetEntry()->Get(SPECIFICS), cryptographer)); 571 if (old_plaintext.get() && 572 old_plaintext->SerializeAsString() == data.SerializeAsString() && 573 cryptographer->CanDecryptUsingDefaultKey(old_ciphertext)) { 574 return; 575 } 576 577 sync_pb::PasswordSpecifics new_value; 578 if (!cryptographer->Encrypt(data, new_value.mutable_encrypted())) { 579 NOTREACHED(); 580 } 581 PutPasswordSpecificsAndMarkForSyncing(new_value); 582} 583 584void WriteNode::SetPreferenceSpecifics( 585 const sync_pb::PreferenceSpecifics& new_value) { 586 DCHECK_EQ(syncable::PREFERENCES, GetModelType()); 587 PutPreferenceSpecificsAndMarkForSyncing(new_value); 588} 589 590void WriteNode::SetThemeSpecifics( 591 const sync_pb::ThemeSpecifics& new_value) { 592 DCHECK_EQ(syncable::THEMES, GetModelType()); 593 PutThemeSpecificsAndMarkForSyncing(new_value); 594} 595 596void WriteNode::SetSessionSpecifics( 597 const sync_pb::SessionSpecifics& new_value) { 598 DCHECK_EQ(syncable::SESSIONS, GetModelType()); 599 PutSessionSpecificsAndMarkForSyncing(new_value); 600} 601 602void WriteNode::ResetFromSpecifics() { 603 sync_pb::EntitySpecifics new_data; 604 new_data.CopyFrom(GetUnencryptedSpecifics(GetEntry())); 605 EncryptIfNecessary(&new_data); 606 PutSpecificsAndMarkForSyncing(new_data); 607} 608 609void WriteNode::PutPasswordSpecificsAndMarkForSyncing( 610 const sync_pb::PasswordSpecifics& new_value) { 611 sync_pb::EntitySpecifics entity_specifics; 612 entity_specifics.MutableExtension(sync_pb::password)->CopyFrom(new_value); 613 PutSpecificsAndMarkForSyncing(entity_specifics); 614} 615 616void WriteNode::PutPreferenceSpecificsAndMarkForSyncing( 617 const sync_pb::PreferenceSpecifics& new_value) { 618 sync_pb::EntitySpecifics entity_specifics; 619 entity_specifics.MutableExtension(sync_pb::preference)->CopyFrom(new_value); 620 EncryptIfNecessary(&entity_specifics); 621 PutSpecificsAndMarkForSyncing(entity_specifics); 622} 623 624void WriteNode::SetTypedUrlSpecifics( 625 const sync_pb::TypedUrlSpecifics& new_value) { 626 DCHECK_EQ(syncable::TYPED_URLS, GetModelType()); 627 PutTypedUrlSpecificsAndMarkForSyncing(new_value); 628} 629 630void WriteNode::SetExtensionSpecifics( 631 const sync_pb::ExtensionSpecifics& new_value) { 632 DCHECK_EQ(syncable::EXTENSIONS, GetModelType()); 633 PutExtensionSpecificsAndMarkForSyncing(new_value); 634} 635 636void WriteNode::PutAppSpecificsAndMarkForSyncing( 637 const sync_pb::AppSpecifics& new_value) { 638 sync_pb::EntitySpecifics entity_specifics; 639 entity_specifics.MutableExtension(sync_pb::app)->CopyFrom(new_value); 640 EncryptIfNecessary(&entity_specifics); 641 PutSpecificsAndMarkForSyncing(entity_specifics); 642} 643 644void WriteNode::PutThemeSpecificsAndMarkForSyncing( 645 const sync_pb::ThemeSpecifics& new_value) { 646 sync_pb::EntitySpecifics entity_specifics; 647 entity_specifics.MutableExtension(sync_pb::theme)->CopyFrom(new_value); 648 EncryptIfNecessary(&entity_specifics); 649 PutSpecificsAndMarkForSyncing(entity_specifics); 650} 651 652void WriteNode::PutTypedUrlSpecificsAndMarkForSyncing( 653 const sync_pb::TypedUrlSpecifics& new_value) { 654 sync_pb::EntitySpecifics entity_specifics; 655 entity_specifics.MutableExtension(sync_pb::typed_url)->CopyFrom(new_value); 656 EncryptIfNecessary(&entity_specifics); 657 PutSpecificsAndMarkForSyncing(entity_specifics); 658} 659 660void WriteNode::PutExtensionSpecificsAndMarkForSyncing( 661 const sync_pb::ExtensionSpecifics& new_value) { 662 sync_pb::EntitySpecifics entity_specifics; 663 entity_specifics.MutableExtension(sync_pb::extension)->CopyFrom(new_value); 664 EncryptIfNecessary(&entity_specifics); 665 PutSpecificsAndMarkForSyncing(entity_specifics); 666} 667 668void WriteNode::PutSessionSpecificsAndMarkForSyncing( 669 const sync_pb::SessionSpecifics& new_value) { 670 sync_pb::EntitySpecifics entity_specifics; 671 entity_specifics.MutableExtension(sync_pb::session)->CopyFrom(new_value); 672 EncryptIfNecessary(&entity_specifics); 673 PutSpecificsAndMarkForSyncing(entity_specifics); 674} 675 676void WriteNode::PutSpecificsAndMarkForSyncing( 677 const sync_pb::EntitySpecifics& specifics) { 678 // Skip redundant changes. 679 if (specifics.SerializeAsString() == 680 entry_->Get(SPECIFICS).SerializeAsString()) { 681 return; 682 } 683 entry_->Put(SPECIFICS, specifics); 684 MarkForSyncing(); 685} 686 687void WriteNode::SetExternalId(int64 id) { 688 if (GetExternalId() != id) 689 entry_->Put(syncable::LOCAL_EXTERNAL_ID, id); 690} 691 692WriteNode::WriteNode(WriteTransaction* transaction) 693 : entry_(NULL), transaction_(transaction) { 694 DCHECK(transaction); 695} 696 697WriteNode::~WriteNode() { 698 delete entry_; 699} 700 701// Find an existing node matching the ID |id|, and bind this WriteNode to it. 702// Return true on success. 703bool WriteNode::InitByIdLookup(int64 id) { 704 DCHECK(!entry_) << "Init called twice"; 705 DCHECK_NE(id, kInvalidId); 706 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(), 707 syncable::GET_BY_HANDLE, id); 708 return (entry_->good() && !entry_->Get(syncable::IS_DEL) && 709 DecryptIfNecessary(entry_)); 710} 711 712// Find a node by client tag, and bind this WriteNode to it. 713// Return true if the write node was found, and was not deleted. 714// Undeleting a deleted node is possible by ClientTag. 715bool WriteNode::InitByClientTagLookup(syncable::ModelType model_type, 716 const std::string& tag) { 717 DCHECK(!entry_) << "Init called twice"; 718 if (tag.empty()) 719 return false; 720 721 const std::string hash = GenerateSyncableHash(model_type, tag); 722 723 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(), 724 syncable::GET_BY_CLIENT_TAG, hash); 725 return (entry_->good() && !entry_->Get(syncable::IS_DEL) && 726 DecryptIfNecessary(entry_)); 727} 728 729bool WriteNode::InitByTagLookup(const std::string& tag) { 730 DCHECK(!entry_) << "Init called twice"; 731 if (tag.empty()) 732 return false; 733 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(), 734 syncable::GET_BY_SERVER_TAG, tag); 735 if (!entry_->good()) 736 return false; 737 if (entry_->Get(syncable::IS_DEL)) 738 return false; 739 syncable::ModelType model_type = GetModelType(); 740 DCHECK_EQ(syncable::NIGORI, model_type); 741 return true; 742} 743 744void WriteNode::PutModelType(syncable::ModelType model_type) { 745 // Set an empty specifics of the appropriate datatype. The presence 746 // of the specific extension will identify the model type. 747 DCHECK(GetModelType() == model_type || 748 GetModelType() == syncable::UNSPECIFIED); // Immutable once set. 749 750 sync_pb::EntitySpecifics specifics; 751 syncable::AddDefaultExtensionValue(model_type, &specifics); 752 PutSpecificsAndMarkForSyncing(specifics); 753 DCHECK_EQ(model_type, GetModelType()); 754} 755 756// Create a new node with default properties, and bind this WriteNode to it. 757// Return true on success. 758bool WriteNode::InitByCreation(syncable::ModelType model_type, 759 const BaseNode& parent, 760 const BaseNode* predecessor) { 761 DCHECK(!entry_) << "Init called twice"; 762 // |predecessor| must be a child of |parent| or NULL. 763 if (predecessor && predecessor->GetParentId() != parent.GetId()) { 764 DCHECK(false); 765 return false; 766 } 767 768 syncable::Id parent_id = parent.GetEntry()->Get(syncable::ID); 769 770 // Start out with a dummy name. We expect 771 // the caller to set a meaningful name after creation. 772 string dummy(kDefaultNameForNewNodes); 773 774 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(), 775 syncable::CREATE, parent_id, dummy); 776 777 if (!entry_->good()) 778 return false; 779 780 // Entries are untitled folders by default. 781 entry_->Put(syncable::IS_DIR, true); 782 783 PutModelType(model_type); 784 785 // Now set the predecessor, which sets IS_UNSYNCED as necessary. 786 PutPredecessor(predecessor); 787 788 return true; 789} 790 791// Create a new node with default properties and a client defined unique tag, 792// and bind this WriteNode to it. 793// Return true on success. If the tag exists in the database, then 794// we will attempt to undelete the node. 795// TODO(chron): Code datatype into hash tag. 796// TODO(chron): Is model type ever lost? 797bool WriteNode::InitUniqueByCreation(syncable::ModelType model_type, 798 const BaseNode& parent, 799 const std::string& tag) { 800 DCHECK(!entry_) << "Init called twice"; 801 802 const std::string hash = GenerateSyncableHash(model_type, tag); 803 804 syncable::Id parent_id = parent.GetEntry()->Get(syncable::ID); 805 806 // Start out with a dummy name. We expect 807 // the caller to set a meaningful name after creation. 808 string dummy(kDefaultNameForNewNodes); 809 810 // Check if we have this locally and need to undelete it. 811 scoped_ptr<syncable::MutableEntry> existing_entry( 812 new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(), 813 syncable::GET_BY_CLIENT_TAG, hash)); 814 815 if (existing_entry->good()) { 816 if (existing_entry->Get(syncable::IS_DEL)) { 817 // Rules for undelete: 818 // BASE_VERSION: Must keep the same. 819 // ID: Essential to keep the same. 820 // META_HANDLE: Must be the same, so we can't "split" the entry. 821 // IS_DEL: Must be set to false, will cause reindexing. 822 // This one is weird because IS_DEL is true for "update only" 823 // items. It should be OK to undelete an update only. 824 // MTIME/CTIME: Seems reasonable to just leave them alone. 825 // IS_UNSYNCED: Must set this to true or face database insurrection. 826 // We do this below this block. 827 // IS_UNAPPLIED_UPDATE: Either keep it the same or also set BASE_VERSION 828 // to SERVER_VERSION. We keep it the same here. 829 // IS_DIR: We'll leave it the same. 830 // SPECIFICS: Reset it. 831 832 existing_entry->Put(syncable::IS_DEL, false); 833 834 // Client tags are immutable and must be paired with the ID. 835 // If a server update comes down with an ID and client tag combo, 836 // and it already exists, always overwrite it and store only one copy. 837 // We have to undelete entries because we can't disassociate IDs from 838 // tags and updates. 839 840 existing_entry->Put(syncable::NON_UNIQUE_NAME, dummy); 841 existing_entry->Put(syncable::PARENT_ID, parent_id); 842 entry_ = existing_entry.release(); 843 } else { 844 return false; 845 } 846 } else { 847 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(), 848 syncable::CREATE, parent_id, dummy); 849 if (!entry_->good()) { 850 return false; 851 } 852 853 // Only set IS_DIR for new entries. Don't bitflip undeleted ones. 854 entry_->Put(syncable::UNIQUE_CLIENT_TAG, hash); 855 } 856 857 // We don't support directory and tag combinations. 858 entry_->Put(syncable::IS_DIR, false); 859 860 // Will clear specifics data. 861 PutModelType(model_type); 862 863 // Now set the predecessor, which sets IS_UNSYNCED as necessary. 864 PutPredecessor(NULL); 865 866 return true; 867} 868 869bool WriteNode::SetPosition(const BaseNode& new_parent, 870 const BaseNode* predecessor) { 871 // |predecessor| must be a child of |new_parent| or NULL. 872 if (predecessor && predecessor->GetParentId() != new_parent.GetId()) { 873 DCHECK(false); 874 return false; 875 } 876 877 syncable::Id new_parent_id = new_parent.GetEntry()->Get(syncable::ID); 878 879 // Filter out redundant changes if both the parent and the predecessor match. 880 if (new_parent_id == entry_->Get(syncable::PARENT_ID)) { 881 const syncable::Id& old = entry_->Get(syncable::PREV_ID); 882 if ((!predecessor && old.IsRoot()) || 883 (predecessor && (old == predecessor->GetEntry()->Get(syncable::ID)))) { 884 return true; 885 } 886 } 887 888 // Atomically change the parent. This will fail if it would 889 // introduce a cycle in the hierarchy. 890 if (!entry_->Put(syncable::PARENT_ID, new_parent_id)) 891 return false; 892 893 // Now set the predecessor, which sets IS_UNSYNCED as necessary. 894 PutPredecessor(predecessor); 895 896 return true; 897} 898 899const syncable::Entry* WriteNode::GetEntry() const { 900 return entry_; 901} 902 903const BaseTransaction* WriteNode::GetTransaction() const { 904 return transaction_; 905} 906 907void WriteNode::Remove() { 908 entry_->Put(syncable::IS_DEL, true); 909 MarkForSyncing(); 910} 911 912void WriteNode::PutPredecessor(const BaseNode* predecessor) { 913 syncable::Id predecessor_id = predecessor ? 914 predecessor->GetEntry()->Get(syncable::ID) : syncable::Id(); 915 entry_->PutPredecessor(predecessor_id); 916 // Mark this entry as unsynced, to wake up the syncer. 917 MarkForSyncing(); 918} 919 920void WriteNode::SetFaviconBytes(const vector<unsigned char>& bytes) { 921 sync_pb::BookmarkSpecifics new_value = GetBookmarkSpecifics(); 922 new_value.set_favicon(bytes.empty() ? NULL : &bytes[0], bytes.size()); 923 SetBookmarkSpecifics(new_value); 924} 925 926void WriteNode::MarkForSyncing() { 927 syncable::MarkForSyncing(entry_); 928} 929 930////////////////////////////////////////////////////////////////////////// 931// ReadNode member definitions 932ReadNode::ReadNode(const BaseTransaction* transaction) 933 : entry_(NULL), transaction_(transaction) { 934 DCHECK(transaction); 935} 936 937ReadNode::ReadNode() { 938 entry_ = NULL; 939 transaction_ = NULL; 940} 941 942ReadNode::~ReadNode() { 943 delete entry_; 944} 945 946void ReadNode::InitByRootLookup() { 947 DCHECK(!entry_) << "Init called twice"; 948 syncable::BaseTransaction* trans = transaction_->GetWrappedTrans(); 949 entry_ = new syncable::Entry(trans, syncable::GET_BY_ID, trans->root_id()); 950 if (!entry_->good()) 951 DCHECK(false) << "Could not lookup root node for reading."; 952} 953 954bool ReadNode::InitByIdLookup(int64 id) { 955 DCHECK(!entry_) << "Init called twice"; 956 DCHECK_NE(id, kInvalidId); 957 syncable::BaseTransaction* trans = transaction_->GetWrappedTrans(); 958 entry_ = new syncable::Entry(trans, syncable::GET_BY_HANDLE, id); 959 if (!entry_->good()) 960 return false; 961 if (entry_->Get(syncable::IS_DEL)) 962 return false; 963 syncable::ModelType model_type = GetModelType(); 964 LOG_IF(WARNING, model_type == syncable::UNSPECIFIED || 965 model_type == syncable::TOP_LEVEL_FOLDER) 966 << "SyncAPI InitByIdLookup referencing unusual object."; 967 return DecryptIfNecessary(entry_); 968} 969 970bool ReadNode::InitByClientTagLookup(syncable::ModelType model_type, 971 const std::string& tag) { 972 DCHECK(!entry_) << "Init called twice"; 973 if (tag.empty()) 974 return false; 975 976 const std::string hash = GenerateSyncableHash(model_type, tag); 977 978 entry_ = new syncable::Entry(transaction_->GetWrappedTrans(), 979 syncable::GET_BY_CLIENT_TAG, hash); 980 return (entry_->good() && !entry_->Get(syncable::IS_DEL) && 981 DecryptIfNecessary(entry_)); 982} 983 984const syncable::Entry* ReadNode::GetEntry() const { 985 return entry_; 986} 987 988const BaseTransaction* ReadNode::GetTransaction() const { 989 return transaction_; 990} 991 992bool ReadNode::InitByTagLookup(const std::string& tag) { 993 DCHECK(!entry_) << "Init called twice"; 994 if (tag.empty()) 995 return false; 996 syncable::BaseTransaction* trans = transaction_->GetWrappedTrans(); 997 entry_ = new syncable::Entry(trans, syncable::GET_BY_SERVER_TAG, tag); 998 if (!entry_->good()) 999 return false; 1000 if (entry_->Get(syncable::IS_DEL)) 1001 return false; 1002 syncable::ModelType model_type = GetModelType(); 1003 LOG_IF(WARNING, model_type == syncable::UNSPECIFIED || 1004 model_type == syncable::TOP_LEVEL_FOLDER) 1005 << "SyncAPI InitByTagLookup referencing unusually typed object."; 1006 return DecryptIfNecessary(entry_); 1007} 1008 1009////////////////////////////////////////////////////////////////////////// 1010// ReadTransaction member definitions 1011ReadTransaction::ReadTransaction(UserShare* share) 1012 : BaseTransaction(share), 1013 transaction_(NULL), 1014 close_transaction_(true) { 1015 transaction_ = new syncable::ReadTransaction(GetLookup(), __FILE__, __LINE__); 1016} 1017 1018ReadTransaction::ReadTransaction(UserShare* share, 1019 syncable::BaseTransaction* trans) 1020 : BaseTransaction(share), 1021 transaction_(trans), 1022 close_transaction_(false) {} 1023 1024ReadTransaction::~ReadTransaction() { 1025 if (close_transaction_) { 1026 delete transaction_; 1027 } 1028} 1029 1030syncable::BaseTransaction* ReadTransaction::GetWrappedTrans() const { 1031 return transaction_; 1032} 1033 1034////////////////////////////////////////////////////////////////////////// 1035// WriteTransaction member definitions 1036WriteTransaction::WriteTransaction(UserShare* share) 1037 : BaseTransaction(share), 1038 transaction_(NULL) { 1039 transaction_ = new syncable::WriteTransaction(GetLookup(), syncable::SYNCAPI, 1040 __FILE__, __LINE__); 1041} 1042 1043WriteTransaction::~WriteTransaction() { 1044 delete transaction_; 1045} 1046 1047syncable::BaseTransaction* WriteTransaction::GetWrappedTrans() const { 1048 return transaction_; 1049} 1050 1051SyncManager::ChangeRecord::ChangeRecord() 1052 : id(kInvalidId), action(ACTION_ADD) {} 1053 1054SyncManager::ChangeRecord::~ChangeRecord() {} 1055 1056DictionaryValue* SyncManager::ChangeRecord::ToValue( 1057 const BaseTransaction* trans) const { 1058 DictionaryValue* value = new DictionaryValue(); 1059 std::string action_str; 1060 switch (action) { 1061 case ACTION_ADD: 1062 action_str = "Add"; 1063 break; 1064 case ACTION_DELETE: 1065 action_str = "Delete"; 1066 break; 1067 case ACTION_UPDATE: 1068 action_str = "Update"; 1069 break; 1070 default: 1071 NOTREACHED(); 1072 action_str = "Unknown"; 1073 break; 1074 } 1075 value->SetString("action", action_str); 1076 Value* node_value = NULL; 1077 if (action == ACTION_DELETE) { 1078 DictionaryValue* node_dict = new DictionaryValue(); 1079 node_dict->SetString("id", base::Int64ToString(id)); 1080 node_dict->Set("specifics", 1081 browser_sync::EntitySpecificsToValue(specifics)); 1082 if (extra.get()) { 1083 node_dict->Set("extra", extra->ToValue()); 1084 } 1085 node_value = node_dict; 1086 } else { 1087 ReadNode node(trans); 1088 if (node.InitByIdLookup(id)) { 1089 node_value = node.ToValue(); 1090 } 1091 } 1092 if (!node_value) { 1093 NOTREACHED(); 1094 node_value = Value::CreateNullValue(); 1095 } 1096 value->Set("node", node_value); 1097 return value; 1098} 1099 1100bool BaseNode::ContainsString(const std::string& lowercase_query) const { 1101 DCHECK(GetEntry()); 1102 // TODO(lipalani) - figure out what to do if the node is encrypted. 1103 const sync_pb::EntitySpecifics& specifics = GetEntry()->Get(SPECIFICS); 1104 std::string temp; 1105 // The protobuf serialized string contains the original strings. So 1106 // we will just serialize it and search it. 1107 specifics.SerializeToString(&temp); 1108 1109 // Now convert to lower case. 1110 StringToLowerASCII(&temp); 1111 1112 return temp.find(lowercase_query) != std::string::npos; 1113} 1114 1115SyncManager::ExtraPasswordChangeRecordData::ExtraPasswordChangeRecordData() {} 1116 1117SyncManager::ExtraPasswordChangeRecordData::ExtraPasswordChangeRecordData( 1118 const sync_pb::PasswordSpecificsData& data) 1119 : unencrypted_(data) { 1120} 1121 1122SyncManager::ExtraPasswordChangeRecordData::~ExtraPasswordChangeRecordData() {} 1123 1124DictionaryValue* SyncManager::ExtraPasswordChangeRecordData::ToValue() const { 1125 return browser_sync::PasswordSpecificsDataToValue(unencrypted_); 1126} 1127 1128const sync_pb::PasswordSpecificsData& 1129 SyncManager::ExtraPasswordChangeRecordData::unencrypted() const { 1130 return unencrypted_; 1131} 1132 1133namespace { 1134 1135struct NotificationInfo { 1136 int total_count; 1137 std::string payload; 1138 1139 NotificationInfo() : total_count(0) {} 1140 1141 ~NotificationInfo() {} 1142 1143 // Returned pointer owned by the caller. 1144 DictionaryValue* ToValue() const { 1145 DictionaryValue* value = new DictionaryValue(); 1146 value->SetInteger("totalCount", total_count); 1147 value->SetString("payload", payload); 1148 return value; 1149 } 1150}; 1151 1152typedef std::map<syncable::ModelType, NotificationInfo> NotificationInfoMap; 1153 1154// returned pointer is owned by the caller. 1155DictionaryValue* NotificationInfoToValue( 1156 const NotificationInfoMap& notification_info) { 1157 DictionaryValue* value = new DictionaryValue(); 1158 1159 for (NotificationInfoMap::const_iterator it = notification_info.begin(); 1160 it != notification_info.end(); ++it) { 1161 const std::string& model_type_str = 1162 syncable::ModelTypeToString(it->first); 1163 value->Set(model_type_str, it->second.ToValue()); 1164 } 1165 1166 return value; 1167} 1168 1169} // namespace 1170 1171////////////////////////////////////////////////////////////////////////// 1172// SyncManager's implementation: SyncManager::SyncInternal 1173class SyncManager::SyncInternal 1174 : public net::NetworkChangeNotifier::IPAddressObserver, 1175 public sync_notifier::SyncNotifierObserver, 1176 public browser_sync::JsBackend, 1177 public SyncEngineEventListener, 1178 public ServerConnectionEventListener, 1179 public syncable::DirectoryChangeListener { 1180 static const int kDefaultNudgeDelayMilliseconds; 1181 static const int kPreferencesNudgeDelayMilliseconds; 1182 public: 1183 explicit SyncInternal(SyncManager* sync_manager) 1184 : core_message_loop_(NULL), 1185 parent_router_(NULL), 1186 sync_manager_(sync_manager), 1187 registrar_(NULL), 1188 initialized_(false), 1189 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { 1190 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1191 } 1192 1193 virtual ~SyncInternal() { 1194 CHECK(!core_message_loop_); 1195 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1196 } 1197 1198 bool Init(const FilePath& database_location, 1199 const std::string& sync_server_and_path, 1200 int port, 1201 bool use_ssl, 1202 HttpPostProviderFactory* post_factory, 1203 ModelSafeWorkerRegistrar* model_safe_worker_registrar, 1204 const char* user_agent, 1205 const SyncCredentials& credentials, 1206 sync_notifier::SyncNotifier* sync_notifier, 1207 const std::string& restored_key_for_bootstrapping, 1208 bool setup_for_test_mode); 1209 1210 // Sign into sync with given credentials. 1211 // We do not verify the tokens given. After this call, the tokens are set 1212 // and the sync DB is open. True if successful, false if something 1213 // went wrong. 1214 bool SignIn(const SyncCredentials& credentials); 1215 1216 // Update tokens that we're using in Sync. Email must stay the same. 1217 void UpdateCredentials(const SyncCredentials& credentials); 1218 1219 // Called when the user disables or enables a sync type. 1220 void UpdateEnabledTypes(); 1221 1222 // Tell the sync engine to start the syncing process. 1223 void StartSyncing(); 1224 1225 // Whether or not the Nigori node is encrypted using an explicit passphrase. 1226 bool IsUsingExplicitPassphrase(); 1227 1228 // Set the datatypes we want to encrypt and encrypt any nodes as necessary. 1229 void EncryptDataTypes(const syncable::ModelTypeSet& encrypted_types); 1230 1231 // Try to set the current passphrase to |passphrase|, and record whether 1232 // it is an explicit passphrase or implicitly using gaia in the Nigori 1233 // node. 1234 void SetPassphrase(const std::string& passphrase, bool is_explicit); 1235 1236 // Call periodically from a database-safe thread to persist recent changes 1237 // to the syncapi model. 1238 void SaveChanges(); 1239 1240 // DirectoryChangeListener implementation. 1241 // This listener is called upon completion of a syncable transaction, and 1242 // builds the list of sync-engine initiated changes that will be forwarded to 1243 // the SyncManager's Observers. 1244 virtual void HandleTransactionCompleteChangeEvent( 1245 const ModelTypeBitSet& models_with_changes); 1246 virtual ModelTypeBitSet HandleTransactionEndingChangeEvent( 1247 syncable::BaseTransaction* trans); 1248 virtual void HandleCalculateChangesChangeEventFromSyncApi( 1249 const OriginalEntries& originals, 1250 const WriterTag& writer, 1251 syncable::BaseTransaction* trans); 1252 virtual void HandleCalculateChangesChangeEventFromSyncer( 1253 const OriginalEntries& originals, 1254 const WriterTag& writer, 1255 syncable::BaseTransaction* trans); 1256 1257 // Listens for notifications from the ServerConnectionManager 1258 void HandleServerConnectionEvent(const ServerConnectionEvent& event); 1259 1260 // Open the directory named with username_for_share 1261 bool OpenDirectory(); 1262 1263 // SyncNotifierObserver implementation. 1264 virtual void OnNotificationStateChange( 1265 bool notifications_enabled); 1266 1267 virtual void OnIncomingNotification( 1268 const syncable::ModelTypePayloadMap& type_payloads); 1269 1270 virtual void StoreState(const std::string& cookie); 1271 1272 void AddObserver(SyncManager::Observer* observer); 1273 1274 void RemoveObserver(SyncManager::Observer* observer); 1275 1276 // Accessors for the private members. 1277 DirectoryManager* dir_manager() { return share_.dir_manager.get(); } 1278 SyncAPIServerConnectionManager* connection_manager() { 1279 return connection_manager_.get(); 1280 } 1281 SyncerThread* syncer_thread() { return syncer_thread_.get(); } 1282 UserShare* GetUserShare() { return &share_; } 1283 1284 // Return the currently active (validated) username for use with syncable 1285 // types. 1286 const std::string& username_for_share() const { 1287 return share_.name; 1288 } 1289 1290 Status GetStatus(); 1291 1292 void RequestNudge(const tracked_objects::Location& nudge_location); 1293 1294 void RequestNudgeWithDataTypes(const TimeDelta& delay, 1295 browser_sync::NudgeSource source, const ModelTypeBitSet& types, 1296 const tracked_objects::Location& nudge_location); 1297 1298 // See SyncManager::Shutdown for information. 1299 void Shutdown(); 1300 1301 // Whether we're initialized to the point of being able to accept changes 1302 // (and hence allow transaction creation). See initialized_ for details. 1303 bool initialized() const { 1304 base::AutoLock lock(initialized_mutex_); 1305 return initialized_; 1306 } 1307 1308 // If this is a deletion for a password, sets the legacy 1309 // ExtraPasswordChangeRecordData field of |buffer|. Otherwise sets 1310 // |buffer|'s specifics field to contain the unencrypted data. 1311 void SetExtraChangeRecordData(int64 id, 1312 syncable::ModelType type, 1313 ChangeReorderBuffer* buffer, 1314 Cryptographer* cryptographer, 1315 const syncable::EntryKernel& original, 1316 bool existed_before, 1317 bool exists_now); 1318 1319 // Called only by our NetworkChangeNotifier. 1320 virtual void OnIPAddressChanged(); 1321 1322 bool InitialSyncEndedForAllEnabledTypes() { 1323 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share()); 1324 if (!lookup.good()) { 1325 DCHECK(false) << "ScopedDirLookup failed when checking initial sync"; 1326 return false; 1327 } 1328 1329 ModelSafeRoutingInfo enabled_types; 1330 registrar_->GetModelSafeRoutingInfo(&enabled_types); 1331 for (ModelSafeRoutingInfo::const_iterator i = enabled_types.begin(); 1332 i != enabled_types.end(); ++i) { 1333 if (!lookup->initial_sync_ended_for_type(i->first)) 1334 return false; 1335 } 1336 return true; 1337 } 1338 1339 syncable::AutofillMigrationState GetAutofillMigrationState() { 1340 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share()); 1341 if (!lookup.good()) { 1342 DCHECK(false) << "ScopedDirLookup failed when checking initial sync"; 1343 return syncable::NOT_MIGRATED; 1344 } 1345 1346 return lookup->get_autofill_migration_state(); 1347 } 1348 1349 void SetAutofillMigrationState(syncable::AutofillMigrationState state) { 1350 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share()); 1351 if (!lookup.good()) { 1352 DCHECK(false) << "ScopedDirLookup failed when checking initial sync"; 1353 return; 1354 } 1355 1356 return lookup->set_autofill_migration_state(state); 1357 } 1358 1359 void SetAutofillMigrationDebugInfo( 1360 syncable::AutofillMigrationDebugInfo::PropertyToSet property_to_set, 1361 const syncable::AutofillMigrationDebugInfo& info) { 1362 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share()); 1363 if (!lookup.good()) { 1364 DCHECK(false) << "ScopedDirLookup failed when checking initial sync"; 1365 return; 1366 } 1367 1368 return lookup->set_autofill_migration_state_debug_info( 1369 property_to_set, info); 1370 } 1371 1372 syncable::AutofillMigrationDebugInfo 1373 GetAutofillMigrationDebugInfo() { 1374 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share()); 1375 if (!lookup.good()) { 1376 DCHECK(false) << "ScopedDirLookup failed when checking initial sync"; 1377 syncable::AutofillMigrationDebugInfo null_value = {0}; 1378 return null_value; 1379 } 1380 return lookup->get_autofill_migration_debug_info(); 1381 } 1382 1383 // SyncEngineEventListener implementation. 1384 virtual void OnSyncEngineEvent(const SyncEngineEvent& event); 1385 1386 // ServerConnectionEventListener implementation. 1387 virtual void OnServerConnectionEvent(const ServerConnectionEvent2& event); 1388 1389 // browser_sync::JsBackend implementation. 1390 virtual void SetParentJsEventRouter(browser_sync::JsEventRouter* router); 1391 virtual void RemoveParentJsEventRouter(); 1392 virtual const browser_sync::JsEventRouter* GetParentJsEventRouter() const; 1393 virtual void ProcessMessage(const std::string& name, 1394 const browser_sync::JsArgList& args, 1395 const browser_sync::JsEventHandler* sender); 1396 1397 ListValue* FindNodesContainingString(const std::string& query); 1398 1399 private: 1400 // Helper to call OnAuthError when no authentication credentials are 1401 // available. 1402 void RaiseAuthNeededEvent(); 1403 1404 // Helper to set initialized_ to true and raise an event to clients to notify 1405 // that initialization is complete and it is safe to send us changes. If 1406 // already initialized, this is a no-op. 1407 void MarkAndNotifyInitializationComplete(); 1408 1409 // Sends notifications to peers. 1410 void SendNotification(); 1411 1412 // Determine if the parents or predecessors differ between the old and new 1413 // versions of an entry stored in |a| and |b|. Note that a node's index may 1414 // change without its NEXT_ID changing if the node at NEXT_ID also moved (but 1415 // the relative order is unchanged). To handle such cases, we rely on the 1416 // caller to treat a position update on any sibling as updating the positions 1417 // of all siblings. 1418 static bool VisiblePositionsDiffer(const syncable::EntryKernel& a, 1419 const syncable::Entry& b) { 1420 // If the datatype isn't one where the browser model cares about position, 1421 // don't bother notifying that data model of position-only changes. 1422 if (!b.ShouldMaintainPosition()) 1423 return false; 1424 if (a.ref(syncable::NEXT_ID) != b.Get(syncable::NEXT_ID)) 1425 return true; 1426 if (a.ref(syncable::PARENT_ID) != b.Get(syncable::PARENT_ID)) 1427 return true; 1428 return false; 1429 } 1430 1431 // Determine if any of the fields made visible to clients of the Sync API 1432 // differ between the versions of an entry stored in |a| and |b|. A return 1433 // value of false means that it should be OK to ignore this change. 1434 static bool VisiblePropertiesDiffer(const syncable::EntryKernel& a, 1435 const syncable::Entry& b, 1436 Cryptographer* cryptographer) { 1437 syncable::ModelType model_type = b.GetModelType(); 1438 // Suppress updates to items that aren't tracked by any browser model. 1439 if (model_type == syncable::UNSPECIFIED || 1440 model_type == syncable::TOP_LEVEL_FOLDER) { 1441 return false; 1442 } 1443 if (a.ref(syncable::NON_UNIQUE_NAME) != b.Get(syncable::NON_UNIQUE_NAME)) 1444 return true; 1445 if (a.ref(syncable::IS_DIR) != b.Get(syncable::IS_DIR)) 1446 return true; 1447 // Check if data has changed (account for encryption). 1448 std::string a_str, b_str; 1449 if (a.ref(SPECIFICS).has_encrypted()) { 1450 const sync_pb::EncryptedData& encrypted = a.ref(SPECIFICS).encrypted(); 1451 a_str = cryptographer->DecryptToString(encrypted); 1452 } else { 1453 a_str = a.ref(SPECIFICS).SerializeAsString(); 1454 } 1455 if (b.Get(SPECIFICS).has_encrypted()) { 1456 const sync_pb::EncryptedData& encrypted = b.Get(SPECIFICS).encrypted(); 1457 b_str = cryptographer->DecryptToString(encrypted); 1458 } else { 1459 b_str = b.Get(SPECIFICS).SerializeAsString(); 1460 } 1461 if (a_str != b_str) { 1462 return true; 1463 } 1464 if (VisiblePositionsDiffer(a, b)) 1465 return true; 1466 return false; 1467 } 1468 1469 bool ChangeBuffersAreEmpty() { 1470 for (int i = 0; i < syncable::MODEL_TYPE_COUNT; ++i) { 1471 if (!change_buffers_[i].IsEmpty()) 1472 return false; 1473 } 1474 return true; 1475 } 1476 1477 void CheckServerReachable() { 1478 if (connection_manager()) { 1479 connection_manager()->CheckServerReachable(); 1480 } else { 1481 NOTREACHED() << "Should be valid connection manager!"; 1482 } 1483 } 1484 1485 void ReEncryptEverything(WriteTransaction* trans); 1486 1487 // Initializes (bootstraps) the Cryptographer if NIGORI has finished 1488 // initial sync so that it can immediately start encrypting / decrypting. 1489 // If the restored key is incompatible with the current version of the NIGORI 1490 // node (which could happen if a restart occurred just after an update to 1491 // NIGORI was downloaded and the user must enter a new passphrase to decrypt) 1492 // then we will raise OnPassphraseRequired and set pending keys for 1493 // decryption. Otherwise, the cryptographer is made ready (is_ready()). 1494 void BootstrapEncryption(const std::string& restored_key_for_bootstrapping); 1495 1496 // Called for every notification. This updates the notification statistics 1497 // to be displayed in about:sync. 1498 void UpdateNotificationInfo( 1499 const syncable::ModelTypePayloadMap& type_payloads); 1500 1501 // Helper for migration to new nigori proto to set 1502 // 'using_explicit_passphrase' in the NigoriSpecifics. 1503 // TODO(tim): Bug 62103. Remove this after it has been pushed out to dev 1504 // channel users. 1505 void SetUsingExplicitPassphrasePrefForMigration( 1506 WriteTransaction* const trans); 1507 1508 // Checks for server reachabilty and requests a nudge. 1509 void OnIPAddressChangedImpl(); 1510 1511 // Functions called by ProcessMessage(). 1512 browser_sync::JsArgList ProcessGetNodeByIdMessage( 1513 const browser_sync::JsArgList& args); 1514 1515 browser_sync::JsArgList ProcessFindNodesContainingString( 1516 const browser_sync::JsArgList& args); 1517 1518 // We couple the DirectoryManager and username together in a UserShare member 1519 // so we can return a handle to share_ to clients of the API for use when 1520 // constructing any transaction type. 1521 UserShare share_; 1522 1523 MessageLoop* core_message_loop_; 1524 1525 ObserverList<SyncManager::Observer> observers_; 1526 1527 browser_sync::JsEventRouter* parent_router_; 1528 1529 // The ServerConnectionManager used to abstract communication between the 1530 // client (the Syncer) and the sync server. 1531 scoped_ptr<SyncAPIServerConnectionManager> connection_manager_; 1532 1533 // The thread that runs the Syncer. Needs to be explicitly Start()ed. 1534 scoped_ptr<SyncerThread> syncer_thread_; 1535 1536 // The SyncNotifier which notifies us when updates need to be downloaded. 1537 sync_notifier::SyncNotifier* sync_notifier_; 1538 1539 // A multi-purpose status watch object that aggregates stats from various 1540 // sync components. 1541 AllStatus allstatus_; 1542 1543 // Each element of this array is a store of change records produced by 1544 // HandleChangeEvent during the CALCULATE_CHANGES step. The changes are 1545 // segregated by model type, and are stored here to be processed and 1546 // forwarded to the observer slightly later, at the TRANSACTION_ENDING 1547 // step by HandleTransactionEndingChangeEvent. The list is cleared in the 1548 // TRANSACTION_COMPLETE step by HandleTransactionCompleteChangeEvent. 1549 ChangeReorderBuffer change_buffers_[syncable::MODEL_TYPE_COUNT]; 1550 1551 // Event listener hookup for the ServerConnectionManager. 1552 scoped_ptr<EventListenerHookup> connection_manager_hookup_; 1553 1554 // The sync dir_manager to which we belong. 1555 SyncManager* const sync_manager_; 1556 1557 // The entity that provides us with information about which types to sync. 1558 // The instance is shared between the SyncManager and the Syncer. 1559 ModelSafeWorkerRegistrar* registrar_; 1560 1561 // Set to true once Init has been called, and we know of an authenticated 1562 // valid) username either from a fresh authentication attempt (as in 1563 // first-use case) or from a previous attempt stored in our UserSettings 1564 // (as in the steady-state), and the syncable::Directory has been opened, 1565 // meaning we are ready to accept changes. Protected by initialized_mutex_ 1566 // as it can get read/set by both the SyncerThread and the AuthWatcherThread. 1567 bool initialized_; 1568 mutable base::Lock initialized_mutex_; 1569 1570 // True if the SyncManager should be running in test mode (no syncer thread 1571 // actually communicating with the server). 1572 bool setup_for_test_mode_; 1573 1574 ScopedRunnableMethodFactory<SyncManager::SyncInternal> method_factory_; 1575 1576 // Map used to store the notification info to be displayed in about:sync page. 1577 // TODO(lipalani) - prefill the map with enabled data types. 1578 NotificationInfoMap notification_info_map_; 1579}; 1580const int SyncManager::SyncInternal::kDefaultNudgeDelayMilliseconds = 200; 1581const int SyncManager::SyncInternal::kPreferencesNudgeDelayMilliseconds = 2000; 1582 1583SyncManager::Observer::~Observer() {} 1584 1585SyncManager::SyncManager() { 1586 data_ = new SyncInternal(this); 1587} 1588 1589bool SyncManager::Init(const FilePath& database_location, 1590 const char* sync_server_and_path, 1591 int sync_server_port, 1592 bool use_ssl, 1593 HttpPostProviderFactory* post_factory, 1594 ModelSafeWorkerRegistrar* registrar, 1595 const char* user_agent, 1596 const SyncCredentials& credentials, 1597 sync_notifier::SyncNotifier* sync_notifier, 1598 const std::string& restored_key_for_bootstrapping, 1599 bool setup_for_test_mode) { 1600 DCHECK(post_factory); 1601 VLOG(1) << "SyncManager starting Init..."; 1602 string server_string(sync_server_and_path); 1603 return data_->Init(database_location, 1604 server_string, 1605 sync_server_port, 1606 use_ssl, 1607 post_factory, 1608 registrar, 1609 user_agent, 1610 credentials, 1611 sync_notifier, 1612 restored_key_for_bootstrapping, 1613 setup_for_test_mode); 1614} 1615 1616void SyncManager::UpdateCredentials(const SyncCredentials& credentials) { 1617 data_->UpdateCredentials(credentials); 1618} 1619 1620void SyncManager::UpdateEnabledTypes() { 1621 data_->UpdateEnabledTypes(); 1622} 1623 1624 1625bool SyncManager::InitialSyncEndedForAllEnabledTypes() { 1626 return data_->InitialSyncEndedForAllEnabledTypes(); 1627} 1628 1629void SyncManager::StartSyncing() { 1630 data_->StartSyncing(); 1631} 1632 1633syncable::AutofillMigrationState 1634 SyncManager::GetAutofillMigrationState() { 1635 return data_->GetAutofillMigrationState(); 1636} 1637 1638void SyncManager::SetAutofillMigrationState( 1639 syncable::AutofillMigrationState state) { 1640 return data_->SetAutofillMigrationState(state); 1641} 1642 1643syncable::AutofillMigrationDebugInfo 1644 SyncManager::GetAutofillMigrationDebugInfo() { 1645 return data_->GetAutofillMigrationDebugInfo(); 1646} 1647 1648void SyncManager::SetAutofillMigrationDebugInfo( 1649 syncable::AutofillMigrationDebugInfo::PropertyToSet property_to_set, 1650 const syncable::AutofillMigrationDebugInfo& info) { 1651 return data_->SetAutofillMigrationDebugInfo(property_to_set, info); 1652} 1653 1654void SyncManager::SetPassphrase(const std::string& passphrase, 1655 bool is_explicit) { 1656 data_->SetPassphrase(passphrase, is_explicit); 1657} 1658 1659void SyncManager::EncryptDataTypes( 1660 const syncable::ModelTypeSet& encrypted_types) { 1661 data_->EncryptDataTypes(encrypted_types); 1662} 1663 1664bool SyncManager::IsUsingExplicitPassphrase() { 1665 return data_ && data_->IsUsingExplicitPassphrase(); 1666} 1667 1668void SyncManager::RequestNudge(const tracked_objects::Location& location) { 1669 data_->RequestNudge(location); 1670} 1671 1672void SyncManager::RequestClearServerData() { 1673 if (data_->syncer_thread()) 1674 data_->syncer_thread()->ScheduleClearUserData(); 1675} 1676 1677void SyncManager::RequestConfig(const syncable::ModelTypeBitSet& types) { 1678 if (!data_->syncer_thread()) 1679 return; 1680 StartConfigurationMode(NULL); 1681 data_->syncer_thread()->ScheduleConfig(types); 1682} 1683 1684void SyncManager::StartConfigurationMode(ModeChangeCallback* callback) { 1685 if (!data_->syncer_thread()) 1686 return; 1687 data_->syncer_thread()->Start( 1688 browser_sync::SyncerThread::CONFIGURATION_MODE, callback); 1689} 1690 1691const std::string& SyncManager::GetAuthenticatedUsername() { 1692 DCHECK(data_); 1693 return data_->username_for_share(); 1694} 1695 1696bool SyncManager::SyncInternal::Init( 1697 const FilePath& database_location, 1698 const std::string& sync_server_and_path, 1699 int port, 1700 bool use_ssl, 1701 HttpPostProviderFactory* post_factory, 1702 ModelSafeWorkerRegistrar* model_safe_worker_registrar, 1703 const char* user_agent, 1704 const SyncCredentials& credentials, 1705 sync_notifier::SyncNotifier* sync_notifier, 1706 const std::string& restored_key_for_bootstrapping, 1707 bool setup_for_test_mode) { 1708 1709 VLOG(1) << "Starting SyncInternal initialization."; 1710 1711 core_message_loop_ = MessageLoop::current(); 1712 DCHECK(core_message_loop_); 1713 registrar_ = model_safe_worker_registrar; 1714 setup_for_test_mode_ = setup_for_test_mode; 1715 1716 sync_notifier_ = sync_notifier; 1717 sync_notifier_->AddObserver(this); 1718 1719 share_.dir_manager.reset(new DirectoryManager(database_location)); 1720 1721 connection_manager_.reset(new SyncAPIServerConnectionManager( 1722 sync_server_and_path, port, use_ssl, user_agent, post_factory)); 1723 1724 net::NetworkChangeNotifier::AddIPAddressObserver(this); 1725 1726 connection_manager()->AddListener(this); 1727 1728 // TODO(akalin): CheckServerReachable() can block, which may cause jank if we 1729 // try to shut down sync. Fix this. 1730 core_message_loop_->PostTask(FROM_HERE, 1731 method_factory_.NewRunnableMethod(&SyncInternal::CheckServerReachable)); 1732 1733 // Test mode does not use a syncer context or syncer thread. 1734 if (!setup_for_test_mode_) { 1735 // Build a SyncSessionContext and store the worker in it. 1736 VLOG(1) << "Sync is bringing up SyncSessionContext."; 1737 std::vector<SyncEngineEventListener*> listeners; 1738 listeners.push_back(&allstatus_); 1739 listeners.push_back(this); 1740 SyncSessionContext* context = new SyncSessionContext( 1741 connection_manager_.get(), 1742 dir_manager(), 1743 model_safe_worker_registrar, 1744 listeners); 1745 context->set_account_name(credentials.email); 1746 // The SyncerThread takes ownership of |context|. 1747 syncer_thread_.reset(new SyncerThread(context, new Syncer())); 1748 } 1749 1750 bool signed_in = SignIn(credentials); 1751 1752 if (signed_in && syncer_thread()) { 1753 syncer_thread()->Start( 1754 browser_sync::SyncerThread::CONFIGURATION_MODE, NULL); 1755 } 1756 1757 // Do this once the directory is opened. 1758 BootstrapEncryption(restored_key_for_bootstrapping); 1759 MarkAndNotifyInitializationComplete(); 1760 return signed_in; 1761} 1762 1763void SyncManager::SyncInternal::BootstrapEncryption( 1764 const std::string& restored_key_for_bootstrapping) { 1765 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share()); 1766 if (!lookup.good()) { 1767 NOTREACHED(); 1768 return; 1769 } 1770 1771 if (!lookup->initial_sync_ended_for_type(syncable::NIGORI)) 1772 return; 1773 1774 sync_pb::NigoriSpecifics nigori; 1775 { 1776 // Cryptographer should only be accessed while holding a transaction. 1777 ReadTransaction trans(GetUserShare()); 1778 Cryptographer* cryptographer = trans.GetCryptographer(); 1779 cryptographer->Bootstrap(restored_key_for_bootstrapping); 1780 1781 ReadNode node(&trans); 1782 if (!node.InitByTagLookup(kNigoriTag)) { 1783 NOTREACHED(); 1784 return; 1785 } 1786 1787 nigori.CopyFrom(node.GetNigoriSpecifics()); 1788 if (!nigori.encrypted().blob().empty()) { 1789 if (cryptographer->CanDecrypt(nigori.encrypted())) { 1790 cryptographer->SetKeys(nigori.encrypted()); 1791 } else { 1792 cryptographer->SetPendingKeys(nigori.encrypted()); 1793 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, 1794 OnPassphraseRequired(true)); 1795 } 1796 } 1797 } 1798 1799 // Refresh list of encrypted datatypes. 1800 syncable::ModelTypeSet encrypted_types = 1801 syncable::GetEncryptedDataTypesFromNigori(nigori); 1802 1803 // Ensure any datatypes that need encryption are encrypted. 1804 EncryptDataTypes(encrypted_types); 1805} 1806 1807void SyncManager::SyncInternal::StartSyncing() { 1808 // Start the syncer thread. This won't actually 1809 // result in any syncing until at least the 1810 // DirectoryManager broadcasts the OPENED event, 1811 // and a valid server connection is detected. 1812 if (syncer_thread()) // NULL during certain unittests. 1813 syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL); 1814} 1815 1816void SyncManager::SyncInternal::MarkAndNotifyInitializationComplete() { 1817 // There is only one real time we need this mutex. If we get an auth 1818 // success, and before the initial sync ends we get an auth failure. In this 1819 // case we'll be listening to both the AuthWatcher and Syncer, and it's a race 1820 // between their respective threads to call MarkAndNotify. We need to make 1821 // sure the observer is notified once and only once. 1822 { 1823 base::AutoLock lock(initialized_mutex_); 1824 if (initialized_) 1825 return; 1826 initialized_ = true; 1827 } 1828 1829 // Notify that initialization is complete. 1830 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, 1831 OnInitializationComplete()); 1832} 1833 1834void SyncManager::SyncInternal::SendNotification() { 1835 DCHECK_EQ(MessageLoop::current(), core_message_loop_); 1836 if (!sync_notifier_) { 1837 VLOG(1) << "Not sending notification: sync_notifier_ is NULL"; 1838 return; 1839 } 1840 allstatus_.IncrementNotificationsSent(); 1841 sync_notifier_->SendNotification(); 1842} 1843 1844bool SyncManager::SyncInternal::OpenDirectory() { 1845 DCHECK(!initialized()) << "Should only happen once"; 1846 1847 bool share_opened = dir_manager()->Open(username_for_share()); 1848 DCHECK(share_opened); 1849 if (!share_opened) { 1850 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, 1851 OnStopSyncingPermanently()); 1852 1853 LOG(ERROR) << "Could not open share for:" << username_for_share(); 1854 return false; 1855 } 1856 1857 // Database has to be initialized for the guid to be available. 1858 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share()); 1859 if (!lookup.good()) { 1860 NOTREACHED(); 1861 return false; 1862 } 1863 1864 connection_manager()->set_client_id(lookup->cache_guid()); 1865 1866 lookup->SetChangeListener(this); 1867 return true; 1868} 1869 1870bool SyncManager::SyncInternal::SignIn(const SyncCredentials& credentials) { 1871 DCHECK_EQ(MessageLoop::current(), core_message_loop_); 1872 DCHECK(share_.name.empty()); 1873 share_.name = credentials.email; 1874 1875 VLOG(1) << "Signing in user: " << username_for_share(); 1876 if (!OpenDirectory()) 1877 return false; 1878 1879 // Retrieve and set the sync notifier state. This should be done 1880 // only after OpenDirectory is called. 1881 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share()); 1882 std::string state; 1883 if (lookup.good()) { 1884 state = lookup->GetAndClearNotificationState(); 1885 } else { 1886 LOG(ERROR) << "Could not read notification state"; 1887 } 1888 if (VLOG_IS_ON(1)) { 1889 std::string encoded_state; 1890 base::Base64Encode(state, &encoded_state); 1891 VLOG(1) << "Read notification state: " << encoded_state; 1892 } 1893 sync_notifier_->SetState(state); 1894 1895 UpdateCredentials(credentials); 1896 UpdateEnabledTypes(); 1897 return true; 1898} 1899 1900void SyncManager::SyncInternal::UpdateCredentials( 1901 const SyncCredentials& credentials) { 1902 DCHECK_EQ(MessageLoop::current(), core_message_loop_); 1903 DCHECK_EQ(credentials.email, share_.name); 1904 DCHECK(!credentials.email.empty()); 1905 DCHECK(!credentials.sync_token.empty()); 1906 connection_manager()->set_auth_token(credentials.sync_token); 1907 sync_notifier_->UpdateCredentials( 1908 credentials.email, credentials.sync_token); 1909 if (!setup_for_test_mode_) { 1910 CheckServerReachable(); 1911 } 1912} 1913 1914void SyncManager::SyncInternal::UpdateEnabledTypes() { 1915 DCHECK_EQ(MessageLoop::current(), core_message_loop_); 1916 ModelSafeRoutingInfo routes; 1917 registrar_->GetModelSafeRoutingInfo(&routes); 1918 syncable::ModelTypeSet enabled_types; 1919 for (ModelSafeRoutingInfo::const_iterator it = routes.begin(); 1920 it != routes.end(); ++it) { 1921 enabled_types.insert(it->first); 1922 } 1923 sync_notifier_->UpdateEnabledTypes(enabled_types); 1924} 1925 1926void SyncManager::SyncInternal::RaiseAuthNeededEvent() { 1927 FOR_EACH_OBSERVER( 1928 SyncManager::Observer, observers_, 1929 OnAuthError(AuthError(AuthError::INVALID_GAIA_CREDENTIALS))); 1930} 1931 1932void SyncManager::SyncInternal::SetUsingExplicitPassphrasePrefForMigration( 1933 WriteTransaction* const trans) { 1934 WriteNode node(trans); 1935 if (!node.InitByTagLookup(kNigoriTag)) { 1936 // TODO(albertb): Plumb an UnrecoverableError all the way back to the PSS. 1937 NOTREACHED(); 1938 return; 1939 } 1940 sync_pb::NigoriSpecifics specifics(node.GetNigoriSpecifics()); 1941 specifics.set_using_explicit_passphrase(true); 1942 node.SetNigoriSpecifics(specifics); 1943} 1944 1945void SyncManager::SyncInternal::SetPassphrase( 1946 const std::string& passphrase, bool is_explicit) { 1947 // All accesses to the cryptographer are protected by a transaction. 1948 WriteTransaction trans(GetUserShare()); 1949 Cryptographer* cryptographer = trans.GetCryptographer(); 1950 KeyParams params = {"localhost", "dummy", passphrase}; 1951 1952 if (cryptographer->has_pending_keys()) { 1953 if (!cryptographer->DecryptPendingKeys(params)) { 1954 VLOG(1) << "Passphrase failed to decrypt pending keys."; 1955 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, 1956 OnPassphraseFailed()); 1957 return; 1958 } 1959 1960 // TODO(tim): If this is the first time the user has entered a passphrase 1961 // since the protocol changed to store passphrase preferences in the cloud, 1962 // make sure we update this preference. See bug 62103. 1963 if (is_explicit) 1964 SetUsingExplicitPassphrasePrefForMigration(&trans); 1965 1966 // Nudge the syncer so that encrypted datatype updates that were waiting for 1967 // this passphrase get applied as soon as possible. 1968 RequestNudge(FROM_HERE); 1969 } else { 1970 VLOG(1) << "No pending keys, adding provided passphrase."; 1971 WriteNode node(&trans); 1972 if (!node.InitByTagLookup(kNigoriTag)) { 1973 // TODO(albertb): Plumb an UnrecoverableError all the way back to the PSS. 1974 NOTREACHED(); 1975 return; 1976 } 1977 1978 // Prevent an implicit SetPassphrase request from changing an explicitly 1979 // set passphrase. 1980 if (!is_explicit && node.GetNigoriSpecifics().using_explicit_passphrase()) 1981 return; 1982 1983 cryptographer->AddKey(params); 1984 1985 // TODO(tim): Bug 58231. It would be nice if SetPassphrase didn't require 1986 // messing with the Nigori node, because we can't call SetPassphrase until 1987 // download conditions are met vs Cryptographer init. It seems like it's 1988 // safe to defer this work. 1989 sync_pb::NigoriSpecifics specifics(node.GetNigoriSpecifics()); 1990 specifics.clear_encrypted(); 1991 cryptographer->GetKeys(specifics.mutable_encrypted()); 1992 specifics.set_using_explicit_passphrase(is_explicit); 1993 node.SetNigoriSpecifics(specifics); 1994 ReEncryptEverything(&trans); 1995 } 1996 1997 std::string bootstrap_token; 1998 cryptographer->GetBootstrapToken(&bootstrap_token); 1999 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, 2000 OnPassphraseAccepted(bootstrap_token)); 2001} 2002 2003bool SyncManager::SyncInternal::IsUsingExplicitPassphrase() { 2004 ReadTransaction trans(&share_); 2005 ReadNode node(&trans); 2006 if (!node.InitByTagLookup(kNigoriTag)) { 2007 // TODO(albertb): Plumb an UnrecoverableError all the way back to the PSS. 2008 NOTREACHED(); 2009 return false; 2010 } 2011 2012 return node.GetNigoriSpecifics().using_explicit_passphrase(); 2013} 2014 2015void SyncManager::SyncInternal::EncryptDataTypes( 2016 const syncable::ModelTypeSet& encrypted_types) { 2017 VLOG(1) << "Attempting to encrypt datatypes " 2018 << syncable::ModelTypeSetToString(encrypted_types); 2019 2020 WriteTransaction trans(GetUserShare()); 2021 WriteNode node(&trans); 2022 if (!node.InitByTagLookup(kNigoriTag)) { 2023 LOG(ERROR) << "Unable to set encrypted datatypes because Nigori node not " 2024 << "found."; 2025 NOTREACHED(); 2026 return; 2027 } 2028 2029 // Update the Nigori node set of encrypted datatypes so other machines notice. 2030 // Note, we merge the current encrypted types with those requested. Once a 2031 // datatypes is marked as needing encryption, it is never unmarked. 2032 sync_pb::NigoriSpecifics nigori; 2033 nigori.CopyFrom(node.GetNigoriSpecifics()); 2034 syncable::ModelTypeSet current_encrypted_types = 2035 syncable::GetEncryptedDataTypesFromNigori(nigori); 2036 syncable::ModelTypeSet newly_encrypted_types; 2037 std::set_union(current_encrypted_types.begin(), current_encrypted_types.end(), 2038 encrypted_types.begin(), encrypted_types.end(), 2039 std::inserter(newly_encrypted_types, 2040 newly_encrypted_types.begin())); 2041 syncable::FillNigoriEncryptedTypes(newly_encrypted_types, &nigori); 2042 node.SetNigoriSpecifics(nigori); 2043 2044 // TODO(zea): only reencrypt this datatype? ReEncrypting everything is a 2045 // safer approach, and should not impact anything that is already encrypted 2046 // (redundant changes are ignored). 2047 ReEncryptEverything(&trans); 2048 return; 2049} 2050 2051namespace { 2052 2053void FindChildNodesContainingString(const std::string& lowercase_query, 2054 const ReadNode& parent_node, 2055 sync_api::ReadTransaction* trans, 2056 ListValue* result) { 2057 int64 child_id = parent_node.GetFirstChildId(); 2058 while (child_id != kInvalidId) { 2059 ReadNode node(trans); 2060 if (node.InitByIdLookup(child_id)) { 2061 if (node.ContainsString(lowercase_query)) { 2062 result->Append(new StringValue(base::Int64ToString(child_id))); 2063 } 2064 FindChildNodesContainingString(lowercase_query, node, trans, result); 2065 child_id = node.GetSuccessorId(); 2066 } else { 2067 LOG(WARNING) << "Lookup of node failed. Id: " << child_id; 2068 return; 2069 } 2070 } 2071} 2072} // namespace 2073 2074// Returned pointer owned by the caller. 2075ListValue* SyncManager::SyncInternal::FindNodesContainingString( 2076 const std::string& query) { 2077 // Convert the query string to lower case to perform case insensitive 2078 // searches. 2079 std::string lowercase_query = query; 2080 StringToLowerASCII(&lowercase_query); 2081 ReadTransaction trans(GetUserShare()); 2082 ReadNode root(&trans); 2083 root.InitByRootLookup(); 2084 2085 ListValue* result = new ListValue(); 2086 2087 base::Time start_time = base::Time::Now(); 2088 FindChildNodesContainingString(lowercase_query, root, &trans, result); 2089 base::Time end_time = base::Time::Now(); 2090 2091 base::TimeDelta delta = end_time - start_time; 2092 VLOG(1) << "Time taken in milliseconds to search " << delta.InMilliseconds(); 2093 2094 return result; 2095} 2096 2097void SyncManager::SyncInternal::ReEncryptEverything(WriteTransaction* trans) { 2098 syncable::ModelTypeSet encrypted_types = 2099 GetEncryptedDataTypes(trans->GetWrappedTrans()); 2100 ModelSafeRoutingInfo routes; 2101 registrar_->GetModelSafeRoutingInfo(&routes); 2102 std::string tag; 2103 for (syncable::ModelTypeSet::iterator iter = encrypted_types.begin(); 2104 iter != encrypted_types.end(); ++iter) { 2105 if (*iter == syncable::PASSWORDS || routes.count(*iter) == 0) 2106 continue; 2107 ReadNode type_root(trans); 2108 tag = syncable::ModelTypeToRootTag(*iter); 2109 if (!type_root.InitByTagLookup(tag)) { 2110 NOTREACHED(); 2111 return; 2112 } 2113 2114 // Iterate through all children of this datatype. 2115 std::queue<int64> to_visit; 2116 int64 child_id = type_root.GetFirstChildId(); 2117 to_visit.push(child_id); 2118 while (!to_visit.empty()) { 2119 child_id = to_visit.front(); 2120 to_visit.pop(); 2121 if (child_id == kInvalidId) 2122 continue; 2123 2124 WriteNode child(trans); 2125 if (!child.InitByIdLookup(child_id)) { 2126 NOTREACHED(); 2127 return; 2128 } 2129 if (child.GetIsFolder()) { 2130 to_visit.push(child.GetFirstChildId()); 2131 } else { 2132 // Rewrite the specifics of the node with encrypted data if necessary. 2133 child.ResetFromSpecifics(); 2134 } 2135 to_visit.push(child.GetSuccessorId()); 2136 } 2137 } 2138 2139 if (routes.count(syncable::PASSWORDS) > 0) { 2140 // Passwords are encrypted with their own legacy scheme. 2141 encrypted_types.insert(syncable::PASSWORDS); 2142 ReadNode passwords_root(trans); 2143 std::string passwords_tag = 2144 syncable::ModelTypeToRootTag(syncable::PASSWORDS); 2145 if (!passwords_root.InitByTagLookup(passwords_tag)) { 2146 LOG(WARNING) << "No passwords to reencrypt."; 2147 return; 2148 } 2149 2150 int64 child_id = passwords_root.GetFirstChildId(); 2151 while (child_id != kInvalidId) { 2152 WriteNode child(trans); 2153 if (!child.InitByIdLookup(child_id)) { 2154 NOTREACHED(); 2155 return; 2156 } 2157 child.SetPasswordSpecifics(child.GetPasswordSpecifics()); 2158 child_id = child.GetSuccessorId(); 2159 } 2160 } 2161 2162 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, 2163 OnEncryptionComplete(encrypted_types)); 2164} 2165 2166SyncManager::~SyncManager() { 2167 delete data_; 2168} 2169 2170void SyncManager::AddObserver(Observer* observer) { 2171 data_->AddObserver(observer); 2172} 2173 2174void SyncManager::RemoveObserver(Observer* observer) { 2175 data_->RemoveObserver(observer); 2176} 2177 2178browser_sync::JsBackend* SyncManager::GetJsBackend() { 2179 return data_; 2180} 2181 2182void SyncManager::Shutdown() { 2183 data_->Shutdown(); 2184} 2185 2186void SyncManager::SyncInternal::Shutdown() { 2187 method_factory_.RevokeAll(); 2188 2189 if (syncer_thread()) { 2190 syncer_thread()->Stop(); 2191 syncer_thread_.reset(); 2192 } 2193 2194 // We NULL out sync_notifer_ so that any pending tasks do not 2195 // trigger further notifications. 2196 // TODO(akalin): NULL the other member variables defensively, too. 2197 if (sync_notifier_) { 2198 sync_notifier_->RemoveObserver(this); 2199 } 2200 2201 // |this| is about to be destroyed, so we have to ensure any messages 2202 // that were posted to core_thread_ before or during syncer thread shutdown 2203 // are flushed out, else they refer to garbage memory. SendNotification 2204 // is an example. 2205 // TODO(tim): Remove this monstrosity, perhaps with ObserverListTS once core 2206 // thread is removed. Bug 78190. 2207 { 2208 CHECK(core_message_loop_); 2209 bool old_state = core_message_loop_->NestableTasksAllowed(); 2210 core_message_loop_->SetNestableTasksAllowed(true); 2211 core_message_loop_->RunAllPending(); 2212 core_message_loop_->SetNestableTasksAllowed(old_state); 2213 } 2214 2215 net::NetworkChangeNotifier::RemoveIPAddressObserver(this); 2216 2217 connection_manager_hookup_.reset(); 2218 2219 if (dir_manager()) { 2220 dir_manager()->FinalSaveChangesForAll(); 2221 dir_manager()->Close(username_for_share()); 2222 } 2223 2224 // Reset the DirectoryManager and UserSettings so they relinquish sqlite 2225 // handles to backing files. 2226 share_.dir_manager.reset(); 2227 2228 core_message_loop_ = NULL; 2229} 2230 2231void SyncManager::SyncInternal::OnIPAddressChanged() { 2232 VLOG(1) << "IP address change detected"; 2233#if defined (OS_CHROMEOS) 2234 // TODO(tim): This is a hack to intentionally lose a race with flimflam at 2235 // shutdown, so we don't cause shutdown to wait for our http request. 2236 // http://crosbug.com/8429 2237 MessageLoop::current()->PostDelayedTask(FROM_HERE, 2238 method_factory_.NewRunnableMethod(&SyncInternal::OnIPAddressChangedImpl), 2239 kChromeOSNetworkChangeReactionDelayHackMsec); 2240#else 2241 OnIPAddressChangedImpl(); 2242#endif // defined(OS_CHROMEOS) 2243} 2244 2245void SyncManager::SyncInternal::OnIPAddressChangedImpl() { 2246 // TODO(akalin): CheckServerReachable() can block, which may cause 2247 // jank if we try to shut down sync. Fix this. 2248 connection_manager()->CheckServerReachable(); 2249 RequestNudge(FROM_HERE); 2250} 2251 2252void SyncManager::SyncInternal::OnServerConnectionEvent( 2253 const ServerConnectionEvent2& event) { 2254 ServerConnectionEvent legacy; 2255 legacy.what_happened = ServerConnectionEvent::STATUS_CHANGED; 2256 legacy.connection_code = event.connection_code; 2257 legacy.server_reachable = event.server_reachable; 2258 HandleServerConnectionEvent(legacy); 2259} 2260 2261void SyncManager::SyncInternal::HandleServerConnectionEvent( 2262 const ServerConnectionEvent& event) { 2263 allstatus_.HandleServerConnectionEvent(event); 2264 if (event.what_happened == ServerConnectionEvent::STATUS_CHANGED) { 2265 if (event.connection_code == 2266 browser_sync::HttpResponse::SERVER_CONNECTION_OK) { 2267 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, 2268 OnAuthError(AuthError::None())); 2269 } 2270 2271 if (event.connection_code == browser_sync::HttpResponse::SYNC_AUTH_ERROR) { 2272 FOR_EACH_OBSERVER( 2273 SyncManager::Observer, observers_, 2274 OnAuthError(AuthError(AuthError::INVALID_GAIA_CREDENTIALS))); 2275 } 2276 } 2277} 2278 2279void SyncManager::SyncInternal::HandleTransactionCompleteChangeEvent( 2280 const syncable::ModelTypeBitSet& models_with_changes) { 2281 // This notification happens immediately after the transaction mutex is 2282 // released. This allows work to be performed without blocking other threads 2283 // from acquiring a transaction. 2284 if (observers_.size() <= 0) 2285 return; 2286 2287 // Call commit. 2288 for (int i = 0; i < syncable::MODEL_TYPE_COUNT; ++i) { 2289 if (models_with_changes.test(i)) { 2290 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, 2291 OnChangesComplete(syncable::ModelTypeFromInt(i))); 2292 } 2293 } 2294} 2295 2296ModelTypeBitSet SyncManager::SyncInternal::HandleTransactionEndingChangeEvent( 2297 syncable::BaseTransaction* trans) { 2298 // This notification happens immediately before a syncable WriteTransaction 2299 // falls out of scope. It happens while the channel mutex is still held, 2300 // and while the transaction mutex is held, so it cannot be re-entrant. 2301 if (observers_.size() <= 0 || ChangeBuffersAreEmpty()) 2302 return ModelTypeBitSet(); 2303 2304 // This will continue the WriteTransaction using a read only wrapper. 2305 // This is the last chance for read to occur in the WriteTransaction 2306 // that's closing. This special ReadTransaction will not close the 2307 // underlying transaction. 2308 ReadTransaction read_trans(GetUserShare(), trans); 2309 2310 syncable::ModelTypeBitSet models_with_changes; 2311 for (int i = 0; i < syncable::MODEL_TYPE_COUNT; ++i) { 2312 if (change_buffers_[i].IsEmpty()) 2313 continue; 2314 2315 vector<ChangeRecord> ordered_changes; 2316 change_buffers_[i].GetAllChangesInTreeOrder(&read_trans, &ordered_changes); 2317 if (!ordered_changes.empty()) { 2318 FOR_EACH_OBSERVER( 2319 SyncManager::Observer, observers_, 2320 OnChangesApplied(syncable::ModelTypeFromInt(i), &read_trans, 2321 &ordered_changes[0], ordered_changes.size())); 2322 models_with_changes.set(i, true); 2323 } 2324 change_buffers_[i].Clear(); 2325 } 2326 return models_with_changes; 2327} 2328 2329void SyncManager::SyncInternal::HandleCalculateChangesChangeEventFromSyncApi( 2330 const OriginalEntries& originals, 2331 const WriterTag& writer, 2332 syncable::BaseTransaction* trans) { 2333 // We have been notified about a user action changing a sync model. 2334 DCHECK(writer == syncable::SYNCAPI || 2335 writer == syncable::UNITTEST); 2336 LOG_IF(WARNING, !ChangeBuffersAreEmpty()) << 2337 "CALCULATE_CHANGES called with unapplied old changes."; 2338 2339 bool exists_unsynced_items = false; 2340 bool only_preference_changes = true; 2341 syncable::ModelTypeBitSet model_types; 2342 for (syncable::OriginalEntries::const_iterator i = originals.begin(); 2343 i != originals.end() && !exists_unsynced_items; 2344 ++i) { 2345 int64 id = i->ref(syncable::META_HANDLE); 2346 syncable::Entry e(trans, syncable::GET_BY_HANDLE, id); 2347 DCHECK(e.good()); 2348 2349 syncable::ModelType model_type = e.GetModelType(); 2350 2351 if (e.Get(syncable::IS_UNSYNCED)) { 2352 if (model_type == syncable::TOP_LEVEL_FOLDER || 2353 model_type == syncable::UNSPECIFIED) { 2354 NOTREACHED() << "Permanent or underspecified item changed via syncapi."; 2355 continue; 2356 } 2357 // Unsynced items will cause us to nudge the the syncer. 2358 exists_unsynced_items = true; 2359 2360 model_types[model_type] = true; 2361 if (model_type != syncable::PREFERENCES) 2362 only_preference_changes = false; 2363 } 2364 } 2365 if (exists_unsynced_items && syncer_thread()) { 2366 int nudge_delay = only_preference_changes ? 2367 kPreferencesNudgeDelayMilliseconds : kDefaultNudgeDelayMilliseconds; 2368 core_message_loop_->PostTask(FROM_HERE, 2369 NewRunnableMethod(this, &SyncInternal::RequestNudgeWithDataTypes, 2370 TimeDelta::FromMilliseconds(nudge_delay), 2371 browser_sync::NUDGE_SOURCE_LOCAL, 2372 model_types, 2373 FROM_HERE)); 2374 } 2375} 2376 2377void SyncManager::SyncInternal::SetExtraChangeRecordData(int64 id, 2378 syncable::ModelType type, ChangeReorderBuffer* buffer, 2379 Cryptographer* cryptographer, const syncable::EntryKernel& original, 2380 bool existed_before, bool exists_now) { 2381 // If this is a deletion and the datatype was encrypted, we need to decrypt it 2382 // and attach it to the buffer. 2383 if (!exists_now && existed_before) { 2384 sync_pb::EntitySpecifics original_specifics(original.ref(SPECIFICS)); 2385 if (type == syncable::PASSWORDS) { 2386 // Passwords must use their own legacy ExtraPasswordChangeRecordData. 2387 scoped_ptr<sync_pb::PasswordSpecificsData> data( 2388 DecryptPasswordSpecifics(original_specifics, cryptographer)); 2389 if (!data.get()) { 2390 NOTREACHED(); 2391 return; 2392 } 2393 buffer->SetExtraDataForId(id, new ExtraPasswordChangeRecordData(*data)); 2394 } else if (original_specifics.has_encrypted()) { 2395 // All other datatypes can just create a new unencrypted specifics and 2396 // attach it. 2397 const sync_pb::EncryptedData& encrypted = original_specifics.encrypted(); 2398 if (!cryptographer->Decrypt(encrypted, &original_specifics)) { 2399 NOTREACHED(); 2400 return; 2401 } 2402 } 2403 buffer->SetSpecificsForId(id, original_specifics); 2404 } 2405} 2406 2407void SyncManager::SyncInternal::HandleCalculateChangesChangeEventFromSyncer( 2408 const OriginalEntries& originals, 2409 const WriterTag& writer, 2410 syncable::BaseTransaction* trans) { 2411 // We only expect one notification per sync step, so change_buffers_ should 2412 // contain no pending entries. 2413 DCHECK(writer == syncable::SYNCER || 2414 writer == syncable::UNITTEST); 2415 LOG_IF(WARNING, !ChangeBuffersAreEmpty()) << 2416 "CALCULATE_CHANGES called with unapplied old changes."; 2417 2418 Cryptographer* crypto = dir_manager()->GetCryptographer(trans); 2419 for (syncable::OriginalEntries::const_iterator i = originals.begin(); 2420 i != originals.end(); ++i) { 2421 int64 id = i->ref(syncable::META_HANDLE); 2422 syncable::Entry e(trans, syncable::GET_BY_HANDLE, id); 2423 bool existed_before = !i->ref(syncable::IS_DEL); 2424 bool exists_now = e.good() && !e.Get(syncable::IS_DEL); 2425 DCHECK(e.good()); 2426 2427 // Omit items that aren't associated with a model. 2428 syncable::ModelType type = e.GetModelType(); 2429 if (type == syncable::TOP_LEVEL_FOLDER || type == syncable::UNSPECIFIED) 2430 continue; 2431 2432 if (exists_now && !existed_before) 2433 change_buffers_[type].PushAddedItem(id); 2434 else if (!exists_now && existed_before) 2435 change_buffers_[type].PushDeletedItem(id); 2436 else if (exists_now && existed_before && 2437 VisiblePropertiesDiffer(*i, e, crypto)) { 2438 change_buffers_[type].PushUpdatedItem(id, VisiblePositionsDiffer(*i, e)); 2439 } 2440 2441 SetExtraChangeRecordData(id, type, &change_buffers_[type], crypto, *i, 2442 existed_before, exists_now); 2443 } 2444} 2445 2446SyncManager::Status SyncManager::SyncInternal::GetStatus() { 2447 return allstatus_.status(); 2448} 2449 2450void SyncManager::SyncInternal::RequestNudge( 2451 const tracked_objects::Location& location) { 2452 if (syncer_thread()) 2453 syncer_thread()->ScheduleNudge( 2454 TimeDelta::FromMilliseconds(0), browser_sync::NUDGE_SOURCE_LOCAL, 2455 ModelTypeBitSet(), location); 2456} 2457 2458void SyncManager::SyncInternal::RequestNudgeWithDataTypes( 2459 const TimeDelta& delay, 2460 browser_sync::NudgeSource source, const ModelTypeBitSet& types, 2461 const tracked_objects::Location& nudge_location) { 2462 if (syncer_thread()) 2463 syncer_thread()->ScheduleNudge(delay, source, types, nudge_location); 2464} 2465 2466void SyncManager::SyncInternal::OnSyncEngineEvent( 2467 const SyncEngineEvent& event) { 2468 if (observers_.size() <= 0) 2469 return; 2470 2471 // Only send an event if this is due to a cycle ending and this cycle 2472 // concludes a canonical "sync" process; that is, based on what is known 2473 // locally we are "all happy" and up-to-date. There may be new changes on 2474 // the server, but we'll get them on a subsequent sync. 2475 // 2476 // Notifications are sent at the end of every sync cycle, regardless of 2477 // whether we should sync again. 2478 if (event.what_happened == SyncEngineEvent::SYNC_CYCLE_ENDED) { 2479 ModelSafeRoutingInfo enabled_types; 2480 registrar_->GetModelSafeRoutingInfo(&enabled_types); 2481 { 2482 // Check to see if we need to notify the frontend that we have newly 2483 // encrypted types or that we require a passphrase. 2484 sync_api::ReadTransaction trans(GetUserShare()); 2485 sync_api::ReadNode node(&trans); 2486 if (!node.InitByTagLookup(kNigoriTag)) { 2487 DCHECK(!event.snapshot->is_share_usable); 2488 return; 2489 } 2490 const sync_pb::NigoriSpecifics& nigori = node.GetNigoriSpecifics(); 2491 syncable::ModelTypeSet encrypted_types = 2492 syncable::GetEncryptedDataTypesFromNigori(nigori); 2493 // If passwords are enabled, they're automatically considered encrypted. 2494 if (enabled_types.count(syncable::PASSWORDS) > 0) 2495 encrypted_types.insert(syncable::PASSWORDS); 2496 if (!encrypted_types.empty()) { 2497 Cryptographer* cryptographer = trans.GetCryptographer(); 2498 if (!cryptographer->is_ready() && !cryptographer->has_pending_keys()) { 2499 if (!nigori.encrypted().blob().empty()) { 2500 DCHECK(!cryptographer->CanDecrypt(nigori.encrypted())); 2501 cryptographer->SetPendingKeys(nigori.encrypted()); 2502 } 2503 } 2504 2505 // If we've completed a sync cycle and the cryptographer isn't ready 2506 // yet, prompt the user for a passphrase. 2507 if (cryptographer->has_pending_keys()) { 2508 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, 2509 OnPassphraseRequired(true)); 2510 } else if (!cryptographer->is_ready()) { 2511 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, 2512 OnPassphraseRequired(false)); 2513 } else { 2514 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, 2515 OnEncryptionComplete(encrypted_types)); 2516 } 2517 } 2518 } 2519 2520 if (!initialized()) 2521 return; 2522 2523 if (!event.snapshot->has_more_to_sync) { 2524 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, 2525 OnSyncCycleCompleted(event.snapshot)); 2526 } 2527 2528 // This is here for tests, which are still using p2p notifications. 2529 // SendNotification does not do anything if we are using server based 2530 // notifications. 2531 // TODO(chron): Consider changing this back to track has_more_to_sync 2532 // only notify peers if a successful commit has occurred. 2533 bool new_notification = 2534 (event.snapshot->syncer_status.num_successful_commits > 0); 2535 if (new_notification) { 2536 core_message_loop_->PostTask( 2537 FROM_HERE, 2538 NewRunnableMethod( 2539 this, 2540 &SyncManager::SyncInternal::SendNotification)); 2541 } 2542 } 2543 2544 if (event.what_happened == SyncEngineEvent::STOP_SYNCING_PERMANENTLY) { 2545 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, 2546 OnStopSyncingPermanently()); 2547 return; 2548 } 2549 2550 if (event.what_happened == SyncEngineEvent::CLEAR_SERVER_DATA_SUCCEEDED) { 2551 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, 2552 OnClearServerDataSucceeded()); 2553 return; 2554 } 2555 2556 if (event.what_happened == SyncEngineEvent::CLEAR_SERVER_DATA_FAILED) { 2557 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, 2558 OnClearServerDataFailed()); 2559 return; 2560 } 2561 2562 if (event.what_happened == SyncEngineEvent::UPDATED_TOKEN) { 2563 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, 2564 OnUpdatedToken(event.updated_token)); 2565 return; 2566 } 2567} 2568 2569void SyncManager::SyncInternal::SetParentJsEventRouter( 2570 browser_sync::JsEventRouter* router) { 2571 DCHECK(router); 2572 parent_router_ = router; 2573} 2574 2575void SyncManager::SyncInternal::RemoveParentJsEventRouter() { 2576 parent_router_ = NULL; 2577} 2578 2579const browser_sync::JsEventRouter* 2580 SyncManager::SyncInternal::GetParentJsEventRouter() const { 2581 return parent_router_; 2582} 2583 2584namespace { 2585 2586void LogNoRouter(const std::string& name, 2587 const browser_sync::JsArgList& args) { 2588 VLOG(1) << "No parent router; not replying to message " << name 2589 << " with args " << args.ToString(); 2590} 2591 2592} // namespace 2593 2594void SyncManager::SyncInternal::ProcessMessage( 2595 const std::string& name, const browser_sync::JsArgList& args, 2596 const browser_sync::JsEventHandler* sender) { 2597 DCHECK(initialized_); 2598 if (name == "getNotificationState") { 2599 if (!parent_router_) { 2600 LogNoRouter(name, args); 2601 return; 2602 } 2603 bool notifications_enabled = allstatus_.status().notifications_enabled; 2604 ListValue return_args; 2605 return_args.Append(Value::CreateBooleanValue(notifications_enabled)); 2606 parent_router_->RouteJsEvent( 2607 "onGetNotificationStateFinished", 2608 browser_sync::JsArgList(return_args), sender); 2609 } else if (name == "getNotificationInfo") { 2610 if (!parent_router_) { 2611 LogNoRouter(name, args); 2612 return; 2613 } 2614 2615 ListValue return_args; 2616 return_args.Append(NotificationInfoToValue(notification_info_map_)); 2617 parent_router_->RouteJsEvent("onGetNotificationInfoFinished", 2618 browser_sync::JsArgList(return_args), sender); 2619 } else if (name == "getRootNode") { 2620 if (!parent_router_) { 2621 LogNoRouter(name, args); 2622 return; 2623 } 2624 ReadTransaction trans(GetUserShare()); 2625 ReadNode root(&trans); 2626 root.InitByRootLookup(); 2627 ListValue return_args; 2628 return_args.Append(root.ToValue()); 2629 parent_router_->RouteJsEvent( 2630 "onGetRootNodeFinished", 2631 browser_sync::JsArgList(return_args), sender); 2632 } else if (name == "getNodeById") { 2633 if (!parent_router_) { 2634 LogNoRouter(name, args); 2635 return; 2636 } 2637 parent_router_->RouteJsEvent( 2638 "onGetNodeByIdFinished", ProcessGetNodeByIdMessage(args), sender); 2639 } else if (name == "findNodesContainingString") { 2640 if (!parent_router_) { 2641 LogNoRouter(name, args); 2642 return; 2643 } 2644 parent_router_->RouteJsEvent( 2645 "onFindNodesContainingStringFinished", 2646 ProcessFindNodesContainingString(args), sender); 2647 } else { 2648 VLOG(1) << "Dropping unknown message " << name 2649 << " with args " << args.ToString(); 2650 } 2651} 2652 2653browser_sync::JsArgList SyncManager::SyncInternal::ProcessGetNodeByIdMessage( 2654 const browser_sync::JsArgList& args) { 2655 ListValue null_return_args_list; 2656 null_return_args_list.Append(Value::CreateNullValue()); 2657 browser_sync::JsArgList null_return_args(null_return_args_list); 2658 std::string id_str; 2659 if (!args.Get().GetString(0, &id_str)) { 2660 return null_return_args; 2661 } 2662 int64 id; 2663 if (!base::StringToInt64(id_str, &id)) { 2664 return null_return_args; 2665 } 2666 if (id == kInvalidId) { 2667 return null_return_args; 2668 } 2669 ReadTransaction trans(GetUserShare()); 2670 ReadNode node(&trans); 2671 if (!node.InitByIdLookup(id)) { 2672 return null_return_args; 2673 } 2674 ListValue return_args; 2675 return_args.Append(node.ToValue()); 2676 return browser_sync::JsArgList(return_args); 2677} 2678 2679browser_sync::JsArgList SyncManager::SyncInternal:: 2680 ProcessFindNodesContainingString( 2681 const browser_sync::JsArgList& args) { 2682 std::string query; 2683 ListValue return_args; 2684 if (!args.Get().GetString(0, &query)) { 2685 return_args.Append(new ListValue()); 2686 return browser_sync::JsArgList(return_args); 2687 } 2688 2689 ListValue* result = FindNodesContainingString(query); 2690 return_args.Append(result); 2691 return browser_sync::JsArgList(return_args); 2692} 2693 2694void SyncManager::SyncInternal::OnNotificationStateChange( 2695 bool notifications_enabled) { 2696 VLOG(1) << "P2P: Notifications enabled = " 2697 << (notifications_enabled ? "true" : "false"); 2698 allstatus_.SetNotificationsEnabled(notifications_enabled); 2699 if (syncer_thread()) { 2700 syncer_thread()->set_notifications_enabled(notifications_enabled); 2701 } 2702 if (parent_router_) { 2703 ListValue args; 2704 args.Append(Value::CreateBooleanValue(notifications_enabled)); 2705 // TODO(akalin): Tidy up grammar in event names. 2706 parent_router_->RouteJsEvent("onSyncNotificationStateChange", 2707 browser_sync::JsArgList(args), NULL); 2708 } 2709} 2710 2711void SyncManager::SyncInternal::UpdateNotificationInfo( 2712 const syncable::ModelTypePayloadMap& type_payloads) { 2713 for (syncable::ModelTypePayloadMap::const_iterator it = type_payloads.begin(); 2714 it != type_payloads.end(); ++it) { 2715 NotificationInfo* info = ¬ification_info_map_[it->first]; 2716 info->total_count++; 2717 info->payload = it->second; 2718 } 2719} 2720 2721void SyncManager::SyncInternal::OnIncomingNotification( 2722 const syncable::ModelTypePayloadMap& type_payloads) { 2723 if (!type_payloads.empty()) { 2724 if (syncer_thread()) { 2725 syncer_thread()->ScheduleNudgeWithPayloads( 2726 TimeDelta::FromMilliseconds(kSyncerThreadDelayMsec), 2727 browser_sync::NUDGE_SOURCE_NOTIFICATION, 2728 type_payloads, FROM_HERE); 2729 } 2730 allstatus_.IncrementNotificationsReceived(); 2731 UpdateNotificationInfo(type_payloads); 2732 } else { 2733 LOG(WARNING) << "Sync received notification without any type information."; 2734 } 2735 2736 if (parent_router_) { 2737 ListValue args; 2738 ListValue* changed_types = new ListValue(); 2739 args.Append(changed_types); 2740 for (syncable::ModelTypePayloadMap::const_iterator 2741 it = type_payloads.begin(); 2742 it != type_payloads.end(); ++it) { 2743 const std::string& model_type_str = 2744 syncable::ModelTypeToString(it->first); 2745 changed_types->Append(Value::CreateStringValue(model_type_str)); 2746 } 2747 parent_router_->RouteJsEvent("onSyncIncomingNotification", 2748 browser_sync::JsArgList(args), NULL); 2749 } 2750} 2751 2752void SyncManager::SyncInternal::StoreState( 2753 const std::string& state) { 2754 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share()); 2755 if (!lookup.good()) { 2756 LOG(ERROR) << "Could not write notification state"; 2757 // TODO(akalin): Propagate result callback all the way to this 2758 // function and call it with "false" to signal failure. 2759 return; 2760 } 2761 if (VLOG_IS_ON(1)) { 2762 std::string encoded_state; 2763 base::Base64Encode(state, &encoded_state); 2764 VLOG(1) << "Writing notification state: " << encoded_state; 2765 } 2766 lookup->SetNotificationState(state); 2767 lookup->SaveChanges(); 2768} 2769 2770void SyncManager::SyncInternal::AddObserver( 2771 SyncManager::Observer* observer) { 2772 observers_.AddObserver(observer); 2773} 2774 2775void SyncManager::SyncInternal::RemoveObserver( 2776 SyncManager::Observer* observer) { 2777 observers_.RemoveObserver(observer); 2778} 2779 2780SyncManager::Status::Summary SyncManager::GetStatusSummary() const { 2781 return data_->GetStatus().summary; 2782} 2783 2784SyncManager::Status SyncManager::GetDetailedStatus() const { 2785 return data_->GetStatus(); 2786} 2787 2788SyncManager::SyncInternal* SyncManager::GetImpl() const { return data_; } 2789 2790void SyncManager::SaveChanges() { 2791 data_->SaveChanges(); 2792} 2793 2794void SyncManager::SyncInternal::SaveChanges() { 2795 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share()); 2796 if (!lookup.good()) { 2797 DCHECK(false) << "ScopedDirLookup creation failed; Unable to SaveChanges"; 2798 return; 2799 } 2800 lookup->SaveChanges(); 2801} 2802 2803////////////////////////////////////////////////////////////////////////// 2804// BaseTransaction member definitions 2805BaseTransaction::BaseTransaction(UserShare* share) 2806 : lookup_(NULL) { 2807 DCHECK(share && share->dir_manager.get()); 2808 lookup_ = new syncable::ScopedDirLookup(share->dir_manager.get(), 2809 share->name); 2810 cryptographer_ = share->dir_manager->GetCryptographer(this); 2811 if (!(lookup_->good())) 2812 DCHECK(false) << "ScopedDirLookup failed on valid DirManager."; 2813} 2814BaseTransaction::~BaseTransaction() { 2815 delete lookup_; 2816} 2817 2818UserShare* SyncManager::GetUserShare() const { 2819 DCHECK(data_->initialized()) << "GetUserShare requires initialization!"; 2820 return data_->GetUserShare(); 2821} 2822 2823bool SyncManager::HasUnsyncedItems() const { 2824 sync_api::ReadTransaction trans(GetUserShare()); 2825 return (trans.GetWrappedTrans()->directory()->unsynced_entity_count() != 0); 2826} 2827 2828void SyncManager::TriggerOnNotificationStateChangeForTest( 2829 bool notifications_enabled) { 2830 data_->OnNotificationStateChange(notifications_enabled); 2831} 2832 2833void SyncManager::TriggerOnIncomingNotificationForTest( 2834 const syncable::ModelTypeBitSet& model_types) { 2835 syncable::ModelTypePayloadMap model_types_with_payloads = 2836 syncable::ModelTypePayloadMapFromBitSet(model_types, 2837 std::string()); 2838 2839 data_->OnIncomingNotification(model_types_with_payloads); 2840} 2841 2842} // namespace sync_api 2843