| OLD | NEW |
| (Empty) | |
| 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/sync_manager.h" |
| 6 |
| 7 #include <string> |
| 8 #include <vector> |
| 9 |
| 10 #include "base/base64.h" |
| 11 #include "base/json/json_writer.h" |
| 12 #include "base/string_number_conversions.h" |
| 13 #include "base/values.h" |
| 14 #include "chrome/browser/sync/engine/all_status.h" |
| 15 #include "chrome/browser/sync/engine/base_node.h" |
| 16 #include "chrome/browser/sync/engine/change_reorder_buffer.h" |
| 17 #include "chrome/browser/sync/engine/net/server_connection_manager.h" |
| 18 #include "chrome/browser/sync/engine/net/syncapi_server_connection_manager.h" |
| 19 #include "chrome/browser/sync/engine/nigori_util.h" |
| 20 #include "chrome/browser/sync/engine/read_node.h" |
| 21 #include "chrome/browser/sync/engine/syncapi_internal.h" |
| 22 #include "chrome/browser/sync/engine/syncer_types.h" |
| 23 #include "chrome/browser/sync/engine/sync_scheduler.h" |
| 24 #include "chrome/browser/sync/engine/user_share.h" |
| 25 #include "chrome/browser/sync/js/js_arg_list.h" |
| 26 #include "chrome/browser/sync/js/js_backend.h" |
| 27 #include "chrome/browser/sync/js/js_event_details.h" |
| 28 #include "chrome/browser/sync/js/js_event_handler.h" |
| 29 #include "chrome/browser/sync/js/js_reply_handler.h" |
| 30 #include "chrome/browser/sync/js/js_sync_manager_observer.h" |
| 31 #include "chrome/browser/sync/js/js_transaction_observer.h" |
| 32 #include "chrome/browser/sync/notifier/sync_notifier.h" |
| 33 #include "chrome/browser/sync/notifier/sync_notifier_observer.h" |
| 34 #include "chrome/browser/sync/protocol/proto_value_conversions.h" |
| 35 #include "chrome/browser/sync/syncable/directory_change_delegate.h" |
| 36 #include "chrome/browser/sync/syncable/directory_manager.h" |
| 37 #include "chrome/browser/sync/syncable/model_type.h" |
| 38 #include "chrome/browser/sync/syncable/syncable.h" |
| 39 #include "chrome/browser/sync/util/cryptographer.h" |
| 40 #include "chrome/browser/sync/weak_handle.h" |
| 41 #include "net/base/network_change_notifier.h" |
| 42 |
| 43 using std::string; |
| 44 using std::vector; |
| 45 |
| 46 using base::TimeDelta; |
| 47 using browser_sync::AllStatus; |
| 48 using browser_sync::Cryptographer; |
| 49 using browser_sync::JsArgList; |
| 50 using browser_sync::JsBackend; |
| 51 using browser_sync::JsEventDetails; |
| 52 using browser_sync::JsEventHandler; |
| 53 using browser_sync::JsEventHandler; |
| 54 using browser_sync::JsReplyHandler; |
| 55 using browser_sync::JsSyncManagerObserver; |
| 56 using browser_sync::JsTransactionObserver; |
| 57 using browser_sync::ModelSafeWorkerRegistrar; |
| 58 using browser_sync::kNigoriTag; |
| 59 using browser_sync::KeyParams; |
| 60 using browser_sync::ModelSafeRoutingInfo; |
| 61 using browser_sync::ServerConnectionEvent; |
| 62 using browser_sync::ServerConnectionEventListener; |
| 63 using browser_sync::SyncEngineEvent; |
| 64 using browser_sync::SyncEngineEventListener; |
| 65 using browser_sync::SyncScheduler; |
| 66 using browser_sync::Syncer; |
| 67 using browser_sync::WeakHandle; |
| 68 using browser_sync::sessions::SyncSessionContext; |
| 69 using syncable::DirectoryManager; |
| 70 using syncable::EntryKernelMutationSet; |
| 71 using syncable::ModelType; |
| 72 using syncable::ModelTypeBitSet; |
| 73 using syncable::SPECIFICS; |
| 74 |
| 75 typedef GoogleServiceAuthError AuthError; |
| 76 |
| 77 namespace { |
| 78 |
| 79 static const int kSyncSchedulerDelayMsec = 250; |
| 80 |
| 81 #if defined(OS_CHROMEOS) |
| 82 static const int kChromeOSNetworkChangeReactionDelayHackMsec = 5000; |
| 83 #endif // OS_CHROMEOS |
| 84 |
| 85 } // namespace |
| 86 |
| 87 namespace sync_api { |
| 88 |
| 89 SyncManager::ChangeRecord::ChangeRecord() |
| 90 : id(kInvalidId), action(ACTION_ADD) {} |
| 91 |
| 92 SyncManager::ChangeRecord::~ChangeRecord() {} |
| 93 |
| 94 DictionaryValue* SyncManager::ChangeRecord::ToValue( |
| 95 const BaseTransaction* trans) const { |
| 96 DictionaryValue* value = new DictionaryValue(); |
| 97 std::string action_str; |
| 98 switch (action) { |
| 99 case ACTION_ADD: |
| 100 action_str = "Add"; |
| 101 break; |
| 102 case ACTION_DELETE: |
| 103 action_str = "Delete"; |
| 104 break; |
| 105 case ACTION_UPDATE: |
| 106 action_str = "Update"; |
| 107 break; |
| 108 default: |
| 109 NOTREACHED(); |
| 110 action_str = "Unknown"; |
| 111 break; |
| 112 } |
| 113 value->SetString("action", action_str); |
| 114 Value* node_value = NULL; |
| 115 if (action == ACTION_DELETE) { |
| 116 DictionaryValue* node_dict = new DictionaryValue(); |
| 117 node_dict->SetString("id", base::Int64ToString(id)); |
| 118 node_dict->Set("specifics", |
| 119 browser_sync::EntitySpecificsToValue(specifics)); |
| 120 if (extra.get()) { |
| 121 node_dict->Set("extra", extra->ToValue()); |
| 122 } |
| 123 node_value = node_dict; |
| 124 } else { |
| 125 ReadNode node(trans); |
| 126 if (node.InitByIdLookup(id)) { |
| 127 node_value = node.GetDetailsAsValue(); |
| 128 } |
| 129 } |
| 130 if (!node_value) { |
| 131 NOTREACHED(); |
| 132 node_value = Value::CreateNullValue(); |
| 133 } |
| 134 value->Set("node", node_value); |
| 135 return value; |
| 136 } |
| 137 |
| 138 SyncManager::ExtraPasswordChangeRecordData::ExtraPasswordChangeRecordData() {} |
| 139 |
| 140 SyncManager::ExtraPasswordChangeRecordData::ExtraPasswordChangeRecordData( |
| 141 const sync_pb::PasswordSpecificsData& data) |
| 142 : unencrypted_(data) { |
| 143 } |
| 144 |
| 145 SyncManager::ExtraPasswordChangeRecordData::~ExtraPasswordChangeRecordData() {} |
| 146 |
| 147 DictionaryValue* SyncManager::ExtraPasswordChangeRecordData::ToValue() const { |
| 148 return browser_sync::PasswordSpecificsDataToValue(unencrypted_); |
| 149 } |
| 150 |
| 151 const sync_pb::PasswordSpecificsData& |
| 152 SyncManager::ExtraPasswordChangeRecordData::unencrypted() const { |
| 153 return unencrypted_; |
| 154 } |
| 155 |
| 156 ////////////////////////////////////////////////////////////////////////// |
| 157 // SyncManager's implementation: SyncManager::SyncInternal |
| 158 class SyncManager::SyncInternal |
| 159 : public net::NetworkChangeNotifier::IPAddressObserver, |
| 160 public sync_notifier::SyncNotifierObserver, |
| 161 public JsBackend, |
| 162 public SyncEngineEventListener, |
| 163 public ServerConnectionEventListener, |
| 164 public syncable::DirectoryChangeDelegate { |
| 165 static const int kDefaultNudgeDelayMilliseconds; |
| 166 static const int kPreferencesNudgeDelayMilliseconds; |
| 167 public: |
| 168 explicit SyncInternal(const std::string& name) |
| 169 : weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), |
| 170 registrar_(NULL), |
| 171 initialized_(false), |
| 172 setup_for_test_mode_(false), |
| 173 observing_ip_address_changes_(false) { |
| 174 // Pre-fill |notification_info_map_|. |
| 175 for (int i = syncable::FIRST_REAL_MODEL_TYPE; |
| 176 i < syncable::MODEL_TYPE_COUNT; ++i) { |
| 177 notification_info_map_.insert( |
| 178 std::make_pair(syncable::ModelTypeFromInt(i), NotificationInfo())); |
| 179 } |
| 180 |
| 181 // Bind message handlers. |
| 182 BindJsMessageHandler( |
| 183 "getNotificationState", |
| 184 &SyncManager::SyncInternal::GetNotificationState); |
| 185 BindJsMessageHandler( |
| 186 "getNotificationInfo", |
| 187 &SyncManager::SyncInternal::GetNotificationInfo); |
| 188 BindJsMessageHandler( |
| 189 "getRootNodeDetails", |
| 190 &SyncManager::SyncInternal::GetRootNodeDetails); |
| 191 BindJsMessageHandler( |
| 192 "getNodeSummariesById", |
| 193 &SyncManager::SyncInternal::GetNodeSummariesById); |
| 194 BindJsMessageHandler( |
| 195 "getNodeDetailsById", |
| 196 &SyncManager::SyncInternal::GetNodeDetailsById); |
| 197 BindJsMessageHandler( |
| 198 "getChildNodeIds", |
| 199 &SyncManager::SyncInternal::GetChildNodeIds); |
| 200 BindJsMessageHandler( |
| 201 "findNodesContainingString", |
| 202 &SyncManager::SyncInternal::FindNodesContainingString); |
| 203 } |
| 204 |
| 205 virtual ~SyncInternal() { |
| 206 CHECK(!initialized_); |
| 207 } |
| 208 |
| 209 bool Init(const FilePath& database_location, |
| 210 const WeakHandle<JsEventHandler>& event_handler, |
| 211 const std::string& sync_server_and_path, |
| 212 int port, |
| 213 bool use_ssl, |
| 214 HttpPostProviderFactory* post_factory, |
| 215 ModelSafeWorkerRegistrar* model_safe_worker_registrar, |
| 216 const std::string& user_agent, |
| 217 const SyncCredentials& credentials, |
| 218 sync_notifier::SyncNotifier* sync_notifier, |
| 219 const std::string& restored_key_for_bootstrapping, |
| 220 bool setup_for_test_mode); |
| 221 |
| 222 // Sign into sync with given credentials. |
| 223 // We do not verify the tokens given. After this call, the tokens are set |
| 224 // and the sync DB is open. True if successful, false if something |
| 225 // went wrong. |
| 226 bool SignIn(const SyncCredentials& credentials); |
| 227 |
| 228 // Update tokens that we're using in Sync. Email must stay the same. |
| 229 void UpdateCredentials(const SyncCredentials& credentials); |
| 230 |
| 231 // Called when the user disables or enables a sync type. |
| 232 void UpdateEnabledTypes(); |
| 233 |
| 234 // Tell the sync engine to start the syncing process. |
| 235 void StartSyncingNormally(); |
| 236 |
| 237 // Whether or not the Nigori node is encrypted using an explicit passphrase. |
| 238 bool IsUsingExplicitPassphrase(); |
| 239 |
| 240 // Update the Cryptographer from the current nigori node. |
| 241 // Note: opens a transaction and can trigger an ON_PASSPHRASE_REQUIRED, so |
| 242 // should only be called after syncapi is fully initialized. |
| 243 // Returns true if cryptographer is ready, false otherwise. |
| 244 bool UpdateCryptographerFromNigori(); |
| 245 |
| 246 // Set the datatypes we want to encrypt and encrypt any nodes as necessary. |
| 247 // Note: |encrypted_types| will be unioned with the current set of encrypted |
| 248 // types, as we do not currently support decrypting datatypes. |
| 249 void EncryptDataTypes(const syncable::ModelTypeSet& encrypted_types); |
| 250 |
| 251 // Try to set the current passphrase to |passphrase|, and record whether |
| 252 // it is an explicit passphrase or implicitly using gaia in the Nigori |
| 253 // node. |
| 254 void SetPassphrase(const std::string& passphrase, bool is_explicit); |
| 255 |
| 256 // Call periodically from a database-safe thread to persist recent changes |
| 257 // to the syncapi model. |
| 258 void SaveChanges(); |
| 259 |
| 260 // DirectoryChangeDelegate implementation. |
| 261 // This listener is called upon completion of a syncable transaction, and |
| 262 // builds the list of sync-engine initiated changes that will be forwarded to |
| 263 // the SyncManager's Observers. |
| 264 virtual void HandleTransactionCompleteChangeEvent( |
| 265 const ModelTypeBitSet& models_with_changes); |
| 266 virtual ModelTypeBitSet HandleTransactionEndingChangeEvent( |
| 267 syncable::BaseTransaction* trans); |
| 268 virtual void HandleCalculateChangesChangeEventFromSyncApi( |
| 269 const EntryKernelMutationSet& mutations, |
| 270 syncable::BaseTransaction* trans); |
| 271 virtual void HandleCalculateChangesChangeEventFromSyncer( |
| 272 const EntryKernelMutationSet& mutations, |
| 273 syncable::BaseTransaction* trans); |
| 274 |
| 275 // Listens for notifications from the ServerConnectionManager |
| 276 void HandleServerConnectionEvent(const ServerConnectionEvent& event); |
| 277 |
| 278 // Open the directory named with username_for_share |
| 279 bool OpenDirectory(); |
| 280 |
| 281 // SyncNotifierObserver implementation. |
| 282 virtual void OnNotificationStateChange( |
| 283 bool notifications_enabled); |
| 284 |
| 285 virtual void OnIncomingNotification( |
| 286 const syncable::ModelTypePayloadMap& type_payloads); |
| 287 |
| 288 virtual void StoreState(const std::string& cookie); |
| 289 |
| 290 // Thread-safe observers_ accessors. |
| 291 void CopyObservers(ObserverList<SyncManager::Observer>* observers_copy); |
| 292 bool HaveObservers() const; |
| 293 void AddObserver(SyncManager::Observer* observer); |
| 294 void RemoveObserver(SyncManager::Observer* observer); |
| 295 |
| 296 // Accessors for the private members. |
| 297 DirectoryManager* dir_manager() { return share_.dir_manager.get(); } |
| 298 SyncAPIServerConnectionManager* connection_manager() { |
| 299 return connection_manager_.get(); |
| 300 } |
| 301 SyncScheduler* scheduler() { return scheduler_.get(); } |
| 302 UserShare* GetUserShare() { |
| 303 DCHECK(initialized_); |
| 304 return &share_; |
| 305 } |
| 306 |
| 307 // Return the currently active (validated) username for use with syncable |
| 308 // types. |
| 309 const std::string& username_for_share() const { |
| 310 return share_.name; |
| 311 } |
| 312 |
| 313 Status GetStatus(); |
| 314 |
| 315 void RequestNudge(const tracked_objects::Location& nudge_location); |
| 316 |
| 317 void RequestNudgeForDataType( |
| 318 const tracked_objects::Location& nudge_location, |
| 319 const ModelType& type); |
| 320 |
| 321 void RequestEarlyExit(); |
| 322 |
| 323 // See SyncManager::Shutdown for information. |
| 324 void Shutdown(); |
| 325 |
| 326 // If this is a deletion for a password, sets the legacy |
| 327 // ExtraPasswordChangeRecordData field of |buffer|. Otherwise sets |
| 328 // |buffer|'s specifics field to contain the unencrypted data. |
| 329 void SetExtraChangeRecordData(int64 id, |
| 330 syncable::ModelType type, |
| 331 ChangeReorderBuffer* buffer, |
| 332 Cryptographer* cryptographer, |
| 333 const syncable::EntryKernel& original, |
| 334 bool existed_before, |
| 335 bool exists_now); |
| 336 |
| 337 // Called only by our NetworkChangeNotifier. |
| 338 virtual void OnIPAddressChanged(); |
| 339 |
| 340 bool InitialSyncEndedForAllEnabledTypes() { |
| 341 syncable::ModelTypeSet types; |
| 342 ModelSafeRoutingInfo enabled_types; |
| 343 registrar_->GetModelSafeRoutingInfo(&enabled_types); |
| 344 for (ModelSafeRoutingInfo::const_iterator i = enabled_types.begin(); |
| 345 i != enabled_types.end(); ++i) { |
| 346 types.insert(i->first); |
| 347 } |
| 348 |
| 349 return InitialSyncEndedForTypes(types, &share_); |
| 350 } |
| 351 |
| 352 // SyncEngineEventListener implementation. |
| 353 virtual void OnSyncEngineEvent(const SyncEngineEvent& event); |
| 354 |
| 355 // ServerConnectionEventListener implementation. |
| 356 virtual void OnServerConnectionEvent(const ServerConnectionEvent& event); |
| 357 |
| 358 // JsBackend implementation. |
| 359 virtual void SetJsEventHandler( |
| 360 const WeakHandle<JsEventHandler>& event_handler) OVERRIDE; |
| 361 virtual void ProcessJsMessage( |
| 362 const std::string& name, const JsArgList& args, |
| 363 const WeakHandle<JsReplyHandler>& reply_handler) OVERRIDE; |
| 364 |
| 365 private: |
| 366 struct NotificationInfo { |
| 367 int total_count; |
| 368 std::string payload; |
| 369 |
| 370 NotificationInfo() : total_count(0) {} |
| 371 |
| 372 ~NotificationInfo() {} |
| 373 |
| 374 // Returned pointer owned by the caller. |
| 375 DictionaryValue* ToValue() const { |
| 376 DictionaryValue* value = new DictionaryValue(); |
| 377 value->SetInteger("totalCount", total_count); |
| 378 value->SetString("payload", payload); |
| 379 return value; |
| 380 } |
| 381 }; |
| 382 |
| 383 typedef std::map<syncable::ModelType, NotificationInfo> NotificationInfoMap; |
| 384 typedef JsArgList |
| 385 (SyncManager::SyncInternal::*UnboundJsMessageHandler)(const JsArgList&); |
| 386 typedef base::Callback<JsArgList(JsArgList)> JsMessageHandler; |
| 387 typedef std::map<std::string, JsMessageHandler> JsMessageHandlerMap; |
| 388 |
| 389 // Helper to call OnAuthError when no authentication credentials are |
| 390 // available. |
| 391 void RaiseAuthNeededEvent(); |
| 392 |
| 393 // Determine if the parents or predecessors differ between the old and new |
| 394 // versions of an entry stored in |a| and |b|. Note that a node's index may |
| 395 // change without its NEXT_ID changing if the node at NEXT_ID also moved (but |
| 396 // the relative order is unchanged). To handle such cases, we rely on the |
| 397 // caller to treat a position update on any sibling as updating the positions |
| 398 // of all siblings. |
| 399 static bool VisiblePositionsDiffer( |
| 400 const syncable::EntryKernelMutation& mutation) { |
| 401 const syncable::EntryKernel& a = mutation.original; |
| 402 const syncable::EntryKernel& b = mutation.mutated; |
| 403 // If the datatype isn't one where the browser model cares about position, |
| 404 // don't bother notifying that data model of position-only changes. |
| 405 if (!ShouldMaintainPosition( |
| 406 syncable::GetModelTypeFromSpecifics(b.ref(SPECIFICS)))) |
| 407 return false; |
| 408 if (a.ref(syncable::NEXT_ID) != b.ref(syncable::NEXT_ID)) |
| 409 return true; |
| 410 if (a.ref(syncable::PARENT_ID) != b.ref(syncable::PARENT_ID)) |
| 411 return true; |
| 412 return false; |
| 413 } |
| 414 |
| 415 // Determine if any of the fields made visible to clients of the Sync API |
| 416 // differ between the versions of an entry stored in |a| and |b|. A return |
| 417 // value of false means that it should be OK to ignore this change. |
| 418 static bool VisiblePropertiesDiffer( |
| 419 const syncable::EntryKernelMutation& mutation, |
| 420 Cryptographer* cryptographer) { |
| 421 const syncable::EntryKernel& a = mutation.original; |
| 422 const syncable::EntryKernel& b = mutation.mutated; |
| 423 const sync_pb::EntitySpecifics& a_specifics = a.ref(SPECIFICS); |
| 424 const sync_pb::EntitySpecifics& b_specifics = b.ref(SPECIFICS); |
| 425 DCHECK_EQ(syncable::GetModelTypeFromSpecifics(a_specifics), |
| 426 syncable::GetModelTypeFromSpecifics(b_specifics)); |
| 427 syncable::ModelType model_type = |
| 428 syncable::GetModelTypeFromSpecifics(b_specifics); |
| 429 // Suppress updates to items that aren't tracked by any browser model. |
| 430 if (model_type < syncable::FIRST_REAL_MODEL_TYPE || |
| 431 !a.ref(syncable::UNIQUE_SERVER_TAG).empty()) { |
| 432 return false; |
| 433 } |
| 434 if (a.ref(syncable::IS_DIR) != b.ref(syncable::IS_DIR)) |
| 435 return true; |
| 436 if (!AreSpecificsEqual(cryptographer, |
| 437 a.ref(syncable::SPECIFICS), |
| 438 b.ref(syncable::SPECIFICS))) { |
| 439 return true; |
| 440 } |
| 441 // We only care if the name has changed if neither specifics is encrypted |
| 442 // (encrypted nodes blow away the NON_UNIQUE_NAME). |
| 443 if (!a_specifics.has_encrypted() && !b_specifics.has_encrypted() && |
| 444 a.ref(syncable::NON_UNIQUE_NAME) != b.ref(syncable::NON_UNIQUE_NAME)) |
| 445 return true; |
| 446 if (VisiblePositionsDiffer(mutation)) |
| 447 return true; |
| 448 return false; |
| 449 } |
| 450 |
| 451 bool ChangeBuffersAreEmpty() { |
| 452 for (int i = 0; i < syncable::MODEL_TYPE_COUNT; ++i) { |
| 453 if (!change_buffers_[i].IsEmpty()) |
| 454 return false; |
| 455 } |
| 456 return true; |
| 457 } |
| 458 |
| 459 void CheckServerReachable() { |
| 460 if (connection_manager()) { |
| 461 connection_manager()->CheckServerReachable(); |
| 462 } else { |
| 463 NOTREACHED() << "Should be valid connection manager!"; |
| 464 } |
| 465 } |
| 466 |
| 467 void ReEncryptEverything(WriteTransaction* trans); |
| 468 |
| 469 // Initializes (bootstraps) the Cryptographer if NIGORI has finished |
| 470 // initial sync so that it can immediately start encrypting / decrypting. |
| 471 // If the restored key is incompatible with the current version of the NIGORI |
| 472 // node (which could happen if a restart occurred just after an update to |
| 473 // NIGORI was downloaded and the user must enter a new passphrase to decrypt) |
| 474 // then we will raise OnPassphraseRequired and set pending keys for |
| 475 // decryption. Otherwise, the cryptographer is made ready (is_ready()). |
| 476 void BootstrapEncryption(const std::string& restored_key_for_bootstrapping); |
| 477 |
| 478 // Called for every notification. This updates the notification statistics |
| 479 // to be displayed in about:sync. |
| 480 void UpdateNotificationInfo( |
| 481 const syncable::ModelTypePayloadMap& type_payloads); |
| 482 |
| 483 // Checks for server reachabilty and requests a nudge. |
| 484 void OnIPAddressChangedImpl(); |
| 485 |
| 486 // Helper function used only by the constructor. |
| 487 void BindJsMessageHandler( |
| 488 const std::string& name, UnboundJsMessageHandler unbound_message_handler); |
| 489 |
| 490 // Returned pointer is owned by the caller. |
| 491 static DictionaryValue* NotificationInfoToValue( |
| 492 const NotificationInfoMap& notification_info); |
| 493 |
| 494 // JS message handlers. |
| 495 JsArgList GetNotificationState(const JsArgList& args); |
| 496 JsArgList GetNotificationInfo(const JsArgList& args); |
| 497 JsArgList GetRootNodeDetails(const JsArgList& args); |
| 498 JsArgList GetNodeSummariesById(const JsArgList& args); |
| 499 JsArgList GetNodeDetailsById(const JsArgList& args); |
| 500 JsArgList GetChildNodeIds(const JsArgList& args); |
| 501 JsArgList FindNodesContainingString(const JsArgList& args); |
| 502 |
| 503 const std::string name_; |
| 504 |
| 505 base::ThreadChecker thread_checker_; |
| 506 |
| 507 base::WeakPtrFactory<SyncInternal> weak_ptr_factory_; |
| 508 |
| 509 // Thread-safe handle used by |
| 510 // HandleCalculateChangesChangeEventFromSyncApi(), which can be |
| 511 // called from any thread. Valid only between between calls to |
| 512 // Init() and Shutdown(). |
| 513 // |
| 514 // TODO(akalin): Ideally, we wouldn't need to store this; instead, |
| 515 // we'd have another worker class which implements |
| 516 // HandleCalculateChangesChangeEventFromSyncApi() and we'd pass it a |
| 517 // WeakHandle when we construct it. |
| 518 WeakHandle<SyncInternal> weak_handle_this_; |
| 519 |
| 520 // We couple the DirectoryManager and username together in a UserShare member |
| 521 // so we can return a handle to share_ to clients of the API for use when |
| 522 // constructing any transaction type. |
| 523 UserShare share_; |
| 524 |
| 525 // We have to lock around every observers_ access because it can get accessed |
| 526 // from any thread and added to/removed from on the core thread. |
| 527 mutable base::Lock observers_lock_; |
| 528 ObserverList<SyncManager::Observer> observers_; |
| 529 |
| 530 // The ServerConnectionManager used to abstract communication between the |
| 531 // client (the Syncer) and the sync server. |
| 532 scoped_ptr<SyncAPIServerConnectionManager> connection_manager_; |
| 533 |
| 534 // The scheduler that runs the Syncer. Needs to be explicitly |
| 535 // Start()ed. |
| 536 scoped_ptr<SyncScheduler> scheduler_; |
| 537 |
| 538 // The SyncNotifier which notifies us when updates need to be downloaded. |
| 539 scoped_ptr<sync_notifier::SyncNotifier> sync_notifier_; |
| 540 |
| 541 // A multi-purpose status watch object that aggregates stats from various |
| 542 // sync components. |
| 543 AllStatus allstatus_; |
| 544 |
| 545 // Each element of this array is a store of change records produced by |
| 546 // HandleChangeEvent during the CALCULATE_CHANGES step. The changes are |
| 547 // segregated by model type, and are stored here to be processed and |
| 548 // forwarded to the observer slightly later, at the TRANSACTION_ENDING |
| 549 // step by HandleTransactionEndingChangeEvent. The list is cleared in the |
| 550 // TRANSACTION_COMPLETE step by HandleTransactionCompleteChangeEvent. |
| 551 ChangeReorderBuffer change_buffers_[syncable::MODEL_TYPE_COUNT]; |
| 552 |
| 553 // The entity that provides us with information about which types to sync. |
| 554 // The instance is shared between the SyncManager and the Syncer. |
| 555 ModelSafeWorkerRegistrar* registrar_; |
| 556 |
| 557 // Set to true once Init has been called. |
| 558 bool initialized_; |
| 559 |
| 560 // True if the SyncManager should be running in test mode (no sync |
| 561 // scheduler actually communicating with the server). |
| 562 bool setup_for_test_mode_; |
| 563 |
| 564 // Whether we should respond to an IP address change notification. |
| 565 bool observing_ip_address_changes_; |
| 566 |
| 567 // Map used to store the notification info to be displayed in |
| 568 // about:sync page. |
| 569 NotificationInfoMap notification_info_map_; |
| 570 |
| 571 // These are for interacting with chrome://sync-internals. |
| 572 JsMessageHandlerMap js_message_handlers_; |
| 573 WeakHandle<JsEventHandler> js_event_handler_; |
| 574 JsSyncManagerObserver js_sync_manager_observer_; |
| 575 JsTransactionObserver js_transaction_observer_; |
| 576 }; |
| 577 const int SyncManager::SyncInternal::kDefaultNudgeDelayMilliseconds = 200; |
| 578 const int SyncManager::SyncInternal::kPreferencesNudgeDelayMilliseconds = 2000; |
| 579 |
| 580 SyncManager::Observer::~Observer() {} |
| 581 |
| 582 SyncManager::SyncManager(const std::string& name) |
| 583 : data_(new SyncInternal(name)) {} |
| 584 |
| 585 SyncManager::Status::Status() |
| 586 : summary(INVALID), |
| 587 authenticated(false), |
| 588 server_up(false), |
| 589 server_reachable(false), |
| 590 server_broken(false), |
| 591 notifications_enabled(false), |
| 592 notifications_received(0), |
| 593 notifiable_commits(0), |
| 594 max_consecutive_errors(0), |
| 595 unsynced_count(0), |
| 596 conflicting_count(0), |
| 597 syncing(false), |
| 598 initial_sync_ended(false), |
| 599 syncer_stuck(false), |
| 600 updates_available(0), |
| 601 updates_received(0), |
| 602 tombstone_updates_received(0), |
| 603 disk_full(false), |
| 604 num_local_overwrites_total(0), |
| 605 num_server_overwrites_total(0), |
| 606 nonempty_get_updates(0), |
| 607 empty_get_updates(0), |
| 608 useless_sync_cycles(0), |
| 609 useful_sync_cycles(0), |
| 610 cryptographer_ready(false), |
| 611 crypto_has_pending_keys(false) { |
| 612 } |
| 613 |
| 614 SyncManager::Status::~Status() { |
| 615 } |
| 616 |
| 617 bool SyncManager::Init( |
| 618 const FilePath& database_location, |
| 619 const WeakHandle<JsEventHandler>& event_handler, |
| 620 const std::string& sync_server_and_path, |
| 621 int sync_server_port, |
| 622 bool use_ssl, |
| 623 HttpPostProviderFactory* post_factory, |
| 624 ModelSafeWorkerRegistrar* registrar, |
| 625 const std::string& user_agent, |
| 626 const SyncCredentials& credentials, |
| 627 sync_notifier::SyncNotifier* sync_notifier, |
| 628 const std::string& restored_key_for_bootstrapping, |
| 629 bool setup_for_test_mode) { |
| 630 DCHECK(post_factory); |
| 631 VLOG(1) << "SyncManager starting Init..."; |
| 632 string server_string(sync_server_and_path); |
| 633 return data_->Init(database_location, |
| 634 event_handler, |
| 635 server_string, |
| 636 sync_server_port, |
| 637 use_ssl, |
| 638 post_factory, |
| 639 registrar, |
| 640 user_agent, |
| 641 credentials, |
| 642 sync_notifier, |
| 643 restored_key_for_bootstrapping, |
| 644 setup_for_test_mode); |
| 645 } |
| 646 |
| 647 void SyncManager::UpdateCredentials(const SyncCredentials& credentials) { |
| 648 data_->UpdateCredentials(credentials); |
| 649 } |
| 650 |
| 651 void SyncManager::UpdateEnabledTypes() { |
| 652 data_->UpdateEnabledTypes(); |
| 653 } |
| 654 |
| 655 bool SyncManager::InitialSyncEndedForAllEnabledTypes() { |
| 656 return data_->InitialSyncEndedForAllEnabledTypes(); |
| 657 } |
| 658 |
| 659 void SyncManager::StartSyncingNormally() { |
| 660 data_->StartSyncingNormally(); |
| 661 } |
| 662 |
| 663 void SyncManager::SetPassphrase(const std::string& passphrase, |
| 664 bool is_explicit) { |
| 665 data_->SetPassphrase(passphrase, is_explicit); |
| 666 } |
| 667 |
| 668 void SyncManager::EncryptDataTypes( |
| 669 const syncable::ModelTypeSet& encrypted_types) { |
| 670 data_->EncryptDataTypes(encrypted_types); |
| 671 } |
| 672 |
| 673 bool SyncManager::IsUsingExplicitPassphrase() { |
| 674 return data_ && data_->IsUsingExplicitPassphrase(); |
| 675 } |
| 676 |
| 677 void SyncManager::RequestCleanupDisabledTypes() { |
| 678 if (data_->scheduler()) |
| 679 data_->scheduler()->ScheduleCleanupDisabledTypes(); |
| 680 } |
| 681 |
| 682 void SyncManager::RequestClearServerData() { |
| 683 if (data_->scheduler()) |
| 684 data_->scheduler()->ScheduleClearUserData(); |
| 685 } |
| 686 |
| 687 void SyncManager::RequestConfig(const syncable::ModelTypeBitSet& types, |
| 688 ConfigureReason reason) { |
| 689 if (!data_->scheduler()) { |
| 690 LOG(INFO) |
| 691 << "SyncManager::RequestConfig: bailing out because scheduler is " |
| 692 << "null"; |
| 693 return; |
| 694 } |
| 695 StartConfigurationMode(NULL); |
| 696 data_->scheduler()->ScheduleConfig(types, reason); |
| 697 } |
| 698 |
| 699 void SyncManager::StartConfigurationMode(ModeChangeCallback* callback) { |
| 700 if (!data_->scheduler()) { |
| 701 LOG(INFO) |
| 702 << "SyncManager::StartConfigurationMode: could not start " |
| 703 << "configuration mode because because scheduler is null"; |
| 704 return; |
| 705 } |
| 706 data_->scheduler()->Start( |
| 707 browser_sync::SyncScheduler::CONFIGURATION_MODE, callback); |
| 708 } |
| 709 |
| 710 const std::string& SyncManager::GetAuthenticatedUsername() { |
| 711 DCHECK(data_); |
| 712 return data_->username_for_share(); |
| 713 } |
| 714 |
| 715 bool SyncManager::SyncInternal::Init( |
| 716 const FilePath& database_location, |
| 717 const WeakHandle<JsEventHandler>& event_handler, |
| 718 const std::string& sync_server_and_path, |
| 719 int port, |
| 720 bool use_ssl, |
| 721 HttpPostProviderFactory* post_factory, |
| 722 ModelSafeWorkerRegistrar* model_safe_worker_registrar, |
| 723 const std::string& user_agent, |
| 724 const SyncCredentials& credentials, |
| 725 sync_notifier::SyncNotifier* sync_notifier, |
| 726 const std::string& restored_key_for_bootstrapping, |
| 727 bool setup_for_test_mode) { |
| 728 CHECK(!initialized_); |
| 729 |
| 730 DCHECK(thread_checker_.CalledOnValidThread()); |
| 731 |
| 732 VLOG(1) << "Starting SyncInternal initialization."; |
| 733 |
| 734 weak_handle_this_ = MakeWeakHandle(weak_ptr_factory_.GetWeakPtr()); |
| 735 |
| 736 registrar_ = model_safe_worker_registrar; |
| 737 setup_for_test_mode_ = setup_for_test_mode; |
| 738 |
| 739 sync_notifier_.reset(sync_notifier); |
| 740 |
| 741 AddObserver(&js_sync_manager_observer_); |
| 742 SetJsEventHandler(event_handler); |
| 743 |
| 744 share_.dir_manager.reset(new DirectoryManager(database_location)); |
| 745 |
| 746 connection_manager_.reset(new SyncAPIServerConnectionManager( |
| 747 sync_server_and_path, port, use_ssl, user_agent, post_factory)); |
| 748 |
| 749 net::NetworkChangeNotifier::AddIPAddressObserver(this); |
| 750 observing_ip_address_changes_ = true; |
| 751 |
| 752 connection_manager()->AddListener(this); |
| 753 |
| 754 // TODO(akalin): CheckServerReachable() can block, which may cause jank if we |
| 755 // try to shut down sync. Fix this. |
| 756 MessageLoop::current()->PostTask( |
| 757 FROM_HERE, base::Bind(&SyncInternal::CheckServerReachable, |
| 758 weak_ptr_factory_.GetWeakPtr())); |
| 759 |
| 760 // Test mode does not use a syncer context or syncer thread. |
| 761 if (!setup_for_test_mode_) { |
| 762 // Build a SyncSessionContext and store the worker in it. |
| 763 VLOG(1) << "Sync is bringing up SyncSessionContext."; |
| 764 std::vector<SyncEngineEventListener*> listeners; |
| 765 listeners.push_back(&allstatus_); |
| 766 listeners.push_back(this); |
| 767 SyncSessionContext* context = new SyncSessionContext( |
| 768 connection_manager_.get(), |
| 769 dir_manager(), |
| 770 model_safe_worker_registrar, |
| 771 listeners); |
| 772 context->set_account_name(credentials.email); |
| 773 // The SyncScheduler takes ownership of |context|. |
| 774 scheduler_.reset(new SyncScheduler(name_, context, new Syncer())); |
| 775 } |
| 776 |
| 777 bool signed_in = SignIn(credentials); |
| 778 |
| 779 if (signed_in && scheduler()) { |
| 780 scheduler()->Start( |
| 781 browser_sync::SyncScheduler::CONFIGURATION_MODE, NULL); |
| 782 } |
| 783 |
| 784 initialized_ = true; |
| 785 |
| 786 // Notify that initialization is complete. |
| 787 ObserverList<SyncManager::Observer> temp_obs_list; |
| 788 CopyObservers(&temp_obs_list); |
| 789 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list, |
| 790 OnInitializationComplete( |
| 791 WeakHandle<JsBackend>(weak_ptr_factory_.GetWeakPtr()))); |
| 792 |
| 793 // The following calls check that initialized_ is true. |
| 794 |
| 795 BootstrapEncryption(restored_key_for_bootstrapping); |
| 796 |
| 797 sync_notifier_->AddObserver(this); |
| 798 |
| 799 return signed_in; |
| 800 } |
| 801 |
| 802 void SyncManager::SyncInternal::BootstrapEncryption( |
| 803 const std::string& restored_key_for_bootstrapping) { |
| 804 // Cryptographer should only be accessed while holding a transaction. |
| 805 ReadTransaction trans(FROM_HERE, GetUserShare()); |
| 806 Cryptographer* cryptographer = trans.GetCryptographer(); |
| 807 |
| 808 // Set the bootstrap token before bailing out if nigori node is not there. |
| 809 // This could happen if server asked us to migrate nigri. |
| 810 cryptographer->Bootstrap(restored_key_for_bootstrapping); |
| 811 } |
| 812 |
| 813 bool SyncManager::SyncInternal::UpdateCryptographerFromNigori() { |
| 814 DCHECK(initialized_); |
| 815 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share()); |
| 816 if (!lookup.good()) { |
| 817 NOTREACHED() << "BootstrapEncryption: lookup not good so bailing out"; |
| 818 return false; |
| 819 } |
| 820 if (!lookup->initial_sync_ended_for_type(syncable::NIGORI)) |
| 821 return false; // Should only happen during first time sync. |
| 822 |
| 823 ReadTransaction trans(FROM_HERE, GetUserShare()); |
| 824 Cryptographer* cryptographer = trans.GetCryptographer(); |
| 825 |
| 826 ReadNode node(&trans); |
| 827 if (!node.InitByTagLookup(kNigoriTag)) { |
| 828 NOTREACHED(); |
| 829 return false; |
| 830 } |
| 831 Cryptographer::UpdateResult result = |
| 832 cryptographer->Update(node.GetNigoriSpecifics()); |
| 833 if (result == Cryptographer::NEEDS_PASSPHRASE) { |
| 834 ObserverList<SyncManager::Observer> temp_obs_list; |
| 835 CopyObservers(&temp_obs_list); |
| 836 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list, |
| 837 OnPassphraseRequired(sync_api::REASON_DECRYPTION)); |
| 838 } |
| 839 |
| 840 allstatus_.SetCryptographerReady(cryptographer->is_ready()); |
| 841 allstatus_.SetCryptoHasPendingKeys(cryptographer->has_pending_keys()); |
| 842 |
| 843 return cryptographer->is_ready(); |
| 844 } |
| 845 |
| 846 void SyncManager::SyncInternal::StartSyncingNormally() { |
| 847 // Start the sync scheduler. This won't actually result in any |
| 848 // syncing until at least the DirectoryManager broadcasts the OPENED |
| 849 // event, and a valid server connection is detected. |
| 850 if (scheduler()) // NULL during certain unittests. |
| 851 scheduler()->Start(SyncScheduler::NORMAL_MODE, NULL); |
| 852 } |
| 853 |
| 854 bool SyncManager::SyncInternal::OpenDirectory() { |
| 855 DCHECK(!initialized_) << "Should only happen once"; |
| 856 |
| 857 bool share_opened = dir_manager()->Open(username_for_share(), this); |
| 858 DCHECK(share_opened); |
| 859 if (!share_opened) { |
| 860 ObserverList<SyncManager::Observer> temp_obs_list; |
| 861 CopyObservers(&temp_obs_list); |
| 862 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list, |
| 863 OnStopSyncingPermanently()); |
| 864 |
| 865 LOG(ERROR) << "Could not open share for:" << username_for_share(); |
| 866 return false; |
| 867 } |
| 868 |
| 869 // Database has to be initialized for the guid to be available. |
| 870 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share()); |
| 871 if (!lookup.good()) { |
| 872 NOTREACHED(); |
| 873 return false; |
| 874 } |
| 875 |
| 876 connection_manager()->set_client_id(lookup->cache_guid()); |
| 877 lookup->AddTransactionObserver(&js_transaction_observer_); |
| 878 return true; |
| 879 } |
| 880 |
| 881 bool SyncManager::SyncInternal::SignIn(const SyncCredentials& credentials) { |
| 882 DCHECK(thread_checker_.CalledOnValidThread()); |
| 883 DCHECK(share_.name.empty()); |
| 884 share_.name = credentials.email; |
| 885 |
| 886 VLOG(1) << "Signing in user: " << username_for_share(); |
| 887 if (!OpenDirectory()) |
| 888 return false; |
| 889 |
| 890 // Retrieve and set the sync notifier state. This should be done |
| 891 // only after OpenDirectory is called. |
| 892 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share()); |
| 893 std::string unique_id; |
| 894 std::string state; |
| 895 if (lookup.good()) { |
| 896 unique_id = lookup->cache_guid(); |
| 897 state = lookup->GetNotificationState(); |
| 898 VLOG(1) << "Read notification unique ID: " << unique_id; |
| 899 if (VLOG_IS_ON(1)) { |
| 900 std::string encoded_state; |
| 901 base::Base64Encode(state, &encoded_state); |
| 902 VLOG(1) << "Read notification state: " << encoded_state; |
| 903 } |
| 904 } else { |
| 905 LOG(ERROR) << "Could not read notification unique ID/state"; |
| 906 } |
| 907 sync_notifier_->SetUniqueId(unique_id); |
| 908 sync_notifier_->SetState(state); |
| 909 |
| 910 UpdateCredentials(credentials); |
| 911 UpdateEnabledTypes(); |
| 912 return true; |
| 913 } |
| 914 |
| 915 void SyncManager::SyncInternal::UpdateCredentials( |
| 916 const SyncCredentials& credentials) { |
| 917 DCHECK(thread_checker_.CalledOnValidThread()); |
| 918 DCHECK_EQ(credentials.email, share_.name); |
| 919 DCHECK(!credentials.email.empty()); |
| 920 DCHECK(!credentials.sync_token.empty()); |
| 921 |
| 922 observing_ip_address_changes_ = true; |
| 923 if (connection_manager()->set_auth_token(credentials.sync_token)) { |
| 924 sync_notifier_->UpdateCredentials( |
| 925 credentials.email, credentials.sync_token); |
| 926 if (!setup_for_test_mode_) { |
| 927 CheckServerReachable(); |
| 928 } |
| 929 } |
| 930 } |
| 931 |
| 932 void SyncManager::SyncInternal::UpdateEnabledTypes() { |
| 933 DCHECK(thread_checker_.CalledOnValidThread()); |
| 934 ModelSafeRoutingInfo routes; |
| 935 registrar_->GetModelSafeRoutingInfo(&routes); |
| 936 syncable::ModelTypeSet enabled_types; |
| 937 for (ModelSafeRoutingInfo::const_iterator it = routes.begin(); |
| 938 it != routes.end(); ++it) { |
| 939 enabled_types.insert(it->first); |
| 940 } |
| 941 sync_notifier_->UpdateEnabledTypes(enabled_types); |
| 942 } |
| 943 |
| 944 void SyncManager::SyncInternal::RaiseAuthNeededEvent() { |
| 945 ObserverList<SyncManager::Observer> temp_obs_list; |
| 946 CopyObservers(&temp_obs_list); |
| 947 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list, |
| 948 OnAuthError(AuthError(AuthError::INVALID_GAIA_CREDENTIALS))); |
| 949 } |
| 950 |
| 951 void SyncManager::SyncInternal::SetPassphrase( |
| 952 const std::string& passphrase, bool is_explicit) { |
| 953 // We do not accept empty passphrases. |
| 954 if (passphrase.empty()) { |
| 955 VLOG(1) << "Rejecting empty passphrase."; |
| 956 ObserverList<SyncManager::Observer> temp_obs_list; |
| 957 CopyObservers(&temp_obs_list); |
| 958 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list, |
| 959 OnPassphraseRequired(sync_api::REASON_SET_PASSPHRASE_FAILED)); |
| 960 return; |
| 961 } |
| 962 |
| 963 // All accesses to the cryptographer are protected by a transaction. |
| 964 WriteTransaction trans(FROM_HERE, GetUserShare()); |
| 965 Cryptographer* cryptographer = trans.GetCryptographer(); |
| 966 KeyParams params = {"localhost", "dummy", passphrase}; |
| 967 |
| 968 WriteNode node(&trans); |
| 969 if (!node.InitByTagLookup(kNigoriTag)) { |
| 970 // TODO(albertb): Plumb an UnrecoverableError all the way back to the PSS. |
| 971 NOTREACHED(); |
| 972 return; |
| 973 } |
| 974 |
| 975 if (cryptographer->has_pending_keys()) { |
| 976 bool suceeded = false; |
| 977 |
| 978 // See if the explicit flag matches what is set in nigori. If not we dont |
| 979 // even try the passphrase. Note: This could mean that we wont try setting |
| 980 // the gaia password as passphrase if custom is elected by the user. Which |
| 981 // is fine because nigori node has all the old passwords in it. |
| 982 if (node.GetNigoriSpecifics().using_explicit_passphrase() == is_explicit) { |
| 983 if (cryptographer->DecryptPendingKeys(params)) { |
| 984 suceeded = true; |
| 985 } else { |
| 986 VLOG(1) << "Passphrase failed to decrypt pending keys."; |
| 987 } |
| 988 } else { |
| 989 VLOG(1) << "Not trying the passphrase because the explicit flags dont " |
| 990 << "match. Nigori node's explicit flag is " |
| 991 << node.GetNigoriSpecifics().using_explicit_passphrase(); |
| 992 } |
| 993 |
| 994 if (!suceeded) { |
| 995 ObserverList<SyncManager::Observer> temp_obs_list; |
| 996 CopyObservers(&temp_obs_list); |
| 997 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list, |
| 998 OnPassphraseRequired(sync_api::REASON_SET_PASSPHRASE_FAILED)); |
| 999 return; |
| 1000 } |
| 1001 |
| 1002 // Nudge the syncer so that encrypted datatype updates that were waiting for |
| 1003 // this passphrase get applied as soon as possible. |
| 1004 RequestNudge(FROM_HERE); |
| 1005 } else { |
| 1006 VLOG(1) << "No pending keys, adding provided passphrase."; |
| 1007 |
| 1008 // Prevent an implicit SetPassphrase request from changing an explicitly |
| 1009 // set passphrase. |
| 1010 if (!is_explicit && node.GetNigoriSpecifics().using_explicit_passphrase()) |
| 1011 return; |
| 1012 |
| 1013 cryptographer->AddKey(params); |
| 1014 |
| 1015 // TODO(tim): Bug 58231. It would be nice if SetPassphrase didn't require |
| 1016 // messing with the Nigori node, because we can't call SetPassphrase until |
| 1017 // download conditions are met vs Cryptographer init. It seems like it's |
| 1018 // safe to defer this work. |
| 1019 sync_pb::NigoriSpecifics specifics(node.GetNigoriSpecifics()); |
| 1020 specifics.clear_encrypted(); |
| 1021 cryptographer->GetKeys(specifics.mutable_encrypted()); |
| 1022 specifics.set_using_explicit_passphrase(is_explicit); |
| 1023 node.SetNigoriSpecifics(specifics); |
| 1024 ReEncryptEverything(&trans); |
| 1025 } |
| 1026 |
| 1027 VLOG(1) << "Passphrase accepted, bootstrapping encryption."; |
| 1028 std::string bootstrap_token; |
| 1029 cryptographer->GetBootstrapToken(&bootstrap_token); |
| 1030 ObserverList<SyncManager::Observer> temp_obs_list; |
| 1031 CopyObservers(&temp_obs_list); |
| 1032 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list, |
| 1033 OnPassphraseAccepted(bootstrap_token)); |
| 1034 } |
| 1035 |
| 1036 bool SyncManager::SyncInternal::IsUsingExplicitPassphrase() { |
| 1037 ReadTransaction trans(FROM_HERE, &share_); |
| 1038 ReadNode node(&trans); |
| 1039 if (!node.InitByTagLookup(kNigoriTag)) { |
| 1040 // TODO(albertb): Plumb an UnrecoverableError all the way back to the PSS. |
| 1041 NOTREACHED(); |
| 1042 return false; |
| 1043 } |
| 1044 |
| 1045 return node.GetNigoriSpecifics().using_explicit_passphrase(); |
| 1046 } |
| 1047 |
| 1048 void SyncManager::SyncInternal::EncryptDataTypes( |
| 1049 const syncable::ModelTypeSet& encrypted_types) { |
| 1050 DCHECK(initialized_); |
| 1051 VLOG(1) << "Attempting to encrypt datatypes " |
| 1052 << syncable::ModelTypeSetToString(encrypted_types); |
| 1053 |
| 1054 WriteTransaction trans(FROM_HERE, GetUserShare()); |
| 1055 WriteNode node(&trans); |
| 1056 if (!node.InitByTagLookup(kNigoriTag)) { |
| 1057 NOTREACHED() << "Unable to set encrypted datatypes because Nigori node not " |
| 1058 << "found."; |
| 1059 return; |
| 1060 } |
| 1061 |
| 1062 Cryptographer* cryptographer = trans.GetCryptographer(); |
| 1063 |
| 1064 if (!cryptographer->is_initialized()) { |
| 1065 VLOG(1) << "Attempting to encrypt datatypes when cryptographer not " |
| 1066 << "initialized, prompting for passphrase."; |
| 1067 ObserverList<SyncManager::Observer> temp_obs_list; |
| 1068 CopyObservers(&temp_obs_list); |
| 1069 // TODO(zea): this isn't really decryption, but that's the only way we have |
| 1070 // to prompt the user for a passsphrase. See http://crbug.com/91379. |
| 1071 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list, |
| 1072 OnPassphraseRequired(sync_api::REASON_DECRYPTION)); |
| 1073 return; |
| 1074 } |
| 1075 |
| 1076 // Update the Nigori node's set of encrypted datatypes. |
| 1077 // Note, we merge the current encrypted types with those requested. Once a |
| 1078 // datatypes is marked as needing encryption, it is never unmarked. |
| 1079 sync_pb::NigoriSpecifics nigori; |
| 1080 nigori.CopyFrom(node.GetNigoriSpecifics()); |
| 1081 syncable::ModelTypeSet current_encrypted_types = GetEncryptedTypes(&trans); |
| 1082 syncable::ModelTypeSet newly_encrypted_types; |
| 1083 std::set_union(current_encrypted_types.begin(), current_encrypted_types.end(), |
| 1084 encrypted_types.begin(), encrypted_types.end(), |
| 1085 std::inserter(newly_encrypted_types, |
| 1086 newly_encrypted_types.begin())); |
| 1087 allstatus_.SetEncryptedTypes(newly_encrypted_types); |
| 1088 if (newly_encrypted_types == current_encrypted_types) { |
| 1089 // Set of encrypted types has not changed, just notify and return. |
| 1090 ObserverList<SyncManager::Observer> temp_obs_list; |
| 1091 CopyObservers(&temp_obs_list); |
| 1092 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list, |
| 1093 OnEncryptionComplete(current_encrypted_types)); |
| 1094 return; |
| 1095 } |
| 1096 syncable::FillNigoriEncryptedTypes(newly_encrypted_types, &nigori); |
| 1097 node.SetNigoriSpecifics(nigori); |
| 1098 |
| 1099 cryptographer->SetEncryptedTypes(nigori); |
| 1100 |
| 1101 // TODO(zea): only reencrypt this datatype? ReEncrypting everything is a |
| 1102 // safer approach, and should not impact anything that is already encrypted |
| 1103 // (redundant changes are ignored). |
| 1104 ReEncryptEverything(&trans); |
| 1105 return; |
| 1106 } |
| 1107 |
| 1108 // TODO(zea): Add unit tests that ensure no sync changes are made when not |
| 1109 // needed. |
| 1110 void SyncManager::SyncInternal::ReEncryptEverything(WriteTransaction* trans) { |
| 1111 syncable::ModelTypeSet encrypted_types = |
| 1112 GetEncryptedTypes(trans); |
| 1113 ModelSafeRoutingInfo routes; |
| 1114 registrar_->GetModelSafeRoutingInfo(&routes); |
| 1115 std::string tag; |
| 1116 for (syncable::ModelTypeSet::iterator iter = encrypted_types.begin(); |
| 1117 iter != encrypted_types.end(); ++iter) { |
| 1118 if (*iter == syncable::PASSWORDS || routes.count(*iter) == 0) |
| 1119 continue; |
| 1120 ReadNode type_root(trans); |
| 1121 tag = syncable::ModelTypeToRootTag(*iter); |
| 1122 if (!type_root.InitByTagLookup(tag)) { |
| 1123 NOTREACHED(); |
| 1124 return; |
| 1125 } |
| 1126 |
| 1127 // Iterate through all children of this datatype. |
| 1128 std::queue<int64> to_visit; |
| 1129 int64 child_id = type_root.GetFirstChildId(); |
| 1130 to_visit.push(child_id); |
| 1131 while (!to_visit.empty()) { |
| 1132 child_id = to_visit.front(); |
| 1133 to_visit.pop(); |
| 1134 if (child_id == kInvalidId) |
| 1135 continue; |
| 1136 |
| 1137 WriteNode child(trans); |
| 1138 if (!child.InitByIdLookup(child_id)) { |
| 1139 NOTREACHED(); |
| 1140 continue; |
| 1141 } |
| 1142 if (child.GetIsFolder()) { |
| 1143 to_visit.push(child.GetFirstChildId()); |
| 1144 } |
| 1145 if (child.GetEntry()->Get(syncable::UNIQUE_SERVER_TAG).empty()) { |
| 1146 // Rewrite the specifics of the node with encrypted data if necessary |
| 1147 // (only rewrite the non-unique folders). |
| 1148 child.ResetFromSpecifics(); |
| 1149 } |
| 1150 to_visit.push(child.GetSuccessorId()); |
| 1151 } |
| 1152 } |
| 1153 |
| 1154 if (routes.count(syncable::PASSWORDS) > 0) { |
| 1155 // Passwords are encrypted with their own legacy scheme. |
| 1156 ReadNode passwords_root(trans); |
| 1157 std::string passwords_tag = |
| 1158 syncable::ModelTypeToRootTag(syncable::PASSWORDS); |
| 1159 // It's possible we'll have the password routing info and not the password |
| 1160 // root if we attempted to SetPassphrase before passwords was enabled. |
| 1161 if (passwords_root.InitByTagLookup(passwords_tag)) { |
| 1162 int64 child_id = passwords_root.GetFirstChildId(); |
| 1163 while (child_id != kInvalidId) { |
| 1164 WriteNode child(trans); |
| 1165 if (!child.InitByIdLookup(child_id)) { |
| 1166 NOTREACHED(); |
| 1167 return; |
| 1168 } |
| 1169 child.SetPasswordSpecifics(child.GetPasswordSpecifics()); |
| 1170 child_id = child.GetSuccessorId(); |
| 1171 } |
| 1172 } |
| 1173 } |
| 1174 |
| 1175 ObserverList<SyncManager::Observer> temp_obs_list; |
| 1176 CopyObservers(&temp_obs_list); |
| 1177 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list, |
| 1178 OnEncryptionComplete(encrypted_types)); |
| 1179 } |
| 1180 |
| 1181 SyncManager::~SyncManager() { |
| 1182 delete data_; |
| 1183 } |
| 1184 |
| 1185 void SyncManager::AddObserver(Observer* observer) { |
| 1186 data_->AddObserver(observer); |
| 1187 } |
| 1188 |
| 1189 void SyncManager::RemoveObserver(Observer* observer) { |
| 1190 data_->RemoveObserver(observer); |
| 1191 } |
| 1192 |
| 1193 void SyncManager::RequestEarlyExit() { |
| 1194 data_->RequestEarlyExit(); |
| 1195 } |
| 1196 |
| 1197 void SyncManager::SyncInternal::RequestEarlyExit() { |
| 1198 if (scheduler()) { |
| 1199 scheduler()->RequestEarlyExit(); |
| 1200 } |
| 1201 } |
| 1202 |
| 1203 void SyncManager::Shutdown() { |
| 1204 data_->Shutdown(); |
| 1205 } |
| 1206 |
| 1207 void SyncManager::SyncInternal::Shutdown() { |
| 1208 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1209 |
| 1210 // Prevent any in-flight method calls from running. Also |
| 1211 // invalidates |weak_handle_this_|. |
| 1212 weak_ptr_factory_.InvalidateWeakPtrs(); |
| 1213 |
| 1214 // Automatically stops the scheduler. |
| 1215 scheduler_.reset(); |
| 1216 |
| 1217 SetJsEventHandler(WeakHandle<JsEventHandler>()); |
| 1218 RemoveObserver(&js_sync_manager_observer_); |
| 1219 |
| 1220 if (sync_notifier_.get()) { |
| 1221 sync_notifier_->RemoveObserver(this); |
| 1222 } |
| 1223 sync_notifier_.reset(); |
| 1224 |
| 1225 if (connection_manager_.get()) { |
| 1226 connection_manager_->RemoveListener(this); |
| 1227 } |
| 1228 connection_manager_.reset(); |
| 1229 |
| 1230 net::NetworkChangeNotifier::RemoveIPAddressObserver(this); |
| 1231 observing_ip_address_changes_ = false; |
| 1232 |
| 1233 if (dir_manager()) { |
| 1234 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share()); |
| 1235 if (lookup.good()) { |
| 1236 lookup->RemoveTransactionObserver(&js_transaction_observer_); |
| 1237 } else { |
| 1238 NOTREACHED(); |
| 1239 } |
| 1240 dir_manager()->FinalSaveChangesForAll(); |
| 1241 dir_manager()->Close(username_for_share()); |
| 1242 } |
| 1243 |
| 1244 // Reset the DirectoryManager and UserSettings so they relinquish sqlite |
| 1245 // handles to backing files. |
| 1246 share_.dir_manager.reset(); |
| 1247 |
| 1248 setup_for_test_mode_ = false; |
| 1249 registrar_ = NULL; |
| 1250 |
| 1251 initialized_ = false; |
| 1252 |
| 1253 // We reset this here, since only now we know it will not be |
| 1254 // accessed from other threads (since we shut down everything). |
| 1255 weak_handle_this_.Reset(); |
| 1256 } |
| 1257 |
| 1258 void SyncManager::SyncInternal::OnIPAddressChanged() { |
| 1259 VLOG(1) << "IP address change detected"; |
| 1260 if (!observing_ip_address_changes_) { |
| 1261 VLOG(1) << "IP address change dropped."; |
| 1262 return; |
| 1263 } |
| 1264 |
| 1265 #if defined (OS_CHROMEOS) |
| 1266 // TODO(tim): This is a hack to intentionally lose a race with flimflam at |
| 1267 // shutdown, so we don't cause shutdown to wait for our http request. |
| 1268 // http://crosbug.com/8429 |
| 1269 MessageLoop::current()->PostDelayedTask( |
| 1270 FROM_HERE, |
| 1271 base::Bind(&SyncInternal::OnIPAddressChangedImpl, |
| 1272 weak_ptr_factory_.GetWeakPtr()), |
| 1273 kChromeOSNetworkChangeReactionDelayHackMsec); |
| 1274 #else |
| 1275 OnIPAddressChangedImpl(); |
| 1276 #endif // defined(OS_CHROMEOS) |
| 1277 } |
| 1278 |
| 1279 void SyncManager::SyncInternal::OnIPAddressChangedImpl() { |
| 1280 // TODO(akalin): CheckServerReachable() can block, which may cause |
| 1281 // jank if we try to shut down sync. Fix this. |
| 1282 connection_manager()->CheckServerReachable(); |
| 1283 } |
| 1284 |
| 1285 void SyncManager::SyncInternal::OnServerConnectionEvent( |
| 1286 const ServerConnectionEvent& event) { |
| 1287 allstatus_.HandleServerConnectionEvent(event); |
| 1288 if (event.connection_code == |
| 1289 browser_sync::HttpResponse::SERVER_CONNECTION_OK) { |
| 1290 ObserverList<SyncManager::Observer> temp_obs_list; |
| 1291 CopyObservers(&temp_obs_list); |
| 1292 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list, |
| 1293 OnAuthError(AuthError::None())); |
| 1294 } |
| 1295 |
| 1296 if (event.connection_code == browser_sync::HttpResponse::SYNC_AUTH_ERROR) { |
| 1297 observing_ip_address_changes_ = false; |
| 1298 ObserverList<SyncManager::Observer> temp_obs_list; |
| 1299 CopyObservers(&temp_obs_list); |
| 1300 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list, |
| 1301 OnAuthError(AuthError(AuthError::INVALID_GAIA_CREDENTIALS))); |
| 1302 } |
| 1303 |
| 1304 if (event.connection_code == |
| 1305 browser_sync::HttpResponse::SYNC_SERVER_ERROR) { |
| 1306 ObserverList<SyncManager::Observer> temp_obs_list; |
| 1307 CopyObservers(&temp_obs_list); |
| 1308 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list, |
| 1309 OnAuthError(AuthError(AuthError::CONNECTION_FAILED))); |
| 1310 } |
| 1311 } |
| 1312 |
| 1313 void SyncManager::SyncInternal::HandleTransactionCompleteChangeEvent( |
| 1314 const syncable::ModelTypeBitSet& models_with_changes) { |
| 1315 // This notification happens immediately after the transaction mutex is |
| 1316 // released. This allows work to be performed without blocking other threads |
| 1317 // from acquiring a transaction. |
| 1318 if (!HaveObservers()) |
| 1319 return; |
| 1320 |
| 1321 // Call commit. |
| 1322 for (int i = 0; i < syncable::MODEL_TYPE_COUNT; ++i) { |
| 1323 if (models_with_changes.test(i)) { |
| 1324 ObserverList<SyncManager::Observer> temp_obs_list; |
| 1325 CopyObservers(&temp_obs_list); |
| 1326 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list, |
| 1327 OnChangesComplete(syncable::ModelTypeFromInt(i))); |
| 1328 } |
| 1329 } |
| 1330 } |
| 1331 |
| 1332 ModelTypeBitSet SyncManager::SyncInternal::HandleTransactionEndingChangeEvent( |
| 1333 syncable::BaseTransaction* trans) { |
| 1334 // This notification happens immediately before a syncable WriteTransaction |
| 1335 // falls out of scope. It happens while the channel mutex is still held, |
| 1336 // and while the transaction mutex is held, so it cannot be re-entrant. |
| 1337 if (!HaveObservers() || ChangeBuffersAreEmpty()) |
| 1338 return ModelTypeBitSet(); |
| 1339 |
| 1340 // This will continue the WriteTransaction using a read only wrapper. |
| 1341 // This is the last chance for read to occur in the WriteTransaction |
| 1342 // that's closing. This special ReadTransaction will not close the |
| 1343 // underlying transaction. |
| 1344 ReadTransaction read_trans(GetUserShare(), trans); |
| 1345 |
| 1346 syncable::ModelTypeBitSet models_with_changes; |
| 1347 for (int i = 0; i < syncable::MODEL_TYPE_COUNT; ++i) { |
| 1348 if (change_buffers_[i].IsEmpty()) |
| 1349 continue; |
| 1350 |
| 1351 vector<ChangeRecord> ordered_changes; |
| 1352 change_buffers_[i].GetAllChangesInTreeOrder(&read_trans, &ordered_changes); |
| 1353 if (!ordered_changes.empty()) { |
| 1354 ObserverList<SyncManager::Observer> temp_obs_list; |
| 1355 CopyObservers(&temp_obs_list); |
| 1356 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list, |
| 1357 OnChangesApplied(syncable::ModelTypeFromInt(i), &read_trans, |
| 1358 &ordered_changes[0], ordered_changes.size())); |
| 1359 models_with_changes.set(i, true); |
| 1360 } |
| 1361 change_buffers_[i].Clear(); |
| 1362 } |
| 1363 return models_with_changes; |
| 1364 } |
| 1365 |
| 1366 void SyncManager::SyncInternal::HandleCalculateChangesChangeEventFromSyncApi( |
| 1367 const EntryKernelMutationSet& mutations, |
| 1368 syncable::BaseTransaction* trans) { |
| 1369 if (!scheduler()) { |
| 1370 return; |
| 1371 } |
| 1372 |
| 1373 // We have been notified about a user action changing a sync model. |
| 1374 LOG_IF(WARNING, !ChangeBuffersAreEmpty()) << |
| 1375 "CALCULATE_CHANGES called with unapplied old changes."; |
| 1376 |
| 1377 // The mutated model type, or UNSPECIFIED if nothing was mutated. |
| 1378 syncable::ModelType mutated_model_type = syncable::UNSPECIFIED; |
| 1379 |
| 1380 // Find the first real mutation. We assume that only a single model |
| 1381 // type is mutated per transaction. |
| 1382 for (syncable::EntryKernelMutationSet::const_iterator it = |
| 1383 mutations.begin(); it != mutations.end(); ++it) { |
| 1384 if (!it->mutated.ref(syncable::IS_UNSYNCED)) { |
| 1385 continue; |
| 1386 } |
| 1387 |
| 1388 syncable::ModelType model_type = |
| 1389 syncable::GetModelTypeFromSpecifics(it->mutated.ref(SPECIFICS)); |
| 1390 if (model_type < syncable::FIRST_REAL_MODEL_TYPE) { |
| 1391 NOTREACHED() << "Permanent or underspecified item changed via syncapi."; |
| 1392 continue; |
| 1393 } |
| 1394 |
| 1395 // Found real mutation. |
| 1396 if (mutated_model_type == syncable::UNSPECIFIED) { |
| 1397 mutated_model_type = model_type; |
| 1398 break; |
| 1399 } |
| 1400 } |
| 1401 |
| 1402 // Nudge if necessary. |
| 1403 if (mutated_model_type != syncable::UNSPECIFIED) { |
| 1404 if (weak_handle_this_.IsInitialized()) { |
| 1405 weak_handle_this_.Call(FROM_HERE, |
| 1406 &SyncInternal::RequestNudgeForDataType, |
| 1407 FROM_HERE, |
| 1408 mutated_model_type); |
| 1409 } else { |
| 1410 NOTREACHED(); |
| 1411 } |
| 1412 } |
| 1413 } |
| 1414 |
| 1415 void SyncManager::SyncInternal::SetExtraChangeRecordData(int64 id, |
| 1416 syncable::ModelType type, ChangeReorderBuffer* buffer, |
| 1417 Cryptographer* cryptographer, const syncable::EntryKernel& original, |
| 1418 bool existed_before, bool exists_now) { |
| 1419 // If this is a deletion and the datatype was encrypted, we need to decrypt it |
| 1420 // and attach it to the buffer. |
| 1421 if (!exists_now && existed_before) { |
| 1422 sync_pb::EntitySpecifics original_specifics(original.ref(SPECIFICS)); |
| 1423 if (type == syncable::PASSWORDS) { |
| 1424 // Passwords must use their own legacy ExtraPasswordChangeRecordData. |
| 1425 scoped_ptr<sync_pb::PasswordSpecificsData> data( |
| 1426 DecryptPasswordSpecifics(original_specifics, cryptographer)); |
| 1427 if (!data.get()) { |
| 1428 NOTREACHED(); |
| 1429 return; |
| 1430 } |
| 1431 buffer->SetExtraDataForId(id, new ExtraPasswordChangeRecordData(*data)); |
| 1432 } else if (original_specifics.has_encrypted()) { |
| 1433 // All other datatypes can just create a new unencrypted specifics and |
| 1434 // attach it. |
| 1435 const sync_pb::EncryptedData& encrypted = original_specifics.encrypted(); |
| 1436 if (!cryptographer->Decrypt(encrypted, &original_specifics)) { |
| 1437 NOTREACHED(); |
| 1438 return; |
| 1439 } |
| 1440 } |
| 1441 buffer->SetSpecificsForId(id, original_specifics); |
| 1442 } |
| 1443 } |
| 1444 |
| 1445 void SyncManager::SyncInternal::HandleCalculateChangesChangeEventFromSyncer( |
| 1446 const EntryKernelMutationSet& mutations, |
| 1447 syncable::BaseTransaction* trans) { |
| 1448 // We only expect one notification per sync step, so change_buffers_ should |
| 1449 // contain no pending entries. |
| 1450 LOG_IF(WARNING, !ChangeBuffersAreEmpty()) << |
| 1451 "CALCULATE_CHANGES called with unapplied old changes."; |
| 1452 |
| 1453 Cryptographer* crypto = dir_manager()->GetCryptographer(trans); |
| 1454 for (syncable::EntryKernelMutationSet::const_iterator it = |
| 1455 mutations.begin(); it != mutations.end(); ++it) { |
| 1456 bool existed_before = !it->original.ref(syncable::IS_DEL); |
| 1457 bool exists_now = !it->mutated.ref(syncable::IS_DEL); |
| 1458 |
| 1459 // Omit items that aren't associated with a model. |
| 1460 syncable::ModelType type = |
| 1461 syncable::GetModelTypeFromSpecifics(it->mutated.ref(SPECIFICS)); |
| 1462 if (type < syncable::FIRST_REAL_MODEL_TYPE) |
| 1463 continue; |
| 1464 |
| 1465 int64 id = it->original.ref(syncable::META_HANDLE); |
| 1466 if (exists_now && !existed_before) |
| 1467 change_buffers_[type].PushAddedItem(id); |
| 1468 else if (!exists_now && existed_before) |
| 1469 change_buffers_[type].PushDeletedItem(id); |
| 1470 else if (exists_now && existed_before && |
| 1471 VisiblePropertiesDiffer(*it, crypto)) { |
| 1472 change_buffers_[type].PushUpdatedItem( |
| 1473 id, VisiblePositionsDiffer(*it)); |
| 1474 } |
| 1475 |
| 1476 SetExtraChangeRecordData(id, type, &change_buffers_[type], crypto, |
| 1477 it->original, existed_before, exists_now); |
| 1478 } |
| 1479 } |
| 1480 |
| 1481 SyncManager::Status SyncManager::SyncInternal::GetStatus() { |
| 1482 return allstatus_.status(); |
| 1483 } |
| 1484 |
| 1485 void SyncManager::SyncInternal::RequestNudge( |
| 1486 const tracked_objects::Location& location) { |
| 1487 if (scheduler()) |
| 1488 scheduler()->ScheduleNudge( |
| 1489 TimeDelta::FromMilliseconds(0), browser_sync::NUDGE_SOURCE_LOCAL, |
| 1490 ModelTypeBitSet(), location); |
| 1491 } |
| 1492 |
| 1493 void SyncManager::SyncInternal::RequestNudgeForDataType( |
| 1494 const tracked_objects::Location& nudge_location, |
| 1495 const ModelType& type) { |
| 1496 if (!scheduler()) { |
| 1497 NOTREACHED(); |
| 1498 return; |
| 1499 } |
| 1500 base::TimeDelta nudge_delay; |
| 1501 switch (type) { |
| 1502 case syncable::PREFERENCES: |
| 1503 nudge_delay = |
| 1504 TimeDelta::FromMilliseconds(kPreferencesNudgeDelayMilliseconds); |
| 1505 break; |
| 1506 case syncable::SESSIONS: |
| 1507 nudge_delay = scheduler()->sessions_commit_delay(); |
| 1508 break; |
| 1509 default: |
| 1510 nudge_delay = |
| 1511 TimeDelta::FromMilliseconds(kDefaultNudgeDelayMilliseconds); |
| 1512 break; |
| 1513 } |
| 1514 syncable::ModelTypeBitSet types; |
| 1515 types.set(type); |
| 1516 scheduler()->ScheduleNudge(nudge_delay, |
| 1517 browser_sync::NUDGE_SOURCE_LOCAL, |
| 1518 types, |
| 1519 nudge_location); |
| 1520 } |
| 1521 |
| 1522 void SyncManager::SyncInternal::OnSyncEngineEvent( |
| 1523 const SyncEngineEvent& event) { |
| 1524 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1525 if (!HaveObservers()) { |
| 1526 LOG(INFO) |
| 1527 << "OnSyncEngineEvent returning because observers_.size() is zero"; |
| 1528 return; |
| 1529 } |
| 1530 |
| 1531 // Only send an event if this is due to a cycle ending and this cycle |
| 1532 // concludes a canonical "sync" process; that is, based on what is known |
| 1533 // locally we are "all happy" and up-to-date. There may be new changes on |
| 1534 // the server, but we'll get them on a subsequent sync. |
| 1535 // |
| 1536 // Notifications are sent at the end of every sync cycle, regardless of |
| 1537 // whether we should sync again. |
| 1538 if (event.what_happened == SyncEngineEvent::SYNC_CYCLE_ENDED) { |
| 1539 ModelSafeRoutingInfo enabled_types; |
| 1540 registrar_->GetModelSafeRoutingInfo(&enabled_types); |
| 1541 { |
| 1542 // Check to see if we need to notify the frontend that we have newly |
| 1543 // encrypted types or that we require a passphrase. |
| 1544 sync_api::ReadTransaction trans(FROM_HERE, GetUserShare()); |
| 1545 Cryptographer* cryptographer = trans.GetCryptographer(); |
| 1546 // If we've completed a sync cycle and the cryptographer isn't ready |
| 1547 // yet, prompt the user for a passphrase. |
| 1548 if (cryptographer->has_pending_keys()) { |
| 1549 VLOG(1) << "OnPassPhraseRequired Sent"; |
| 1550 ObserverList<SyncManager::Observer> temp_obs_list; |
| 1551 CopyObservers(&temp_obs_list); |
| 1552 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list, |
| 1553 OnPassphraseRequired(sync_api::REASON_DECRYPTION)); |
| 1554 } else if (!cryptographer->is_ready() && |
| 1555 event.snapshot->initial_sync_ended.test(syncable::NIGORI)) { |
| 1556 VLOG(1) << "OnPassphraseRequired sent because cryptographer is not " |
| 1557 << "ready"; |
| 1558 ObserverList<SyncManager::Observer> temp_obs_list; |
| 1559 CopyObservers(&temp_obs_list); |
| 1560 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list, |
| 1561 OnPassphraseRequired(sync_api::REASON_ENCRYPTION)); |
| 1562 } |
| 1563 |
| 1564 allstatus_.SetCryptographerReady(cryptographer->is_ready()); |
| 1565 allstatus_.SetCryptoHasPendingKeys(cryptographer->has_pending_keys()); |
| 1566 allstatus_.SetEncryptedTypes(cryptographer->GetEncryptedTypes()); |
| 1567 |
| 1568 // If everything is in order(we have the passphrase) then there is no |
| 1569 // need to inform the listeners. They will just wait for sync |
| 1570 // completion event and if no errors have been raised it means |
| 1571 // encryption was succesful. |
| 1572 } |
| 1573 |
| 1574 if (!initialized_) { |
| 1575 LOG(INFO) << "OnSyncCycleCompleted not sent because sync api is not " |
| 1576 << "initialized"; |
| 1577 return; |
| 1578 } |
| 1579 |
| 1580 if (!event.snapshot->has_more_to_sync) { |
| 1581 VLOG(1) << "OnSyncCycleCompleted sent"; |
| 1582 ObserverList<SyncManager::Observer> temp_obs_list; |
| 1583 CopyObservers(&temp_obs_list); |
| 1584 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list, |
| 1585 OnSyncCycleCompleted(event.snapshot)); |
| 1586 } |
| 1587 |
| 1588 // This is here for tests, which are still using p2p notifications. |
| 1589 // |
| 1590 // TODO(chron): Consider changing this back to track has_more_to_sync |
| 1591 // only notify peers if a successful commit has occurred. |
| 1592 bool is_notifiable_commit = |
| 1593 (event.snapshot->syncer_status.num_successful_commits > 0); |
| 1594 if (is_notifiable_commit) { |
| 1595 allstatus_.IncrementNotifiableCommits(); |
| 1596 if (sync_notifier_.get()) { |
| 1597 sync_notifier_->SendNotification(); |
| 1598 } else { |
| 1599 VLOG(1) << "Not sending notification: sync_notifier_ is NULL"; |
| 1600 } |
| 1601 } |
| 1602 } |
| 1603 |
| 1604 if (event.what_happened == SyncEngineEvent::STOP_SYNCING_PERMANENTLY) { |
| 1605 ObserverList<SyncManager::Observer> temp_obs_list; |
| 1606 CopyObservers(&temp_obs_list); |
| 1607 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list, |
| 1608 OnStopSyncingPermanently()); |
| 1609 return; |
| 1610 } |
| 1611 |
| 1612 if (event.what_happened == SyncEngineEvent::CLEAR_SERVER_DATA_SUCCEEDED) { |
| 1613 ObserverList<SyncManager::Observer> temp_obs_list; |
| 1614 CopyObservers(&temp_obs_list); |
| 1615 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list, |
| 1616 OnClearServerDataSucceeded()); |
| 1617 return; |
| 1618 } |
| 1619 |
| 1620 if (event.what_happened == SyncEngineEvent::CLEAR_SERVER_DATA_FAILED) { |
| 1621 ObserverList<SyncManager::Observer> temp_obs_list; |
| 1622 CopyObservers(&temp_obs_list); |
| 1623 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list, |
| 1624 OnClearServerDataFailed()); |
| 1625 return; |
| 1626 } |
| 1627 |
| 1628 if (event.what_happened == SyncEngineEvent::UPDATED_TOKEN) { |
| 1629 ObserverList<SyncManager::Observer> temp_obs_list; |
| 1630 CopyObservers(&temp_obs_list); |
| 1631 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list, |
| 1632 OnUpdatedToken(event.updated_token)); |
| 1633 return; |
| 1634 } |
| 1635 } |
| 1636 |
| 1637 void SyncManager::SyncInternal::SetJsEventHandler( |
| 1638 const WeakHandle<JsEventHandler>& event_handler) { |
| 1639 js_event_handler_ = event_handler; |
| 1640 js_sync_manager_observer_.SetJsEventHandler(js_event_handler_); |
| 1641 js_transaction_observer_.SetJsEventHandler(js_event_handler_); |
| 1642 } |
| 1643 |
| 1644 void SyncManager::SyncInternal::ProcessJsMessage( |
| 1645 const std::string& name, const JsArgList& args, |
| 1646 const WeakHandle<JsReplyHandler>& reply_handler) { |
| 1647 if (!initialized_) { |
| 1648 NOTREACHED(); |
| 1649 return; |
| 1650 } |
| 1651 |
| 1652 if (!reply_handler.IsInitialized()) { |
| 1653 VLOG(1) << "Uninitialized reply handler; dropping unknown message " |
| 1654 << name << " with args " << args.ToString(); |
| 1655 return; |
| 1656 } |
| 1657 |
| 1658 JsMessageHandler js_message_handler = js_message_handlers_[name]; |
| 1659 if (js_message_handler.is_null()) { |
| 1660 VLOG(1) << "Dropping unknown message " << name |
| 1661 << " with args " << args.ToString(); |
| 1662 return; |
| 1663 } |
| 1664 |
| 1665 reply_handler.Call(FROM_HERE, |
| 1666 &JsReplyHandler::HandleJsReply, |
| 1667 name, js_message_handler.Run(args)); |
| 1668 } |
| 1669 |
| 1670 void SyncManager::SyncInternal::BindJsMessageHandler( |
| 1671 const std::string& name, |
| 1672 UnboundJsMessageHandler unbound_message_handler) { |
| 1673 js_message_handlers_[name] = |
| 1674 base::Bind(unbound_message_handler, base::Unretained(this)); |
| 1675 } |
| 1676 |
| 1677 DictionaryValue* SyncManager::SyncInternal::NotificationInfoToValue( |
| 1678 const NotificationInfoMap& notification_info) { |
| 1679 DictionaryValue* value = new DictionaryValue(); |
| 1680 |
| 1681 for (NotificationInfoMap::const_iterator it = notification_info.begin(); |
| 1682 it != notification_info.end(); ++it) { |
| 1683 const std::string& model_type_str = |
| 1684 syncable::ModelTypeToString(it->first); |
| 1685 value->Set(model_type_str, it->second.ToValue()); |
| 1686 } |
| 1687 |
| 1688 return value; |
| 1689 } |
| 1690 |
| 1691 JsArgList SyncManager::SyncInternal::GetNotificationState( |
| 1692 const JsArgList& args) { |
| 1693 bool notifications_enabled = allstatus_.status().notifications_enabled; |
| 1694 ListValue return_args; |
| 1695 return_args.Append(Value::CreateBooleanValue(notifications_enabled)); |
| 1696 return JsArgList(&return_args); |
| 1697 } |
| 1698 |
| 1699 JsArgList SyncManager::SyncInternal::GetNotificationInfo( |
| 1700 const JsArgList& args) { |
| 1701 ListValue return_args; |
| 1702 return_args.Append(NotificationInfoToValue(notification_info_map_)); |
| 1703 return JsArgList(&return_args); |
| 1704 } |
| 1705 |
| 1706 JsArgList SyncManager::SyncInternal::GetRootNodeDetails( |
| 1707 const JsArgList& args) { |
| 1708 ReadTransaction trans(FROM_HERE, GetUserShare()); |
| 1709 ReadNode root(&trans); |
| 1710 root.InitByRootLookup(); |
| 1711 ListValue return_args; |
| 1712 return_args.Append(root.GetDetailsAsValue()); |
| 1713 return JsArgList(&return_args); |
| 1714 } |
| 1715 |
| 1716 namespace { |
| 1717 |
| 1718 int64 GetId(const ListValue& ids, int i) { |
| 1719 std::string id_str; |
| 1720 if (!ids.GetString(i, &id_str)) { |
| 1721 return kInvalidId; |
| 1722 } |
| 1723 int64 id = kInvalidId; |
| 1724 if (!base::StringToInt64(id_str, &id)) { |
| 1725 return kInvalidId; |
| 1726 } |
| 1727 return id; |
| 1728 } |
| 1729 |
| 1730 JsArgList GetNodeInfoById(const JsArgList& args, |
| 1731 UserShare* user_share, |
| 1732 DictionaryValue* (BaseNode::*info_getter)() const) { |
| 1733 CHECK(info_getter); |
| 1734 ListValue return_args; |
| 1735 ListValue* node_summaries = new ListValue(); |
| 1736 return_args.Append(node_summaries); |
| 1737 ListValue* id_list = NULL; |
| 1738 ReadTransaction trans(FROM_HERE, user_share); |
| 1739 if (args.Get().GetList(0, &id_list)) { |
| 1740 CHECK(id_list); |
| 1741 for (size_t i = 0; i < id_list->GetSize(); ++i) { |
| 1742 int64 id = GetId(*id_list, i); |
| 1743 if (id == kInvalidId) { |
| 1744 continue; |
| 1745 } |
| 1746 ReadNode node(&trans); |
| 1747 if (!node.InitByIdLookup(id)) { |
| 1748 continue; |
| 1749 } |
| 1750 node_summaries->Append((node.*info_getter)()); |
| 1751 } |
| 1752 } |
| 1753 return JsArgList(&return_args); |
| 1754 } |
| 1755 |
| 1756 } // namespace |
| 1757 |
| 1758 JsArgList SyncManager::SyncInternal::GetNodeSummariesById( |
| 1759 const JsArgList& args) { |
| 1760 return GetNodeInfoById(args, GetUserShare(), &BaseNode::GetSummaryAsValue); |
| 1761 } |
| 1762 |
| 1763 JsArgList SyncManager::SyncInternal::GetNodeDetailsById( |
| 1764 const JsArgList& args) { |
| 1765 return GetNodeInfoById(args, GetUserShare(), &BaseNode::GetDetailsAsValue); |
| 1766 } |
| 1767 |
| 1768 JsArgList SyncManager::SyncInternal::GetChildNodeIds( |
| 1769 const JsArgList& args) { |
| 1770 ListValue return_args; |
| 1771 ListValue* child_ids = new ListValue(); |
| 1772 return_args.Append(child_ids); |
| 1773 int64 id = GetId(args.Get(), 0); |
| 1774 if (id != kInvalidId) { |
| 1775 ReadTransaction trans(FROM_HERE, GetUserShare()); |
| 1776 syncable::Directory::ChildHandles child_handles; |
| 1777 trans.GetLookup()->GetChildHandlesByHandle(trans.GetWrappedTrans(), |
| 1778 id, &child_handles); |
| 1779 for (syncable::Directory::ChildHandles::const_iterator it = |
| 1780 child_handles.begin(); it != child_handles.end(); ++it) { |
| 1781 child_ids->Append(Value::CreateStringValue( |
| 1782 base::Int64ToString(*it))); |
| 1783 } |
| 1784 } |
| 1785 return JsArgList(&return_args); |
| 1786 } |
| 1787 |
| 1788 JsArgList SyncManager::SyncInternal::FindNodesContainingString( |
| 1789 const JsArgList& args) { |
| 1790 std::string query; |
| 1791 ListValue return_args; |
| 1792 if (!args.Get().GetString(0, &query)) { |
| 1793 return_args.Append(new ListValue()); |
| 1794 return JsArgList(&return_args); |
| 1795 } |
| 1796 |
| 1797 // Convert the query string to lower case to perform case insensitive |
| 1798 // searches. |
| 1799 std::string lowercase_query = query; |
| 1800 StringToLowerASCII(&lowercase_query); |
| 1801 |
| 1802 ListValue* result = new ListValue(); |
| 1803 return_args.Append(result); |
| 1804 |
| 1805 ReadTransaction trans(FROM_HERE, GetUserShare()); |
| 1806 std::vector<const syncable::EntryKernel*> entry_kernels; |
| 1807 trans.GetLookup()->GetAllEntryKernels(trans.GetWrappedTrans(), |
| 1808 &entry_kernels); |
| 1809 |
| 1810 for (std::vector<const syncable::EntryKernel*>::const_iterator it = |
| 1811 entry_kernels.begin(); it != entry_kernels.end(); ++it) { |
| 1812 if ((*it)->ContainsString(lowercase_query)) { |
| 1813 result->Append(new StringValue(base::Int64ToString( |
| 1814 (*it)->ref(syncable::META_HANDLE)))); |
| 1815 } |
| 1816 } |
| 1817 |
| 1818 return JsArgList(&return_args); |
| 1819 } |
| 1820 |
| 1821 void SyncManager::SyncInternal::OnNotificationStateChange( |
| 1822 bool notifications_enabled) { |
| 1823 VLOG(1) << "P2P: Notifications enabled = " |
| 1824 << (notifications_enabled ? "true" : "false"); |
| 1825 allstatus_.SetNotificationsEnabled(notifications_enabled); |
| 1826 if (scheduler()) { |
| 1827 scheduler()->set_notifications_enabled(notifications_enabled); |
| 1828 } |
| 1829 if (js_event_handler_.IsInitialized()) { |
| 1830 DictionaryValue details; |
| 1831 details.Set("enabled", Value::CreateBooleanValue(notifications_enabled)); |
| 1832 js_event_handler_.Call(FROM_HERE, |
| 1833 &JsEventHandler::HandleJsEvent, |
| 1834 "onNotificationStateChange", |
| 1835 JsEventDetails(&details)); |
| 1836 } |
| 1837 } |
| 1838 |
| 1839 void SyncManager::SyncInternal::UpdateNotificationInfo( |
| 1840 const syncable::ModelTypePayloadMap& type_payloads) { |
| 1841 for (syncable::ModelTypePayloadMap::const_iterator it = type_payloads.begin(); |
| 1842 it != type_payloads.end(); ++it) { |
| 1843 NotificationInfo* info = ¬ification_info_map_[it->first]; |
| 1844 info->total_count++; |
| 1845 info->payload = it->second; |
| 1846 } |
| 1847 } |
| 1848 |
| 1849 void SyncManager::SyncInternal::OnIncomingNotification( |
| 1850 const syncable::ModelTypePayloadMap& type_payloads) { |
| 1851 if (!type_payloads.empty()) { |
| 1852 if (scheduler()) { |
| 1853 scheduler()->ScheduleNudgeWithPayloads( |
| 1854 TimeDelta::FromMilliseconds(kSyncSchedulerDelayMsec), |
| 1855 browser_sync::NUDGE_SOURCE_NOTIFICATION, |
| 1856 type_payloads, FROM_HERE); |
| 1857 } |
| 1858 allstatus_.IncrementNotificationsReceived(); |
| 1859 UpdateNotificationInfo(type_payloads); |
| 1860 } else { |
| 1861 LOG(WARNING) << "Sync received notification without any type information."; |
| 1862 } |
| 1863 |
| 1864 if (js_event_handler_.IsInitialized()) { |
| 1865 DictionaryValue details; |
| 1866 ListValue* changed_types = new ListValue(); |
| 1867 details.Set("changedTypes", changed_types); |
| 1868 for (syncable::ModelTypePayloadMap::const_iterator |
| 1869 it = type_payloads.begin(); |
| 1870 it != type_payloads.end(); ++it) { |
| 1871 const std::string& model_type_str = |
| 1872 syncable::ModelTypeToString(it->first); |
| 1873 changed_types->Append(Value::CreateStringValue(model_type_str)); |
| 1874 } |
| 1875 js_event_handler_.Call(FROM_HERE, |
| 1876 &JsEventHandler::HandleJsEvent, |
| 1877 "onIncomingNotification", |
| 1878 JsEventDetails(&details)); |
| 1879 } |
| 1880 } |
| 1881 |
| 1882 void SyncManager::SyncInternal::StoreState( |
| 1883 const std::string& state) { |
| 1884 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share()); |
| 1885 if (!lookup.good()) { |
| 1886 LOG(ERROR) << "Could not write notification state"; |
| 1887 // TODO(akalin): Propagate result callback all the way to this |
| 1888 // function and call it with "false" to signal failure. |
| 1889 return; |
| 1890 } |
| 1891 if (VLOG_IS_ON(1)) { |
| 1892 std::string encoded_state; |
| 1893 base::Base64Encode(state, &encoded_state); |
| 1894 VLOG(1) << "Writing notification state: " << encoded_state; |
| 1895 } |
| 1896 lookup->SetNotificationState(state); |
| 1897 lookup->SaveChanges(); |
| 1898 } |
| 1899 |
| 1900 // Note: it is possible that an observer will remove itself after we have made |
| 1901 // a copy, but before the copy is consumed. This could theoretically result |
| 1902 // in accessing a garbage pointer, but can only occur when an about:sync window |
| 1903 // is closed in the middle of a notification. |
| 1904 // See crbug.com/85481. |
| 1905 void SyncManager::SyncInternal::CopyObservers( |
| 1906 ObserverList<SyncManager::Observer>* observers_copy) { |
| 1907 DCHECK_EQ(0U, observers_copy->size()); |
| 1908 base::AutoLock lock(observers_lock_); |
| 1909 if (observers_.size() == 0) |
| 1910 return; |
| 1911 ObserverListBase<SyncManager::Observer>::Iterator it(observers_); |
| 1912 SyncManager::Observer* obs; |
| 1913 while ((obs = it.GetNext()) != NULL) |
| 1914 observers_copy->AddObserver(obs); |
| 1915 } |
| 1916 |
| 1917 bool SyncManager::SyncInternal::HaveObservers() const { |
| 1918 base::AutoLock lock(observers_lock_); |
| 1919 return observers_.size() > 0; |
| 1920 } |
| 1921 |
| 1922 void SyncManager::SyncInternal::AddObserver( |
| 1923 SyncManager::Observer* observer) { |
| 1924 base::AutoLock lock(observers_lock_); |
| 1925 observers_.AddObserver(observer); |
| 1926 } |
| 1927 |
| 1928 void SyncManager::SyncInternal::RemoveObserver( |
| 1929 SyncManager::Observer* observer) { |
| 1930 base::AutoLock lock(observers_lock_); |
| 1931 observers_.RemoveObserver(observer); |
| 1932 } |
| 1933 |
| 1934 SyncManager::Status::Summary SyncManager::GetStatusSummary() const { |
| 1935 return data_->GetStatus().summary; |
| 1936 } |
| 1937 |
| 1938 SyncManager::Status SyncManager::GetDetailedStatus() const { |
| 1939 return data_->GetStatus(); |
| 1940 } |
| 1941 |
| 1942 SyncManager::SyncInternal* SyncManager::GetImpl() const { return data_; } |
| 1943 |
| 1944 void SyncManager::SaveChanges() { |
| 1945 data_->SaveChanges(); |
| 1946 } |
| 1947 |
| 1948 void SyncManager::SyncInternal::SaveChanges() { |
| 1949 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share()); |
| 1950 if (!lookup.good()) { |
| 1951 DCHECK(false) << "ScopedDirLookup creation failed; Unable to SaveChanges"; |
| 1952 return; |
| 1953 } |
| 1954 lookup->SaveChanges(); |
| 1955 } |
| 1956 |
| 1957 UserShare* SyncManager::GetUserShare() const { |
| 1958 return data_->GetUserShare(); |
| 1959 } |
| 1960 |
| 1961 void SyncManager::RefreshEncryption() { |
| 1962 if (data_->UpdateCryptographerFromNigori()) |
| 1963 data_->EncryptDataTypes(syncable::ModelTypeSet()); |
| 1964 } |
| 1965 |
| 1966 syncable::ModelTypeSet SyncManager::GetEncryptedDataTypes() const { |
| 1967 sync_api::ReadTransaction trans(FROM_HERE, GetUserShare()); |
| 1968 return GetEncryptedTypes(&trans); |
| 1969 } |
| 1970 |
| 1971 bool SyncManager::HasUnsyncedItems() const { |
| 1972 sync_api::ReadTransaction trans(FROM_HERE, GetUserShare()); |
| 1973 return (trans.GetWrappedTrans()->directory()->unsynced_entity_count() != 0); |
| 1974 } |
| 1975 |
| 1976 void SyncManager::LogUnsyncedItems(int level) const { |
| 1977 std::vector<int64> unsynced_handles; |
| 1978 sync_api::ReadTransaction trans(FROM_HERE, GetUserShare()); |
| 1979 trans.GetWrappedTrans()->directory()->GetUnsyncedMetaHandles( |
| 1980 trans.GetWrappedTrans(), &unsynced_handles); |
| 1981 |
| 1982 for (std::vector<int64>::const_iterator it = unsynced_handles.begin(); |
| 1983 it != unsynced_handles.end(); ++it) { |
| 1984 ReadNode node(&trans); |
| 1985 if (node.InitByIdLookup(*it)) { |
| 1986 scoped_ptr<DictionaryValue> value(node.GetDetailsAsValue()); |
| 1987 std::string info; |
| 1988 base::JSONWriter::Write(value.get(), true, &info); |
| 1989 VLOG(level) << info; |
| 1990 } |
| 1991 } |
| 1992 } |
| 1993 |
| 1994 void SyncManager::TriggerOnNotificationStateChangeForTest( |
| 1995 bool notifications_enabled) { |
| 1996 data_->OnNotificationStateChange(notifications_enabled); |
| 1997 } |
| 1998 |
| 1999 void SyncManager::TriggerOnIncomingNotificationForTest( |
| 2000 const syncable::ModelTypeBitSet& model_types) { |
| 2001 syncable::ModelTypePayloadMap model_types_with_payloads = |
| 2002 syncable::ModelTypePayloadMapFromBitSet(model_types, |
| 2003 std::string()); |
| 2004 |
| 2005 data_->OnIncomingNotification(model_types_with_payloads); |
| 2006 } |
| 2007 |
| 2008 } // namespace sync_api |
| OLD | NEW |