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