syncapi.cc revision 4a5e2dc747d50c653511c68ccb2cfbfb740bd5a7
1// Copyright (c) 2010 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 "build/build_config.h" 8 9#include <bitset> 10#include <iomanip> 11#include <list> 12#include <string> 13#include <vector> 14 15#include "base/basictypes.h" 16#include "base/base64.h" 17#include "base/lock.h" 18#include "base/logging.h" 19#include "base/message_loop.h" 20#include "base/platform_thread.h" 21#include "base/scoped_ptr.h" 22#include "base/sha1.h" 23#include "base/string_util.h" 24#include "base/task.h" 25#include "base/utf_string_conversions.h" 26#include "chrome/browser/browser_process.h" 27#include "chrome/browser/browser_thread.h" 28#include "chrome/browser/sync/sync_constants.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/net/server_connection_manager.h" 33#include "chrome/browser/sync/engine/net/syncapi_server_connection_manager.h" 34#include "chrome/browser/sync/engine/syncer.h" 35#include "chrome/browser/sync/engine/syncer_thread.h" 36#include "chrome/browser/sync/notifier/server_notifier_thread.h" 37#include "chrome/browser/sync/notifier/state_writer.h" 38#include "chrome/browser/sync/protocol/app_specifics.pb.h" 39#include "chrome/browser/sync/protocol/autofill_specifics.pb.h" 40#include "chrome/browser/sync/protocol/bookmark_specifics.pb.h" 41#include "chrome/browser/sync/protocol/extension_specifics.pb.h" 42#include "chrome/browser/sync/protocol/nigori_specifics.pb.h" 43#include "chrome/browser/sync/protocol/password_specifics.pb.h" 44#include "chrome/browser/sync/protocol/preference_specifics.pb.h" 45#include "chrome/browser/sync/protocol/session_specifics.pb.h" 46#include "chrome/browser/sync/protocol/service_constants.h" 47#include "chrome/browser/sync/protocol/sync.pb.h" 48#include "chrome/browser/sync/protocol/theme_specifics.pb.h" 49#include "chrome/browser/sync/protocol/typed_url_specifics.pb.h" 50#include "chrome/browser/sync/sessions/sync_session_context.h" 51#include "chrome/browser/sync/syncable/directory_manager.h" 52#include "chrome/browser/sync/syncable/syncable.h" 53#include "chrome/browser/sync/util/crypto_helpers.h" 54#include "chrome/common/chrome_switches.h" 55#include "chrome/common/deprecated/event_sys.h" 56#include "chrome/common/net/gaia/gaia_authenticator.h" 57#include "jingle/notifier/listener/mediator_thread_impl.h" 58#include "jingle/notifier/listener/notification_constants.h" 59#include "jingle/notifier/listener/talk_mediator.h" 60#include "jingle/notifier/listener/talk_mediator_impl.h" 61#include "net/base/network_change_notifier.h" 62 63using browser_sync::AllStatus; 64using browser_sync::Cryptographer; 65using browser_sync::KeyParams; 66using browser_sync::ModelSafeRoutingInfo; 67using browser_sync::ModelSafeWorker; 68using browser_sync::ModelSafeWorkerRegistrar; 69using browser_sync::ServerConnectionEvent; 70using browser_sync::ServerConnectionEventListener; 71using browser_sync::SyncEngineEvent; 72using browser_sync::SyncEngineEventListener; 73using browser_sync::Syncer; 74using browser_sync::SyncerThread; 75using browser_sync::kNigoriTag; 76using browser_sync::sessions::SyncSessionContext; 77using notifier::TalkMediator; 78using notifier::TalkMediatorImpl; 79using std::list; 80using std::hex; 81using std::string; 82using std::vector; 83using syncable::Directory; 84using syncable::DirectoryManager; 85using syncable::Entry; 86using syncable::SPECIFICS; 87 88typedef GoogleServiceAuthError AuthError; 89 90static const int kThreadExitTimeoutMsec = 60000; 91static const int kSSLPort = 443; 92 93#if defined(OS_CHROMEOS) 94static const int kChromeOSNetworkChangeReactionDelayHackMsec = 5000; 95#endif // OS_CHROMEOS 96 97// We manage the lifetime of sync_api::SyncManager::SyncInternal ourselves. 98DISABLE_RUNNABLE_METHOD_REFCOUNT(sync_api::SyncManager::SyncInternal); 99 100namespace sync_api { 101 102static const FilePath::CharType kBookmarkSyncUserSettingsDatabase[] = 103 FILE_PATH_LITERAL("BookmarkSyncSettings.sqlite3"); 104static const char kDefaultNameForNewNodes[] = " "; 105 106// The list of names which are reserved for use by the server. 107static const char* kForbiddenServerNames[] = { "", ".", ".." }; 108 109////////////////////////////////////////////////////////////////////////// 110// Static helper functions. 111 112// Helper function to look up the int64 metahandle of an object given the ID 113// string. 114static int64 IdToMetahandle(syncable::BaseTransaction* trans, 115 const syncable::Id& id) { 116 syncable::Entry entry(trans, syncable::GET_BY_ID, id); 117 if (!entry.good()) 118 return kInvalidId; 119 return entry.Get(syncable::META_HANDLE); 120} 121 122// Checks whether |name| is a server-illegal name followed by zero or more space 123// characters. The three server-illegal names are the empty string, dot, and 124// dot-dot. Very long names (>255 bytes in UTF-8 Normalization Form C) are 125// also illegal, but are not considered here. 126static bool IsNameServerIllegalAfterTrimming(const std::string& name) { 127 size_t untrimmed_count = name.find_last_not_of(' ') + 1; 128 for (size_t i = 0; i < arraysize(kForbiddenServerNames); ++i) { 129 if (name.compare(0, untrimmed_count, kForbiddenServerNames[i]) == 0) 130 return true; 131 } 132 return false; 133} 134 135static bool EndsWithSpace(const std::string& string) { 136 return !string.empty() && *string.rbegin() == ' '; 137} 138 139// When taking a name from the syncapi, append a space if it matches the 140// pattern of a server-illegal name followed by zero or more spaces. 141static void SyncAPINameToServerName(const std::wstring& sync_api_name, 142 std::string* out) { 143 *out = WideToUTF8(sync_api_name); 144 if (IsNameServerIllegalAfterTrimming(*out)) 145 out->append(" "); 146} 147 148// In the reverse direction, if a server name matches the pattern of a 149// server-illegal name followed by one or more spaces, remove the trailing 150// space. 151static void ServerNameToSyncAPIName(const std::string& server_name, 152 std::wstring* out) { 153 int length_to_copy = server_name.length(); 154 if (IsNameServerIllegalAfterTrimming(server_name) && 155 EndsWithSpace(server_name)) 156 --length_to_copy; 157 if (!UTF8ToWide(server_name.c_str(), length_to_copy, out)) { 158 NOTREACHED() << "Could not convert server name from UTF8 to wide"; 159 } 160} 161 162UserShare::UserShare() {} 163 164UserShare::~UserShare() {} 165 166//////////////////////////////////// 167// BaseNode member definitions. 168 169BaseNode::BaseNode() {} 170 171BaseNode::~BaseNode() {} 172 173std::string BaseNode::GenerateSyncableHash( 174 syncable::ModelType model_type, const std::string& client_tag) { 175 // blank PB with just the extension in it has termination symbol, 176 // handy for delimiter 177 sync_pb::EntitySpecifics serialized_type; 178 syncable::AddDefaultExtensionValue(model_type, &serialized_type); 179 std::string hash_input; 180 serialized_type.AppendToString(&hash_input); 181 hash_input.append(client_tag); 182 183 std::string encode_output; 184 CHECK(base::Base64Encode(base::SHA1HashString(hash_input), &encode_output)); 185 return encode_output; 186} 187 188sync_pb::PasswordSpecificsData* DecryptPasswordSpecifics( 189 const sync_pb::EntitySpecifics& specifics, Cryptographer* crypto) { 190 if (!specifics.HasExtension(sync_pb::password)) 191 return NULL; 192 const sync_pb::EncryptedData& encrypted = 193 specifics.GetExtension(sync_pb::password).encrypted(); 194 scoped_ptr<sync_pb::PasswordSpecificsData> data( 195 new sync_pb::PasswordSpecificsData); 196 if (!crypto->Decrypt(encrypted, data.get())) 197 return NULL; 198 return data.release(); 199} 200 201bool BaseNode::DecryptIfNecessary(Entry* entry) { 202 if (GetIsFolder()) return true; // Ignore the top-level password folder. 203 const sync_pb::EntitySpecifics& specifics = 204 entry->Get(syncable::SPECIFICS); 205 if (specifics.HasExtension(sync_pb::password)) { 206 scoped_ptr<sync_pb::PasswordSpecificsData> data(DecryptPasswordSpecifics( 207 specifics, GetTransaction()->GetCryptographer())); 208 if (!data.get()) 209 return false; 210 password_data_.swap(data); 211 } 212 return true; 213} 214 215int64 BaseNode::GetParentId() const { 216 return IdToMetahandle(GetTransaction()->GetWrappedTrans(), 217 GetEntry()->Get(syncable::PARENT_ID)); 218} 219 220int64 BaseNode::GetId() const { 221 return GetEntry()->Get(syncable::META_HANDLE); 222} 223 224bool BaseNode::GetIsFolder() const { 225 return GetEntry()->Get(syncable::IS_DIR); 226} 227 228std::wstring BaseNode::GetTitle() const { 229 std::wstring result; 230 ServerNameToSyncAPIName(GetEntry()->Get(syncable::NON_UNIQUE_NAME), &result); 231 return result; 232} 233 234GURL BaseNode::GetURL() const { 235 return GURL(GetBookmarkSpecifics().url()); 236} 237 238int64 BaseNode::GetPredecessorId() const { 239 syncable::Id id_string = GetEntry()->Get(syncable::PREV_ID); 240 if (id_string.IsRoot()) 241 return kInvalidId; 242 return IdToMetahandle(GetTransaction()->GetWrappedTrans(), id_string); 243} 244 245int64 BaseNode::GetSuccessorId() const { 246 syncable::Id id_string = GetEntry()->Get(syncable::NEXT_ID); 247 if (id_string.IsRoot()) 248 return kInvalidId; 249 return IdToMetahandle(GetTransaction()->GetWrappedTrans(), id_string); 250} 251 252int64 BaseNode::GetFirstChildId() const { 253 syncable::Directory* dir = GetTransaction()->GetLookup(); 254 syncable::BaseTransaction* trans = GetTransaction()->GetWrappedTrans(); 255 syncable::Id id_string = 256 dir->GetFirstChildId(trans, GetEntry()->Get(syncable::ID)); 257 if (id_string.IsRoot()) 258 return kInvalidId; 259 return IdToMetahandle(GetTransaction()->GetWrappedTrans(), id_string); 260} 261 262void BaseNode::GetFaviconBytes(std::vector<unsigned char>* output) const { 263 if (!output) 264 return; 265 const std::string& favicon = GetBookmarkSpecifics().favicon(); 266 output->assign(reinterpret_cast<const unsigned char*>(favicon.data()), 267 reinterpret_cast<const unsigned char*>(favicon.data() + 268 favicon.length())); 269} 270 271int64 BaseNode::GetExternalId() const { 272 return GetEntry()->Get(syncable::LOCAL_EXTERNAL_ID); 273} 274 275const sync_pb::AppSpecifics& BaseNode::GetAppSpecifics() const { 276 DCHECK(GetModelType() == syncable::APPS); 277 return GetEntry()->Get(SPECIFICS).GetExtension(sync_pb::app); 278} 279 280const sync_pb::AutofillSpecifics& BaseNode::GetAutofillSpecifics() const { 281 DCHECK(GetModelType() == syncable::AUTOFILL); 282 return GetEntry()->Get(SPECIFICS).GetExtension(sync_pb::autofill); 283} 284 285const sync_pb::BookmarkSpecifics& BaseNode::GetBookmarkSpecifics() const { 286 DCHECK(GetModelType() == syncable::BOOKMARKS); 287 return GetEntry()->Get(SPECIFICS).GetExtension(sync_pb::bookmark); 288} 289 290const sync_pb::NigoriSpecifics& BaseNode::GetNigoriSpecifics() const { 291 DCHECK(GetModelType() == syncable::NIGORI); 292 return GetEntry()->Get(SPECIFICS).GetExtension(sync_pb::nigori); 293} 294 295const sync_pb::PasswordSpecificsData& BaseNode::GetPasswordSpecifics() const { 296 DCHECK(GetModelType() == syncable::PASSWORDS); 297 DCHECK(password_data_.get()); 298 return *password_data_; 299} 300 301const sync_pb::PreferenceSpecifics& BaseNode::GetPreferenceSpecifics() const { 302 DCHECK(GetModelType() == syncable::PREFERENCES); 303 return GetEntry()->Get(SPECIFICS).GetExtension(sync_pb::preference); 304} 305 306const sync_pb::ThemeSpecifics& BaseNode::GetThemeSpecifics() const { 307 DCHECK(GetModelType() == syncable::THEMES); 308 return GetEntry()->Get(SPECIFICS).GetExtension(sync_pb::theme); 309} 310 311const sync_pb::TypedUrlSpecifics& BaseNode::GetTypedUrlSpecifics() const { 312 DCHECK(GetModelType() == syncable::TYPED_URLS); 313 return GetEntry()->Get(SPECIFICS).GetExtension(sync_pb::typed_url); 314} 315 316const sync_pb::ExtensionSpecifics& BaseNode::GetExtensionSpecifics() const { 317 DCHECK(GetModelType() == syncable::EXTENSIONS); 318 return GetEntry()->Get(SPECIFICS).GetExtension(sync_pb::extension); 319} 320 321const sync_pb::SessionSpecifics& BaseNode::GetSessionSpecifics() const { 322 DCHECK(GetModelType() == syncable::SESSIONS); 323 return GetEntry()->Get(SPECIFICS).GetExtension(sync_pb::session); 324} 325 326syncable::ModelType BaseNode::GetModelType() const { 327 return GetEntry()->GetModelType(); 328} 329 330//////////////////////////////////// 331// WriteNode member definitions 332void WriteNode::SetIsFolder(bool folder) { 333 if (entry_->Get(syncable::IS_DIR) == folder) 334 return; // Skip redundant changes. 335 336 entry_->Put(syncable::IS_DIR, folder); 337 MarkForSyncing(); 338} 339 340void WriteNode::SetTitle(const std::wstring& title) { 341 std::string server_legal_name; 342 SyncAPINameToServerName(title, &server_legal_name); 343 344 string old_name = entry_->Get(syncable::NON_UNIQUE_NAME); 345 346 if (server_legal_name == old_name) 347 return; // Skip redundant changes. 348 349 entry_->Put(syncable::NON_UNIQUE_NAME, server_legal_name); 350 MarkForSyncing(); 351} 352 353void WriteNode::SetURL(const GURL& url) { 354 sync_pb::BookmarkSpecifics new_value = GetBookmarkSpecifics(); 355 new_value.set_url(url.spec()); 356 SetBookmarkSpecifics(new_value); 357} 358 359void WriteNode::SetAppSpecifics( 360 const sync_pb::AppSpecifics& new_value) { 361 DCHECK(GetModelType() == syncable::APPS); 362 PutAppSpecificsAndMarkForSyncing(new_value); 363} 364 365void WriteNode::SetAutofillSpecifics( 366 const sync_pb::AutofillSpecifics& new_value) { 367 DCHECK(GetModelType() == syncable::AUTOFILL); 368 PutAutofillSpecificsAndMarkForSyncing(new_value); 369} 370 371void WriteNode::PutAutofillSpecificsAndMarkForSyncing( 372 const sync_pb::AutofillSpecifics& new_value) { 373 sync_pb::EntitySpecifics entity_specifics; 374 entity_specifics.MutableExtension(sync_pb::autofill)->CopyFrom(new_value); 375 PutSpecificsAndMarkForSyncing(entity_specifics); 376} 377 378void WriteNode::SetBookmarkSpecifics( 379 const sync_pb::BookmarkSpecifics& new_value) { 380 DCHECK(GetModelType() == syncable::BOOKMARKS); 381 PutBookmarkSpecificsAndMarkForSyncing(new_value); 382} 383 384void WriteNode::PutBookmarkSpecificsAndMarkForSyncing( 385 const sync_pb::BookmarkSpecifics& new_value) { 386 sync_pb::EntitySpecifics entity_specifics; 387 entity_specifics.MutableExtension(sync_pb::bookmark)->CopyFrom(new_value); 388 PutSpecificsAndMarkForSyncing(entity_specifics); 389} 390 391void WriteNode::SetNigoriSpecifics( 392 const sync_pb::NigoriSpecifics& new_value) { 393 DCHECK(GetModelType() == syncable::NIGORI); 394 PutNigoriSpecificsAndMarkForSyncing(new_value); 395} 396 397void WriteNode::PutNigoriSpecificsAndMarkForSyncing( 398 const sync_pb::NigoriSpecifics& new_value) { 399 sync_pb::EntitySpecifics entity_specifics; 400 entity_specifics.MutableExtension(sync_pb::nigori)->CopyFrom(new_value); 401 PutSpecificsAndMarkForSyncing(entity_specifics); 402} 403 404void WriteNode::SetPasswordSpecifics( 405 const sync_pb::PasswordSpecificsData& data) { 406 DCHECK(GetModelType() == syncable::PASSWORDS); 407 408 sync_pb::PasswordSpecifics new_value; 409 if (!GetTransaction()->GetCryptographer()->Encrypt( 410 data, 411 new_value.mutable_encrypted())) { 412 NOTREACHED(); 413 } 414 415 PutPasswordSpecificsAndMarkForSyncing(new_value); 416} 417 418void WriteNode::SetPreferenceSpecifics( 419 const sync_pb::PreferenceSpecifics& new_value) { 420 DCHECK(GetModelType() == syncable::PREFERENCES); 421 PutPreferenceSpecificsAndMarkForSyncing(new_value); 422} 423 424void WriteNode::SetThemeSpecifics( 425 const sync_pb::ThemeSpecifics& new_value) { 426 DCHECK(GetModelType() == syncable::THEMES); 427 PutThemeSpecificsAndMarkForSyncing(new_value); 428} 429 430void WriteNode::SetSessionSpecifics( 431 const sync_pb::SessionSpecifics& new_value) { 432 DCHECK(GetModelType() == syncable::SESSIONS); 433 PutSessionSpecificsAndMarkForSyncing(new_value); 434} 435 436 437void WriteNode::PutPasswordSpecificsAndMarkForSyncing( 438 const sync_pb::PasswordSpecifics& new_value) { 439 sync_pb::EntitySpecifics entity_specifics; 440 entity_specifics.MutableExtension(sync_pb::password)->CopyFrom(new_value); 441 PutSpecificsAndMarkForSyncing(entity_specifics); 442} 443 444void WriteNode::PutPreferenceSpecificsAndMarkForSyncing( 445 const sync_pb::PreferenceSpecifics& new_value) { 446 sync_pb::EntitySpecifics entity_specifics; 447 entity_specifics.MutableExtension(sync_pb::preference)->CopyFrom(new_value); 448 PutSpecificsAndMarkForSyncing(entity_specifics); 449} 450 451void WriteNode::SetTypedUrlSpecifics( 452 const sync_pb::TypedUrlSpecifics& new_value) { 453 DCHECK(GetModelType() == syncable::TYPED_URLS); 454 PutTypedUrlSpecificsAndMarkForSyncing(new_value); 455} 456 457void WriteNode::SetExtensionSpecifics( 458 const sync_pb::ExtensionSpecifics& new_value) { 459 DCHECK(GetModelType() == syncable::EXTENSIONS); 460 PutExtensionSpecificsAndMarkForSyncing(new_value); 461} 462 463void WriteNode::PutAppSpecificsAndMarkForSyncing( 464 const sync_pb::AppSpecifics& new_value) { 465 sync_pb::EntitySpecifics entity_specifics; 466 entity_specifics.MutableExtension(sync_pb::app)->CopyFrom(new_value); 467 PutSpecificsAndMarkForSyncing(entity_specifics); 468} 469 470void WriteNode::PutThemeSpecificsAndMarkForSyncing( 471 const sync_pb::ThemeSpecifics& new_value) { 472 sync_pb::EntitySpecifics entity_specifics; 473 entity_specifics.MutableExtension(sync_pb::theme)->CopyFrom(new_value); 474 PutSpecificsAndMarkForSyncing(entity_specifics); 475} 476 477void WriteNode::PutTypedUrlSpecificsAndMarkForSyncing( 478 const sync_pb::TypedUrlSpecifics& new_value) { 479 sync_pb::EntitySpecifics entity_specifics; 480 entity_specifics.MutableExtension(sync_pb::typed_url)->CopyFrom(new_value); 481 PutSpecificsAndMarkForSyncing(entity_specifics); 482} 483 484void WriteNode::PutExtensionSpecificsAndMarkForSyncing( 485 const sync_pb::ExtensionSpecifics& new_value) { 486 sync_pb::EntitySpecifics entity_specifics; 487 entity_specifics.MutableExtension(sync_pb::extension)->CopyFrom(new_value); 488 PutSpecificsAndMarkForSyncing(entity_specifics); 489} 490 491 492void WriteNode::PutSessionSpecificsAndMarkForSyncing( 493 const sync_pb::SessionSpecifics& new_value) { 494 sync_pb::EntitySpecifics entity_specifics; 495 entity_specifics.MutableExtension(sync_pb::session)->CopyFrom(new_value); 496 PutSpecificsAndMarkForSyncing(entity_specifics); 497} 498 499 500void WriteNode::PutSpecificsAndMarkForSyncing( 501 const sync_pb::EntitySpecifics& specifics) { 502 // Skip redundant changes. 503 if (specifics.SerializeAsString() == 504 entry_->Get(SPECIFICS).SerializeAsString()) { 505 return; 506 } 507 entry_->Put(SPECIFICS, specifics); 508 MarkForSyncing(); 509} 510 511void WriteNode::SetExternalId(int64 id) { 512 if (GetExternalId() != id) 513 entry_->Put(syncable::LOCAL_EXTERNAL_ID, id); 514} 515 516WriteNode::WriteNode(WriteTransaction* transaction) 517 : entry_(NULL), transaction_(transaction) { 518 DCHECK(transaction); 519} 520 521WriteNode::~WriteNode() { 522 delete entry_; 523} 524 525// Find an existing node matching the ID |id|, and bind this WriteNode to it. 526// Return true on success. 527bool WriteNode::InitByIdLookup(int64 id) { 528 DCHECK(!entry_) << "Init called twice"; 529 DCHECK_NE(id, kInvalidId); 530 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(), 531 syncable::GET_BY_HANDLE, id); 532 return (entry_->good() && !entry_->Get(syncable::IS_DEL) && 533 DecryptIfNecessary(entry_)); 534} 535 536// Find a node by client tag, and bind this WriteNode to it. 537// Return true if the write node was found, and was not deleted. 538// Undeleting a deleted node is possible by ClientTag. 539bool WriteNode::InitByClientTagLookup(syncable::ModelType model_type, 540 const std::string& tag) { 541 DCHECK(!entry_) << "Init called twice"; 542 if (tag.empty()) 543 return false; 544 545 const std::string hash = GenerateSyncableHash(model_type, tag); 546 547 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(), 548 syncable::GET_BY_CLIENT_TAG, hash); 549 return (entry_->good() && !entry_->Get(syncable::IS_DEL) && 550 DecryptIfNecessary(entry_)); 551} 552 553bool WriteNode::InitByTagLookup(const std::string& tag) { 554 DCHECK(!entry_) << "Init called twice"; 555 if (tag.empty()) 556 return false; 557 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(), 558 syncable::GET_BY_SERVER_TAG, tag); 559 if (!entry_->good()) 560 return false; 561 if (entry_->Get(syncable::IS_DEL)) 562 return false; 563 syncable::ModelType model_type = GetModelType(); 564 DCHECK(model_type == syncable::NIGORI); 565 return true; 566} 567 568void WriteNode::PutModelType(syncable::ModelType model_type) { 569 // Set an empty specifics of the appropriate datatype. The presence 570 // of the specific extension will identify the model type. 571 DCHECK(GetModelType() == model_type || 572 GetModelType() == syncable::UNSPECIFIED); // Immutable once set. 573 574 sync_pb::EntitySpecifics specifics; 575 syncable::AddDefaultExtensionValue(model_type, &specifics); 576 PutSpecificsAndMarkForSyncing(specifics); 577 DCHECK(GetModelType() == model_type); 578} 579 580// Create a new node with default properties, and bind this WriteNode to it. 581// Return true on success. 582bool WriteNode::InitByCreation(syncable::ModelType model_type, 583 const BaseNode& parent, 584 const BaseNode* predecessor) { 585 DCHECK(!entry_) << "Init called twice"; 586 // |predecessor| must be a child of |parent| or NULL. 587 if (predecessor && predecessor->GetParentId() != parent.GetId()) { 588 DCHECK(false); 589 return false; 590 } 591 592 syncable::Id parent_id = parent.GetEntry()->Get(syncable::ID); 593 594 // Start out with a dummy name. We expect 595 // the caller to set a meaningful name after creation. 596 string dummy(kDefaultNameForNewNodes); 597 598 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(), 599 syncable::CREATE, parent_id, dummy); 600 601 if (!entry_->good()) 602 return false; 603 604 // Entries are untitled folders by default. 605 entry_->Put(syncable::IS_DIR, true); 606 607 PutModelType(model_type); 608 609 // Now set the predecessor, which sets IS_UNSYNCED as necessary. 610 PutPredecessor(predecessor); 611 612 return true; 613} 614 615// Create a new node with default properties and a client defined unique tag, 616// and bind this WriteNode to it. 617// Return true on success. If the tag exists in the database, then 618// we will attempt to undelete the node. 619// TODO(chron): Code datatype into hash tag. 620// TODO(chron): Is model type ever lost? 621bool WriteNode::InitUniqueByCreation(syncable::ModelType model_type, 622 const BaseNode& parent, 623 const std::string& tag) { 624 DCHECK(!entry_) << "Init called twice"; 625 626 const std::string hash = GenerateSyncableHash(model_type, tag); 627 628 syncable::Id parent_id = parent.GetEntry()->Get(syncable::ID); 629 630 // Start out with a dummy name. We expect 631 // the caller to set a meaningful name after creation. 632 string dummy(kDefaultNameForNewNodes); 633 634 // Check if we have this locally and need to undelete it. 635 scoped_ptr<syncable::MutableEntry> existing_entry( 636 new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(), 637 syncable::GET_BY_CLIENT_TAG, hash)); 638 639 if (existing_entry->good()) { 640 if (existing_entry->Get(syncable::IS_DEL)) { 641 // Rules for undelete: 642 // BASE_VERSION: Must keep the same. 643 // ID: Essential to keep the same. 644 // META_HANDLE: Must be the same, so we can't "split" the entry. 645 // IS_DEL: Must be set to false, will cause reindexing. 646 // This one is weird because IS_DEL is true for "update only" 647 // items. It should be OK to undelete an update only. 648 // MTIME/CTIME: Seems reasonable to just leave them alone. 649 // IS_UNSYNCED: Must set this to true or face database insurrection. 650 // We do this below this block. 651 // IS_UNAPPLIED_UPDATE: Either keep it the same or also set BASE_VERSION 652 // to SERVER_VERSION. We keep it the same here. 653 // IS_DIR: We'll leave it the same. 654 // SPECIFICS: Reset it. 655 656 existing_entry->Put(syncable::IS_DEL, false); 657 658 // Client tags are immutable and must be paired with the ID. 659 // If a server update comes down with an ID and client tag combo, 660 // and it already exists, always overwrite it and store only one copy. 661 // We have to undelete entries because we can't disassociate IDs from 662 // tags and updates. 663 664 existing_entry->Put(syncable::NON_UNIQUE_NAME, dummy); 665 existing_entry->Put(syncable::PARENT_ID, parent_id); 666 entry_ = existing_entry.release(); 667 } else { 668 return false; 669 } 670 } else { 671 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(), 672 syncable::CREATE, parent_id, dummy); 673 if (!entry_->good()) { 674 return false; 675 } 676 677 // Only set IS_DIR for new entries. Don't bitflip undeleted ones. 678 entry_->Put(syncable::UNIQUE_CLIENT_TAG, hash); 679 } 680 681 // We don't support directory and tag combinations. 682 entry_->Put(syncable::IS_DIR, false); 683 684 // Will clear specifics data. 685 PutModelType(model_type); 686 687 // Now set the predecessor, which sets IS_UNSYNCED as necessary. 688 PutPredecessor(NULL); 689 690 return true; 691} 692 693bool WriteNode::SetPosition(const BaseNode& new_parent, 694 const BaseNode* predecessor) { 695 // |predecessor| must be a child of |new_parent| or NULL. 696 if (predecessor && predecessor->GetParentId() != new_parent.GetId()) { 697 DCHECK(false); 698 return false; 699 } 700 701 syncable::Id new_parent_id = new_parent.GetEntry()->Get(syncable::ID); 702 703 // Filter out redundant changes if both the parent and the predecessor match. 704 if (new_parent_id == entry_->Get(syncable::PARENT_ID)) { 705 const syncable::Id& old = entry_->Get(syncable::PREV_ID); 706 if ((!predecessor && old.IsRoot()) || 707 (predecessor && (old == predecessor->GetEntry()->Get(syncable::ID)))) { 708 return true; 709 } 710 } 711 712 // Atomically change the parent. This will fail if it would 713 // introduce a cycle in the hierarchy. 714 if (!entry_->Put(syncable::PARENT_ID, new_parent_id)) 715 return false; 716 717 // Now set the predecessor, which sets IS_UNSYNCED as necessary. 718 PutPredecessor(predecessor); 719 720 return true; 721} 722 723const syncable::Entry* WriteNode::GetEntry() const { 724 return entry_; 725} 726 727const BaseTransaction* WriteNode::GetTransaction() const { 728 return transaction_; 729} 730 731void WriteNode::Remove() { 732 entry_->Put(syncable::IS_DEL, true); 733 MarkForSyncing(); 734} 735 736void WriteNode::PutPredecessor(const BaseNode* predecessor) { 737 syncable::Id predecessor_id = predecessor ? 738 predecessor->GetEntry()->Get(syncable::ID) : syncable::Id(); 739 entry_->PutPredecessor(predecessor_id); 740 // Mark this entry as unsynced, to wake up the syncer. 741 MarkForSyncing(); 742} 743 744void WriteNode::SetFaviconBytes(const vector<unsigned char>& bytes) { 745 sync_pb::BookmarkSpecifics new_value = GetBookmarkSpecifics(); 746 new_value.set_favicon(bytes.empty() ? NULL : &bytes[0], bytes.size()); 747 SetBookmarkSpecifics(new_value); 748} 749 750void WriteNode::MarkForSyncing() { 751 syncable::MarkForSyncing(entry_); 752} 753 754////////////////////////////////////////////////////////////////////////// 755// ReadNode member definitions 756ReadNode::ReadNode(const BaseTransaction* transaction) 757 : entry_(NULL), transaction_(transaction) { 758 DCHECK(transaction); 759} 760 761ReadNode::~ReadNode() { 762 delete entry_; 763} 764 765void ReadNode::InitByRootLookup() { 766 DCHECK(!entry_) << "Init called twice"; 767 syncable::BaseTransaction* trans = transaction_->GetWrappedTrans(); 768 entry_ = new syncable::Entry(trans, syncable::GET_BY_ID, trans->root_id()); 769 if (!entry_->good()) 770 DCHECK(false) << "Could not lookup root node for reading."; 771} 772 773bool ReadNode::InitByIdLookup(int64 id) { 774 DCHECK(!entry_) << "Init called twice"; 775 DCHECK_NE(id, kInvalidId); 776 syncable::BaseTransaction* trans = transaction_->GetWrappedTrans(); 777 entry_ = new syncable::Entry(trans, syncable::GET_BY_HANDLE, id); 778 if (!entry_->good()) 779 return false; 780 if (entry_->Get(syncable::IS_DEL)) 781 return false; 782 syncable::ModelType model_type = GetModelType(); 783 LOG_IF(WARNING, model_type == syncable::UNSPECIFIED || 784 model_type == syncable::TOP_LEVEL_FOLDER) 785 << "SyncAPI InitByIdLookup referencing unusual object."; 786 return DecryptIfNecessary(entry_); 787} 788 789bool ReadNode::InitByClientTagLookup(syncable::ModelType model_type, 790 const std::string& tag) { 791 DCHECK(!entry_) << "Init called twice"; 792 if (tag.empty()) 793 return false; 794 795 const std::string hash = GenerateSyncableHash(model_type, tag); 796 797 entry_ = new syncable::Entry(transaction_->GetWrappedTrans(), 798 syncable::GET_BY_CLIENT_TAG, hash); 799 return (entry_->good() && !entry_->Get(syncable::IS_DEL) && 800 DecryptIfNecessary(entry_)); 801} 802 803const syncable::Entry* ReadNode::GetEntry() const { 804 return entry_; 805} 806 807const BaseTransaction* ReadNode::GetTransaction() const { 808 return transaction_; 809} 810 811bool ReadNode::InitByTagLookup(const std::string& tag) { 812 DCHECK(!entry_) << "Init called twice"; 813 if (tag.empty()) 814 return false; 815 syncable::BaseTransaction* trans = transaction_->GetWrappedTrans(); 816 entry_ = new syncable::Entry(trans, syncable::GET_BY_SERVER_TAG, tag); 817 if (!entry_->good()) 818 return false; 819 if (entry_->Get(syncable::IS_DEL)) 820 return false; 821 syncable::ModelType model_type = GetModelType(); 822 LOG_IF(WARNING, model_type == syncable::UNSPECIFIED || 823 model_type == syncable::TOP_LEVEL_FOLDER) 824 << "SyncAPI InitByTagLookup referencing unusually typed object."; 825 return DecryptIfNecessary(entry_); 826} 827 828////////////////////////////////////////////////////////////////////////// 829// ReadTransaction member definitions 830ReadTransaction::ReadTransaction(UserShare* share) 831 : BaseTransaction(share), 832 transaction_(NULL), 833 close_transaction_(true) { 834 transaction_ = new syncable::ReadTransaction(GetLookup(), __FILE__, __LINE__); 835} 836 837ReadTransaction::ReadTransaction(UserShare* share, 838 syncable::BaseTransaction* trans) 839 : BaseTransaction(share), 840 transaction_(trans), 841 close_transaction_(false) {} 842 843ReadTransaction::~ReadTransaction() { 844 if (close_transaction_) { 845 delete transaction_; 846 } 847} 848 849syncable::BaseTransaction* ReadTransaction::GetWrappedTrans() const { 850 return transaction_; 851} 852 853////////////////////////////////////////////////////////////////////////// 854// WriteTransaction member definitions 855WriteTransaction::WriteTransaction(UserShare* share) 856 : BaseTransaction(share), 857 transaction_(NULL) { 858 transaction_ = new syncable::WriteTransaction(GetLookup(), syncable::SYNCAPI, 859 __FILE__, __LINE__); 860} 861 862WriteTransaction::~WriteTransaction() { 863 delete transaction_; 864} 865 866syncable::BaseTransaction* WriteTransaction::GetWrappedTrans() const { 867 return transaction_; 868} 869 870// A GaiaAuthenticator that uses HttpPostProviders instead of CURL. 871class BridgedGaiaAuthenticator : public gaia::GaiaAuthenticator { 872 public: 873 BridgedGaiaAuthenticator(const string& user_agent, const string& service_id, 874 const string& gaia_url, 875 HttpPostProviderFactory* factory) 876 : GaiaAuthenticator(user_agent, service_id, gaia_url), 877 gaia_source_(user_agent), post_factory_(factory) { 878 } 879 880 virtual ~BridgedGaiaAuthenticator() { 881 } 882 883 virtual bool Post(const GURL& url, const string& post_body, 884 unsigned long* response_code, string* response_body) { 885 string connection_url = "https://"; 886 connection_url += url.host(); 887 connection_url += url.path(); 888 HttpPostProviderInterface* http = post_factory_->Create(); 889 http->SetUserAgent(gaia_source_.c_str()); 890 // SSL is on 443 for Gaia Posts always. 891 http->SetURL(connection_url.c_str(), kSSLPort); 892 http->SetPostPayload("application/x-www-form-urlencoded", 893 post_body.length(), post_body.c_str()); 894 895 int os_error_code = 0; 896 int int_response_code = 0; 897 if (!http->MakeSynchronousPost(&os_error_code, &int_response_code)) { 898 VLOG(1) << "Http POST failed, error returns: " << os_error_code; 899 return false; 900 } 901 *response_code = static_cast<int>(int_response_code); 902 response_body->assign(http->GetResponseContent(), 903 http->GetResponseContentLength()); 904 post_factory_->Destroy(http); 905 return true; 906 } 907 908 virtual int GetBackoffDelaySeconds(int current_backoff_delay) { 909 return SyncerThread::GetRecommendedDelaySeconds(current_backoff_delay); 910 } 911 private: 912 const std::string gaia_source_; 913 scoped_ptr<HttpPostProviderFactory> post_factory_; 914 DISALLOW_COPY_AND_ASSIGN(BridgedGaiaAuthenticator); 915}; 916 917SyncManager::ChangeRecord::ChangeRecord() 918 : id(kInvalidId), action(ACTION_ADD) {} 919 920SyncManager::ChangeRecord::~ChangeRecord() {} 921 922SyncManager::ExtraPasswordChangeRecordData::ExtraPasswordChangeRecordData( 923 const sync_pb::PasswordSpecificsData& data) 924 : unencrypted_(data) { 925} 926 927SyncManager::ExtraPasswordChangeRecordData::~ExtraPasswordChangeRecordData() {} 928 929////////////////////////////////////////////////////////////////////////// 930// SyncManager's implementation: SyncManager::SyncInternal 931class SyncManager::SyncInternal 932 : public net::NetworkChangeNotifier::Observer, 933 public TalkMediator::Delegate, 934 public sync_notifier::StateWriter, 935 public browser_sync::ChannelEventHandler<syncable::DirectoryChangeEvent>, 936 public SyncEngineEventListener, 937 public ServerConnectionEventListener { 938 static const int kDefaultNudgeDelayMilliseconds; 939 static const int kPreferencesNudgeDelayMilliseconds; 940 public: 941 explicit SyncInternal(SyncManager* sync_manager) 942 : core_message_loop_(NULL), 943 observer_(NULL), 944 sync_manager_(sync_manager), 945 registrar_(NULL), 946 notification_pending_(false), 947 initialized_(false), 948 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { 949 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 950 } 951 952 virtual ~SyncInternal() { 953 DCHECK(!core_message_loop_); 954 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 955 } 956 957 bool Init(const FilePath& database_location, 958 const std::string& sync_server_and_path, 959 int port, 960 bool use_ssl, 961 HttpPostProviderFactory* post_factory, 962 ModelSafeWorkerRegistrar* model_safe_worker_registrar, 963 const char* user_agent, 964 const SyncCredentials& credentials, 965 const notifier::NotifierOptions& notifier_options, 966 const std::string& restored_key_for_bootstrapping, 967 bool setup_for_test_mode); 968 969 // Sign into sync with given credentials. 970 // We do not verify the tokens given. After this call, the tokens are set 971 // and the sync DB is open. True if successful, false if something 972 // went wrong. 973 bool SignIn(const SyncCredentials& credentials); 974 975 // Update tokens that we're using in Sync. Email must stay the same. 976 void UpdateCredentials(const SyncCredentials& credentials); 977 978 // Tell the sync engine to start the syncing process. 979 void StartSyncing(); 980 981 // Whether or not the Nigori node is encrypted using an explicit passphrase. 982 bool IsUsingExplicitPassphrase(); 983 984 // Try to set the current passphrase to |passphrase|, and record whether 985 // it is an explicit passphrase or implicitly using gaia in the Nigori 986 // node. 987 void SetPassphrase(const std::string& passphrase, bool is_explicit); 988 989 // Call periodically from a database-safe thread to persist recent changes 990 // to the syncapi model. 991 void SaveChanges(); 992 993 // This listener is called upon completion of a syncable transaction, and 994 // builds the list of sync-engine initiated changes that will be forwarded to 995 // the SyncManager's Observers. 996 virtual void HandleChannelEvent(const syncable::DirectoryChangeEvent& event); 997 void HandleTransactionCompleteChangeEvent( 998 const syncable::DirectoryChangeEvent& event); 999 void HandleTransactionEndingChangeEvent( 1000 const syncable::DirectoryChangeEvent& event); 1001 void HandleCalculateChangesChangeEventFromSyncApi( 1002 const syncable::DirectoryChangeEvent& event); 1003 void HandleCalculateChangesChangeEventFromSyncer( 1004 const syncable::DirectoryChangeEvent& event); 1005 1006 // Listens for notifications from the ServerConnectionManager 1007 virtual void OnServerConnectionEvent(const ServerConnectionEvent& event); 1008 1009 // Open the directory named with username_for_share 1010 bool OpenDirectory(); 1011 1012 // Login to the talk mediator with the given credentials. 1013 void TalkMediatorLogin( 1014 const std::string& email, const std::string& token); 1015 1016 // TalkMediator::Delegate implementation. 1017 virtual void OnNotificationStateChange( 1018 bool notifications_enabled); 1019 1020 virtual void OnIncomingNotification( 1021 const IncomingNotificationData& notification_data); 1022 1023 virtual void OnOutgoingNotification(); 1024 1025 // sync_notifier::StateWriter implementation. 1026 virtual void WriteState(const std::string& state); 1027 1028 // Accessors for the private members. 1029 DirectoryManager* dir_manager() { return share_.dir_manager.get(); } 1030 SyncAPIServerConnectionManager* connection_manager() { 1031 return connection_manager_.get(); 1032 } 1033 SyncerThread* syncer_thread() { return syncer_thread_.get(); } 1034 TalkMediator* talk_mediator() { return talk_mediator_.get(); } 1035 void set_observer(SyncManager::Observer* observer) { observer_ = observer; } 1036 UserShare* GetUserShare() { return &share_; } 1037 1038 // Return the currently active (validated) username for use with syncable 1039 // types. 1040 const std::string& username_for_share() const { 1041 return share_.name; 1042 } 1043 1044 // Note about SyncManager::Status implementation: Status is a trimmed 1045 // down AllStatus::Status, augmented with authentication failure information 1046 // gathered from the internal AuthWatcher. The sync UI itself hooks up to 1047 // various sources like the AuthWatcher individually, but with syncapi we try 1048 // to keep everything status-related in one place. This means we have to 1049 // privately manage state about authentication failures, and whenever the 1050 // status or status summary is requested we aggregate this state with 1051 // AllStatus::Status information. 1052 Status ComputeAggregatedStatus(); 1053 Status::Summary ComputeAggregatedStatusSummary(); 1054 1055 // See SyncManager::Shutdown for information. 1056 void Shutdown(); 1057 1058 // Whether we're initialized to the point of being able to accept changes 1059 // (and hence allow transaction creation). See initialized_ for details. 1060 bool initialized() const { 1061 AutoLock lock(initialized_mutex_); 1062 return initialized_; 1063 } 1064 1065 void SetExtraChangeRecordData(int64 id, 1066 syncable::ModelType type, 1067 ChangeReorderBuffer* buffer, 1068 Cryptographer* cryptographer, 1069 const syncable::EntryKernel& original, 1070 bool existed_before, 1071 bool exists_now); 1072 1073 // Called only by our NetworkChangeNotifier. 1074 virtual void OnIPAddressChanged(); 1075 1076 bool InitialSyncEndedForAllEnabledTypes() { 1077 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share()); 1078 if (!lookup.good()) { 1079 DCHECK(false) << "ScopedDirLookup failed when checking initial sync"; 1080 return false; 1081 } 1082 1083 ModelSafeRoutingInfo enabled_types; 1084 registrar_->GetModelSafeRoutingInfo(&enabled_types); 1085 for (ModelSafeRoutingInfo::const_iterator i = enabled_types.begin(); 1086 i != enabled_types.end(); ++i) { 1087 if (!lookup->initial_sync_ended_for_type(i->first)) 1088 return false; 1089 } 1090 return true; 1091 } 1092 1093 // SyncEngineEventListener implementation. 1094 virtual void OnSyncEngineEvent(const SyncEngineEvent& event); 1095 private: 1096 // Helper to handle the details of initializing the TalkMediator. 1097 // Must be called only after OpenDirectory() is called. 1098 void InitializeTalkMediator(); 1099 1100 // Helper to call OnAuthError when no authentication credentials are 1101 // available. 1102 void RaiseAuthNeededEvent(); 1103 1104 // Helper to set initialized_ to true and raise an event to clients to notify 1105 // that initialization is complete and it is safe to send us changes. If 1106 // already initialized, this is a no-op. 1107 void MarkAndNotifyInitializationComplete(); 1108 1109 // If there's a pending notification to be sent, either from the 1110 // new_pending_notification flag or a previous unsuccessfully sent 1111 // notification, tries to send a notification. 1112 void SendPendingXMPPNotification(bool new_pending_notification); 1113 1114 // Determine if the parents or predecessors differ between the old and new 1115 // versions of an entry stored in |a| and |b|. Note that a node's index may 1116 // change without its NEXT_ID changing if the node at NEXT_ID also moved (but 1117 // the relative order is unchanged). To handle such cases, we rely on the 1118 // caller to treat a position update on any sibling as updating the positions 1119 // of all siblings. 1120 static bool VisiblePositionsDiffer(const syncable::EntryKernel& a, 1121 const syncable::Entry& b) { 1122 // If the datatype isn't one where the browser model cares about position, 1123 // don't bother notifying that data model of position-only changes. 1124 if (!b.ShouldMaintainPosition()) 1125 return false; 1126 if (a.ref(syncable::NEXT_ID) != b.Get(syncable::NEXT_ID)) 1127 return true; 1128 if (a.ref(syncable::PARENT_ID) != b.Get(syncable::PARENT_ID)) 1129 return true; 1130 return false; 1131 } 1132 1133 // Determine if any of the fields made visible to clients of the Sync API 1134 // differ between the versions of an entry stored in |a| and |b|. A return 1135 // value of false means that it should be OK to ignore this change. 1136 static bool VisiblePropertiesDiffer(const syncable::EntryKernel& a, 1137 const syncable::Entry& b) { 1138 syncable::ModelType model_type = b.GetModelType(); 1139 // Suppress updates to items that aren't tracked by any browser model. 1140 if (model_type == syncable::UNSPECIFIED || 1141 model_type == syncable::TOP_LEVEL_FOLDER) { 1142 return false; 1143 } 1144 if (a.ref(syncable::NON_UNIQUE_NAME) != b.Get(syncable::NON_UNIQUE_NAME)) 1145 return true; 1146 if (a.ref(syncable::IS_DIR) != b.Get(syncable::IS_DIR)) 1147 return true; 1148 if (a.ref(SPECIFICS).SerializeAsString() != 1149 b.Get(SPECIFICS).SerializeAsString()) { 1150 return true; 1151 } 1152 if (VisiblePositionsDiffer(a, b)) 1153 return true; 1154 return false; 1155 } 1156 1157 bool ChangeBuffersAreEmpty() { 1158 for (int i = 0; i < syncable::MODEL_TYPE_COUNT; ++i) { 1159 if (!change_buffers_[i].IsEmpty()) 1160 return false; 1161 } 1162 return true; 1163 } 1164 1165 void CheckServerReachable() { 1166 if (connection_manager()) { 1167 connection_manager()->CheckServerReachable(); 1168 } else { 1169 NOTREACHED() << "Should be valid connection manager!"; 1170 } 1171 } 1172 1173 void ReEncryptEverything(WriteTransaction* trans); 1174 1175 // Initializes (bootstraps) the Cryptographer if NIGORI has finished 1176 // initial sync so that it can immediately start encrypting / decrypting. 1177 // If the restored key is incompatible with the current version of the NIGORI 1178 // node (which could happen if a restart occurred just after an update to 1179 // NIGORI was downloaded and the user must enter a new passphrase to decrypt) 1180 // then we will raise OnPassphraseRequired and set pending keys for 1181 // decryption. Otherwise, the cryptographer is made ready (is_ready()). 1182 void BootstrapEncryption(const std::string& restored_key_for_bootstrapping); 1183 1184 // Helper for migration to new nigori proto to set 1185 // 'using_explicit_passphrase' in the NigoriSpecifics. 1186 // TODO(tim): Bug 62103. Remove this after it has been pushed out to dev 1187 // channel users. 1188 void SetUsingExplicitPassphrasePrefForMigration(); 1189 1190 // Checks for server reachabilty and requests a nudge. 1191 void OnIPAddressChangedImpl(); 1192 1193 // We couple the DirectoryManager and username together in a UserShare member 1194 // so we can return a handle to share_ to clients of the API for use when 1195 // constructing any transaction type. 1196 UserShare share_; 1197 1198 MessageLoop* core_message_loop_; 1199 1200 // Observer registered via SetObserver/RemoveObserver. 1201 // WARNING: This can be NULL! 1202 SyncManager::Observer* observer_; 1203 1204 // The ServerConnectionManager used to abstract communication between the 1205 // client (the Syncer) and the sync server. 1206 scoped_ptr<SyncAPIServerConnectionManager> connection_manager_; 1207 1208 // The thread that runs the Syncer. Needs to be explicitly Start()ed. 1209 scoped_refptr<SyncerThread> syncer_thread_; 1210 1211 // Notification (xmpp) handler. 1212 scoped_ptr<TalkMediator> talk_mediator_; 1213 1214 // A multi-purpose status watch object that aggregates stats from various 1215 // sync components. 1216 AllStatus allstatus_; 1217 1218 // Each element of this array is a store of change records produced by 1219 // HandleChangeEvent during the CALCULATE_CHANGES step. The changes are 1220 // segregated by model type, and are stored here to be processed and 1221 // forwarded to the observer slightly later, at the TRANSACTION_ENDING 1222 // step by HandleTransactionEndingChangeEvent. The list is cleared in the 1223 // TRANSACTION_COMPLETE step by HandleTransactionCompleteChangeEvent. 1224 ChangeReorderBuffer change_buffers_[syncable::MODEL_TYPE_COUNT]; 1225 1226 // Bit vector keeping track of which models need to have their 1227 // OnChangesComplete observer set. 1228 // 1229 // Set by HandleTransactionEndingChangeEvent, cleared in 1230 // HandleTransactionCompleteChangeEvent. 1231 std::bitset<syncable::MODEL_TYPE_COUNT> model_has_change_; 1232 1233 // The event listener hookup that is registered for HandleChangeEvent. 1234 scoped_ptr<browser_sync::ChannelHookup<syncable::DirectoryChangeEvent> > 1235 dir_change_hookup_; 1236 1237 // The sync dir_manager to which we belong. 1238 SyncManager* const sync_manager_; 1239 1240 // The entity that provides us with information about which types to sync. 1241 // The instance is shared between the SyncManager and the Syncer. 1242 ModelSafeWorkerRegistrar* registrar_; 1243 1244 // True if the next SyncCycle should notify peers of an update. 1245 bool notification_pending_; 1246 1247 // Set to true once Init has been called, and we know of an authenticated 1248 // valid) username either from a fresh authentication attempt (as in 1249 // first-use case) or from a previous attempt stored in our UserSettings 1250 // (as in the steady-state), and the syncable::Directory has been opened, 1251 // meaning we are ready to accept changes. Protected by initialized_mutex_ 1252 // as it can get read/set by both the SyncerThread and the AuthWatcherThread. 1253 bool initialized_; 1254 mutable Lock initialized_mutex_; 1255 1256 notifier::NotifierOptions notifier_options_; 1257 1258 // True if the SyncManager should be running in test mode (no syncer thread 1259 // actually communicating with the server). 1260 bool setup_for_test_mode_; 1261 1262 ScopedRunnableMethodFactory<SyncManager::SyncInternal> method_factory_; 1263}; 1264const int SyncManager::SyncInternal::kDefaultNudgeDelayMilliseconds = 200; 1265const int SyncManager::SyncInternal::kPreferencesNudgeDelayMilliseconds = 2000; 1266 1267SyncManager::SyncManager() { 1268 data_ = new SyncInternal(this); 1269} 1270 1271bool SyncManager::Init(const FilePath& database_location, 1272 const char* sync_server_and_path, 1273 int sync_server_port, 1274 bool use_ssl, 1275 HttpPostProviderFactory* post_factory, 1276 ModelSafeWorkerRegistrar* registrar, 1277 const char* user_agent, 1278 const SyncCredentials& credentials, 1279 const notifier::NotifierOptions& notifier_options, 1280 const std::string& restored_key_for_bootstrapping, 1281 bool setup_for_test_mode) { 1282 DCHECK(post_factory); 1283 VLOG(1) << "SyncManager starting Init..."; 1284 string server_string(sync_server_and_path); 1285 return data_->Init(database_location, 1286 server_string, 1287 sync_server_port, 1288 use_ssl, 1289 post_factory, 1290 registrar, 1291 user_agent, 1292 credentials, 1293 notifier_options, 1294 restored_key_for_bootstrapping, 1295 setup_for_test_mode); 1296} 1297 1298void SyncManager::UpdateCredentials(const SyncCredentials& credentials) { 1299 data_->UpdateCredentials(credentials); 1300} 1301 1302 1303bool SyncManager::InitialSyncEndedForAllEnabledTypes() { 1304 return data_->InitialSyncEndedForAllEnabledTypes(); 1305} 1306 1307void SyncManager::StartSyncing() { 1308 data_->StartSyncing(); 1309} 1310 1311void SyncManager::SetPassphrase(const std::string& passphrase, 1312 bool is_explicit) { 1313 data_->SetPassphrase(passphrase, is_explicit); 1314} 1315 1316bool SyncManager::IsUsingExplicitPassphrase() { 1317 return data_ && data_->IsUsingExplicitPassphrase(); 1318} 1319 1320bool SyncManager::RequestPause() { 1321 if (data_->syncer_thread()) 1322 return data_->syncer_thread()->RequestPause(); 1323 return false; 1324} 1325 1326bool SyncManager::RequestResume() { 1327 if (data_->syncer_thread()) 1328 return data_->syncer_thread()->RequestResume(); 1329 return false; 1330} 1331 1332void SyncManager::RequestNudge() { 1333 if (data_->syncer_thread()) 1334 data_->syncer_thread()->NudgeSyncer(0, SyncerThread::kLocal); 1335} 1336 1337void SyncManager::RequestClearServerData() { 1338 if (data_->syncer_thread()) 1339 data_->syncer_thread()->NudgeSyncer(0, SyncerThread::kClearPrivateData); 1340} 1341 1342const std::string& SyncManager::GetAuthenticatedUsername() { 1343 DCHECK(data_); 1344 return data_->username_for_share(); 1345} 1346 1347bool SyncManager::SyncInternal::Init( 1348 const FilePath& database_location, 1349 const std::string& sync_server_and_path, 1350 int port, 1351 bool use_ssl, 1352 HttpPostProviderFactory* post_factory, 1353 ModelSafeWorkerRegistrar* model_safe_worker_registrar, 1354 const char* user_agent, 1355 const SyncCredentials& credentials, 1356 const notifier::NotifierOptions& notifier_options, 1357 const std::string& restored_key_for_bootstrapping, 1358 bool setup_for_test_mode) { 1359 1360 VLOG(1) << "Starting SyncInternal initialization."; 1361 1362 core_message_loop_ = MessageLoop::current(); 1363 DCHECK(core_message_loop_); 1364 notifier_options_ = notifier_options; 1365 registrar_ = model_safe_worker_registrar; 1366 setup_for_test_mode_ = setup_for_test_mode; 1367 1368 share_.dir_manager.reset(new DirectoryManager(database_location)); 1369 1370 connection_manager_.reset(new SyncAPIServerConnectionManager( 1371 sync_server_and_path, port, use_ssl, user_agent, post_factory)); 1372 1373 connection_manager_->AddListener(this); 1374 1375 net::NetworkChangeNotifier::AddObserver(this); 1376 // TODO(akalin): CheckServerReachable() can block, which may cause jank if we 1377 // try to shut down sync. Fix this. 1378 core_message_loop_->PostTask(FROM_HERE, 1379 method_factory_.NewRunnableMethod(&SyncInternal::CheckServerReachable)); 1380 1381 // Test mode does not use a syncer context or syncer thread. 1382 if (!setup_for_test_mode) { 1383 // Build a SyncSessionContext and store the worker in it. 1384 VLOG(1) << "Sync is bringing up SyncSessionContext."; 1385 std::vector<SyncEngineEventListener*> listeners; 1386 listeners.push_back(&allstatus_); 1387 listeners.push_back(this); 1388 SyncSessionContext* context = new SyncSessionContext( 1389 connection_manager_.get(), 1390 dir_manager(), 1391 model_safe_worker_registrar, 1392 listeners); 1393 1394 // The SyncerThread takes ownership of |context|. 1395 syncer_thread_ = new SyncerThread(context); 1396 } 1397 1398 bool signed_in = SignIn(credentials); 1399 1400 // Do this once the directory is opened. 1401 BootstrapEncryption(restored_key_for_bootstrapping); 1402 return signed_in; 1403} 1404 1405void SyncManager::SyncInternal::BootstrapEncryption( 1406 const std::string& restored_key_for_bootstrapping) { 1407 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share()); 1408 if (!lookup.good()) { 1409 NOTREACHED(); 1410 return; 1411 } 1412 1413 if (!lookup->initial_sync_ended_for_type(syncable::NIGORI)) 1414 return; 1415 1416 Cryptographer* cryptographer = share_.dir_manager->cryptographer(); 1417 cryptographer->Bootstrap(restored_key_for_bootstrapping); 1418 1419 ReadTransaction trans(GetUserShare()); 1420 ReadNode node(&trans); 1421 if (!node.InitByTagLookup(kNigoriTag)) { 1422 NOTREACHED(); 1423 return; 1424 } 1425 1426 const sync_pb::NigoriSpecifics& nigori = node.GetNigoriSpecifics(); 1427 if (!nigori.encrypted().blob().empty()) { 1428 if (cryptographer->CanDecrypt(nigori.encrypted())) { 1429 cryptographer->SetKeys(nigori.encrypted()); 1430 } else { 1431 cryptographer->SetPendingKeys(nigori.encrypted()); 1432 observer_->OnPassphraseRequired(); 1433 } 1434 } 1435} 1436 1437void SyncManager::SyncInternal::StartSyncing() { 1438 if (syncer_thread()) // NULL during certain unittests. 1439 syncer_thread()->Start(); // Start the syncer thread. This won't actually 1440 // result in any syncing until at least the 1441 // DirectoryManager broadcasts the OPENED event, 1442 // and a valid server connection is detected. 1443} 1444 1445void SyncManager::SyncInternal::MarkAndNotifyInitializationComplete() { 1446 // There is only one real time we need this mutex. If we get an auth 1447 // success, and before the initial sync ends we get an auth failure. In this 1448 // case we'll be listening to both the AuthWatcher and Syncer, and it's a race 1449 // between their respective threads to call MarkAndNotify. We need to make 1450 // sure the observer is notified once and only once. 1451 { 1452 AutoLock lock(initialized_mutex_); 1453 if (initialized_) 1454 return; 1455 initialized_ = true; 1456 } 1457 1458 // Notify that initialization is complete. 1459 if (observer_) 1460 observer_->OnInitializationComplete(); 1461} 1462 1463void SyncManager::SyncInternal::SendPendingXMPPNotification( 1464 bool new_pending_notification) { 1465 DCHECK_EQ(MessageLoop::current(), core_message_loop_); 1466 DCHECK_NE(notifier_options_.notification_method, 1467 notifier::NOTIFICATION_SERVER); 1468 notification_pending_ = notification_pending_ || new_pending_notification; 1469 if (!notification_pending_) { 1470 VLOG(1) << "Not sending notification: no pending notification"; 1471 return; 1472 } 1473 if (!talk_mediator_.get()) { 1474 VLOG(1) << "Not sending notification: shutting down (talk_mediator_ is " 1475 "NULL)"; 1476 return; 1477 } 1478 VLOG(1) << "Sending XMPP notification..."; 1479 OutgoingNotificationData notification_data; 1480 notification_data.service_id = browser_sync::kSyncServiceId; 1481 notification_data.service_url = browser_sync::kSyncServiceUrl; 1482 notification_data.send_content = true; 1483 notification_data.priority = browser_sync::kSyncPriority; 1484 notification_data.write_to_cache_only = true; 1485 notification_data.service_specific_data = 1486 browser_sync::kSyncServiceSpecificData; 1487 notification_data.require_subscription = true; 1488 bool success = talk_mediator_->SendNotification(notification_data); 1489 if (success) { 1490 notification_pending_ = false; 1491 VLOG(1) << "Sent XMPP notification"; 1492 } else { 1493 VLOG(1) << "Could not send XMPP notification"; 1494 } 1495} 1496 1497bool SyncManager::SyncInternal::OpenDirectory() { 1498 DCHECK(!initialized()) << "Should only happen once"; 1499 1500 bool share_opened = dir_manager()->Open(username_for_share()); 1501 DCHECK(share_opened); 1502 if (!share_opened) { 1503 if (observer_) 1504 observer_->OnStopSyncingPermanently(); 1505 1506 LOG(ERROR) << "Could not open share for:" << username_for_share(); 1507 return false; 1508 } 1509 1510 // Database has to be initialized for the guid to be available. 1511 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share()); 1512 if (!lookup.good()) { 1513 NOTREACHED(); 1514 return false; 1515 } 1516 1517 connection_manager()->set_client_id(lookup->cache_guid()); 1518 1519 if (syncer_thread()) 1520 syncer_thread()->CreateSyncer(username_for_share()); 1521 1522 MarkAndNotifyInitializationComplete(); 1523 dir_change_hookup_.reset(lookup->AddChangeObserver(this)); 1524 return true; 1525} 1526 1527bool SyncManager::SyncInternal::SignIn(const SyncCredentials& credentials) { 1528 DCHECK_EQ(MessageLoop::current(), core_message_loop_); 1529 DCHECK(share_.name.empty()); 1530 share_.name = credentials.email; 1531 1532 VLOG(1) << "Signing in user: " << username_for_share(); 1533 if (!OpenDirectory()) 1534 return false; 1535 1536 UpdateCredentials(credentials); 1537 return true; 1538} 1539 1540void SyncManager::SyncInternal::UpdateCredentials( 1541 const SyncCredentials& credentials) { 1542 DCHECK_EQ(MessageLoop::current(), core_message_loop_); 1543 DCHECK(share_.name == credentials.email); 1544 connection_manager()->set_auth_token(credentials.sync_token); 1545 TalkMediatorLogin(credentials.email, credentials.sync_token); 1546 CheckServerReachable(); 1547 sync_manager_->RequestNudge(); 1548} 1549 1550void SyncManager::SyncInternal::InitializeTalkMediator() { 1551 if (notifier_options_.notification_method == 1552 notifier::NOTIFICATION_SERVER) { 1553 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share()); 1554 std::string state; 1555 if (lookup.good()) 1556 state = lookup->GetAndClearNotificationState(); 1557 else 1558 LOG(ERROR) << "Could not read notification state"; 1559 if (VLOG_IS_ON(1)) { 1560 std::string encoded_state; 1561 base::Base64Encode(state, &encoded_state); 1562 VLOG(1) << "Read notification state: " << encoded_state; 1563 } 1564 sync_notifier::ServerNotifierThread* server_notifier_thread = 1565 new sync_notifier::ServerNotifierThread( 1566 notifier_options_, state, this); 1567 talk_mediator_.reset( 1568 new TalkMediatorImpl(server_notifier_thread, 1569 notifier_options_.invalidate_xmpp_login, 1570 notifier_options_.allow_insecure_connection)); 1571 } else { 1572 notifier::MediatorThread* mediator_thread = 1573 new notifier::MediatorThreadImpl(notifier_options_); 1574 talk_mediator_.reset( 1575 new TalkMediatorImpl(mediator_thread, 1576 notifier_options_.invalidate_xmpp_login, 1577 notifier_options_.allow_insecure_connection)); 1578 talk_mediator_->AddSubscribedServiceUrl(browser_sync::kSyncServiceUrl); 1579 } 1580 talk_mediator_->SetDelegate(this); 1581} 1582 1583void SyncManager::SyncInternal::RaiseAuthNeededEvent() { 1584 if (observer_) { 1585 observer_->OnAuthError(AuthError(AuthError::INVALID_GAIA_CREDENTIALS)); 1586 } 1587} 1588 1589void SyncManager::SyncInternal::SetUsingExplicitPassphrasePrefForMigration() { 1590 WriteTransaction trans(&share_); 1591 WriteNode node(&trans); 1592 if (!node.InitByTagLookup(kNigoriTag)) { 1593 // TODO(albertb): Plumb an UnrecoverableError all the way back to the PSS. 1594 NOTREACHED(); 1595 return; 1596 } 1597 sync_pb::NigoriSpecifics specifics(node.GetNigoriSpecifics()); 1598 specifics.set_using_explicit_passphrase(true); 1599 node.SetNigoriSpecifics(specifics); 1600} 1601 1602void SyncManager::SyncInternal::SetPassphrase( 1603 const std::string& passphrase, bool is_explicit) { 1604 Cryptographer* cryptographer = dir_manager()->cryptographer(); 1605 KeyParams params = {"localhost", "dummy", passphrase}; 1606 if (cryptographer->has_pending_keys()) { 1607 if (!cryptographer->DecryptPendingKeys(params)) { 1608 observer_->OnPassphraseRequired(); 1609 return; 1610 } 1611 1612 // TODO(tim): If this is the first time the user has entered a passphrase 1613 // since the protocol changed to store passphrase preferences in the cloud, 1614 // make sure we update this preference. See bug 62103. 1615 if (is_explicit) 1616 SetUsingExplicitPassphrasePrefForMigration(); 1617 1618 // Nudge the syncer so that passwords updates that were waiting for this 1619 // passphrase get applied as soon as possible. 1620 sync_manager_->RequestNudge(); 1621 } else { 1622 WriteTransaction trans(GetUserShare()); 1623 WriteNode node(&trans); 1624 if (!node.InitByTagLookup(kNigoriTag)) { 1625 // TODO(albertb): Plumb an UnrecoverableError all the way back to the PSS. 1626 NOTREACHED(); 1627 return; 1628 } 1629 1630 // Prevent an implicit SetPassphrase request from changing an explicitly 1631 // set passphrase. 1632 if (!is_explicit && node.GetNigoriSpecifics().using_explicit_passphrase()) 1633 return; 1634 1635 cryptographer->AddKey(params); 1636 1637 // TODO(tim): Bug 58231. It would be nice if SetPassphrase didn't require 1638 // messing with the Nigori node, because we can't call SetPassphrase until 1639 // download conditions are met vs Cryptographer init. It seems like it's 1640 // safe to defer this work. 1641 sync_pb::NigoriSpecifics specifics; 1642 cryptographer->GetKeys(specifics.mutable_encrypted()); 1643 specifics.set_using_explicit_passphrase(is_explicit); 1644 node.SetNigoriSpecifics(specifics); 1645 ReEncryptEverything(&trans); 1646 } 1647 1648 std::string bootstrap_token; 1649 cryptographer->GetBootstrapToken(&bootstrap_token); 1650 observer_->OnPassphraseAccepted(bootstrap_token); 1651} 1652 1653bool SyncManager::SyncInternal::IsUsingExplicitPassphrase() { 1654 ReadTransaction trans(&share_); 1655 ReadNode node(&trans); 1656 if (!node.InitByTagLookup(kNigoriTag)) { 1657 // TODO(albertb): Plumb an UnrecoverableError all the way back to the PSS. 1658 NOTREACHED(); 1659 return false; 1660 } 1661 1662 return node.GetNigoriSpecifics().using_explicit_passphrase(); 1663} 1664 1665void SyncManager::SyncInternal::ReEncryptEverything(WriteTransaction* trans) { 1666 // TODO(tim): bug 59242. We shouldn't lookup by data type and instead use 1667 // a protocol flag or existence of an EncryptedData message, but for now, 1668 // encryption is on if-and-only-if the type is passwords, and we haven't 1669 // ironed out the protocol for generic encryption. 1670 static const char* passwords_tag = "google_chrome_passwords"; 1671 ReadNode passwords_root(trans); 1672 if (!passwords_root.InitByTagLookup(passwords_tag)) { 1673 LOG(WARNING) << "No passwords to reencrypt."; 1674 return; 1675 } 1676 1677 int64 child_id = passwords_root.GetFirstChildId(); 1678 while (child_id != kInvalidId) { 1679 WriteNode child(trans); 1680 if (!child.InitByIdLookup(child_id)) { 1681 NOTREACHED(); 1682 return; 1683 } 1684 child.SetPasswordSpecifics(child.GetPasswordSpecifics()); 1685 child_id = child.GetSuccessorId(); 1686 } 1687} 1688 1689SyncManager::~SyncManager() { 1690 delete data_; 1691} 1692 1693void SyncManager::SetObserver(Observer* observer) { 1694 data_->set_observer(observer); 1695} 1696 1697void SyncManager::RemoveObserver() { 1698 data_->set_observer(NULL); 1699} 1700 1701void SyncManager::Shutdown() { 1702 data_->Shutdown(); 1703} 1704 1705void SyncManager::SyncInternal::Shutdown() { 1706 method_factory_.RevokeAll(); 1707 1708 // We NULL out talk_mediator_ so that any tasks pumped below do not 1709 // trigger further XMPP actions. 1710 // 1711 // TODO(akalin): NULL the other member variables defensively, too. 1712 scoped_ptr<TalkMediator> talk_mediator(talk_mediator_.release()); 1713 1714 if (syncer_thread()) { 1715 if (!syncer_thread()->Stop(kThreadExitTimeoutMsec)) { 1716 LOG(FATAL) << "Unable to stop the syncer, it won't be happy..."; 1717 } 1718 syncer_thread_ = NULL; 1719 } 1720 1721 // Shutdown the xmpp buzz connection. 1722 if (talk_mediator.get()) { 1723 VLOG(1) << "P2P: Mediator logout started."; 1724 talk_mediator->Logout(); 1725 VLOG(1) << "P2P: Mediator logout completed."; 1726 talk_mediator.reset(); 1727 VLOG(1) << "P2P: Mediator destroyed."; 1728 } 1729 1730 // Pump any messages the auth watcher, syncer thread, or talk 1731 // mediator posted before they shut down. (See OnSyncEngineEvent(), 1732 // and HandleTalkMediatorEvent() for the 1733 // events that may be posted.) 1734 { 1735 CHECK(core_message_loop_); 1736 bool old_state = core_message_loop_->NestableTasksAllowed(); 1737 core_message_loop_->SetNestableTasksAllowed(true); 1738 core_message_loop_->RunAllPending(); 1739 core_message_loop_->SetNestableTasksAllowed(old_state); 1740 } 1741 1742 net::NetworkChangeNotifier::RemoveObserver(this); 1743 1744 connection_manager_->RemoveListener(this); 1745 1746 if (dir_manager()) { 1747 dir_manager()->FinalSaveChangesForAll(); 1748 dir_manager()->Close(username_for_share()); 1749 } 1750 1751 // Reset the DirectoryManager and UserSettings so they relinquish sqlite 1752 // handles to backing files. 1753 share_.dir_manager.reset(); 1754 1755 // We don't want to process any more events. 1756 dir_change_hookup_.reset(); 1757 1758 core_message_loop_ = NULL; 1759} 1760 1761void SyncManager::SyncInternal::OnIPAddressChanged() { 1762 VLOG(1) << "IP address change detected"; 1763#if defined (OS_CHROMEOS) 1764 // TODO(tim): This is a hack to intentionally lose a race with flimflam at 1765 // shutdown, so we don't cause shutdown to wait for our http request. 1766 // http://crosbug.com/8429 1767 MessageLoop::current()->PostDelayedTask(FROM_HERE, 1768 method_factory_.NewRunnableMethod(&SyncInternal::OnIPAddressChangedImpl), 1769 kChromeOSNetworkChangeReactionDelayHackMsec); 1770#else 1771 OnIPAddressChangedImpl(); 1772#endif // defined(OS_CHROMEOS) 1773} 1774 1775void SyncManager::SyncInternal::OnIPAddressChangedImpl() { 1776 // TODO(akalin): CheckServerReachable() can block, which may cause 1777 // jank if we try to shut down sync. Fix this. 1778 connection_manager()->CheckServerReachable(); 1779 sync_manager_->RequestNudge(); 1780} 1781 1782// Listen to model changes, filter out ones initiated by the sync API, and 1783// saves the rest (hopefully just backend Syncer changes resulting from 1784// ApplyUpdates) to data_->changelist. 1785void SyncManager::SyncInternal::HandleChannelEvent( 1786 const syncable::DirectoryChangeEvent& event) { 1787 if (event.todo == syncable::DirectoryChangeEvent::TRANSACTION_COMPLETE) { 1788 // Safe to perform slow I/O operations now, go ahead and commit. 1789 HandleTransactionCompleteChangeEvent(event); 1790 return; 1791 } else if (event.todo == syncable::DirectoryChangeEvent::TRANSACTION_ENDING) { 1792 HandleTransactionEndingChangeEvent(event); 1793 return; 1794 } else if (event.todo == syncable::DirectoryChangeEvent::CALCULATE_CHANGES) { 1795 if (event.writer == syncable::SYNCAPI) { 1796 HandleCalculateChangesChangeEventFromSyncApi(event); 1797 return; 1798 } 1799 HandleCalculateChangesChangeEventFromSyncer(event); 1800 return; 1801 } else if (event.todo == syncable::DirectoryChangeEvent::SHUTDOWN) { 1802 dir_change_hookup_.reset(); 1803 } 1804} 1805 1806void SyncManager::SyncInternal::HandleTransactionCompleteChangeEvent( 1807 const syncable::DirectoryChangeEvent& event) { 1808 // This notification happens immediately after the channel mutex is released 1809 // This allows work to be performed without holding the WriteTransaction lock 1810 // but before the transaction is finished. 1811 DCHECK_EQ(event.todo, syncable::DirectoryChangeEvent::TRANSACTION_COMPLETE); 1812 if (!observer_) 1813 return; 1814 1815 // Call commit 1816 for (int i = 0; i < syncable::MODEL_TYPE_COUNT; ++i) { 1817 if (model_has_change_.test(i)) { 1818 observer_->OnChangesComplete(syncable::ModelTypeFromInt(i)); 1819 model_has_change_.reset(i); 1820 } 1821 } 1822} 1823 1824void SyncManager::SyncInternal::OnServerConnectionEvent( 1825 const ServerConnectionEvent& event) { 1826 allstatus_.HandleServerConnectionEvent(event); 1827 if (event.what_happened == ServerConnectionEvent::STATUS_CHANGED) { 1828 if (event.connection_code == 1829 browser_sync::HttpResponse::SERVER_CONNECTION_OK) { 1830 if (observer_) { 1831 observer_->OnAuthError(AuthError::None()); 1832 } 1833 } 1834 1835 if (event.connection_code == browser_sync::HttpResponse::SYNC_AUTH_ERROR) { 1836 if (observer_) { 1837 observer_->OnAuthError(AuthError(AuthError::INVALID_GAIA_CREDENTIALS)); 1838 } 1839 } 1840 } else { 1841 DCHECK_EQ(ServerConnectionEvent::SHUTDOWN, event.what_happened); 1842 connection_manager_->RemoveListener(this); 1843 } 1844} 1845 1846void SyncManager::SyncInternal::HandleTransactionEndingChangeEvent( 1847 const syncable::DirectoryChangeEvent& event) { 1848 // This notification happens immediately before a syncable WriteTransaction 1849 // falls out of scope. It happens while the channel mutex is still held, 1850 // and while the transaction mutex is held, so it cannot be re-entrant. 1851 DCHECK_EQ(event.todo, syncable::DirectoryChangeEvent::TRANSACTION_ENDING); 1852 if (!observer_ || ChangeBuffersAreEmpty()) 1853 return; 1854 1855 // This will continue the WriteTransaction using a read only wrapper. 1856 // This is the last chance for read to occur in the WriteTransaction 1857 // that's closing. This special ReadTransaction will not close the 1858 // underlying transaction. 1859 ReadTransaction trans(GetUserShare(), event.trans); 1860 1861 for (int i = 0; i < syncable::MODEL_TYPE_COUNT; ++i) { 1862 if (change_buffers_[i].IsEmpty()) 1863 continue; 1864 1865 vector<ChangeRecord> ordered_changes; 1866 change_buffers_[i].GetAllChangesInTreeOrder(&trans, &ordered_changes); 1867 if (!ordered_changes.empty()) { 1868 observer_->OnChangesApplied(syncable::ModelTypeFromInt(i), &trans, 1869 &ordered_changes[0], ordered_changes.size()); 1870 model_has_change_.set(i, true); 1871 } 1872 change_buffers_[i].Clear(); 1873 } 1874} 1875 1876void SyncManager::SyncInternal::HandleCalculateChangesChangeEventFromSyncApi( 1877 const syncable::DirectoryChangeEvent& event) { 1878 // We have been notified about a user action changing the bookmark model. 1879 DCHECK_EQ(event.todo, syncable::DirectoryChangeEvent::CALCULATE_CHANGES); 1880 DCHECK(event.writer == syncable::SYNCAPI || 1881 event.writer == syncable::UNITTEST); 1882 LOG_IF(WARNING, !ChangeBuffersAreEmpty()) << 1883 "CALCULATE_CHANGES called with unapplied old changes."; 1884 1885 bool exists_unsynced_items = false; 1886 bool only_preference_changes = true; 1887 syncable::ModelTypeBitSet model_types; 1888 for (syncable::OriginalEntries::const_iterator i = event.originals->begin(); 1889 i != event.originals->end() && !exists_unsynced_items; 1890 ++i) { 1891 int64 id = i->ref(syncable::META_HANDLE); 1892 syncable::Entry e(event.trans, syncable::GET_BY_HANDLE, id); 1893 DCHECK(e.good()); 1894 1895 syncable::ModelType model_type = e.GetModelType(); 1896 1897 if (e.Get(syncable::IS_UNSYNCED)) { 1898 if (model_type == syncable::TOP_LEVEL_FOLDER || 1899 model_type == syncable::UNSPECIFIED) { 1900 NOTREACHED() << "Permanent or underspecified item changed via syncapi."; 1901 continue; 1902 } 1903 // Unsynced items will cause us to nudge the the syncer. 1904 exists_unsynced_items = true; 1905 1906 model_types[model_type] = true; 1907 if (model_type != syncable::PREFERENCES) 1908 only_preference_changes = false; 1909 } 1910 } 1911 if (exists_unsynced_items && syncer_thread()) { 1912 int nudge_delay = only_preference_changes ? 1913 kPreferencesNudgeDelayMilliseconds : kDefaultNudgeDelayMilliseconds; 1914 syncer_thread()->NudgeSyncerWithDataTypes( 1915 nudge_delay, 1916 SyncerThread::kLocal, 1917 model_types); 1918 } 1919} 1920 1921void SyncManager::SyncInternal::SetExtraChangeRecordData(int64 id, 1922 syncable::ModelType type, ChangeReorderBuffer* buffer, 1923 Cryptographer* cryptographer, const syncable::EntryKernel& original, 1924 bool existed_before, bool exists_now) { 1925 // If this is a deletion, attach the entity specifics as extra data 1926 // so that the delete can be processed. 1927 if (!exists_now && existed_before) { 1928 buffer->SetSpecificsForId(id, original.ref(SPECIFICS)); 1929 if (type == syncable::PASSWORDS) { 1930 // Need to dig a bit deeper as passwords are encrypted. 1931 scoped_ptr<sync_pb::PasswordSpecificsData> data( 1932 DecryptPasswordSpecifics(original.ref(SPECIFICS), cryptographer)); 1933 if (!data.get()) { 1934 NOTREACHED(); 1935 return; 1936 } 1937 buffer->SetExtraDataForId(id, new ExtraPasswordChangeRecordData(*data)); 1938 } 1939 } 1940} 1941 1942void SyncManager::SyncInternal::HandleCalculateChangesChangeEventFromSyncer( 1943 const syncable::DirectoryChangeEvent& event) { 1944 // We only expect one notification per sync step, so change_buffers_ should 1945 // contain no pending entries. 1946 DCHECK_EQ(event.todo, syncable::DirectoryChangeEvent::CALCULATE_CHANGES); 1947 DCHECK(event.writer == syncable::SYNCER || 1948 event.writer == syncable::UNITTEST); 1949 LOG_IF(WARNING, !ChangeBuffersAreEmpty()) << 1950 "CALCULATE_CHANGES called with unapplied old changes."; 1951 1952 for (syncable::OriginalEntries::const_iterator i = event.originals->begin(); 1953 i != event.originals->end(); ++i) { 1954 int64 id = i->ref(syncable::META_HANDLE); 1955 syncable::Entry e(event.trans, syncable::GET_BY_HANDLE, id); 1956 bool existed_before = !i->ref(syncable::IS_DEL); 1957 bool exists_now = e.good() && !e.Get(syncable::IS_DEL); 1958 DCHECK(e.good()); 1959 1960 // Omit items that aren't associated with a model. 1961 syncable::ModelType type = e.GetModelType(); 1962 if (type == syncable::TOP_LEVEL_FOLDER || type == syncable::UNSPECIFIED) 1963 continue; 1964 1965 if (exists_now && !existed_before) 1966 change_buffers_[type].PushAddedItem(id); 1967 else if (!exists_now && existed_before) 1968 change_buffers_[type].PushDeletedItem(id); 1969 else if (exists_now && existed_before && VisiblePropertiesDiffer(*i, e)) 1970 change_buffers_[type].PushUpdatedItem(id, VisiblePositionsDiffer(*i, e)); 1971 1972 SetExtraChangeRecordData(id, type, &change_buffers_[type], 1973 dir_manager()->cryptographer(), *i, 1974 existed_before, exists_now); 1975 } 1976} 1977 1978SyncManager::Status::Summary 1979SyncManager::SyncInternal::ComputeAggregatedStatusSummary() { 1980 switch (allstatus_.status().icon) { 1981 case AllStatus::OFFLINE: 1982 return Status::OFFLINE; 1983 case AllStatus::OFFLINE_UNSYNCED: 1984 return Status::OFFLINE_UNSYNCED; 1985 case AllStatus::SYNCING: 1986 return Status::SYNCING; 1987 case AllStatus::READY: 1988 return Status::READY; 1989 case AllStatus::CONFLICT: 1990 return Status::CONFLICT; 1991 case AllStatus::OFFLINE_UNUSABLE: 1992 return Status::OFFLINE_UNUSABLE; 1993 default: 1994 return Status::INVALID; 1995 } 1996} 1997 1998SyncManager::Status SyncManager::SyncInternal::ComputeAggregatedStatus() { 1999 Status return_status = 2000 { ComputeAggregatedStatusSummary(), 2001 allstatus_.status().authenticated, 2002 allstatus_.status().server_up, 2003 allstatus_.status().server_reachable, 2004 allstatus_.status().server_broken, 2005 allstatus_.status().notifications_enabled, 2006 allstatus_.status().notifications_received, 2007 allstatus_.status().notifications_sent, 2008 allstatus_.status().unsynced_count, 2009 allstatus_.status().conflicting_count, 2010 allstatus_.status().syncing, 2011 allstatus_.status().initial_sync_ended, 2012 allstatus_.status().syncer_stuck, 2013 allstatus_.status().updates_available, 2014 allstatus_.status().updates_received, 2015 allstatus_.status().disk_full, 2016 false, // TODO(ncarter): invalid store? 2017 allstatus_.status().max_consecutive_errors}; 2018 return return_status; 2019} 2020 2021void SyncManager::SyncInternal::OnSyncEngineEvent( 2022 const SyncEngineEvent& event) { 2023 if (!observer_) 2024 return; 2025 2026 // Only send an event if this is due to a cycle ending and this cycle 2027 // concludes a canonical "sync" process; that is, based on what is known 2028 // locally we are "all happy" and up-to-date. There may be new changes on 2029 // the server, but we'll get them on a subsequent sync. 2030 // 2031 // Notifications are sent at the end of every sync cycle, regardless of 2032 // whether we should sync again. 2033 if (event.what_happened == SyncEngineEvent::SYNC_CYCLE_ENDED) { 2034 ModelSafeRoutingInfo enabled_types; 2035 registrar_->GetModelSafeRoutingInfo(&enabled_types); 2036 if (enabled_types.count(syncable::PASSWORDS) > 0) { 2037 Cryptographer* cryptographer = 2038 GetUserShare()->dir_manager->cryptographer(); 2039 if (!cryptographer->is_ready() && !cryptographer->has_pending_keys()) { 2040 sync_api::ReadTransaction trans(GetUserShare()); 2041 sync_api::ReadNode node(&trans); 2042 if (!node.InitByTagLookup(kNigoriTag)) { 2043 DCHECK(!event.snapshot->is_share_usable); 2044 return; 2045 } 2046 const sync_pb::NigoriSpecifics& nigori = node.GetNigoriSpecifics(); 2047 if (!nigori.encrypted().blob().empty()) { 2048 DCHECK(!cryptographer->CanDecrypt(nigori.encrypted())); 2049 cryptographer->SetPendingKeys(nigori.encrypted()); 2050 } 2051 } 2052 2053 // If we've completed a sync cycle and the cryptographer isn't ready yet, 2054 // prompt the user for a passphrase. 2055 if (!cryptographer->is_ready() || cryptographer->has_pending_keys()) { 2056 observer_->OnPassphraseRequired(); 2057 } 2058 } 2059 2060 if (!initialized()) 2061 return; 2062 2063 if (!event.snapshot->has_more_to_sync) { 2064 observer_->OnSyncCycleCompleted(event.snapshot); 2065 } 2066 2067 if (notifier_options_.notification_method != 2068 notifier::NOTIFICATION_SERVER) { 2069 // TODO(chron): Consider changing this back to track has_more_to_sync 2070 // only notify peers if a successful commit has occurred. 2071 bool new_pending_notification = 2072 (event.snapshot->syncer_status.num_successful_commits > 0); 2073 core_message_loop_->PostTask( 2074 FROM_HERE, 2075 NewRunnableMethod( 2076 this, 2077 &SyncManager::SyncInternal::SendPendingXMPPNotification, 2078 new_pending_notification)); 2079 } 2080 } 2081 2082 if (event.what_happened == SyncEngineEvent::SYNCER_THREAD_PAUSED) { 2083 observer_->OnPaused(); 2084 return; 2085 } 2086 2087 if (event.what_happened == SyncEngineEvent::SYNCER_THREAD_RESUMED) { 2088 observer_->OnResumed(); 2089 return; 2090 } 2091 2092 if (event.what_happened == SyncEngineEvent::STOP_SYNCING_PERMANENTLY) { 2093 observer_->OnStopSyncingPermanently(); 2094 return; 2095 } 2096 2097 if (event.what_happened == SyncEngineEvent::CLEAR_SERVER_DATA_SUCCEEDED) { 2098 observer_->OnClearServerDataSucceeded(); 2099 return; 2100 } 2101 2102 if (event.what_happened == SyncEngineEvent::CLEAR_SERVER_DATA_FAILED) { 2103 observer_->OnClearServerDataFailed(); 2104 return; 2105 } 2106 2107 if (event.what_happened == SyncEngineEvent::UPDATED_TOKEN) { 2108 observer_->OnUpdatedToken(event.updated_token); 2109 return; 2110 } 2111} 2112 2113void SyncManager::SyncInternal::OnNotificationStateChange( 2114 bool notifications_enabled) { 2115 VLOG(1) << "P2P: Notifications enabled = " 2116 << (notifications_enabled ? "true" : "false"); 2117 allstatus_.SetNotificationsEnabled(notifications_enabled); 2118 if (syncer_thread()) { 2119 syncer_thread()->SetNotificationsEnabled(notifications_enabled); 2120 } 2121 if ((notifier_options_.notification_method != 2122 notifier::NOTIFICATION_SERVER) && notifications_enabled) { 2123 // Nudge the syncer thread when notifications are enabled, in case there is 2124 // any data that has not yet been synced. If we are listening to 2125 // server-issued notifications, we are already guaranteed to receive a 2126 // notification on a successful connection. 2127 if (syncer_thread()) { 2128 syncer_thread()->NudgeSyncer(0, SyncerThread::kLocal); 2129 } 2130 2131 // Send a notification as soon as subscriptions are on 2132 // (see http://code.google.com/p/chromium/issues/detail?id=38563 ). 2133 core_message_loop_->PostTask( 2134 FROM_HERE, 2135 NewRunnableMethod( 2136 this, 2137 &SyncManager::SyncInternal::SendPendingXMPPNotification, 2138 true)); 2139 } 2140} 2141 2142void SyncManager::SyncInternal::TalkMediatorLogin( 2143 const std::string& email, const std::string& token) { 2144 DCHECK_EQ(MessageLoop::current(), core_message_loop_); 2145 DCHECK(!email.empty()); 2146 DCHECK(!token.empty()); 2147 InitializeTalkMediator(); 2148 talk_mediator_->SetAuthToken(email, token, SYNC_SERVICE_NAME); 2149 talk_mediator_->Login(); 2150} 2151 2152void SyncManager::SyncInternal::OnIncomingNotification( 2153 const IncomingNotificationData& notification_data) { 2154 syncable::ModelTypeBitSet model_types; 2155 2156 // Check if the service url is a sync URL. An empty service URL is 2157 // treated as a legacy sync notification. If we're listening to 2158 // server-issued notifications, no need to check the service_url. 2159 if (notifier_options_.notification_method == 2160 notifier::NOTIFICATION_SERVER) { 2161 VLOG(1) << "Sync received server notification: " << 2162 notification_data.service_specific_data; 2163 2164 if (!syncable::ModelTypeBitSetFromString( 2165 notification_data.service_specific_data, 2166 &model_types)) { 2167 LOG(DFATAL) << "Could not extract model types from server data."; 2168 model_types.set(); 2169 } 2170 } else if (notification_data.service_url.empty() || 2171 (notification_data.service_url == 2172 browser_sync::kSyncLegacyServiceUrl) || 2173 (notification_data.service_url == 2174 browser_sync::kSyncServiceUrl)) { 2175 VLOG(1) << "Sync received P2P notification."; 2176 2177 // Catch for sync integration tests (uses p2p). Just set all datatypes. 2178 model_types.set(); 2179 } else { 2180 LOG(WARNING) << "Notification fron unexpected source: " 2181 << notification_data.service_url; 2182 } 2183 2184 if (model_types.any()) { 2185 if (syncer_thread()) { 2186 // Introduce a delay to help coalesce initial notifications. 2187 syncer_thread()->NudgeSyncerWithDataTypes( 2188 250, 2189 SyncerThread::kNotification, 2190 model_types); 2191 } 2192 allstatus_.IncrementNotificationsReceived(); 2193 } else { 2194 LOG(WARNING) << "Sync received notification without any type information."; 2195 } 2196} 2197 2198void SyncManager::SyncInternal::OnOutgoingNotification() { 2199 DCHECK_NE(notifier_options_.notification_method, 2200 notifier::NOTIFICATION_SERVER); 2201 allstatus_.IncrementNotificationsSent(); 2202} 2203 2204void SyncManager::SyncInternal::WriteState(const std::string& state) { 2205 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share()); 2206 if (!lookup.good()) { 2207 LOG(ERROR) << "Could not write notification state"; 2208 // TODO(akalin): Propagate result callback all the way to this 2209 // function and call it with "false" to signal failure. 2210 return; 2211 } 2212 if (VLOG_IS_ON(1)) { 2213 std::string encoded_state; 2214 base::Base64Encode(state, &encoded_state); 2215 VLOG(1) << "Writing notification state: " << encoded_state; 2216 } 2217 lookup->SetNotificationState(state); 2218 lookup->SaveChanges(); 2219} 2220 2221SyncManager::Status::Summary SyncManager::GetStatusSummary() const { 2222 return data_->ComputeAggregatedStatusSummary(); 2223} 2224 2225SyncManager::Status SyncManager::GetDetailedStatus() const { 2226 return data_->ComputeAggregatedStatus(); 2227} 2228 2229SyncManager::SyncInternal* SyncManager::GetImpl() const { return data_; } 2230 2231void SyncManager::SaveChanges() { 2232 data_->SaveChanges(); 2233} 2234 2235void SyncManager::SyncInternal::SaveChanges() { 2236 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share()); 2237 if (!lookup.good()) { 2238 DCHECK(false) << "ScopedDirLookup creation failed; Unable to SaveChanges"; 2239 return; 2240 } 2241 lookup->SaveChanges(); 2242} 2243 2244////////////////////////////////////////////////////////////////////////// 2245// BaseTransaction member definitions 2246BaseTransaction::BaseTransaction(UserShare* share) 2247 : lookup_(NULL) { 2248 DCHECK(share && share->dir_manager.get()); 2249 lookup_ = new syncable::ScopedDirLookup(share->dir_manager.get(), 2250 share->name); 2251 cryptographer_ = share->dir_manager->cryptographer(); 2252 if (!(lookup_->good())) 2253 DCHECK(false) << "ScopedDirLookup failed on valid DirManager."; 2254} 2255BaseTransaction::~BaseTransaction() { 2256 delete lookup_; 2257} 2258 2259UserShare* SyncManager::GetUserShare() const { 2260 DCHECK(data_->initialized()) << "GetUserShare requires initialization!"; 2261 return data_->GetUserShare(); 2262} 2263 2264bool SyncManager::HasUnsyncedItems() const { 2265 sync_api::ReadTransaction trans(GetUserShare()); 2266 return (trans.GetWrappedTrans()->directory()->unsynced_entity_count() != 0); 2267} 2268 2269} // namespace sync_api 2270