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