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