| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 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/internal_api/sync_manager.h" | |
| 6 | |
| 7 #include <string> | |
| 8 | |
| 9 #include "base/base64.h" | |
| 10 #include "base/bind.h" | |
| 11 #include "base/callback.h" | |
| 12 #include "base/command_line.h" | |
| 13 #include "base/compiler_specific.h" | |
| 14 #include "base/json/json_writer.h" | |
| 15 #include "base/memory/ref_counted.h" | |
| 16 #include "base/metrics/histogram.h" | |
| 17 #include "base/observer_list.h" | |
| 18 #include "base/string_number_conversions.h" | |
| 19 #include "base/values.h" | |
| 20 #include "chrome/browser/sync/internal_api/all_status.h" | |
| 21 #include "chrome/browser/sync/internal_api/base_node.h" | |
| 22 #include "chrome/browser/sync/internal_api/change_reorder_buffer.h" | |
| 23 #include "chrome/browser/sync/internal_api/configure_reason.h" | |
| 24 #include "chrome/browser/sync/internal_api/debug_info_event_listener.h" | |
| 25 #include "chrome/browser/sync/internal_api/js_mutation_event_observer.h" | |
| 26 #include "chrome/browser/sync/internal_api/js_sync_manager_observer.h" | |
| 27 #include "chrome/browser/sync/internal_api/read_node.h" | |
| 28 #include "chrome/browser/sync/internal_api/read_transaction.h" | |
| 29 #include "chrome/browser/sync/internal_api/syncapi_internal.h" | |
| 30 #include "chrome/browser/sync/internal_api/syncapi_server_connection_manager.h" | |
| 31 #include "chrome/browser/sync/internal_api/user_share.h" | |
| 32 #include "chrome/browser/sync/internal_api/write_node.h" | |
| 33 #include "chrome/browser/sync/internal_api/write_transaction.h" | |
| 34 #include "net/base/network_change_notifier.h" | |
| 35 #include "sync/engine/net/server_connection_manager.h" | |
| 36 #include "sync/engine/nigori_util.h" | |
| 37 #include "sync/engine/polling_constants.h" | |
| 38 #include "sync/engine/sync_scheduler.h" | |
| 39 #include "sync/engine/syncer_types.h" | |
| 40 #include "sync/js/js_arg_list.h" | |
| 41 #include "sync/js/js_backend.h" | |
| 42 #include "sync/js/js_event_details.h" | |
| 43 #include "sync/js/js_event_handler.h" | |
| 44 #include "sync/js/js_reply_handler.h" | |
| 45 #include "sync/notifier/sync_notifier.h" | |
| 46 #include "sync/notifier/sync_notifier_observer.h" | |
| 47 #include "sync/protocol/encryption.pb.h" | |
| 48 #include "sync/protocol/proto_value_conversions.h" | |
| 49 #include "sync/protocol/sync.pb.h" | |
| 50 #include "sync/syncable/directory_change_delegate.h" | |
| 51 #include "sync/syncable/model_type.h" | |
| 52 #include "sync/syncable/model_type_payload_map.h" | |
| 53 #include "sync/syncable/syncable.h" | |
| 54 #include "sync/util/cryptographer.h" | |
| 55 #include "sync/util/get_session_name.h" | |
| 56 #include "sync/util/time.h" | |
| 57 | |
| 58 using base::TimeDelta; | |
| 59 using browser_sync::AllStatus; | |
| 60 using browser_sync::Cryptographer; | |
| 61 using browser_sync::Encryptor; | |
| 62 using browser_sync::JsArgList; | |
| 63 using browser_sync::JsBackend; | |
| 64 using browser_sync::JsEventDetails; | |
| 65 using browser_sync::JsEventHandler; | |
| 66 using browser_sync::JsEventHandler; | |
| 67 using browser_sync::JsReplyHandler; | |
| 68 using browser_sync::JsMutationEventObserver; | |
| 69 using browser_sync::JsSyncManagerObserver; | |
| 70 using browser_sync::ModelSafeWorkerRegistrar; | |
| 71 using browser_sync::kNigoriTag; | |
| 72 using browser_sync::KeyParams; | |
| 73 using browser_sync::ModelSafeRoutingInfo; | |
| 74 using browser_sync::ReportUnrecoverableErrorFunction; | |
| 75 using browser_sync::ServerConnectionEvent; | |
| 76 using browser_sync::ServerConnectionEventListener; | |
| 77 using browser_sync::SyncEngineEvent; | |
| 78 using browser_sync::SyncEngineEventListener; | |
| 79 using browser_sync::SyncScheduler; | |
| 80 using browser_sync::Syncer; | |
| 81 using browser_sync::UnrecoverableErrorHandler; | |
| 82 using browser_sync::WeakHandle; | |
| 83 using browser_sync::sessions::SyncSessionContext; | |
| 84 using syncable::ImmutableWriteTransactionInfo; | |
| 85 using syncable::ModelType; | |
| 86 using syncable::ModelTypeSet; | |
| 87 using syncable::SPECIFICS; | |
| 88 using sync_pb::GetUpdatesCallerInfo; | |
| 89 | |
| 90 namespace { | |
| 91 | |
| 92 // Delays for syncer nudges. | |
| 93 static const int kSyncRefreshDelayMsec = 500; | |
| 94 static const int kSyncSchedulerDelayMsec = 250; | |
| 95 | |
| 96 GetUpdatesCallerInfo::GetUpdatesSource GetSourceFromReason( | |
| 97 sync_api::ConfigureReason reason) { | |
| 98 switch (reason) { | |
| 99 case sync_api::CONFIGURE_REASON_RECONFIGURATION: | |
| 100 return GetUpdatesCallerInfo::RECONFIGURATION; | |
| 101 case sync_api::CONFIGURE_REASON_MIGRATION: | |
| 102 return GetUpdatesCallerInfo::MIGRATION; | |
| 103 case sync_api::CONFIGURE_REASON_NEW_CLIENT: | |
| 104 return GetUpdatesCallerInfo::NEW_CLIENT; | |
| 105 case sync_api::CONFIGURE_REASON_NEWLY_ENABLED_DATA_TYPE: | |
| 106 return GetUpdatesCallerInfo::NEWLY_SUPPORTED_DATATYPE; | |
| 107 default: | |
| 108 NOTREACHED(); | |
| 109 } | |
| 110 | |
| 111 return GetUpdatesCallerInfo::UNKNOWN; | |
| 112 } | |
| 113 | |
| 114 // The maximum number of times we will automatically overwrite the nigori node | |
| 115 // because the encryption keys don't match (per chrome instantiation). | |
| 116 static const int kNigoriOverwriteLimit = 10; | |
| 117 | |
| 118 } // namespace | |
| 119 | |
| 120 namespace sync_api { | |
| 121 | |
| 122 const int SyncManager::kDefaultNudgeDelayMilliseconds = 200; | |
| 123 const int SyncManager::kPreferencesNudgeDelayMilliseconds = 2000; | |
| 124 | |
| 125 // Maximum count and size for traffic recorder. | |
| 126 const unsigned int kMaxMessagesToRecord = 10; | |
| 127 const unsigned int kMaxMessageSizeToRecord = 5 * 1024; | |
| 128 | |
| 129 ////////////////////////////////////////////////////////////////////////// | |
| 130 // SyncManager's implementation: SyncManager::SyncInternal | |
| 131 class SyncManager::SyncInternal | |
| 132 : public net::NetworkChangeNotifier::IPAddressObserver, | |
| 133 public browser_sync::Cryptographer::Observer, | |
| 134 public sync_notifier::SyncNotifierObserver, | |
| 135 public JsBackend, | |
| 136 public SyncEngineEventListener, | |
| 137 public ServerConnectionEventListener, | |
| 138 public syncable::DirectoryChangeDelegate { | |
| 139 public: | |
| 140 explicit SyncInternal(const std::string& name) | |
| 141 : name_(name), | |
| 142 weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), | |
| 143 enable_sync_tabs_for_other_clients_(false), | |
| 144 registrar_(NULL), | |
| 145 change_delegate_(NULL), | |
| 146 initialized_(false), | |
| 147 testing_mode_(NON_TEST), | |
| 148 observing_ip_address_changes_(false), | |
| 149 traffic_recorder_(kMaxMessagesToRecord, kMaxMessageSizeToRecord), | |
| 150 encryptor_(NULL), | |
| 151 unrecoverable_error_handler_(NULL), | |
| 152 report_unrecoverable_error_function_(NULL), | |
| 153 created_on_loop_(MessageLoop::current()), | |
| 154 nigori_overwrite_count_(0) { | |
| 155 // Pre-fill |notification_info_map_|. | |
| 156 for (int i = syncable::FIRST_REAL_MODEL_TYPE; | |
| 157 i < syncable::MODEL_TYPE_COUNT; ++i) { | |
| 158 notification_info_map_.insert( | |
| 159 std::make_pair(syncable::ModelTypeFromInt(i), NotificationInfo())); | |
| 160 } | |
| 161 | |
| 162 // Bind message handlers. | |
| 163 BindJsMessageHandler( | |
| 164 "getNotificationState", | |
| 165 &SyncManager::SyncInternal::GetNotificationState); | |
| 166 BindJsMessageHandler( | |
| 167 "getNotificationInfo", | |
| 168 &SyncManager::SyncInternal::GetNotificationInfo); | |
| 169 BindJsMessageHandler( | |
| 170 "getRootNodeDetails", | |
| 171 &SyncManager::SyncInternal::GetRootNodeDetails); | |
| 172 BindJsMessageHandler( | |
| 173 "getNodeSummariesById", | |
| 174 &SyncManager::SyncInternal::GetNodeSummariesById); | |
| 175 BindJsMessageHandler( | |
| 176 "getNodeDetailsById", | |
| 177 &SyncManager::SyncInternal::GetNodeDetailsById); | |
| 178 BindJsMessageHandler( | |
| 179 "getAllNodes", | |
| 180 &SyncManager::SyncInternal::GetAllNodes); | |
| 181 BindJsMessageHandler( | |
| 182 "getChildNodeIds", | |
| 183 &SyncManager::SyncInternal::GetChildNodeIds); | |
| 184 BindJsMessageHandler( | |
| 185 "getClientServerTraffic", | |
| 186 &SyncManager::SyncInternal::GetClientServerTraffic); | |
| 187 } | |
| 188 | |
| 189 virtual ~SyncInternal() { | |
| 190 CHECK(!initialized_); | |
| 191 } | |
| 192 | |
| 193 bool Init(const FilePath& database_location, | |
| 194 const WeakHandle<JsEventHandler>& event_handler, | |
| 195 const std::string& sync_server_and_path, | |
| 196 int port, | |
| 197 bool use_ssl, | |
| 198 const scoped_refptr<base::TaskRunner>& blocking_task_runner, | |
| 199 HttpPostProviderFactory* post_factory, | |
| 200 ModelSafeWorkerRegistrar* model_safe_worker_registrar, | |
| 201 browser_sync::ExtensionsActivityMonitor* | |
| 202 extensions_activity_monitor, | |
| 203 ChangeDelegate* change_delegate, | |
| 204 const std::string& user_agent, | |
| 205 const SyncCredentials& credentials, | |
| 206 bool enable_sync_tabs_for_other_clients, | |
| 207 sync_notifier::SyncNotifier* sync_notifier, | |
| 208 const std::string& restored_key_for_bootstrapping, | |
| 209 TestingMode testing_mode, | |
| 210 Encryptor* encryptor, | |
| 211 UnrecoverableErrorHandler* unrecoverable_error_handler, | |
| 212 ReportUnrecoverableErrorFunction | |
| 213 report_unrecoverable_error_function); | |
| 214 | |
| 215 // Sign into sync with given credentials. | |
| 216 // We do not verify the tokens given. After this call, the tokens are set | |
| 217 // and the sync DB is open. True if successful, false if something | |
| 218 // went wrong. | |
| 219 bool SignIn(const SyncCredentials& credentials); | |
| 220 | |
| 221 // Update tokens that we're using in Sync. Email must stay the same. | |
| 222 void UpdateCredentials(const SyncCredentials& credentials); | |
| 223 | |
| 224 // Called when the user disables or enables a sync type. | |
| 225 void UpdateEnabledTypes(); | |
| 226 | |
| 227 // Conditionally sets the flag in the Nigori node which instructs other | |
| 228 // clients to start syncing tabs. | |
| 229 void MaybeSetSyncTabsInNigoriNode(ModelTypeSet enabled_types); | |
| 230 | |
| 231 // Tell the sync engine to start the syncing process. | |
| 232 void StartSyncingNormally(); | |
| 233 | |
| 234 // Whether or not the Nigori node is encrypted using an explicit passphrase. | |
| 235 bool IsUsingExplicitPassphrase(); | |
| 236 | |
| 237 // Update the Cryptographer from the current nigori node and write back any | |
| 238 // necessary changes to the nigori node. We also detect missing encryption | |
| 239 // keys and write them into the nigori node. | |
| 240 // Also updates or adds the device information into the 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 // Calls the callback argument with true if cryptographer is ready, false | |
| 244 // otherwise. | |
| 245 void UpdateCryptographerAndNigori( | |
| 246 const std::string& chrome_version, | |
| 247 const base::Closure& done_callback); | |
| 248 | |
| 249 // Stores the current set of encryption keys (if the cryptographer is ready) | |
| 250 // and encrypted types into the nigori node. | |
| 251 void UpdateNigoriEncryptionState(Cryptographer* cryptographer, | |
| 252 WriteNode* nigori_node); | |
| 253 | |
| 254 // Updates the nigori node with any new encrypted types and then | |
| 255 // encrypts the nodes for those new data types as well as other | |
| 256 // nodes that should be encrypted but aren't. Triggers | |
| 257 // OnPassphraseRequired if the cryptographer isn't ready. | |
| 258 void RefreshEncryption(); | |
| 259 | |
| 260 // Re-encrypts the encrypted data types using the passed passphrase, and sets | |
| 261 // a flag in the nigori node specifying whether the current passphrase is | |
| 262 // explicit (custom passphrase) or non-explicit (GAIA). If the existing | |
| 263 // encryption passphrase is "explicit", the data cannot be re-encrypted and | |
| 264 // SetEncryptionPassphrase will do nothing. | |
| 265 // If !is_explicit and there are pending keys, we will attempt to decrypt them | |
| 266 // using this passphrase. If this fails, we will save this encryption key to | |
| 267 // be applied later after the pending keys are resolved. | |
| 268 // Calls FinishSetPassphrase at the end, which notifies observers of the | |
| 269 // result of the set passphrase operation, updates the nigori node, and does | |
| 270 // re-encryption. | |
| 271 void SetEncryptionPassphrase(const std::string& passphrase, bool is_explicit); | |
| 272 | |
| 273 // Provides a passphrase for decrypting the user's existing sync data. Calls | |
| 274 // FinishSetPassphrase at the end, which notifies observers of the result of | |
| 275 // the set passphrase operation, updates the nigori node, and does | |
| 276 // re-encryption. | |
| 277 void SetDecryptionPassphrase(const std::string& passphrase); | |
| 278 | |
| 279 // The final step of SetEncryptionPassphrase and SetDecryptionPassphrase that | |
| 280 // notifies observers of the result of the set passphrase operation, updates | |
| 281 // the nigori node, and does re-encryption. | |
| 282 // |success|: true if the operation was successful and false otherwise. If | |
| 283 // success == false, we send an OnPassphraseRequired notification. | |
| 284 // |bootstrap_token|: used to inform observers if the cryptographer's | |
| 285 // bootstrap token was updated. | |
| 286 // |is_explicit|: used to differentiate between a custom passphrase (true) and | |
| 287 // a GAIA passphrase that is implicitly used for encryption | |
| 288 // (false). | |
| 289 // |trans| and |nigori_node|: used to access data in the cryptographer. | |
| 290 void FinishSetPassphrase( | |
| 291 bool success, | |
| 292 const std::string& bootstrap_token, | |
| 293 bool is_explicit, | |
| 294 WriteTransaction* trans, | |
| 295 WriteNode* nigori_node); | |
| 296 | |
| 297 // Call periodically from a database-safe thread to persist recent changes | |
| 298 // to the syncapi model. | |
| 299 void SaveChanges(); | |
| 300 | |
| 301 // DirectoryChangeDelegate implementation. | |
| 302 // This listener is called upon completion of a syncable transaction, and | |
| 303 // builds the list of sync-engine initiated changes that will be forwarded to | |
| 304 // the SyncManager's Observers. | |
| 305 virtual void HandleTransactionCompleteChangeEvent( | |
| 306 ModelTypeSet models_with_changes) OVERRIDE; | |
| 307 virtual ModelTypeSet HandleTransactionEndingChangeEvent( | |
| 308 const ImmutableWriteTransactionInfo& write_transaction_info, | |
| 309 syncable::BaseTransaction* trans) OVERRIDE; | |
| 310 virtual void HandleCalculateChangesChangeEventFromSyncApi( | |
| 311 const ImmutableWriteTransactionInfo& write_transaction_info, | |
| 312 syncable::BaseTransaction* trans) OVERRIDE; | |
| 313 virtual void HandleCalculateChangesChangeEventFromSyncer( | |
| 314 const ImmutableWriteTransactionInfo& write_transaction_info, | |
| 315 syncable::BaseTransaction* trans) OVERRIDE; | |
| 316 | |
| 317 // Open the directory named with username_for_share | |
| 318 bool OpenDirectory(); | |
| 319 | |
| 320 // Cryptographer::Observer implementation. | |
| 321 virtual void OnEncryptedTypesChanged( | |
| 322 syncable::ModelTypeSet encrypted_types, | |
| 323 bool encrypt_everything) OVERRIDE; | |
| 324 | |
| 325 // SyncNotifierObserver implementation. | |
| 326 virtual void OnNotificationStateChange( | |
| 327 bool notifications_enabled) OVERRIDE; | |
| 328 | |
| 329 virtual void OnIncomingNotification( | |
| 330 const syncable::ModelTypePayloadMap& type_payloads, | |
| 331 sync_notifier::IncomingNotificationSource source) OVERRIDE; | |
| 332 | |
| 333 virtual void StoreState(const std::string& cookie) OVERRIDE; | |
| 334 | |
| 335 void AddObserver(SyncManager::Observer* observer); | |
| 336 void RemoveObserver(SyncManager::Observer* observer); | |
| 337 | |
| 338 // Accessors for the private members. | |
| 339 syncable::Directory* directory() { return share_.directory.get(); } | |
| 340 SyncAPIServerConnectionManager* connection_manager() { | |
| 341 return connection_manager_.get(); | |
| 342 } | |
| 343 SyncScheduler* scheduler() const { return scheduler_.get(); } | |
| 344 UserShare* GetUserShare() { | |
| 345 DCHECK(initialized_); | |
| 346 return &share_; | |
| 347 } | |
| 348 | |
| 349 // Return the currently active (validated) username for use with syncable | |
| 350 // types. | |
| 351 const std::string& username_for_share() const { | |
| 352 return share_.name; | |
| 353 } | |
| 354 | |
| 355 Status GetStatus(); | |
| 356 | |
| 357 void RequestNudge(const tracked_objects::Location& nudge_location); | |
| 358 | |
| 359 void RequestNudgeForDataTypes( | |
| 360 const tracked_objects::Location& nudge_location, | |
| 361 ModelTypeSet type); | |
| 362 | |
| 363 TimeDelta GetNudgeDelayTimeDelta(const ModelType& model_type); | |
| 364 | |
| 365 void NotifyCryptographerState(Cryptographer* cryptographer); | |
| 366 | |
| 367 // See SyncManager::Shutdown* for information. | |
| 368 void StopSyncingForShutdown(const base::Closure& callback); | |
| 369 void ShutdownOnSyncThread(); | |
| 370 | |
| 371 // If this is a deletion for a password, sets the legacy | |
| 372 // ExtraPasswordChangeRecordData field of |buffer|. Otherwise sets | |
| 373 // |buffer|'s specifics field to contain the unencrypted data. | |
| 374 void SetExtraChangeRecordData(int64 id, | |
| 375 syncable::ModelType type, | |
| 376 ChangeReorderBuffer* buffer, | |
| 377 Cryptographer* cryptographer, | |
| 378 const syncable::EntryKernel& original, | |
| 379 bool existed_before, | |
| 380 bool exists_now); | |
| 381 | |
| 382 // Called only by our NetworkChangeNotifier. | |
| 383 virtual void OnIPAddressChanged() OVERRIDE; | |
| 384 | |
| 385 bool InitialSyncEndedForAllEnabledTypes() { | |
| 386 syncable::ModelTypeSet types; | |
| 387 ModelSafeRoutingInfo enabled_types; | |
| 388 registrar_->GetModelSafeRoutingInfo(&enabled_types); | |
| 389 for (ModelSafeRoutingInfo::const_iterator i = enabled_types.begin(); | |
| 390 i != enabled_types.end(); ++i) { | |
| 391 types.Put(i->first); | |
| 392 } | |
| 393 | |
| 394 return InitialSyncEndedForTypes(types, &share_); | |
| 395 } | |
| 396 | |
| 397 // SyncEngineEventListener implementation. | |
| 398 virtual void OnSyncEngineEvent(const SyncEngineEvent& event) OVERRIDE; | |
| 399 | |
| 400 // ServerConnectionEventListener implementation. | |
| 401 virtual void OnServerConnectionEvent( | |
| 402 const ServerConnectionEvent& event) OVERRIDE; | |
| 403 | |
| 404 // JsBackend implementation. | |
| 405 virtual void SetJsEventHandler( | |
| 406 const WeakHandle<JsEventHandler>& event_handler) OVERRIDE; | |
| 407 virtual void ProcessJsMessage( | |
| 408 const std::string& name, const JsArgList& args, | |
| 409 const WeakHandle<JsReplyHandler>& reply_handler) OVERRIDE; | |
| 410 | |
| 411 private: | |
| 412 struct NotificationInfo { | |
| 413 int total_count; | |
| 414 std::string payload; | |
| 415 | |
| 416 NotificationInfo() : total_count(0) {} | |
| 417 | |
| 418 ~NotificationInfo() {} | |
| 419 | |
| 420 // Returned pointer owned by the caller. | |
| 421 DictionaryValue* ToValue() const { | |
| 422 DictionaryValue* value = new DictionaryValue(); | |
| 423 value->SetInteger("totalCount", total_count); | |
| 424 value->SetString("payload", payload); | |
| 425 return value; | |
| 426 } | |
| 427 }; | |
| 428 | |
| 429 typedef std::map<syncable::ModelType, NotificationInfo> NotificationInfoMap; | |
| 430 typedef JsArgList | |
| 431 (SyncManager::SyncInternal::*UnboundJsMessageHandler)(const JsArgList&); | |
| 432 typedef base::Callback<JsArgList(const JsArgList&)> JsMessageHandler; | |
| 433 typedef std::map<std::string, JsMessageHandler> JsMessageHandlerMap; | |
| 434 | |
| 435 // Internal callback of UpdateCryptographerAndNigoriCallback. | |
| 436 void UpdateCryptographerAndNigoriCallback( | |
| 437 const std::string& chrome_version, | |
| 438 const base::Closure& done_callback, | |
| 439 const std::string& session_name); | |
| 440 | |
| 441 // Determine if the parents or predecessors differ between the old and new | |
| 442 // versions of an entry stored in |a| and |b|. Note that a node's index may | |
| 443 // change without its NEXT_ID changing if the node at NEXT_ID also moved (but | |
| 444 // the relative order is unchanged). To handle such cases, we rely on the | |
| 445 // caller to treat a position update on any sibling as updating the positions | |
| 446 // of all siblings. | |
| 447 static bool VisiblePositionsDiffer( | |
| 448 const syncable::EntryKernelMutation& mutation) { | |
| 449 const syncable::EntryKernel& a = mutation.original; | |
| 450 const syncable::EntryKernel& b = mutation.mutated; | |
| 451 // If the datatype isn't one where the browser model cares about position, | |
| 452 // don't bother notifying that data model of position-only changes. | |
| 453 if (!ShouldMaintainPosition( | |
| 454 syncable::GetModelTypeFromSpecifics(b.ref(SPECIFICS)))) | |
| 455 return false; | |
| 456 if (a.ref(syncable::NEXT_ID) != b.ref(syncable::NEXT_ID)) | |
| 457 return true; | |
| 458 if (a.ref(syncable::PARENT_ID) != b.ref(syncable::PARENT_ID)) | |
| 459 return true; | |
| 460 return false; | |
| 461 } | |
| 462 | |
| 463 // Determine if any of the fields made visible to clients of the Sync API | |
| 464 // differ between the versions of an entry stored in |a| and |b|. A return | |
| 465 // value of false means that it should be OK to ignore this change. | |
| 466 static bool VisiblePropertiesDiffer( | |
| 467 const syncable::EntryKernelMutation& mutation, | |
| 468 Cryptographer* cryptographer) { | |
| 469 const syncable::EntryKernel& a = mutation.original; | |
| 470 const syncable::EntryKernel& b = mutation.mutated; | |
| 471 const sync_pb::EntitySpecifics& a_specifics = a.ref(SPECIFICS); | |
| 472 const sync_pb::EntitySpecifics& b_specifics = b.ref(SPECIFICS); | |
| 473 DCHECK_EQ(syncable::GetModelTypeFromSpecifics(a_specifics), | |
| 474 syncable::GetModelTypeFromSpecifics(b_specifics)); | |
| 475 syncable::ModelType model_type = | |
| 476 syncable::GetModelTypeFromSpecifics(b_specifics); | |
| 477 // Suppress updates to items that aren't tracked by any browser model. | |
| 478 if (model_type < syncable::FIRST_REAL_MODEL_TYPE || | |
| 479 !a.ref(syncable::UNIQUE_SERVER_TAG).empty()) { | |
| 480 return false; | |
| 481 } | |
| 482 if (a.ref(syncable::IS_DIR) != b.ref(syncable::IS_DIR)) | |
| 483 return true; | |
| 484 if (!AreSpecificsEqual(cryptographer, | |
| 485 a.ref(syncable::SPECIFICS), | |
| 486 b.ref(syncable::SPECIFICS))) { | |
| 487 return true; | |
| 488 } | |
| 489 // We only care if the name has changed if neither specifics is encrypted | |
| 490 // (encrypted nodes blow away the NON_UNIQUE_NAME). | |
| 491 if (!a_specifics.has_encrypted() && !b_specifics.has_encrypted() && | |
| 492 a.ref(syncable::NON_UNIQUE_NAME) != b.ref(syncable::NON_UNIQUE_NAME)) | |
| 493 return true; | |
| 494 if (VisiblePositionsDiffer(mutation)) | |
| 495 return true; | |
| 496 return false; | |
| 497 } | |
| 498 | |
| 499 bool ChangeBuffersAreEmpty() { | |
| 500 for (int i = 0; i < syncable::MODEL_TYPE_COUNT; ++i) { | |
| 501 if (!change_buffers_[i].IsEmpty()) | |
| 502 return false; | |
| 503 } | |
| 504 return true; | |
| 505 } | |
| 506 | |
| 507 void ReEncryptEverything(WriteTransaction* trans); | |
| 508 | |
| 509 // Called for every notification. This updates the notification statistics | |
| 510 // to be displayed in about:sync. | |
| 511 void UpdateNotificationInfo( | |
| 512 const syncable::ModelTypePayloadMap& type_payloads); | |
| 513 | |
| 514 // Checks for server reachabilty and requests a nudge. | |
| 515 void OnIPAddressChangedImpl(); | |
| 516 | |
| 517 // Helper function used only by the constructor. | |
| 518 void BindJsMessageHandler( | |
| 519 const std::string& name, UnboundJsMessageHandler unbound_message_handler); | |
| 520 | |
| 521 // Returned pointer is owned by the caller. | |
| 522 static DictionaryValue* NotificationInfoToValue( | |
| 523 const NotificationInfoMap& notification_info); | |
| 524 | |
| 525 // JS message handlers. | |
| 526 JsArgList GetNotificationState(const JsArgList& args); | |
| 527 JsArgList GetNotificationInfo(const JsArgList& args); | |
| 528 JsArgList GetRootNodeDetails(const JsArgList& args); | |
| 529 JsArgList GetAllNodes(const JsArgList& args); | |
| 530 JsArgList GetNodeSummariesById(const JsArgList& args); | |
| 531 JsArgList GetNodeDetailsById(const JsArgList& args); | |
| 532 JsArgList GetChildNodeIds(const JsArgList& args); | |
| 533 JsArgList GetClientServerTraffic(const JsArgList& args); | |
| 534 | |
| 535 FilePath database_path_; | |
| 536 | |
| 537 const std::string name_; | |
| 538 | |
| 539 base::ThreadChecker thread_checker_; | |
| 540 | |
| 541 base::WeakPtrFactory<SyncInternal> weak_ptr_factory_; | |
| 542 | |
| 543 // Thread-safe handle used by | |
| 544 // HandleCalculateChangesChangeEventFromSyncApi(), which can be | |
| 545 // called from any thread. Valid only between between calls to | |
| 546 // Init() and Shutdown(). | |
| 547 // | |
| 548 // TODO(akalin): Ideally, we wouldn't need to store this; instead, | |
| 549 // we'd have another worker class which implements | |
| 550 // HandleCalculateChangesChangeEventFromSyncApi() and we'd pass it a | |
| 551 // WeakHandle when we construct it. | |
| 552 WeakHandle<SyncInternal> weak_handle_this_; | |
| 553 | |
| 554 // |blocking_task_runner| is a TaskRunner to be used for tasks that | |
| 555 // may block on disk I/O. | |
| 556 scoped_refptr<base::TaskRunner> blocking_task_runner_; | |
| 557 | |
| 558 // We give a handle to share_ to clients of the API for use when constructing | |
| 559 // any transaction type. | |
| 560 UserShare share_; | |
| 561 | |
| 562 // This can be called from any thread, but only between calls to | |
| 563 // OpenDirectory() and ShutdownOnSyncThread(). | |
| 564 browser_sync::WeakHandle<SyncManager::ChangeObserver> change_observer_; | |
| 565 | |
| 566 ObserverList<SyncManager::Observer> observers_; | |
| 567 | |
| 568 // The ServerConnectionManager used to abstract communication between the | |
| 569 // client (the Syncer) and the sync server. | |
| 570 scoped_ptr<SyncAPIServerConnectionManager> connection_manager_; | |
| 571 | |
| 572 // The scheduler that runs the Syncer. Needs to be explicitly | |
| 573 // Start()ed. | |
| 574 scoped_ptr<SyncScheduler> scheduler_; | |
| 575 | |
| 576 bool enable_sync_tabs_for_other_clients_; | |
| 577 | |
| 578 // The SyncNotifier which notifies us when updates need to be downloaded. | |
| 579 scoped_ptr<sync_notifier::SyncNotifier> sync_notifier_; | |
| 580 | |
| 581 // A multi-purpose status watch object that aggregates stats from various | |
| 582 // sync components. | |
| 583 AllStatus allstatus_; | |
| 584 | |
| 585 // Each element of this array is a store of change records produced by | |
| 586 // HandleChangeEvent during the CALCULATE_CHANGES step. The changes are | |
| 587 // segregated by model type, and are stored here to be processed and | |
| 588 // forwarded to the observer slightly later, at the TRANSACTION_ENDING | |
| 589 // step by HandleTransactionEndingChangeEvent. The list is cleared in the | |
| 590 // TRANSACTION_COMPLETE step by HandleTransactionCompleteChangeEvent. | |
| 591 ChangeReorderBuffer change_buffers_[syncable::MODEL_TYPE_COUNT]; | |
| 592 | |
| 593 // The entity that provides us with information about which types to sync. | |
| 594 // The instance is shared between the SyncManager and the Syncer. | |
| 595 ModelSafeWorkerRegistrar* registrar_; | |
| 596 | |
| 597 SyncManager::ChangeDelegate* change_delegate_; | |
| 598 | |
| 599 // Set to true once Init has been called. | |
| 600 bool initialized_; | |
| 601 | |
| 602 // Controls the disabling of certain SyncManager features. | |
| 603 // Can be used to disable communication with the server and the use of an | |
| 604 // on-disk file for maintaining syncer state. | |
| 605 // TODO(117836): Clean up implementation of SyncManager unit tests. | |
| 606 TestingMode testing_mode_; | |
| 607 | |
| 608 bool observing_ip_address_changes_; | |
| 609 | |
| 610 // Map used to store the notification info to be displayed in | |
| 611 // about:sync page. | |
| 612 NotificationInfoMap notification_info_map_; | |
| 613 | |
| 614 // These are for interacting with chrome://sync-internals. | |
| 615 JsMessageHandlerMap js_message_handlers_; | |
| 616 WeakHandle<JsEventHandler> js_event_handler_; | |
| 617 JsSyncManagerObserver js_sync_manager_observer_; | |
| 618 JsMutationEventObserver js_mutation_event_observer_; | |
| 619 | |
| 620 // This is for keeping track of client events to send to the server. | |
| 621 DebugInfoEventListener debug_info_event_listener_; | |
| 622 | |
| 623 browser_sync::TrafficRecorder traffic_recorder_; | |
| 624 | |
| 625 Encryptor* encryptor_; | |
| 626 UnrecoverableErrorHandler* unrecoverable_error_handler_; | |
| 627 ReportUnrecoverableErrorFunction report_unrecoverable_error_function_; | |
| 628 | |
| 629 MessageLoop* const created_on_loop_; | |
| 630 | |
| 631 // The number of times we've automatically (i.e. not via SetPassphrase or | |
| 632 // conflict resolver) updated the nigori's encryption keys in this chrome | |
| 633 // instantiation. | |
| 634 int nigori_overwrite_count_; | |
| 635 }; | |
| 636 | |
| 637 // A class to calculate nudge delays for types. | |
| 638 class NudgeStrategy { | |
| 639 public: | |
| 640 static TimeDelta GetNudgeDelayTimeDelta(const ModelType& model_type, | |
| 641 SyncManager::SyncInternal* core) { | |
| 642 NudgeDelayStrategy delay_type = GetNudgeDelayStrategy(model_type); | |
| 643 return GetNudgeDelayTimeDeltaFromType(delay_type, | |
| 644 model_type, | |
| 645 core); | |
| 646 } | |
| 647 | |
| 648 private: | |
| 649 // Possible types of nudge delay for datatypes. | |
| 650 // Note: These are just hints. If a sync happens then all dirty entries | |
| 651 // would be committed as part of the sync. | |
| 652 enum NudgeDelayStrategy { | |
| 653 // Sync right away. | |
| 654 IMMEDIATE, | |
| 655 | |
| 656 // Sync this change while syncing another change. | |
| 657 ACCOMPANY_ONLY, | |
| 658 | |
| 659 // The datatype does not use one of the predefined wait times but defines | |
| 660 // its own wait time logic for nudge. | |
| 661 CUSTOM, | |
| 662 }; | |
| 663 | |
| 664 static NudgeDelayStrategy GetNudgeDelayStrategy(const ModelType& type) { | |
| 665 switch (type) { | |
| 666 case syncable::AUTOFILL: | |
| 667 return ACCOMPANY_ONLY; | |
| 668 case syncable::PREFERENCES: | |
| 669 case syncable::SESSIONS: | |
| 670 return CUSTOM; | |
| 671 default: | |
| 672 return IMMEDIATE; | |
| 673 } | |
| 674 } | |
| 675 | |
| 676 static TimeDelta GetNudgeDelayTimeDeltaFromType( | |
| 677 const NudgeDelayStrategy& delay_type, const ModelType& model_type, | |
| 678 const SyncManager::SyncInternal* core) { | |
| 679 CHECK(core); | |
| 680 TimeDelta delay = TimeDelta::FromMilliseconds( | |
| 681 SyncManager::kDefaultNudgeDelayMilliseconds); | |
| 682 switch (delay_type) { | |
| 683 case IMMEDIATE: | |
| 684 delay = TimeDelta::FromMilliseconds( | |
| 685 SyncManager::kDefaultNudgeDelayMilliseconds); | |
| 686 break; | |
| 687 case ACCOMPANY_ONLY: | |
| 688 delay = TimeDelta::FromSeconds( | |
| 689 browser_sync::kDefaultShortPollIntervalSeconds); | |
| 690 break; | |
| 691 case CUSTOM: | |
| 692 switch (model_type) { | |
| 693 case syncable::PREFERENCES: | |
| 694 delay = TimeDelta::FromMilliseconds( | |
| 695 SyncManager::kPreferencesNudgeDelayMilliseconds); | |
| 696 break; | |
| 697 case syncable::SESSIONS: | |
| 698 delay = core->scheduler()->sessions_commit_delay(); | |
| 699 break; | |
| 700 default: | |
| 701 NOTREACHED(); | |
| 702 } | |
| 703 break; | |
| 704 default: | |
| 705 NOTREACHED(); | |
| 706 } | |
| 707 return delay; | |
| 708 } | |
| 709 }; | |
| 710 | |
| 711 SyncManager::ChangeDelegate::~ChangeDelegate() {} | |
| 712 | |
| 713 SyncManager::ChangeObserver::~ChangeObserver() {} | |
| 714 | |
| 715 SyncManager::Observer::~Observer() {} | |
| 716 | |
| 717 SyncManager::SyncManager(const std::string& name) | |
| 718 : data_(new SyncInternal(name)) {} | |
| 719 | |
| 720 SyncManager::Status::Status() | |
| 721 : notifications_enabled(false), | |
| 722 notifications_received(0), | |
| 723 unsynced_count(0), | |
| 724 encryption_conflicts(0), | |
| 725 hierarchy_conflicts(0), | |
| 726 simple_conflicts(0), | |
| 727 server_conflicts(0), | |
| 728 committed_count(0), | |
| 729 syncing(false), | |
| 730 initial_sync_ended(false), | |
| 731 updates_available(0), | |
| 732 updates_received(0), | |
| 733 reflected_updates_received(0), | |
| 734 tombstone_updates_received(0), | |
| 735 num_local_overwrites_total(0), | |
| 736 num_server_overwrites_total(0), | |
| 737 nonempty_get_updates(0), | |
| 738 empty_get_updates(0), | |
| 739 sync_cycles_with_commits(0), | |
| 740 sync_cycles_without_commits(0), | |
| 741 useless_sync_cycles(0), | |
| 742 useful_sync_cycles(0), | |
| 743 cryptographer_ready(false), | |
| 744 crypto_has_pending_keys(false) { | |
| 745 } | |
| 746 | |
| 747 SyncManager::Status::~Status() { | |
| 748 } | |
| 749 | |
| 750 bool SyncManager::Init( | |
| 751 const FilePath& database_location, | |
| 752 const WeakHandle<JsEventHandler>& event_handler, | |
| 753 const std::string& sync_server_and_path, | |
| 754 int sync_server_port, | |
| 755 bool use_ssl, | |
| 756 const scoped_refptr<base::TaskRunner>& blocking_task_runner, | |
| 757 HttpPostProviderFactory* post_factory, | |
| 758 ModelSafeWorkerRegistrar* registrar, | |
| 759 browser_sync::ExtensionsActivityMonitor* extensions_activity_monitor, | |
| 760 ChangeDelegate* change_delegate, | |
| 761 const std::string& user_agent, | |
| 762 const SyncCredentials& credentials, | |
| 763 bool enable_sync_tabs_for_other_clients, | |
| 764 sync_notifier::SyncNotifier* sync_notifier, | |
| 765 const std::string& restored_key_for_bootstrapping, | |
| 766 TestingMode testing_mode, | |
| 767 Encryptor* encryptor, | |
| 768 UnrecoverableErrorHandler* unrecoverable_error_handler, | |
| 769 ReportUnrecoverableErrorFunction report_unrecoverable_error_function) { | |
| 770 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 771 DCHECK(post_factory); | |
| 772 DVLOG(1) << "SyncManager starting Init..."; | |
| 773 std::string server_string(sync_server_and_path); | |
| 774 return data_->Init(database_location, | |
| 775 event_handler, | |
| 776 server_string, | |
| 777 sync_server_port, | |
| 778 use_ssl, | |
| 779 blocking_task_runner, | |
| 780 post_factory, | |
| 781 registrar, | |
| 782 extensions_activity_monitor, | |
| 783 change_delegate, | |
| 784 user_agent, | |
| 785 credentials, | |
| 786 enable_sync_tabs_for_other_clients, | |
| 787 sync_notifier, | |
| 788 restored_key_for_bootstrapping, | |
| 789 testing_mode, | |
| 790 encryptor, | |
| 791 unrecoverable_error_handler, | |
| 792 report_unrecoverable_error_function); | |
| 793 } | |
| 794 | |
| 795 void SyncManager::UpdateCredentials(const SyncCredentials& credentials) { | |
| 796 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 797 data_->UpdateCredentials(credentials); | |
| 798 } | |
| 799 | |
| 800 void SyncManager::UpdateEnabledTypes() { | |
| 801 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 802 data_->UpdateEnabledTypes(); | |
| 803 } | |
| 804 | |
| 805 void SyncManager::MaybeSetSyncTabsInNigoriNode( | |
| 806 ModelTypeSet enabled_types) { | |
| 807 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 808 data_->MaybeSetSyncTabsInNigoriNode(enabled_types); | |
| 809 } | |
| 810 | |
| 811 void SyncManager::ThrowUnrecoverableError() { | |
| 812 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 813 ReadTransaction trans(FROM_HERE, GetUserShare()); | |
| 814 trans.GetWrappedTrans()->OnUnrecoverableError( | |
| 815 FROM_HERE, "Simulating unrecoverable error for testing purposes."); | |
| 816 } | |
| 817 | |
| 818 bool SyncManager::InitialSyncEndedForAllEnabledTypes() { | |
| 819 return data_->InitialSyncEndedForAllEnabledTypes(); | |
| 820 } | |
| 821 | |
| 822 void SyncManager::StartSyncingNormally() { | |
| 823 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 824 data_->StartSyncingNormally(); | |
| 825 } | |
| 826 | |
| 827 void SyncManager::SetEncryptionPassphrase(const std::string& passphrase, | |
| 828 bool is_explicit) { | |
| 829 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 830 data_->SetEncryptionPassphrase(passphrase, is_explicit); | |
| 831 } | |
| 832 | |
| 833 void SyncManager::SetDecryptionPassphrase(const std::string& passphrase) { | |
| 834 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 835 data_->SetDecryptionPassphrase(passphrase); | |
| 836 } | |
| 837 | |
| 838 void SyncManager::EnableEncryptEverything() { | |
| 839 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 840 { | |
| 841 // Update the cryptographer to know we're now encrypting everything. | |
| 842 WriteTransaction trans(FROM_HERE, GetUserShare()); | |
| 843 Cryptographer* cryptographer = trans.GetCryptographer(); | |
| 844 // Only set encrypt everything if we know we can encrypt. This allows the | |
| 845 // user to cancel encryption if they have forgotten their passphrase. | |
| 846 if (cryptographer->is_ready()) | |
| 847 cryptographer->set_encrypt_everything(); | |
| 848 } | |
| 849 | |
| 850 // Reads from cryptographer so will automatically encrypt all | |
| 851 // datatypes and update the nigori node as necessary. Will trigger | |
| 852 // OnPassphraseRequired if necessary. | |
| 853 data_->RefreshEncryption(); | |
| 854 } | |
| 855 | |
| 856 bool SyncManager::EncryptEverythingEnabledForTest() const { | |
| 857 ReadTransaction trans(FROM_HERE, GetUserShare()); | |
| 858 return trans.GetCryptographer()->encrypt_everything(); | |
| 859 } | |
| 860 | |
| 861 bool SyncManager::IsUsingExplicitPassphrase() { | |
| 862 return data_ && data_->IsUsingExplicitPassphrase(); | |
| 863 } | |
| 864 | |
| 865 void SyncManager::RequestCleanupDisabledTypes() { | |
| 866 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 867 if (data_->scheduler()) | |
| 868 data_->scheduler()->ScheduleCleanupDisabledTypes(); | |
| 869 } | |
| 870 | |
| 871 void SyncManager::RequestClearServerData() { | |
| 872 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 873 if (data_->scheduler()) | |
| 874 data_->scheduler()->ScheduleClearUserData(); | |
| 875 } | |
| 876 | |
| 877 void SyncManager::RequestConfig( | |
| 878 ModelTypeSet types, ConfigureReason reason) { | |
| 879 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 880 if (!data_->scheduler()) { | |
| 881 LOG(INFO) | |
| 882 << "SyncManager::RequestConfig: bailing out because scheduler is " | |
| 883 << "null"; | |
| 884 return; | |
| 885 } | |
| 886 StartConfigurationMode(base::Closure()); | |
| 887 data_->scheduler()->ScheduleConfig(types, GetSourceFromReason(reason)); | |
| 888 } | |
| 889 | |
| 890 void SyncManager::StartConfigurationMode(const base::Closure& callback) { | |
| 891 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 892 if (!data_->scheduler()) { | |
| 893 LOG(INFO) | |
| 894 << "SyncManager::StartConfigurationMode: could not start " | |
| 895 << "configuration mode because because scheduler is null"; | |
| 896 return; | |
| 897 } | |
| 898 data_->scheduler()->Start( | |
| 899 browser_sync::SyncScheduler::CONFIGURATION_MODE, callback); | |
| 900 } | |
| 901 | |
| 902 bool SyncManager::SyncInternal::Init( | |
| 903 const FilePath& database_location, | |
| 904 const WeakHandle<JsEventHandler>& event_handler, | |
| 905 const std::string& sync_server_and_path, | |
| 906 int port, | |
| 907 bool use_ssl, | |
| 908 const scoped_refptr<base::TaskRunner>& blocking_task_runner, | |
| 909 HttpPostProviderFactory* post_factory, | |
| 910 ModelSafeWorkerRegistrar* model_safe_worker_registrar, | |
| 911 browser_sync::ExtensionsActivityMonitor* extensions_activity_monitor, | |
| 912 ChangeDelegate* change_delegate, | |
| 913 const std::string& user_agent, | |
| 914 const SyncCredentials& credentials, | |
| 915 bool enable_sync_tabs_for_other_clients, | |
| 916 sync_notifier::SyncNotifier* sync_notifier, | |
| 917 const std::string& restored_key_for_bootstrapping, | |
| 918 TestingMode testing_mode, | |
| 919 Encryptor* encryptor, | |
| 920 UnrecoverableErrorHandler* unrecoverable_error_handler, | |
| 921 ReportUnrecoverableErrorFunction report_unrecoverable_error_function) { | |
| 922 CHECK(!initialized_); | |
| 923 | |
| 924 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 925 | |
| 926 DVLOG(1) << "Starting SyncInternal initialization."; | |
| 927 | |
| 928 weak_handle_this_ = MakeWeakHandle(weak_ptr_factory_.GetWeakPtr()); | |
| 929 | |
| 930 blocking_task_runner_ = blocking_task_runner; | |
| 931 | |
| 932 registrar_ = model_safe_worker_registrar; | |
| 933 change_delegate_ = change_delegate; | |
| 934 testing_mode_ = testing_mode; | |
| 935 | |
| 936 enable_sync_tabs_for_other_clients_ = enable_sync_tabs_for_other_clients; | |
| 937 | |
| 938 sync_notifier_.reset(sync_notifier); | |
| 939 | |
| 940 AddObserver(&js_sync_manager_observer_); | |
| 941 SetJsEventHandler(event_handler); | |
| 942 | |
| 943 AddObserver(&debug_info_event_listener_); | |
| 944 | |
| 945 database_path_ = database_location.Append( | |
| 946 syncable::Directory::kSyncDatabaseFilename); | |
| 947 encryptor_ = encryptor; | |
| 948 unrecoverable_error_handler_ = unrecoverable_error_handler; | |
| 949 report_unrecoverable_error_function_ = report_unrecoverable_error_function; | |
| 950 share_.directory.reset( | |
| 951 new syncable::Directory(encryptor_, | |
| 952 unrecoverable_error_handler_, | |
| 953 report_unrecoverable_error_function_)); | |
| 954 | |
| 955 connection_manager_.reset(new SyncAPIServerConnectionManager( | |
| 956 sync_server_and_path, port, use_ssl, user_agent, post_factory)); | |
| 957 | |
| 958 net::NetworkChangeNotifier::AddIPAddressObserver(this); | |
| 959 observing_ip_address_changes_ = true; | |
| 960 | |
| 961 connection_manager()->AddListener(this); | |
| 962 | |
| 963 | |
| 964 // Test mode does not use a syncer context or syncer thread. | |
| 965 if (testing_mode_ == NON_TEST) { | |
| 966 // Build a SyncSessionContext and store the worker in it. | |
| 967 DVLOG(1) << "Sync is bringing up SyncSessionContext."; | |
| 968 std::vector<SyncEngineEventListener*> listeners; | |
| 969 listeners.push_back(&allstatus_); | |
| 970 listeners.push_back(this); | |
| 971 SyncSessionContext* context = new SyncSessionContext( | |
| 972 connection_manager_.get(), | |
| 973 directory(), | |
| 974 model_safe_worker_registrar, | |
| 975 extensions_activity_monitor, | |
| 976 listeners, | |
| 977 &debug_info_event_listener_, | |
| 978 &traffic_recorder_); | |
| 979 context->set_account_name(credentials.email); | |
| 980 // The SyncScheduler takes ownership of |context|. | |
| 981 scheduler_.reset(new SyncScheduler(name_, context, new Syncer())); | |
| 982 } | |
| 983 | |
| 984 bool signed_in = SignIn(credentials); | |
| 985 | |
| 986 if (signed_in) { | |
| 987 if (scheduler()) { | |
| 988 scheduler()->Start( | |
| 989 browser_sync::SyncScheduler::CONFIGURATION_MODE, base::Closure()); | |
| 990 } | |
| 991 | |
| 992 initialized_ = true; | |
| 993 | |
| 994 // Cryptographer should only be accessed while holding a | |
| 995 // transaction. Grabbing the user share for the transaction | |
| 996 // checks the initialization state, so this must come after | |
| 997 // |initialized_| is set to true. | |
| 998 ReadTransaction trans(FROM_HERE, GetUserShare()); | |
| 999 trans.GetCryptographer()->Bootstrap(restored_key_for_bootstrapping); | |
| 1000 trans.GetCryptographer()->AddObserver(this); | |
| 1001 } | |
| 1002 | |
| 1003 // Notify that initialization is complete. Note: This should be the last to | |
| 1004 // execute if |signed_in| is false. Reason being in that case we would | |
| 1005 // post a task to shutdown sync. But if this function posts any other tasks | |
| 1006 // on the UI thread and if shutdown wins then that tasks would execute on | |
| 1007 // a freed pointer. This is because UI thread is not shut down. | |
| 1008 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
| 1009 OnInitializationComplete( | |
| 1010 MakeWeakHandle(weak_ptr_factory_.GetWeakPtr()), | |
| 1011 signed_in)); | |
| 1012 | |
| 1013 if (!signed_in && testing_mode_ == NON_TEST) | |
| 1014 return false; | |
| 1015 | |
| 1016 sync_notifier_->AddObserver(this); | |
| 1017 | |
| 1018 return signed_in; | |
| 1019 } | |
| 1020 | |
| 1021 void SyncManager::SyncInternal::UpdateCryptographerAndNigori( | |
| 1022 const std::string& chrome_version, | |
| 1023 const base::Closure& done_callback) { | |
| 1024 DCHECK(initialized_); | |
| 1025 browser_sync::GetSessionName( | |
| 1026 blocking_task_runner_, | |
| 1027 base::Bind( | |
| 1028 &SyncManager::SyncInternal::UpdateCryptographerAndNigoriCallback, | |
| 1029 weak_ptr_factory_.GetWeakPtr(), | |
| 1030 chrome_version, | |
| 1031 done_callback)); | |
| 1032 } | |
| 1033 | |
| 1034 void SyncManager::SyncInternal::UpdateNigoriEncryptionState( | |
| 1035 Cryptographer* cryptographer, | |
| 1036 WriteNode* nigori_node) { | |
| 1037 DCHECK(nigori_node); | |
| 1038 sync_pb::NigoriSpecifics nigori = nigori_node->GetNigoriSpecifics(); | |
| 1039 | |
| 1040 if (cryptographer->is_ready() && | |
| 1041 nigori_overwrite_count_ < kNigoriOverwriteLimit) { | |
| 1042 // Does not modify the encrypted blob if the unencrypted data already | |
| 1043 // matches what is about to be written. | |
| 1044 sync_pb::EncryptedData original_keys = nigori.encrypted(); | |
| 1045 if (!cryptographer->GetKeys(nigori.mutable_encrypted())) | |
| 1046 NOTREACHED(); | |
| 1047 | |
| 1048 if (nigori.encrypted().SerializeAsString() != | |
| 1049 original_keys.SerializeAsString()) { | |
| 1050 // We've updated the nigori node's encryption keys. In order to prevent | |
| 1051 // a possible looping of two clients constantly overwriting each other, | |
| 1052 // we limit the absolute number of overwrites per client instantiation. | |
| 1053 nigori_overwrite_count_++; | |
| 1054 UMA_HISTOGRAM_COUNTS("Sync.AutoNigoriOverwrites", | |
| 1055 nigori_overwrite_count_); | |
| 1056 } | |
| 1057 | |
| 1058 // Note: we don't try to set using_explicit_passphrase here since if that | |
| 1059 // is lost the user can always set it again. The main point is to preserve | |
| 1060 // the encryption keys so all data remains decryptable. | |
| 1061 } | |
| 1062 cryptographer->UpdateNigoriFromEncryptedTypes(&nigori); | |
| 1063 | |
| 1064 // If nothing has changed, this is a no-op. | |
| 1065 nigori_node->SetNigoriSpecifics(nigori); | |
| 1066 } | |
| 1067 | |
| 1068 void SyncManager::SyncInternal::UpdateCryptographerAndNigoriCallback( | |
| 1069 const std::string& chrome_version, | |
| 1070 const base::Closure& done_callback, | |
| 1071 const std::string& session_name) { | |
| 1072 if (!directory()->initial_sync_ended_for_type(syncable::NIGORI)) { | |
| 1073 done_callback.Run(); // Should only happen during first time sync. | |
| 1074 return; | |
| 1075 } | |
| 1076 | |
| 1077 bool success = false; | |
| 1078 { | |
| 1079 WriteTransaction trans(FROM_HERE, GetUserShare()); | |
| 1080 Cryptographer* cryptographer = trans.GetCryptographer(); | |
| 1081 WriteNode node(&trans); | |
| 1082 | |
| 1083 if (node.InitByTagLookup(kNigoriTag) == sync_api::BaseNode::INIT_OK) { | |
| 1084 sync_pb::NigoriSpecifics nigori(node.GetNigoriSpecifics()); | |
| 1085 Cryptographer::UpdateResult result = cryptographer->Update(nigori); | |
| 1086 if (result == Cryptographer::NEEDS_PASSPHRASE) { | |
| 1087 sync_pb::EncryptedData pending_keys; | |
| 1088 if (cryptographer->has_pending_keys()) | |
| 1089 pending_keys = cryptographer->GetPendingKeys(); | |
| 1090 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
| 1091 OnPassphraseRequired(sync_api::REASON_DECRYPTION, | |
| 1092 pending_keys)); | |
| 1093 } | |
| 1094 | |
| 1095 | |
| 1096 // Add or update device information. | |
| 1097 bool contains_this_device = false; | |
| 1098 for (int i = 0; i < nigori.device_information_size(); ++i) { | |
| 1099 const sync_pb::DeviceInformation& device_information = | |
| 1100 nigori.device_information(i); | |
| 1101 if (device_information.cache_guid() == directory()->cache_guid()) { | |
| 1102 // Update the version number in case it changed due to an update. | |
| 1103 if (device_information.chrome_version() != chrome_version) { | |
| 1104 sync_pb::DeviceInformation* mutable_device_information = | |
| 1105 nigori.mutable_device_information(i); | |
| 1106 mutable_device_information->set_chrome_version( | |
| 1107 chrome_version); | |
| 1108 } | |
| 1109 contains_this_device = true; | |
| 1110 } | |
| 1111 } | |
| 1112 | |
| 1113 if (!contains_this_device) { | |
| 1114 sync_pb::DeviceInformation* device_information = | |
| 1115 nigori.add_device_information(); | |
| 1116 device_information->set_cache_guid(directory()->cache_guid()); | |
| 1117 #if defined(OS_CHROMEOS) | |
| 1118 device_information->set_platform("ChromeOS"); | |
| 1119 #elif defined(OS_LINUX) | |
| 1120 device_information->set_platform("Linux"); | |
| 1121 #elif defined(OS_MACOSX) | |
| 1122 device_information->set_platform("Mac"); | |
| 1123 #elif defined(OS_WIN) | |
| 1124 device_information->set_platform("Windows"); | |
| 1125 #endif | |
| 1126 device_information->set_name(session_name); | |
| 1127 device_information->set_chrome_version(chrome_version); | |
| 1128 } | |
| 1129 // Disabled to avoid nigori races. TODO(zea): re-enable. crbug.com/122837 | |
| 1130 // node.SetNigoriSpecifics(nigori); | |
| 1131 | |
| 1132 // Make sure the nigori node has the up to date encryption info. | |
| 1133 UpdateNigoriEncryptionState(cryptographer, &node); | |
| 1134 | |
| 1135 NotifyCryptographerState(cryptographer); | |
| 1136 allstatus_.SetEncryptedTypes(cryptographer->GetEncryptedTypes()); | |
| 1137 | |
| 1138 success = cryptographer->is_ready(); | |
| 1139 } else { | |
| 1140 NOTREACHED(); | |
| 1141 } | |
| 1142 } | |
| 1143 | |
| 1144 if (success) | |
| 1145 RefreshEncryption(); | |
| 1146 done_callback.Run(); | |
| 1147 } | |
| 1148 | |
| 1149 void SyncManager::SyncInternal::NotifyCryptographerState( | |
| 1150 Cryptographer * cryptographer) { | |
| 1151 // TODO(lipalani): Explore the possibility of hooking this up to | |
| 1152 // SyncManager::Observer and making |AllStatus| a listener for that. | |
| 1153 allstatus_.SetCryptographerReady(cryptographer->is_ready()); | |
| 1154 allstatus_.SetCryptoHasPendingKeys(cryptographer->has_pending_keys()); | |
| 1155 debug_info_event_listener_.SetCryptographerReady(cryptographer->is_ready()); | |
| 1156 debug_info_event_listener_.SetCrytographerHasPendingKeys( | |
| 1157 cryptographer->has_pending_keys()); | |
| 1158 } | |
| 1159 | |
| 1160 void SyncManager::SyncInternal::StartSyncingNormally() { | |
| 1161 // Start the sync scheduler. | |
| 1162 if (scheduler()) // NULL during certain unittests. | |
| 1163 scheduler()->Start(SyncScheduler::NORMAL_MODE, base::Closure()); | |
| 1164 } | |
| 1165 | |
| 1166 bool SyncManager::SyncInternal::OpenDirectory() { | |
| 1167 DCHECK(!initialized_) << "Should only happen once"; | |
| 1168 | |
| 1169 // Set before Open(). | |
| 1170 change_observer_ = | |
| 1171 browser_sync::MakeWeakHandle(js_mutation_event_observer_.AsWeakPtr()); | |
| 1172 WeakHandle<syncable::TransactionObserver> transaction_observer( | |
| 1173 browser_sync::MakeWeakHandle(js_mutation_event_observer_.AsWeakPtr())); | |
| 1174 | |
| 1175 syncable::DirOpenResult open_result = syncable::NOT_INITIALIZED; | |
| 1176 if (testing_mode_ == TEST_IN_MEMORY) { | |
| 1177 open_result = directory()->OpenInMemoryForTest( | |
| 1178 username_for_share(), this, transaction_observer); | |
| 1179 } else { | |
| 1180 open_result = directory()->Open( | |
| 1181 database_path_, username_for_share(), this, transaction_observer); | |
| 1182 } | |
| 1183 if (open_result != syncable::OPENED) { | |
| 1184 LOG(ERROR) << "Could not open share for:" << username_for_share(); | |
| 1185 return false; | |
| 1186 } | |
| 1187 | |
| 1188 connection_manager()->set_client_id(directory()->cache_guid()); | |
| 1189 return true; | |
| 1190 } | |
| 1191 | |
| 1192 bool SyncManager::SyncInternal::SignIn(const SyncCredentials& credentials) { | |
| 1193 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1194 DCHECK(share_.name.empty()); | |
| 1195 share_.name = credentials.email; | |
| 1196 | |
| 1197 DVLOG(1) << "Signing in user: " << username_for_share(); | |
| 1198 if (!OpenDirectory()) | |
| 1199 return false; | |
| 1200 | |
| 1201 // Retrieve and set the sync notifier state. This should be done | |
| 1202 // only after OpenDirectory is called. | |
| 1203 std::string unique_id = directory()->cache_guid(); | |
| 1204 std::string state = directory()->GetNotificationState(); | |
| 1205 DVLOG(1) << "Read notification unique ID: " << unique_id; | |
| 1206 if (VLOG_IS_ON(1)) { | |
| 1207 std::string encoded_state; | |
| 1208 base::Base64Encode(state, &encoded_state); | |
| 1209 DVLOG(1) << "Read notification state: " << encoded_state; | |
| 1210 } | |
| 1211 allstatus_.SetUniqueId(unique_id); | |
| 1212 sync_notifier_->SetUniqueId(unique_id); | |
| 1213 sync_notifier_->SetState(state); | |
| 1214 | |
| 1215 UpdateCredentials(credentials); | |
| 1216 UpdateEnabledTypes(); | |
| 1217 return true; | |
| 1218 } | |
| 1219 | |
| 1220 void SyncManager::SyncInternal::UpdateCredentials( | |
| 1221 const SyncCredentials& credentials) { | |
| 1222 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1223 DCHECK_EQ(credentials.email, share_.name); | |
| 1224 DCHECK(!credentials.email.empty()); | |
| 1225 DCHECK(!credentials.sync_token.empty()); | |
| 1226 | |
| 1227 observing_ip_address_changes_ = true; | |
| 1228 if (connection_manager()->set_auth_token(credentials.sync_token)) { | |
| 1229 sync_notifier_->UpdateCredentials( | |
| 1230 credentials.email, credentials.sync_token); | |
| 1231 if (testing_mode_ == NON_TEST && initialized_) { | |
| 1232 if (scheduler()) | |
| 1233 scheduler()->OnCredentialsUpdated(); | |
| 1234 } | |
| 1235 } | |
| 1236 } | |
| 1237 | |
| 1238 void SyncManager::SyncInternal::UpdateEnabledTypes() { | |
| 1239 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1240 ModelSafeRoutingInfo routes; | |
| 1241 registrar_->GetModelSafeRoutingInfo(&routes); | |
| 1242 const ModelTypeSet enabled_types = GetRoutingInfoTypes(routes); | |
| 1243 sync_notifier_->UpdateEnabledTypes(enabled_types); | |
| 1244 if (enable_sync_tabs_for_other_clients_) | |
| 1245 MaybeSetSyncTabsInNigoriNode(enabled_types); | |
| 1246 } | |
| 1247 | |
| 1248 void SyncManager::SyncInternal::MaybeSetSyncTabsInNigoriNode( | |
| 1249 const ModelTypeSet enabled_types) { | |
| 1250 // The initialized_ check is to ensure that we don't CHECK in GetUserShare | |
| 1251 // when this is called on start-up. It's ok to ignore that case, since | |
| 1252 // presumably this would've run when the user originally enabled sessions. | |
| 1253 if (initialized_ && enabled_types.Has(syncable::SESSIONS)) { | |
| 1254 WriteTransaction trans(FROM_HERE, GetUserShare()); | |
| 1255 WriteNode node(&trans); | |
| 1256 if (node.InitByTagLookup(kNigoriTag) != sync_api::BaseNode::INIT_OK) { | |
| 1257 LOG(WARNING) << "Unable to set 'sync_tabs' bit because Nigori node not " | |
| 1258 << "found."; | |
| 1259 return; | |
| 1260 } | |
| 1261 | |
| 1262 sync_pb::NigoriSpecifics specifics(node.GetNigoriSpecifics()); | |
| 1263 specifics.set_sync_tabs(true); | |
| 1264 node.SetNigoriSpecifics(specifics); | |
| 1265 } | |
| 1266 } | |
| 1267 | |
| 1268 void SyncManager::SyncInternal::SetEncryptionPassphrase( | |
| 1269 const std::string& passphrase, | |
| 1270 bool is_explicit) { | |
| 1271 // We do not accept empty passphrases. | |
| 1272 if (passphrase.empty()) { | |
| 1273 NOTREACHED() << "Cannot encrypt with an empty passphrase."; | |
| 1274 return; | |
| 1275 } | |
| 1276 | |
| 1277 // All accesses to the cryptographer are protected by a transaction. | |
| 1278 WriteTransaction trans(FROM_HERE, GetUserShare()); | |
| 1279 Cryptographer* cryptographer = trans.GetCryptographer(); | |
| 1280 KeyParams key_params = {"localhost", "dummy", passphrase}; | |
| 1281 WriteNode node(&trans); | |
| 1282 if (node.InitByTagLookup(kNigoriTag) != sync_api::BaseNode::INIT_OK) { | |
| 1283 // TODO(albertb): Plumb an UnrecoverableError all the way back to the PSS. | |
| 1284 NOTREACHED(); | |
| 1285 return; | |
| 1286 } | |
| 1287 | |
| 1288 bool nigori_has_explicit_passphrase = | |
| 1289 node.GetNigoriSpecifics().using_explicit_passphrase(); | |
| 1290 std::string bootstrap_token; | |
| 1291 sync_pb::EncryptedData pending_keys; | |
| 1292 if (cryptographer->has_pending_keys()) | |
| 1293 pending_keys = cryptographer->GetPendingKeys(); | |
| 1294 bool success = false; | |
| 1295 | |
| 1296 | |
| 1297 // There are six cases to handle here: | |
| 1298 // 1. The user has no pending keys and is setting their current GAIA password | |
| 1299 // as the encryption passphrase. This happens either during first time sync | |
| 1300 // with a clean profile, or after re-authenticating on a profile that was | |
| 1301 // already signed in with the cryptographer ready. | |
| 1302 // 2. The user has no pending keys, and is overwriting an (already provided) | |
| 1303 // implicit passphrase with an explicit (custom) passphrase. | |
| 1304 // 3. The user has pending keys for an explicit passphrase that is somehow set | |
| 1305 // to their current GAIA passphrase. | |
| 1306 // 4. The user has pending keys encrypted with their current GAIA passphrase | |
| 1307 // and the caller passes in the current GAIA passphrase. | |
| 1308 // 5. The user has pending keys encrypted with an older GAIA passphrase | |
| 1309 // and the caller passes in the current GAIA passphrase. | |
| 1310 // 6. The user has previously done encryption with an explicit passphrase. | |
| 1311 // Furthermore, we enforce the fact that the bootstrap encryption token will | |
| 1312 // always be derived from the newest GAIA password if the account is using | |
| 1313 // an implicit passphrase (even if the data is encrypted with an old GAIA | |
| 1314 // password). If the account is using an explicit (custom) passphrase, the | |
| 1315 // bootstrap token will be derived from the most recently provided explicit | |
| 1316 // passphrase (that was able to decrypt the data). | |
| 1317 if (!nigori_has_explicit_passphrase) { | |
| 1318 if (!cryptographer->has_pending_keys()) { | |
| 1319 if (cryptographer->AddKey(key_params)) { | |
| 1320 // Case 1 and 2. We set a new GAIA passphrase when there are no pending | |
| 1321 // keys (1), or overwriting an implicit passphrase with a new explicit | |
| 1322 // one (2) when there are no pending keys. | |
| 1323 DVLOG(1) << "Setting " << (is_explicit ? "explicit" : "implicit" ) | |
| 1324 << " passphrase for encryption."; | |
| 1325 cryptographer->GetBootstrapToken(&bootstrap_token); | |
| 1326 success = true; | |
| 1327 } else { | |
| 1328 NOTREACHED() << "Failed to add key to cryptographer."; | |
| 1329 success = false; | |
| 1330 } | |
| 1331 } else { // cryptographer->has_pending_keys() == true | |
| 1332 if (is_explicit) { | |
| 1333 // This can only happen if the nigori node is updated with a new | |
| 1334 // implicit passphrase while a client is attempting to set a new custom | |
| 1335 // passphrase (race condition). | |
| 1336 DVLOG(1) << "Failing because an implicit passphrase is already set."; | |
| 1337 success = false; | |
| 1338 } else { // is_explicit == false | |
| 1339 if (cryptographer->DecryptPendingKeys(key_params)) { | |
| 1340 // Case 4. We successfully decrypted with the implicit GAIA passphrase | |
| 1341 // passed in. | |
| 1342 DVLOG(1) << "Implicit internal passphrase accepted for decryption."; | |
| 1343 cryptographer->GetBootstrapToken(&bootstrap_token); | |
| 1344 success = true; | |
| 1345 } else { | |
| 1346 // Case 5. Encryption was done with an old GAIA password, but we were | |
| 1347 // provided with the current GAIA password. We need to generate a new | |
| 1348 // bootstrap token to preserve it. We build a temporary cryptographer | |
| 1349 // to allow us to extract these params without polluting our current | |
| 1350 // cryptographer. | |
| 1351 DVLOG(1) << "Implicit internal passphrase failed to decrypt, adding " | |
| 1352 << "anyways as default passphrase and persisting via " | |
| 1353 << "bootstrap token."; | |
| 1354 Cryptographer temp_cryptographer(encryptor_); | |
| 1355 temp_cryptographer.AddKey(key_params); | |
| 1356 temp_cryptographer.GetBootstrapToken(&bootstrap_token); | |
| 1357 // We then set the new passphrase as the default passphrase of the | |
| 1358 // real cryptographer, even though we have pending keys. This is safe, | |
| 1359 // as although Cryptographer::is_initialized() will now be true, | |
| 1360 // is_ready() will remain false due to having pending keys. | |
| 1361 cryptographer->AddKey(key_params); | |
| 1362 success = false; | |
| 1363 } | |
| 1364 } // is_explicit | |
| 1365 } // cryptographer->has_pending_keys() | |
| 1366 } else { // nigori_has_explicit_passphrase == true | |
| 1367 // Case 6. We do not want to override a previously set explicit passphrase, | |
| 1368 // so we return a failure. | |
| 1369 DVLOG(1) << "Failing because an explicit passphrase is already set."; | |
| 1370 success = false; | |
| 1371 } | |
| 1372 | |
| 1373 DVLOG_IF(1, !success) | |
| 1374 << "Failure in SetEncryptionPassphrase; notifying and returning."; | |
| 1375 DVLOG_IF(1, success) | |
| 1376 << "Successfully set encryption passphrase; updating nigori and " | |
| 1377 "reencrypting."; | |
| 1378 | |
| 1379 FinishSetPassphrase( | |
| 1380 success, bootstrap_token, is_explicit, &trans, &node); | |
| 1381 } | |
| 1382 | |
| 1383 void SyncManager::SyncInternal::SetDecryptionPassphrase( | |
| 1384 const std::string& passphrase) { | |
| 1385 // We do not accept empty passphrases. | |
| 1386 if (passphrase.empty()) { | |
| 1387 NOTREACHED() << "Cannot decrypt with an empty passphrase."; | |
| 1388 return; | |
| 1389 } | |
| 1390 | |
| 1391 // All accesses to the cryptographer are protected by a transaction. | |
| 1392 WriteTransaction trans(FROM_HERE, GetUserShare()); | |
| 1393 Cryptographer* cryptographer = trans.GetCryptographer(); | |
| 1394 KeyParams key_params = {"localhost", "dummy", passphrase}; | |
| 1395 WriteNode node(&trans); | |
| 1396 if (node.InitByTagLookup(kNigoriTag) != sync_api::BaseNode::INIT_OK) { | |
| 1397 // TODO(albertb): Plumb an UnrecoverableError all the way back to the PSS. | |
| 1398 NOTREACHED(); | |
| 1399 return; | |
| 1400 } | |
| 1401 | |
| 1402 if (!cryptographer->has_pending_keys()) { | |
| 1403 // Note that this *can* happen in a rare situation where data is | |
| 1404 // re-encrypted on another client while a SetDecryptionPassphrase() call is | |
| 1405 // in-flight on this client. It is rare enough that we choose to do nothing. | |
| 1406 NOTREACHED() << "Attempt to set decryption passphrase failed because there " | |
| 1407 << "were no pending keys."; | |
| 1408 return; | |
| 1409 } | |
| 1410 | |
| 1411 bool nigori_has_explicit_passphrase = | |
| 1412 node.GetNigoriSpecifics().using_explicit_passphrase(); | |
| 1413 std::string bootstrap_token; | |
| 1414 sync_pb::EncryptedData pending_keys; | |
| 1415 pending_keys = cryptographer->GetPendingKeys(); | |
| 1416 bool success = false; | |
| 1417 | |
| 1418 // There are three cases to handle here: | |
| 1419 // 7. We're using the current GAIA password to decrypt the pending keys. This | |
| 1420 // happens when signing in to an account with a previously set implicit | |
| 1421 // passphrase, where the data is already encrypted with the newest GAIA | |
| 1422 // password. | |
| 1423 // 8. The user is providing an old GAIA password to decrypt the pending keys. | |
| 1424 // In this case, the user is using an implicit passphrase, but has changed | |
| 1425 // their password since they last encrypted their data, and therefore | |
| 1426 // their current GAIA password was unable to decrypt the data. This will | |
| 1427 // happen when the user is setting up a new profile with a previously | |
| 1428 // encrypted account (after changing passwords). | |
| 1429 // 9. The user is providing a previously set explicit passphrase to decrypt | |
| 1430 // the pending keys. | |
| 1431 if (!nigori_has_explicit_passphrase) { | |
| 1432 if (cryptographer->is_initialized()) { | |
| 1433 // We only want to change the default encryption key to the pending | |
| 1434 // one if the pending keybag already contains the current default. | |
| 1435 // This covers the case where a different client re-encrypted | |
| 1436 // everything with a newer gaia passphrase (and hence the keybag | |
| 1437 // contains keys from all previously used gaia passphrases). | |
| 1438 // Otherwise, we're in a situation where the pending keys are | |
| 1439 // encrypted with an old gaia passphrase, while the default is the | |
| 1440 // current gaia passphrase. In that case, we preserve the default. | |
| 1441 Cryptographer temp_cryptographer(encryptor_); | |
| 1442 temp_cryptographer.SetPendingKeys(cryptographer->GetPendingKeys()); | |
| 1443 if (temp_cryptographer.DecryptPendingKeys(key_params)) { | |
| 1444 // Check to see if the pending bag of keys contains the current | |
| 1445 // default key. | |
| 1446 sync_pb::EncryptedData encrypted; | |
| 1447 cryptographer->GetKeys(&encrypted); | |
| 1448 if (temp_cryptographer.CanDecrypt(encrypted)) { | |
| 1449 DVLOG(1) << "Implicit user provided passphrase accepted for " | |
| 1450 << "decryption, overwriting default."; | |
| 1451 // Case 7. The pending keybag contains the current default. Go ahead | |
| 1452 // and update the cryptographer, letting the default change. | |
| 1453 cryptographer->DecryptPendingKeys(key_params); | |
| 1454 cryptographer->GetBootstrapToken(&bootstrap_token); | |
| 1455 success = true; | |
| 1456 } else { | |
| 1457 // Case 8. The pending keybag does not contain the current default | |
| 1458 // encryption key. We decrypt the pending keys here, and in | |
| 1459 // FinishSetPassphrase, re-encrypt everything with the current GAIA | |
| 1460 // passphrase instead of the passphrase just provided by the user. | |
| 1461 DVLOG(1) << "Implicit user provided passphrase accepted for " | |
| 1462 << "decryption, restoring implicit internal passphrase " | |
| 1463 << "as default."; | |
| 1464 std::string bootstrap_token_from_current_key; | |
| 1465 cryptographer->GetBootstrapToken( | |
| 1466 &bootstrap_token_from_current_key); | |
| 1467 cryptographer->DecryptPendingKeys(key_params); | |
| 1468 // Overwrite the default from the pending keys. | |
| 1469 cryptographer->AddKeyFromBootstrapToken( | |
| 1470 bootstrap_token_from_current_key); | |
| 1471 success = true; | |
| 1472 } | |
| 1473 } else { // !temp_cryptographer.DecryptPendingKeys(..) | |
| 1474 DVLOG(1) << "Implicit user provided passphrase failed to decrypt."; | |
| 1475 success = false; | |
| 1476 } // temp_cryptographer.DecryptPendingKeys(...) | |
| 1477 } else { // cryptographer->is_initialized() == false | |
| 1478 if (cryptographer->DecryptPendingKeys(key_params)) { | |
| 1479 // This can happpen in two cases: | |
| 1480 // - First time sync on android, where we'll never have a | |
| 1481 // !user_provided passphrase. | |
| 1482 // - This is a restart for a client that lost their bootstrap token. | |
| 1483 // In both cases, we should go ahead and initialize the cryptographer | |
| 1484 // and persist the new bootstrap token. | |
| 1485 // | |
| 1486 // Note: at this point, we cannot distinguish between cases 7 and 8 | |
| 1487 // above. This user provided passphrase could be the current or the | |
| 1488 // old. But, as long as we persist the token, there's nothing more | |
| 1489 // we can do. | |
| 1490 cryptographer->GetBootstrapToken(&bootstrap_token); | |
| 1491 DVLOG(1) << "Implicit user provided passphrase accepted, initializing" | |
| 1492 << " cryptographer."; | |
| 1493 success = true; | |
| 1494 } else { | |
| 1495 DVLOG(1) << "Implicit user provided passphrase failed to decrypt."; | |
| 1496 success = false; | |
| 1497 } | |
| 1498 } // cryptographer->is_initialized() | |
| 1499 } else { // nigori_has_explicit_passphrase == true | |
| 1500 // Case 9. Encryption was done with an explicit passphrase, and we decrypt | |
| 1501 // with the passphrase provided by the user. | |
| 1502 if (cryptographer->DecryptPendingKeys(key_params)) { | |
| 1503 DVLOG(1) << "Explicit passphrase accepted for decryption."; | |
| 1504 cryptographer->GetBootstrapToken(&bootstrap_token); | |
| 1505 success = true; | |
| 1506 } else { | |
| 1507 DVLOG(1) << "Explicit passphrase failed to decrypt."; | |
| 1508 success = false; | |
| 1509 } | |
| 1510 } // nigori_has_explicit_passphrase | |
| 1511 | |
| 1512 DVLOG_IF(1, !success) | |
| 1513 << "Failure in SetDecryptionPassphrase; notifying and returning."; | |
| 1514 DVLOG_IF(1, success) | |
| 1515 << "Successfully set decryption passphrase; updating nigori and " | |
| 1516 "reencrypting."; | |
| 1517 | |
| 1518 FinishSetPassphrase(success, | |
| 1519 bootstrap_token, | |
| 1520 nigori_has_explicit_passphrase, | |
| 1521 &trans, | |
| 1522 &node); | |
| 1523 } | |
| 1524 | |
| 1525 void SyncManager::SyncInternal::FinishSetPassphrase( | |
| 1526 bool success, | |
| 1527 const std::string& bootstrap_token, | |
| 1528 bool is_explicit, | |
| 1529 WriteTransaction* trans, | |
| 1530 WriteNode* nigori_node) { | |
| 1531 Cryptographer* cryptographer = trans->GetCryptographer(); | |
| 1532 NotifyCryptographerState(cryptographer); | |
| 1533 | |
| 1534 // It's possible we need to change the bootstrap token even if we failed to | |
| 1535 // set the passphrase (for example if we need to preserve the new GAIA | |
| 1536 // passphrase). | |
| 1537 if (!bootstrap_token.empty()) { | |
| 1538 DVLOG(1) << "Bootstrap token updated."; | |
| 1539 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
| 1540 OnBootstrapTokenUpdated(bootstrap_token)); | |
| 1541 } | |
| 1542 | |
| 1543 if (!success) { | |
| 1544 if (cryptographer->is_ready()) { | |
| 1545 LOG(ERROR) << "Attempt to change passphrase failed while cryptographer " | |
| 1546 << "was ready."; | |
| 1547 } else if (cryptographer->has_pending_keys()) { | |
| 1548 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
| 1549 OnPassphraseRequired(sync_api::REASON_DECRYPTION, | |
| 1550 cryptographer->GetPendingKeys())); | |
| 1551 } else { | |
| 1552 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
| 1553 OnPassphraseRequired(sync_api::REASON_ENCRYPTION, | |
| 1554 sync_pb::EncryptedData())); | |
| 1555 } | |
| 1556 return; | |
| 1557 } | |
| 1558 | |
| 1559 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
| 1560 OnPassphraseAccepted()); | |
| 1561 DCHECK(cryptographer->is_ready()); | |
| 1562 | |
| 1563 // TODO(tim): Bug 58231. It would be nice if setting a passphrase didn't | |
| 1564 // require messing with the Nigori node, because we can't set a passphrase | |
| 1565 // until download conditions are met vs Cryptographer init. It seems like | |
| 1566 // it's safe to defer this work. | |
| 1567 sync_pb::NigoriSpecifics specifics(nigori_node->GetNigoriSpecifics()); | |
| 1568 // Does not modify specifics.encrypted() if the original decrypted data was | |
| 1569 // the same. | |
| 1570 if (!cryptographer->GetKeys(specifics.mutable_encrypted())) { | |
| 1571 NOTREACHED(); | |
| 1572 return; | |
| 1573 } | |
| 1574 specifics.set_using_explicit_passphrase(is_explicit); | |
| 1575 nigori_node->SetNigoriSpecifics(specifics); | |
| 1576 | |
| 1577 // Does nothing if everything is already encrypted or the cryptographer has | |
| 1578 // pending keys. | |
| 1579 ReEncryptEverything(trans); | |
| 1580 } | |
| 1581 | |
| 1582 bool SyncManager::SyncInternal::IsUsingExplicitPassphrase() { | |
| 1583 ReadTransaction trans(FROM_HERE, &share_); | |
| 1584 ReadNode node(&trans); | |
| 1585 if (node.InitByTagLookup(kNigoriTag) != sync_api::BaseNode::INIT_OK) { | |
| 1586 // TODO(albertb): Plumb an UnrecoverableError all the way back to the PSS. | |
| 1587 NOTREACHED(); | |
| 1588 return false; | |
| 1589 } | |
| 1590 | |
| 1591 return node.GetNigoriSpecifics().using_explicit_passphrase(); | |
| 1592 } | |
| 1593 | |
| 1594 void SyncManager::SyncInternal::RefreshEncryption() { | |
| 1595 DCHECK(initialized_); | |
| 1596 | |
| 1597 WriteTransaction trans(FROM_HERE, GetUserShare()); | |
| 1598 WriteNode node(&trans); | |
| 1599 if (node.InitByTagLookup(kNigoriTag) != sync_api::BaseNode::INIT_OK) { | |
| 1600 NOTREACHED() << "Unable to set encrypted datatypes because Nigori node not " | |
| 1601 << "found."; | |
| 1602 return; | |
| 1603 } | |
| 1604 | |
| 1605 Cryptographer* cryptographer = trans.GetCryptographer(); | |
| 1606 | |
| 1607 if (!cryptographer->is_ready()) { | |
| 1608 DVLOG(1) << "Attempting to encrypt datatypes when cryptographer not " | |
| 1609 << "initialized, prompting for passphrase."; | |
| 1610 // TODO(zea): this isn't really decryption, but that's the only way we have | |
| 1611 // to prompt the user for a passsphrase. See http://crbug.com/91379. | |
| 1612 sync_pb::EncryptedData pending_keys; | |
| 1613 if (cryptographer->has_pending_keys()) | |
| 1614 pending_keys = cryptographer->GetPendingKeys(); | |
| 1615 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
| 1616 OnPassphraseRequired(sync_api::REASON_DECRYPTION, | |
| 1617 pending_keys)); | |
| 1618 return; | |
| 1619 } | |
| 1620 | |
| 1621 UpdateNigoriEncryptionState(cryptographer, &node); | |
| 1622 | |
| 1623 allstatus_.SetEncryptedTypes(cryptographer->GetEncryptedTypes()); | |
| 1624 | |
| 1625 // We reencrypt everything regardless of whether the set of encrypted | |
| 1626 // types changed to ensure that any stray unencrypted entries are overwritten. | |
| 1627 ReEncryptEverything(&trans); | |
| 1628 } | |
| 1629 | |
| 1630 void SyncManager::SyncInternal::ReEncryptEverything(WriteTransaction* trans) { | |
| 1631 Cryptographer* cryptographer = trans->GetCryptographer(); | |
| 1632 if (!cryptographer || !cryptographer->is_ready()) | |
| 1633 return; | |
| 1634 syncable::ModelTypeSet encrypted_types = GetEncryptedTypes(trans); | |
| 1635 ModelSafeRoutingInfo routes; | |
| 1636 registrar_->GetModelSafeRoutingInfo(&routes); | |
| 1637 std::string tag; | |
| 1638 for (syncable::ModelTypeSet::Iterator iter = encrypted_types.First(); | |
| 1639 iter.Good(); iter.Inc()) { | |
| 1640 if (iter.Get() == syncable::PASSWORDS || | |
| 1641 iter.Get() == syncable::NIGORI || | |
| 1642 routes.count(iter.Get()) == 0) | |
| 1643 continue; | |
| 1644 ReadNode type_root(trans); | |
| 1645 tag = syncable::ModelTypeToRootTag(iter.Get()); | |
| 1646 if (type_root.InitByTagLookup(tag) != sync_api::BaseNode::INIT_OK) { | |
| 1647 // This can happen when we enable a datatype for the first time on restart | |
| 1648 // (for example when we upgrade) and therefore haven't done the initial | |
| 1649 // download for that type at the time we RefreshEncryption. There's | |
| 1650 // nothing we can do for now, so just move on to the next type. | |
| 1651 continue; | |
| 1652 } | |
| 1653 | |
| 1654 // Iterate through all children of this datatype. | |
| 1655 std::queue<int64> to_visit; | |
| 1656 int64 child_id = type_root.GetFirstChildId(); | |
| 1657 to_visit.push(child_id); | |
| 1658 while (!to_visit.empty()) { | |
| 1659 child_id = to_visit.front(); | |
| 1660 to_visit.pop(); | |
| 1661 if (child_id == kInvalidId) | |
| 1662 continue; | |
| 1663 | |
| 1664 WriteNode child(trans); | |
| 1665 if (child.InitByIdLookup(child_id) != sync_api::BaseNode::INIT_OK) { | |
| 1666 NOTREACHED(); | |
| 1667 continue; | |
| 1668 } | |
| 1669 if (child.GetIsFolder()) { | |
| 1670 to_visit.push(child.GetFirstChildId()); | |
| 1671 } | |
| 1672 if (child.GetEntry()->Get(syncable::UNIQUE_SERVER_TAG).empty()) { | |
| 1673 // Rewrite the specifics of the node with encrypted data if necessary | |
| 1674 // (only rewrite the non-unique folders). | |
| 1675 child.ResetFromSpecifics(); | |
| 1676 } | |
| 1677 to_visit.push(child.GetSuccessorId()); | |
| 1678 } | |
| 1679 } | |
| 1680 | |
| 1681 if (routes.count(syncable::PASSWORDS) > 0) { | |
| 1682 // Passwords are encrypted with their own legacy scheme. | |
| 1683 ReadNode passwords_root(trans); | |
| 1684 std::string passwords_tag = | |
| 1685 syncable::ModelTypeToRootTag(syncable::PASSWORDS); | |
| 1686 // It's possible we'll have the password routing info and not the password | |
| 1687 // root if we attempted to set a passphrase before passwords was enabled. | |
| 1688 if (passwords_root.InitByTagLookup(passwords_tag) == | |
| 1689 sync_api::BaseNode::INIT_OK) { | |
| 1690 int64 child_id = passwords_root.GetFirstChildId(); | |
| 1691 while (child_id != kInvalidId) { | |
| 1692 WriteNode child(trans); | |
| 1693 if (child.InitByIdLookup(child_id) != sync_api::BaseNode::INIT_OK) { | |
| 1694 NOTREACHED(); | |
| 1695 return; | |
| 1696 } | |
| 1697 child.SetPasswordSpecifics(child.GetPasswordSpecifics()); | |
| 1698 child_id = child.GetSuccessorId(); | |
| 1699 } | |
| 1700 } | |
| 1701 } | |
| 1702 | |
| 1703 // NOTE: We notify from within a transaction. | |
| 1704 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, OnEncryptionComplete()); | |
| 1705 } | |
| 1706 | |
| 1707 SyncManager::~SyncManager() { | |
| 1708 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1709 delete data_; | |
| 1710 } | |
| 1711 | |
| 1712 void SyncManager::AddObserver(Observer* observer) { | |
| 1713 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1714 data_->AddObserver(observer); | |
| 1715 } | |
| 1716 | |
| 1717 void SyncManager::RemoveObserver(Observer* observer) { | |
| 1718 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1719 data_->RemoveObserver(observer); | |
| 1720 } | |
| 1721 | |
| 1722 void SyncManager::StopSyncingForShutdown(const base::Closure& callback) { | |
| 1723 data_->StopSyncingForShutdown(callback); | |
| 1724 } | |
| 1725 | |
| 1726 void SyncManager::SyncInternal::StopSyncingForShutdown( | |
| 1727 const base::Closure& callback) { | |
| 1728 DVLOG(2) << "StopSyncingForShutdown"; | |
| 1729 if (scheduler()) // May be null in tests. | |
| 1730 scheduler()->RequestStop(callback); | |
| 1731 else | |
| 1732 created_on_loop_->PostTask(FROM_HERE, callback); | |
| 1733 | |
| 1734 if (connection_manager_.get()) | |
| 1735 connection_manager_->TerminateAllIO(); | |
| 1736 } | |
| 1737 | |
| 1738 void SyncManager::ShutdownOnSyncThread() { | |
| 1739 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1740 data_->ShutdownOnSyncThread(); | |
| 1741 } | |
| 1742 | |
| 1743 void SyncManager::SyncInternal::ShutdownOnSyncThread() { | |
| 1744 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1745 | |
| 1746 // Prevent any in-flight method calls from running. Also | |
| 1747 // invalidates |weak_handle_this_| and |change_observer_|. | |
| 1748 weak_ptr_factory_.InvalidateWeakPtrs(); | |
| 1749 js_mutation_event_observer_.InvalidateWeakPtrs(); | |
| 1750 | |
| 1751 scheduler_.reset(); | |
| 1752 | |
| 1753 SetJsEventHandler(WeakHandle<JsEventHandler>()); | |
| 1754 RemoveObserver(&js_sync_manager_observer_); | |
| 1755 | |
| 1756 RemoveObserver(&debug_info_event_listener_); | |
| 1757 | |
| 1758 if (sync_notifier_.get()) { | |
| 1759 sync_notifier_->RemoveObserver(this); | |
| 1760 } | |
| 1761 sync_notifier_.reset(); | |
| 1762 | |
| 1763 if (connection_manager_.get()) { | |
| 1764 connection_manager_->RemoveListener(this); | |
| 1765 } | |
| 1766 connection_manager_.reset(); | |
| 1767 | |
| 1768 net::NetworkChangeNotifier::RemoveIPAddressObserver(this); | |
| 1769 observing_ip_address_changes_ = false; | |
| 1770 | |
| 1771 if (initialized_ && directory()) { | |
| 1772 { | |
| 1773 // Cryptographer should only be accessed while holding a | |
| 1774 // transaction. | |
| 1775 ReadTransaction trans(FROM_HERE, GetUserShare()); | |
| 1776 trans.GetCryptographer()->RemoveObserver(this); | |
| 1777 } | |
| 1778 directory()->SaveChanges(); | |
| 1779 } | |
| 1780 | |
| 1781 share_.directory.reset(); | |
| 1782 | |
| 1783 change_delegate_ = NULL; | |
| 1784 registrar_ = NULL; | |
| 1785 | |
| 1786 initialized_ = false; | |
| 1787 | |
| 1788 // We reset these here, since only now we know they will not be | |
| 1789 // accessed from other threads (since we shut down everything). | |
| 1790 change_observer_.Reset(); | |
| 1791 weak_handle_this_.Reset(); | |
| 1792 } | |
| 1793 | |
| 1794 void SyncManager::SyncInternal::OnIPAddressChanged() { | |
| 1795 DVLOG(1) << "IP address change detected"; | |
| 1796 if (!observing_ip_address_changes_) { | |
| 1797 DVLOG(1) << "IP address change dropped."; | |
| 1798 return; | |
| 1799 } | |
| 1800 | |
| 1801 OnIPAddressChangedImpl(); | |
| 1802 } | |
| 1803 | |
| 1804 void SyncManager::SyncInternal::OnIPAddressChangedImpl() { | |
| 1805 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1806 if (scheduler()) | |
| 1807 scheduler()->OnConnectionStatusChange(); | |
| 1808 } | |
| 1809 | |
| 1810 void SyncManager::SyncInternal::OnServerConnectionEvent( | |
| 1811 const ServerConnectionEvent& event) { | |
| 1812 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1813 if (event.connection_code == | |
| 1814 browser_sync::HttpResponse::SERVER_CONNECTION_OK) { | |
| 1815 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
| 1816 OnConnectionStatusChange(CONNECTION_OK)); | |
| 1817 } | |
| 1818 | |
| 1819 if (event.connection_code == browser_sync::HttpResponse::SYNC_AUTH_ERROR) { | |
| 1820 observing_ip_address_changes_ = false; | |
| 1821 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
| 1822 OnConnectionStatusChange(CONNECTION_AUTH_ERROR)); | |
| 1823 } | |
| 1824 | |
| 1825 if (event.connection_code == | |
| 1826 browser_sync::HttpResponse::SYNC_SERVER_ERROR) { | |
| 1827 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
| 1828 OnConnectionStatusChange(CONNECTION_SERVER_ERROR)); | |
| 1829 } | |
| 1830 } | |
| 1831 | |
| 1832 void SyncManager::SyncInternal::HandleTransactionCompleteChangeEvent( | |
| 1833 ModelTypeSet models_with_changes) { | |
| 1834 // This notification happens immediately after the transaction mutex is | |
| 1835 // released. This allows work to be performed without blocking other threads | |
| 1836 // from acquiring a transaction. | |
| 1837 if (!change_delegate_) | |
| 1838 return; | |
| 1839 | |
| 1840 // Call commit. | |
| 1841 for (ModelTypeSet::Iterator it = models_with_changes.First(); | |
| 1842 it.Good(); it.Inc()) { | |
| 1843 change_delegate_->OnChangesComplete(it.Get()); | |
| 1844 change_observer_.Call( | |
| 1845 FROM_HERE, &SyncManager::ChangeObserver::OnChangesComplete, it.Get()); | |
| 1846 } | |
| 1847 } | |
| 1848 | |
| 1849 ModelTypeSet | |
| 1850 SyncManager::SyncInternal::HandleTransactionEndingChangeEvent( | |
| 1851 const ImmutableWriteTransactionInfo& write_transaction_info, | |
| 1852 syncable::BaseTransaction* trans) { | |
| 1853 // This notification happens immediately before a syncable WriteTransaction | |
| 1854 // falls out of scope. It happens while the channel mutex is still held, | |
| 1855 // and while the transaction mutex is held, so it cannot be re-entrant. | |
| 1856 if (!change_delegate_ || ChangeBuffersAreEmpty()) | |
| 1857 return ModelTypeSet(); | |
| 1858 | |
| 1859 // This will continue the WriteTransaction using a read only wrapper. | |
| 1860 // This is the last chance for read to occur in the WriteTransaction | |
| 1861 // that's closing. This special ReadTransaction will not close the | |
| 1862 // underlying transaction. | |
| 1863 ReadTransaction read_trans(GetUserShare(), trans); | |
| 1864 | |
| 1865 ModelTypeSet models_with_changes; | |
| 1866 for (int i = syncable::FIRST_REAL_MODEL_TYPE; | |
| 1867 i < syncable::MODEL_TYPE_COUNT; ++i) { | |
| 1868 const syncable::ModelType type = syncable::ModelTypeFromInt(i); | |
| 1869 if (change_buffers_[type].IsEmpty()) | |
| 1870 continue; | |
| 1871 | |
| 1872 ImmutableChangeRecordList ordered_changes; | |
| 1873 // TODO(akalin): Propagate up the error further (see | |
| 1874 // http://crbug.com/100907). | |
| 1875 CHECK(change_buffers_[type].GetAllChangesInTreeOrder(&read_trans, | |
| 1876 &ordered_changes)); | |
| 1877 if (!ordered_changes.Get().empty()) { | |
| 1878 change_delegate_-> | |
| 1879 OnChangesApplied(type, &read_trans, ordered_changes); | |
| 1880 change_observer_.Call(FROM_HERE, | |
| 1881 &SyncManager::ChangeObserver::OnChangesApplied, | |
| 1882 type, write_transaction_info.Get().id, ordered_changes); | |
| 1883 models_with_changes.Put(type); | |
| 1884 } | |
| 1885 change_buffers_[i].Clear(); | |
| 1886 } | |
| 1887 return models_with_changes; | |
| 1888 } | |
| 1889 | |
| 1890 void SyncManager::SyncInternal::HandleCalculateChangesChangeEventFromSyncApi( | |
| 1891 const ImmutableWriteTransactionInfo& write_transaction_info, | |
| 1892 syncable::BaseTransaction* trans) { | |
| 1893 if (!scheduler()) { | |
| 1894 return; | |
| 1895 } | |
| 1896 | |
| 1897 // We have been notified about a user action changing a sync model. | |
| 1898 LOG_IF(WARNING, !ChangeBuffersAreEmpty()) << | |
| 1899 "CALCULATE_CHANGES called with unapplied old changes."; | |
| 1900 | |
| 1901 // The mutated model type, or UNSPECIFIED if nothing was mutated. | |
| 1902 syncable::ModelTypeSet mutated_model_types; | |
| 1903 | |
| 1904 const syncable::ImmutableEntryKernelMutationMap& mutations = | |
| 1905 write_transaction_info.Get().mutations; | |
| 1906 for (syncable::EntryKernelMutationMap::const_iterator it = | |
| 1907 mutations.Get().begin(); it != mutations.Get().end(); ++it) { | |
| 1908 if (!it->second.mutated.ref(syncable::IS_UNSYNCED)) { | |
| 1909 continue; | |
| 1910 } | |
| 1911 | |
| 1912 syncable::ModelType model_type = | |
| 1913 syncable::GetModelTypeFromSpecifics( | |
| 1914 it->second.mutated.ref(SPECIFICS)); | |
| 1915 if (model_type < syncable::FIRST_REAL_MODEL_TYPE) { | |
| 1916 NOTREACHED() << "Permanent or underspecified item changed via syncapi."; | |
| 1917 continue; | |
| 1918 } | |
| 1919 | |
| 1920 // Found real mutation. | |
| 1921 if (model_type != syncable::UNSPECIFIED) { | |
| 1922 mutated_model_types.Put(model_type); | |
| 1923 } | |
| 1924 } | |
| 1925 | |
| 1926 // Nudge if necessary. | |
| 1927 if (!mutated_model_types.Empty()) { | |
| 1928 if (weak_handle_this_.IsInitialized()) { | |
| 1929 weak_handle_this_.Call(FROM_HERE, | |
| 1930 &SyncInternal::RequestNudgeForDataTypes, | |
| 1931 FROM_HERE, | |
| 1932 mutated_model_types); | |
| 1933 } else { | |
| 1934 NOTREACHED(); | |
| 1935 } | |
| 1936 } | |
| 1937 } | |
| 1938 | |
| 1939 void SyncManager::SyncInternal::SetExtraChangeRecordData(int64 id, | |
| 1940 syncable::ModelType type, ChangeReorderBuffer* buffer, | |
| 1941 Cryptographer* cryptographer, const syncable::EntryKernel& original, | |
| 1942 bool existed_before, bool exists_now) { | |
| 1943 // If this is a deletion and the datatype was encrypted, we need to decrypt it | |
| 1944 // and attach it to the buffer. | |
| 1945 if (!exists_now && existed_before) { | |
| 1946 sync_pb::EntitySpecifics original_specifics(original.ref(SPECIFICS)); | |
| 1947 if (type == syncable::PASSWORDS) { | |
| 1948 // Passwords must use their own legacy ExtraPasswordChangeRecordData. | |
| 1949 scoped_ptr<sync_pb::PasswordSpecificsData> data( | |
| 1950 DecryptPasswordSpecifics(original_specifics, cryptographer)); | |
| 1951 if (!data.get()) { | |
| 1952 NOTREACHED(); | |
| 1953 return; | |
| 1954 } | |
| 1955 buffer->SetExtraDataForId(id, new ExtraPasswordChangeRecordData(*data)); | |
| 1956 } else if (original_specifics.has_encrypted()) { | |
| 1957 // All other datatypes can just create a new unencrypted specifics and | |
| 1958 // attach it. | |
| 1959 const sync_pb::EncryptedData& encrypted = original_specifics.encrypted(); | |
| 1960 if (!cryptographer->Decrypt(encrypted, &original_specifics)) { | |
| 1961 NOTREACHED(); | |
| 1962 return; | |
| 1963 } | |
| 1964 } | |
| 1965 buffer->SetSpecificsForId(id, original_specifics); | |
| 1966 } | |
| 1967 } | |
| 1968 | |
| 1969 void SyncManager::SyncInternal::HandleCalculateChangesChangeEventFromSyncer( | |
| 1970 const ImmutableWriteTransactionInfo& write_transaction_info, | |
| 1971 syncable::BaseTransaction* trans) { | |
| 1972 // We only expect one notification per sync step, so change_buffers_ should | |
| 1973 // contain no pending entries. | |
| 1974 LOG_IF(WARNING, !ChangeBuffersAreEmpty()) << | |
| 1975 "CALCULATE_CHANGES called with unapplied old changes."; | |
| 1976 | |
| 1977 Cryptographer* crypto = directory()->GetCryptographer(trans); | |
| 1978 const syncable::ImmutableEntryKernelMutationMap& mutations = | |
| 1979 write_transaction_info.Get().mutations; | |
| 1980 for (syncable::EntryKernelMutationMap::const_iterator it = | |
| 1981 mutations.Get().begin(); it != mutations.Get().end(); ++it) { | |
| 1982 bool existed_before = !it->second.original.ref(syncable::IS_DEL); | |
| 1983 bool exists_now = !it->second.mutated.ref(syncable::IS_DEL); | |
| 1984 | |
| 1985 // Omit items that aren't associated with a model. | |
| 1986 syncable::ModelType type = | |
| 1987 syncable::GetModelTypeFromSpecifics( | |
| 1988 it->second.mutated.ref(SPECIFICS)); | |
| 1989 if (type < syncable::FIRST_REAL_MODEL_TYPE) | |
| 1990 continue; | |
| 1991 | |
| 1992 int64 handle = it->first; | |
| 1993 if (exists_now && !existed_before) | |
| 1994 change_buffers_[type].PushAddedItem(handle); | |
| 1995 else if (!exists_now && existed_before) | |
| 1996 change_buffers_[type].PushDeletedItem(handle); | |
| 1997 else if (exists_now && existed_before && | |
| 1998 VisiblePropertiesDiffer(it->second, crypto)) { | |
| 1999 change_buffers_[type].PushUpdatedItem( | |
| 2000 handle, VisiblePositionsDiffer(it->second)); | |
| 2001 } | |
| 2002 | |
| 2003 SetExtraChangeRecordData(handle, type, &change_buffers_[type], crypto, | |
| 2004 it->second.original, existed_before, exists_now); | |
| 2005 } | |
| 2006 } | |
| 2007 | |
| 2008 SyncManager::Status SyncManager::SyncInternal::GetStatus() { | |
| 2009 return allstatus_.status(); | |
| 2010 } | |
| 2011 | |
| 2012 void SyncManager::SyncInternal::RequestNudge( | |
| 2013 const tracked_objects::Location& location) { | |
| 2014 if (scheduler()) { | |
| 2015 scheduler()->ScheduleNudge( | |
| 2016 TimeDelta::FromMilliseconds(0), browser_sync::NUDGE_SOURCE_LOCAL, | |
| 2017 ModelTypeSet(), location); | |
| 2018 } | |
| 2019 } | |
| 2020 | |
| 2021 TimeDelta SyncManager::SyncInternal::GetNudgeDelayTimeDelta( | |
| 2022 const ModelType& model_type) { | |
| 2023 return NudgeStrategy::GetNudgeDelayTimeDelta(model_type, this); | |
| 2024 } | |
| 2025 | |
| 2026 void SyncManager::SyncInternal::RequestNudgeForDataTypes( | |
| 2027 const tracked_objects::Location& nudge_location, | |
| 2028 ModelTypeSet types) { | |
| 2029 if (!scheduler()) { | |
| 2030 NOTREACHED(); | |
| 2031 return; | |
| 2032 } | |
| 2033 | |
| 2034 debug_info_event_listener_.OnNudgeFromDatatype(types.First().Get()); | |
| 2035 | |
| 2036 // TODO(lipalani) : Calculate the nudge delay based on all types. | |
| 2037 base::TimeDelta nudge_delay = NudgeStrategy::GetNudgeDelayTimeDelta( | |
| 2038 types.First().Get(), | |
| 2039 this); | |
| 2040 scheduler()->ScheduleNudge(nudge_delay, | |
| 2041 browser_sync::NUDGE_SOURCE_LOCAL, | |
| 2042 types, | |
| 2043 nudge_location); | |
| 2044 } | |
| 2045 | |
| 2046 void SyncManager::SyncInternal::OnSyncEngineEvent( | |
| 2047 const SyncEngineEvent& event) { | |
| 2048 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 2049 // Only send an event if this is due to a cycle ending and this cycle | |
| 2050 // concludes a canonical "sync" process; that is, based on what is known | |
| 2051 // locally we are "all happy" and up-to-date. There may be new changes on | |
| 2052 // the server, but we'll get them on a subsequent sync. | |
| 2053 // | |
| 2054 // Notifications are sent at the end of every sync cycle, regardless of | |
| 2055 // whether we should sync again. | |
| 2056 if (event.what_happened == SyncEngineEvent::SYNC_CYCLE_ENDED) { | |
| 2057 ModelSafeRoutingInfo enabled_types; | |
| 2058 registrar_->GetModelSafeRoutingInfo(&enabled_types); | |
| 2059 { | |
| 2060 // Check to see if we need to notify the frontend that we have newly | |
| 2061 // encrypted types or that we require a passphrase. | |
| 2062 ReadTransaction trans(FROM_HERE, GetUserShare()); | |
| 2063 Cryptographer* cryptographer = trans.GetCryptographer(); | |
| 2064 // If we've completed a sync cycle and the cryptographer isn't ready | |
| 2065 // yet, prompt the user for a passphrase. | |
| 2066 if (cryptographer->has_pending_keys()) { | |
| 2067 DVLOG(1) << "OnPassPhraseRequired Sent"; | |
| 2068 sync_pb::EncryptedData pending_keys = cryptographer->GetPendingKeys(); | |
| 2069 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
| 2070 OnPassphraseRequired(sync_api::REASON_DECRYPTION, | |
| 2071 pending_keys)); | |
| 2072 } else if (!cryptographer->is_ready() && | |
| 2073 event.snapshot->initial_sync_ended.Has(syncable::NIGORI)) { | |
| 2074 DVLOG(1) << "OnPassphraseRequired sent because cryptographer is not " | |
| 2075 << "ready"; | |
| 2076 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
| 2077 OnPassphraseRequired(sync_api::REASON_ENCRYPTION, | |
| 2078 sync_pb::EncryptedData())); | |
| 2079 } | |
| 2080 | |
| 2081 NotifyCryptographerState(cryptographer); | |
| 2082 allstatus_.SetEncryptedTypes(cryptographer->GetEncryptedTypes()); | |
| 2083 } | |
| 2084 | |
| 2085 if (!initialized_) { | |
| 2086 LOG(INFO) << "OnSyncCycleCompleted not sent because sync api is not " | |
| 2087 << "initialized"; | |
| 2088 return; | |
| 2089 } | |
| 2090 | |
| 2091 if (!event.snapshot->has_more_to_sync) { | |
| 2092 // To account for a nigori node arriving with stale/bad data, we ensure | |
| 2093 // that the nigori node is up to date at the end of each cycle. | |
| 2094 WriteTransaction trans(FROM_HERE, GetUserShare()); | |
| 2095 WriteNode nigori_node(&trans); | |
| 2096 if (nigori_node.InitByTagLookup(kNigoriTag) == | |
| 2097 sync_api::BaseNode::INIT_OK) { | |
| 2098 Cryptographer* cryptographer = trans.GetCryptographer(); | |
| 2099 UpdateNigoriEncryptionState(cryptographer, &nigori_node); | |
| 2100 } | |
| 2101 | |
| 2102 DVLOG(1) << "Sending OnSyncCycleCompleted"; | |
| 2103 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
| 2104 OnSyncCycleCompleted(event.snapshot)); | |
| 2105 } | |
| 2106 | |
| 2107 // This is here for tests, which are still using p2p notifications. | |
| 2108 // | |
| 2109 // TODO(chron): Consider changing this back to track has_more_to_sync | |
| 2110 // only notify peers if a successful commit has occurred. | |
| 2111 bool is_notifiable_commit = | |
| 2112 (event.snapshot->syncer_status.num_successful_commits > 0); | |
| 2113 if (is_notifiable_commit) { | |
| 2114 if (sync_notifier_.get()) { | |
| 2115 const ModelTypeSet changed_types = | |
| 2116 syncable::ModelTypePayloadMapToEnumSet( | |
| 2117 event.snapshot->source.types); | |
| 2118 sync_notifier_->SendNotification(changed_types); | |
| 2119 } else { | |
| 2120 DVLOG(1) << "Not sending notification: sync_notifier_ is NULL"; | |
| 2121 } | |
| 2122 } | |
| 2123 } | |
| 2124 | |
| 2125 if (event.what_happened == SyncEngineEvent::STOP_SYNCING_PERMANENTLY) { | |
| 2126 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
| 2127 OnStopSyncingPermanently()); | |
| 2128 return; | |
| 2129 } | |
| 2130 | |
| 2131 if (event.what_happened == SyncEngineEvent::CLEAR_SERVER_DATA_SUCCEEDED) { | |
| 2132 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
| 2133 OnClearServerDataSucceeded()); | |
| 2134 return; | |
| 2135 } | |
| 2136 | |
| 2137 if (event.what_happened == SyncEngineEvent::CLEAR_SERVER_DATA_FAILED) { | |
| 2138 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
| 2139 OnClearServerDataFailed()); | |
| 2140 return; | |
| 2141 } | |
| 2142 | |
| 2143 if (event.what_happened == SyncEngineEvent::UPDATED_TOKEN) { | |
| 2144 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
| 2145 OnUpdatedToken(event.updated_token)); | |
| 2146 return; | |
| 2147 } | |
| 2148 | |
| 2149 if (event.what_happened == SyncEngineEvent::ACTIONABLE_ERROR) { | |
| 2150 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
| 2151 OnActionableError( | |
| 2152 event.snapshot->errors.sync_protocol_error)); | |
| 2153 return; | |
| 2154 } | |
| 2155 | |
| 2156 } | |
| 2157 | |
| 2158 void SyncManager::SyncInternal::SetJsEventHandler( | |
| 2159 const WeakHandle<JsEventHandler>& event_handler) { | |
| 2160 js_event_handler_ = event_handler; | |
| 2161 js_sync_manager_observer_.SetJsEventHandler(js_event_handler_); | |
| 2162 js_mutation_event_observer_.SetJsEventHandler(js_event_handler_); | |
| 2163 } | |
| 2164 | |
| 2165 void SyncManager::SyncInternal::ProcessJsMessage( | |
| 2166 const std::string& name, const JsArgList& args, | |
| 2167 const WeakHandle<JsReplyHandler>& reply_handler) { | |
| 2168 if (!initialized_) { | |
| 2169 NOTREACHED(); | |
| 2170 return; | |
| 2171 } | |
| 2172 | |
| 2173 if (!reply_handler.IsInitialized()) { | |
| 2174 DVLOG(1) << "Uninitialized reply handler; dropping unknown message " | |
| 2175 << name << " with args " << args.ToString(); | |
| 2176 return; | |
| 2177 } | |
| 2178 | |
| 2179 JsMessageHandler js_message_handler = js_message_handlers_[name]; | |
| 2180 if (js_message_handler.is_null()) { | |
| 2181 DVLOG(1) << "Dropping unknown message " << name | |
| 2182 << " with args " << args.ToString(); | |
| 2183 return; | |
| 2184 } | |
| 2185 | |
| 2186 reply_handler.Call(FROM_HERE, | |
| 2187 &JsReplyHandler::HandleJsReply, | |
| 2188 name, js_message_handler.Run(args)); | |
| 2189 } | |
| 2190 | |
| 2191 void SyncManager::SyncInternal::BindJsMessageHandler( | |
| 2192 const std::string& name, | |
| 2193 UnboundJsMessageHandler unbound_message_handler) { | |
| 2194 js_message_handlers_[name] = | |
| 2195 base::Bind(unbound_message_handler, base::Unretained(this)); | |
| 2196 } | |
| 2197 | |
| 2198 DictionaryValue* SyncManager::SyncInternal::NotificationInfoToValue( | |
| 2199 const NotificationInfoMap& notification_info) { | |
| 2200 DictionaryValue* value = new DictionaryValue(); | |
| 2201 | |
| 2202 for (NotificationInfoMap::const_iterator it = notification_info.begin(); | |
| 2203 it != notification_info.end(); ++it) { | |
| 2204 const std::string& model_type_str = | |
| 2205 syncable::ModelTypeToString(it->first); | |
| 2206 value->Set(model_type_str, it->second.ToValue()); | |
| 2207 } | |
| 2208 | |
| 2209 return value; | |
| 2210 } | |
| 2211 | |
| 2212 JsArgList SyncManager::SyncInternal::GetNotificationState( | |
| 2213 const JsArgList& args) { | |
| 2214 bool notifications_enabled = allstatus_.status().notifications_enabled; | |
| 2215 ListValue return_args; | |
| 2216 return_args.Append(Value::CreateBooleanValue(notifications_enabled)); | |
| 2217 return JsArgList(&return_args); | |
| 2218 } | |
| 2219 | |
| 2220 JsArgList SyncManager::SyncInternal::GetNotificationInfo( | |
| 2221 const JsArgList& args) { | |
| 2222 ListValue return_args; | |
| 2223 return_args.Append(NotificationInfoToValue(notification_info_map_)); | |
| 2224 return JsArgList(&return_args); | |
| 2225 } | |
| 2226 | |
| 2227 JsArgList SyncManager::SyncInternal::GetRootNodeDetails( | |
| 2228 const JsArgList& args) { | |
| 2229 ReadTransaction trans(FROM_HERE, GetUserShare()); | |
| 2230 ReadNode root(&trans); | |
| 2231 root.InitByRootLookup(); | |
| 2232 ListValue return_args; | |
| 2233 return_args.Append(root.GetDetailsAsValue()); | |
| 2234 return JsArgList(&return_args); | |
| 2235 } | |
| 2236 | |
| 2237 JsArgList SyncManager::SyncInternal::GetClientServerTraffic( | |
| 2238 const JsArgList& args) { | |
| 2239 ListValue return_args; | |
| 2240 ListValue* value = traffic_recorder_.ToValue(); | |
| 2241 if (value != NULL) | |
| 2242 return_args.Append(value); | |
| 2243 return JsArgList(&return_args); | |
| 2244 } | |
| 2245 | |
| 2246 namespace { | |
| 2247 | |
| 2248 int64 GetId(const ListValue& ids, int i) { | |
| 2249 std::string id_str; | |
| 2250 if (!ids.GetString(i, &id_str)) { | |
| 2251 return kInvalidId; | |
| 2252 } | |
| 2253 int64 id = kInvalidId; | |
| 2254 if (!base::StringToInt64(id_str, &id)) { | |
| 2255 return kInvalidId; | |
| 2256 } | |
| 2257 return id; | |
| 2258 } | |
| 2259 | |
| 2260 JsArgList GetNodeInfoById(const JsArgList& args, | |
| 2261 UserShare* user_share, | |
| 2262 DictionaryValue* (BaseNode::*info_getter)() const) { | |
| 2263 CHECK(info_getter); | |
| 2264 ListValue return_args; | |
| 2265 ListValue* node_summaries = new ListValue(); | |
| 2266 return_args.Append(node_summaries); | |
| 2267 ListValue* id_list = NULL; | |
| 2268 ReadTransaction trans(FROM_HERE, user_share); | |
| 2269 if (args.Get().GetList(0, &id_list)) { | |
| 2270 CHECK(id_list); | |
| 2271 for (size_t i = 0; i < id_list->GetSize(); ++i) { | |
| 2272 int64 id = GetId(*id_list, i); | |
| 2273 if (id == kInvalidId) { | |
| 2274 continue; | |
| 2275 } | |
| 2276 ReadNode node(&trans); | |
| 2277 if (node.InitByIdLookup(id) != sync_api::BaseNode::INIT_OK) { | |
| 2278 continue; | |
| 2279 } | |
| 2280 node_summaries->Append((node.*info_getter)()); | |
| 2281 } | |
| 2282 } | |
| 2283 return JsArgList(&return_args); | |
| 2284 } | |
| 2285 | |
| 2286 } // namespace | |
| 2287 | |
| 2288 JsArgList SyncManager::SyncInternal::GetNodeSummariesById( | |
| 2289 const JsArgList& args) { | |
| 2290 return GetNodeInfoById(args, GetUserShare(), &BaseNode::GetSummaryAsValue); | |
| 2291 } | |
| 2292 | |
| 2293 JsArgList SyncManager::SyncInternal::GetNodeDetailsById( | |
| 2294 const JsArgList& args) { | |
| 2295 return GetNodeInfoById(args, GetUserShare(), &BaseNode::GetDetailsAsValue); | |
| 2296 } | |
| 2297 | |
| 2298 JsArgList SyncManager::SyncInternal::GetAllNodes( | |
| 2299 const JsArgList& args) { | |
| 2300 ListValue return_args; | |
| 2301 ListValue* result = new ListValue(); | |
| 2302 return_args.Append(result); | |
| 2303 | |
| 2304 ReadTransaction trans(FROM_HERE, GetUserShare()); | |
| 2305 std::vector<const syncable::EntryKernel*> entry_kernels; | |
| 2306 trans.GetDirectory()->GetAllEntryKernels(trans.GetWrappedTrans(), | |
| 2307 &entry_kernels); | |
| 2308 | |
| 2309 for (std::vector<const syncable::EntryKernel*>::const_iterator it = | |
| 2310 entry_kernels.begin(); it != entry_kernels.end(); ++it) { | |
| 2311 result->Append((*it)->ToValue()); | |
| 2312 } | |
| 2313 | |
| 2314 return JsArgList(&return_args); | |
| 2315 } | |
| 2316 | |
| 2317 JsArgList SyncManager::SyncInternal::GetChildNodeIds( | |
| 2318 const JsArgList& args) { | |
| 2319 ListValue return_args; | |
| 2320 ListValue* child_ids = new ListValue(); | |
| 2321 return_args.Append(child_ids); | |
| 2322 int64 id = GetId(args.Get(), 0); | |
| 2323 if (id != kInvalidId) { | |
| 2324 ReadTransaction trans(FROM_HERE, GetUserShare()); | |
| 2325 syncable::Directory::ChildHandles child_handles; | |
| 2326 trans.GetDirectory()->GetChildHandlesByHandle(trans.GetWrappedTrans(), | |
| 2327 id, &child_handles); | |
| 2328 for (syncable::Directory::ChildHandles::const_iterator it = | |
| 2329 child_handles.begin(); it != child_handles.end(); ++it) { | |
| 2330 child_ids->Append(Value::CreateStringValue( | |
| 2331 base::Int64ToString(*it))); | |
| 2332 } | |
| 2333 } | |
| 2334 return JsArgList(&return_args); | |
| 2335 } | |
| 2336 | |
| 2337 void SyncManager::SyncInternal::OnEncryptedTypesChanged( | |
| 2338 syncable::ModelTypeSet encrypted_types, | |
| 2339 bool encrypt_everything) { | |
| 2340 // NOTE: We're in a transaction. | |
| 2341 FOR_EACH_OBSERVER( | |
| 2342 SyncManager::Observer, observers_, | |
| 2343 OnEncryptedTypesChanged(encrypted_types, encrypt_everything)); | |
| 2344 } | |
| 2345 | |
| 2346 void SyncManager::SyncInternal::OnNotificationStateChange( | |
| 2347 bool notifications_enabled) { | |
| 2348 DVLOG(1) << "P2P: Notifications enabled = " | |
| 2349 << (notifications_enabled ? "true" : "false"); | |
| 2350 allstatus_.SetNotificationsEnabled(notifications_enabled); | |
| 2351 if (scheduler()) { | |
| 2352 scheduler()->set_notifications_enabled(notifications_enabled); | |
| 2353 } | |
| 2354 if (js_event_handler_.IsInitialized()) { | |
| 2355 DictionaryValue details; | |
| 2356 details.Set("enabled", Value::CreateBooleanValue(notifications_enabled)); | |
| 2357 js_event_handler_.Call(FROM_HERE, | |
| 2358 &JsEventHandler::HandleJsEvent, | |
| 2359 "onNotificationStateChange", | |
| 2360 JsEventDetails(&details)); | |
| 2361 } | |
| 2362 } | |
| 2363 | |
| 2364 void SyncManager::SyncInternal::UpdateNotificationInfo( | |
| 2365 const syncable::ModelTypePayloadMap& type_payloads) { | |
| 2366 for (syncable::ModelTypePayloadMap::const_iterator it = type_payloads.begin(); | |
| 2367 it != type_payloads.end(); ++it) { | |
| 2368 NotificationInfo* info = ¬ification_info_map_[it->first]; | |
| 2369 info->total_count++; | |
| 2370 info->payload = it->second; | |
| 2371 } | |
| 2372 } | |
| 2373 | |
| 2374 void SyncManager::SyncInternal::OnIncomingNotification( | |
| 2375 const syncable::ModelTypePayloadMap& type_payloads, | |
| 2376 sync_notifier::IncomingNotificationSource source) { | |
| 2377 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 2378 if (source == sync_notifier::LOCAL_NOTIFICATION) { | |
| 2379 if (scheduler()) { | |
| 2380 scheduler()->ScheduleNudgeWithPayloads( | |
| 2381 TimeDelta::FromMilliseconds(kSyncRefreshDelayMsec), | |
| 2382 browser_sync::NUDGE_SOURCE_LOCAL_REFRESH, | |
| 2383 type_payloads, FROM_HERE); | |
| 2384 } | |
| 2385 } else if (!type_payloads.empty()) { | |
| 2386 if (scheduler()) { | |
| 2387 scheduler()->ScheduleNudgeWithPayloads( | |
| 2388 TimeDelta::FromMilliseconds(kSyncSchedulerDelayMsec), | |
| 2389 browser_sync::NUDGE_SOURCE_NOTIFICATION, | |
| 2390 type_payloads, FROM_HERE); | |
| 2391 } | |
| 2392 allstatus_.IncrementNotificationsReceived(); | |
| 2393 UpdateNotificationInfo(type_payloads); | |
| 2394 debug_info_event_listener_.OnIncomingNotification(type_payloads); | |
| 2395 } else { | |
| 2396 LOG(WARNING) << "Sync received notification without any type information."; | |
| 2397 } | |
| 2398 | |
| 2399 if (js_event_handler_.IsInitialized()) { | |
| 2400 DictionaryValue details; | |
| 2401 ListValue* changed_types = new ListValue(); | |
| 2402 details.Set("changedTypes", changed_types); | |
| 2403 for (syncable::ModelTypePayloadMap::const_iterator | |
| 2404 it = type_payloads.begin(); | |
| 2405 it != type_payloads.end(); ++it) { | |
| 2406 const std::string& model_type_str = | |
| 2407 syncable::ModelTypeToString(it->first); | |
| 2408 changed_types->Append(Value::CreateStringValue(model_type_str)); | |
| 2409 } | |
| 2410 details.SetString("source", (source == sync_notifier::LOCAL_NOTIFICATION) ? | |
| 2411 "LOCAL_NOTIFICATION" : "REMOTE_NOTIFICATION"); | |
| 2412 js_event_handler_.Call(FROM_HERE, | |
| 2413 &JsEventHandler::HandleJsEvent, | |
| 2414 "onIncomingNotification", | |
| 2415 JsEventDetails(&details)); | |
| 2416 } | |
| 2417 } | |
| 2418 | |
| 2419 void SyncManager::SyncInternal::StoreState( | |
| 2420 const std::string& state) { | |
| 2421 if (!directory()) { | |
| 2422 LOG(ERROR) << "Could not write notification state"; | |
| 2423 // TODO(akalin): Propagate result callback all the way to this | |
| 2424 // function and call it with "false" to signal failure. | |
| 2425 return; | |
| 2426 } | |
| 2427 if (VLOG_IS_ON(1)) { | |
| 2428 std::string encoded_state; | |
| 2429 base::Base64Encode(state, &encoded_state); | |
| 2430 DVLOG(1) << "Writing notification state: " << encoded_state; | |
| 2431 } | |
| 2432 directory()->SetNotificationState(state); | |
| 2433 directory()->SaveChanges(); | |
| 2434 } | |
| 2435 | |
| 2436 void SyncManager::SyncInternal::AddObserver( | |
| 2437 SyncManager::Observer* observer) { | |
| 2438 observers_.AddObserver(observer); | |
| 2439 } | |
| 2440 | |
| 2441 void SyncManager::SyncInternal::RemoveObserver( | |
| 2442 SyncManager::Observer* observer) { | |
| 2443 observers_.RemoveObserver(observer); | |
| 2444 } | |
| 2445 | |
| 2446 SyncManager::Status SyncManager::GetDetailedStatus() const { | |
| 2447 return data_->GetStatus(); | |
| 2448 } | |
| 2449 | |
| 2450 void SyncManager::SaveChanges() { | |
| 2451 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 2452 data_->SaveChanges(); | |
| 2453 } | |
| 2454 | |
| 2455 void SyncManager::SyncInternal::SaveChanges() { | |
| 2456 directory()->SaveChanges(); | |
| 2457 } | |
| 2458 | |
| 2459 UserShare* SyncManager::GetUserShare() const { | |
| 2460 return data_->GetUserShare(); | |
| 2461 } | |
| 2462 | |
| 2463 void SyncManager::RefreshNigori(const std::string& chrome_version, | |
| 2464 const base::Closure& done_callback) { | |
| 2465 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 2466 data_->UpdateCryptographerAndNigori( | |
| 2467 chrome_version, | |
| 2468 done_callback); | |
| 2469 } | |
| 2470 | |
| 2471 TimeDelta SyncManager::GetNudgeDelayTimeDelta( | |
| 2472 const ModelType& model_type) { | |
| 2473 return data_->GetNudgeDelayTimeDelta(model_type); | |
| 2474 } | |
| 2475 | |
| 2476 syncable::ModelTypeSet SyncManager::GetEncryptedDataTypesForTest() const { | |
| 2477 ReadTransaction trans(FROM_HERE, GetUserShare()); | |
| 2478 return GetEncryptedTypes(&trans); | |
| 2479 } | |
| 2480 | |
| 2481 bool SyncManager::ReceivedExperimentalTypes(syncable::ModelTypeSet* to_add) | |
| 2482 const { | |
| 2483 ReadTransaction trans(FROM_HERE, GetUserShare()); | |
| 2484 ReadNode node(&trans); | |
| 2485 if (node.InitByTagLookup(kNigoriTag) != sync_api::BaseNode::INIT_OK) { | |
| 2486 DVLOG(1) << "Couldn't find Nigori node."; | |
| 2487 return false; | |
| 2488 } | |
| 2489 if (node.GetNigoriSpecifics().sync_tabs()) { | |
| 2490 to_add->Put(syncable::SESSIONS); | |
| 2491 return true; | |
| 2492 } | |
| 2493 return false; | |
| 2494 } | |
| 2495 | |
| 2496 bool SyncManager::HasUnsyncedItems() const { | |
| 2497 sync_api::ReadTransaction trans(FROM_HERE, GetUserShare()); | |
| 2498 return (trans.GetWrappedTrans()->directory()->unsynced_entity_count() != 0); | |
| 2499 } | |
| 2500 | |
| 2501 void SyncManager::TriggerOnNotificationStateChangeForTest( | |
| 2502 bool notifications_enabled) { | |
| 2503 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 2504 data_->OnNotificationStateChange(notifications_enabled); | |
| 2505 } | |
| 2506 | |
| 2507 void SyncManager::TriggerOnIncomingNotificationForTest( | |
| 2508 ModelTypeSet model_types) { | |
| 2509 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 2510 syncable::ModelTypePayloadMap model_types_with_payloads = | |
| 2511 syncable::ModelTypePayloadMapFromEnumSet(model_types, | |
| 2512 std::string()); | |
| 2513 | |
| 2514 data_->OnIncomingNotification(model_types_with_payloads, | |
| 2515 sync_notifier::REMOTE_NOTIFICATION); | |
| 2516 } | |
| 2517 | |
| 2518 const char* ConnectionStatusToString(ConnectionStatus status) { | |
| 2519 switch (status) { | |
| 2520 case CONNECTION_OK: | |
| 2521 return "CONNECTION_OK"; | |
| 2522 case CONNECTION_AUTH_ERROR: | |
| 2523 return "CONNECTION_AUTH_ERROR"; | |
| 2524 case CONNECTION_SERVER_ERROR: | |
| 2525 return "CONNECTION_SERVER_ERROR"; | |
| 2526 default: | |
| 2527 NOTREACHED(); | |
| 2528 return "INVALID_CONNECTION_STATUS"; | |
| 2529 } | |
| 2530 } | |
| 2531 | |
| 2532 // Helper function that converts a PassphraseRequiredReason value to a string. | |
| 2533 const char* PassphraseRequiredReasonToString( | |
| 2534 PassphraseRequiredReason reason) { | |
| 2535 switch (reason) { | |
| 2536 case REASON_PASSPHRASE_NOT_REQUIRED: | |
| 2537 return "REASON_PASSPHRASE_NOT_REQUIRED"; | |
| 2538 case REASON_ENCRYPTION: | |
| 2539 return "REASON_ENCRYPTION"; | |
| 2540 case REASON_DECRYPTION: | |
| 2541 return "REASON_DECRYPTION"; | |
| 2542 default: | |
| 2543 NOTREACHED(); | |
| 2544 return "INVALID_REASON"; | |
| 2545 } | |
| 2546 } | |
| 2547 | |
| 2548 // Helper function to determine if initial sync had ended for types. | |
| 2549 bool InitialSyncEndedForTypes(syncable::ModelTypeSet types, | |
| 2550 sync_api::UserShare* share) { | |
| 2551 for (syncable::ModelTypeSet::Iterator i = types.First(); | |
| 2552 i.Good(); i.Inc()) { | |
| 2553 if (!share->directory->initial_sync_ended_for_type(i.Get())) | |
| 2554 return false; | |
| 2555 } | |
| 2556 return true; | |
| 2557 } | |
| 2558 | |
| 2559 syncable::ModelTypeSet GetTypesWithEmptyProgressMarkerToken( | |
| 2560 syncable::ModelTypeSet types, | |
| 2561 sync_api::UserShare* share) { | |
| 2562 syncable::ModelTypeSet result; | |
| 2563 for (syncable::ModelTypeSet::Iterator i = types.First(); | |
| 2564 i.Good(); i.Inc()) { | |
| 2565 sync_pb::DataTypeProgressMarker marker; | |
| 2566 share->directory->GetDownloadProgress(i.Get(), &marker); | |
| 2567 | |
| 2568 if (marker.token().empty()) | |
| 2569 result.Put(i.Get()); | |
| 2570 | |
| 2571 } | |
| 2572 return result; | |
| 2573 } | |
| 2574 | |
| 2575 } // namespace sync_api | |
| OLD | NEW |