Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(316)

Side by Side Diff: chrome/browser/sync/engine/syncapi.cc

Issue 7624009: Original patch by rlarocque@chromium.org at http://codereview.chromium.org/7633077/ (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix windows build pt5 Created 9 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome/browser/sync/engine/syncapi.h ('k') | chrome/browser/sync/engine/syncapi_internal.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/sync/engine/syncapi.h"
6
7 #include <algorithm>
8 #include <bitset>
9 #include <iomanip>
10 #include <list>
11 #include <map>
12 #include <queue>
13 #include <string>
14 #include <vector>
15
16 #include "base/base64.h"
17 #include "base/bind.h"
18 #include "base/callback.h"
19 #include "base/command_line.h"
20 #include "base/compiler_specific.h"
21 #include "base/json/json_writer.h"
22 #include "base/logging.h"
23 #include "base/memory/scoped_ptr.h"
24 #include "base/memory/weak_ptr.h"
25 #include "base/message_loop.h"
26 #include "base/observer_list.h"
27 #include "base/sha1.h"
28 #include "base/string_number_conversions.h"
29 #include "base/string_util.h"
30 #include "base/threading/thread_checker.h"
31 #include "base/time.h"
32 #include "base/tracked.h"
33 #include "base/utf_string_conversions.h"
34 #include "base/values.h"
35 #include "chrome/browser/sync/engine/all_status.h"
36 #include "chrome/browser/sync/engine/change_reorder_buffer.h"
37 #include "chrome/browser/sync/engine/http_post_provider_factory.h"
38 #include "chrome/browser/sync/engine/model_safe_worker.h"
39 #include "chrome/browser/sync/engine/nigori_util.h"
40 #include "chrome/browser/sync/engine/nudge_source.h"
41 #include "chrome/browser/sync/engine/net/server_connection_manager.h"
42 #include "chrome/browser/sync/engine/net/syncapi_server_connection_manager.h"
43 #include "chrome/browser/sync/engine/nudge_source.h"
44 #include "chrome/browser/sync/engine/sync_scheduler.h"
45 #include "chrome/browser/sync/engine/syncer.h"
46 #include "chrome/browser/sync/js/js_arg_list.h"
47 #include "chrome/browser/sync/js/js_backend.h"
48 #include "chrome/browser/sync/js/js_event_details.h"
49 #include "chrome/browser/sync/js/js_event_handler.h"
50 #include "chrome/browser/sync/js/js_reply_handler.h"
51 #include "chrome/browser/sync/js/js_sync_manager_observer.h"
52 #include "chrome/browser/sync/js/js_transaction_observer.h"
53 #include "chrome/browser/sync/notifier/sync_notifier.h"
54 #include "chrome/browser/sync/notifier/sync_notifier_observer.h"
55 #include "chrome/browser/sync/protocol/app_specifics.pb.h"
56 #include "chrome/browser/sync/protocol/autofill_specifics.pb.h"
57 #include "chrome/browser/sync/protocol/bookmark_specifics.pb.h"
58 #include "chrome/browser/sync/protocol/extension_specifics.pb.h"
59 #include "chrome/browser/sync/protocol/nigori_specifics.pb.h"
60 #include "chrome/browser/sync/protocol/preference_specifics.pb.h"
61 #include "chrome/browser/sync/protocol/proto_value_conversions.h"
62 #include "chrome/browser/sync/protocol/service_constants.h"
63 #include "chrome/browser/sync/protocol/session_specifics.pb.h"
64 #include "chrome/browser/sync/protocol/sync.pb.h"
65 #include "chrome/browser/sync/protocol/theme_specifics.pb.h"
66 #include "chrome/browser/sync/protocol/typed_url_specifics.pb.h"
67 #include "chrome/browser/sync/sessions/sync_session.h"
68 #include "chrome/browser/sync/sessions/sync_session_context.h"
69 #include "chrome/browser/sync/syncable/directory_change_delegate.h"
70 #include "chrome/browser/sync/syncable/directory_manager.h"
71 #include "chrome/browser/sync/syncable/model_type.h"
72 #include "chrome/browser/sync/syncable/model_type_payload_map.h"
73 #include "chrome/browser/sync/syncable/syncable.h"
74 #include "chrome/browser/sync/weak_handle.h"
75 #include "chrome/common/chrome_switches.h"
76 #include "net/base/network_change_notifier.h"
77
78 using base::TimeDelta;
79 using browser_sync::AllStatus;
80 using browser_sync::Cryptographer;
81 using browser_sync::JsArgList;
82 using browser_sync::JsBackend;
83 using browser_sync::JsEventDetails;
84 using browser_sync::JsEventHandler;
85 using browser_sync::JsReplyHandler;
86 using browser_sync::JsSyncManagerObserver;
87 using browser_sync::JsTransactionObserver;
88 using browser_sync::MakeWeakHandle;
89 using browser_sync::WeakHandle;
90 using browser_sync::KeyParams;
91 using browser_sync::ModelSafeRoutingInfo;
92 using browser_sync::ModelSafeWorker;
93 using browser_sync::ModelSafeWorkerRegistrar;
94 using browser_sync::ServerConnectionEvent;
95 using browser_sync::ServerConnectionEventListener;
96 using browser_sync::SyncEngineEvent;
97 using browser_sync::SyncEngineEventListener;
98 using browser_sync::Syncer;
99 using browser_sync::SyncScheduler;
100 using browser_sync::kNigoriTag;
101 using browser_sync::sessions::SyncSessionContext;
102 using std::list;
103 using std::hex;
104 using std::string;
105 using std::vector;
106 using syncable::Directory;
107 using syncable::DirectoryManager;
108 using syncable::Entry;
109 using syncable::EntryKernelMutationSet;
110 using syncable::kEncryptedString;
111 using syncable::ModelType;
112 using syncable::ModelTypeBitSet;
113 using syncable::WriterTag;
114 using syncable::SPECIFICS;
115 using sync_pb::AutofillProfileSpecifics;
116
117 namespace {
118
119 typedef GoogleServiceAuthError AuthError;
120
121 static const int kThreadExitTimeoutMsec = 60000;
122 static const int kSSLPort = 443;
123 static const int kSyncSchedulerDelayMsec = 250;
124
125 #if defined(OS_CHROMEOS)
126 static const int kChromeOSNetworkChangeReactionDelayHackMsec = 5000;
127 #endif // OS_CHROMEOS
128
129 } // namespace
130
131 namespace sync_api {
132
133 static const FilePath::CharType kBookmarkSyncUserSettingsDatabase[] =
134 FILE_PATH_LITERAL("BookmarkSyncSettings.sqlite3");
135 static const char kDefaultNameForNewNodes[] = " ";
136
137 // The list of names which are reserved for use by the server.
138 static const char* kForbiddenServerNames[] = { "", ".", ".." };
139
140 //////////////////////////////////////////////////////////////////////////
141 // Static helper functions.
142
143 // Helper function to look up the int64 metahandle of an object given the ID
144 // string.
145 static int64 IdToMetahandle(syncable::BaseTransaction* trans,
146 const syncable::Id& id) {
147 syncable::Entry entry(trans, syncable::GET_BY_ID, id);
148 if (!entry.good())
149 return kInvalidId;
150 return entry.Get(syncable::META_HANDLE);
151 }
152
153 // Checks whether |name| is a server-illegal name followed by zero or more space
154 // characters. The three server-illegal names are the empty string, dot, and
155 // dot-dot. Very long names (>255 bytes in UTF-8 Normalization Form C) are
156 // also illegal, but are not considered here.
157 static bool IsNameServerIllegalAfterTrimming(const std::string& name) {
158 size_t untrimmed_count = name.find_last_not_of(' ') + 1;
159 for (size_t i = 0; i < arraysize(kForbiddenServerNames); ++i) {
160 if (name.compare(0, untrimmed_count, kForbiddenServerNames[i]) == 0)
161 return true;
162 }
163 return false;
164 }
165
166 static bool EndsWithSpace(const std::string& string) {
167 return !string.empty() && *string.rbegin() == ' ';
168 }
169
170 // When taking a name from the syncapi, append a space if it matches the
171 // pattern of a server-illegal name followed by zero or more spaces.
172 static void SyncAPINameToServerName(const std::wstring& sync_api_name,
173 std::string* out) {
174 *out = WideToUTF8(sync_api_name);
175 if (IsNameServerIllegalAfterTrimming(*out))
176 out->append(" ");
177 }
178
179 // In the reverse direction, if a server name matches the pattern of a
180 // server-illegal name followed by one or more spaces, remove the trailing
181 // space.
182 static void ServerNameToSyncAPIName(const std::string& server_name,
183 std::string* out) {
184 CHECK(out);
185 int length_to_copy = server_name.length();
186 if (IsNameServerIllegalAfterTrimming(server_name) &&
187 EndsWithSpace(server_name)) {
188 --length_to_copy;
189 }
190 *out = std::string(server_name.c_str(), length_to_copy);
191 }
192
193 // Compare the values of two EntitySpecifics, accounting for encryption.
194 static bool AreSpecificsEqual(const browser_sync::Cryptographer* cryptographer,
195 const sync_pb::EntitySpecifics& left,
196 const sync_pb::EntitySpecifics& right) {
197 // Note that we can't compare encrypted strings directly as they are seeded
198 // with a random value.
199 std::string left_plaintext, right_plaintext;
200 if (left.has_encrypted()) {
201 if (!cryptographer->CanDecrypt(left.encrypted())) {
202 NOTREACHED() << "Attempting to compare undecryptable data.";
203 return false;
204 }
205 left_plaintext = cryptographer->DecryptToString(left.encrypted());
206 } else {
207 left_plaintext = left.SerializeAsString();
208 }
209 if (right.has_encrypted()) {
210 if (!cryptographer->CanDecrypt(right.encrypted())) {
211 NOTREACHED() << "Attempting to compare undecryptable data.";
212 return false;
213 }
214 right_plaintext = cryptographer->DecryptToString(right.encrypted());
215 } else {
216 right_plaintext = right.SerializeAsString();
217 }
218 if (left_plaintext == right_plaintext) {
219 return true;
220 }
221 return false;
222 }
223
224 // Helper function that converts a PassphraseRequiredReason value to a string.
225 std::string PassphraseRequiredReasonToString(
226 PassphraseRequiredReason reason) {
227 switch (reason) {
228 case REASON_PASSPHRASE_NOT_REQUIRED:
229 return "REASON_PASSPHRASE_NOT_REQUIRED";
230 case REASON_ENCRYPTION:
231 return "REASON_ENCRYPTION";
232 case REASON_DECRYPTION:
233 return "REASON_DECRYPTION";
234 case REASON_SET_PASSPHRASE_FAILED:
235 return "REASON_SET_PASSPHRASE_FAILED";
236 default:
237 NOTREACHED();
238 return "INVALID_REASON";
239 }
240 }
241
242 // Helper function to determine if initial sync had ended for types.
243 bool InitialSyncEndedForTypes(syncable::ModelTypeSet types,
244 sync_api::UserShare* share) {
245 syncable::ScopedDirLookup lookup(share->dir_manager.get(),
246 share->name);
247 if (!lookup.good()) {
248 DCHECK(false) << "ScopedDirLookup failed when checking initial sync";
249 return false;
250 }
251
252 for (syncable::ModelTypeSet::const_iterator i = types.begin();
253 i != types.end(); ++i) {
254 if (!lookup->initial_sync_ended_for_type(*i))
255 return false;
256 }
257 return true;
258 }
259
260
261 UserShare::UserShare() {}
262
263 UserShare::~UserShare() {}
264
265 ////////////////////////////////////
266 // BaseNode member definitions.
267
268 BaseNode::BaseNode() : password_data_(new sync_pb::PasswordSpecificsData) {}
269
270 BaseNode::~BaseNode() {}
271
272 std::string BaseNode::GenerateSyncableHash(
273 syncable::ModelType model_type, const std::string& client_tag) {
274 // blank PB with just the extension in it has termination symbol,
275 // handy for delimiter
276 sync_pb::EntitySpecifics serialized_type;
277 syncable::AddDefaultExtensionValue(model_type, &serialized_type);
278 std::string hash_input;
279 serialized_type.AppendToString(&hash_input);
280 hash_input.append(client_tag);
281
282 std::string encode_output;
283 CHECK(base::Base64Encode(base::SHA1HashString(hash_input), &encode_output));
284 return encode_output;
285 }
286
287 sync_pb::PasswordSpecificsData* DecryptPasswordSpecifics(
288 const sync_pb::EntitySpecifics& specifics, Cryptographer* crypto) {
289 if (!specifics.HasExtension(sync_pb::password))
290 return NULL;
291 const sync_pb::PasswordSpecifics& password_specifics =
292 specifics.GetExtension(sync_pb::password);
293 if (!password_specifics.has_encrypted())
294 return NULL;
295 const sync_pb::EncryptedData& encrypted = password_specifics.encrypted();
296 scoped_ptr<sync_pb::PasswordSpecificsData> data(
297 new sync_pb::PasswordSpecificsData);
298 if (!crypto->Decrypt(encrypted, data.get()))
299 return NULL;
300 return data.release();
301 }
302
303 bool BaseNode::DecryptIfNecessary() {
304 if (!GetEntry()->Get(syncable::UNIQUE_SERVER_TAG).empty())
305 return true; // Ignore unique folders.
306 const sync_pb::EntitySpecifics& specifics =
307 GetEntry()->Get(syncable::SPECIFICS);
308 if (specifics.HasExtension(sync_pb::password)) {
309 // Passwords have their own legacy encryption structure.
310 scoped_ptr<sync_pb::PasswordSpecificsData> data(DecryptPasswordSpecifics(
311 specifics, GetTransaction()->GetCryptographer()));
312 if (!data.get()) {
313 LOG(ERROR) << "Failed to decrypt password specifics.";
314 return false;
315 }
316 password_data_.swap(data);
317 return true;
318 }
319
320 // We assume any node with the encrypted field set has encrypted data.
321 if (!specifics.has_encrypted())
322 return true;
323
324 const sync_pb::EncryptedData& encrypted =
325 specifics.encrypted();
326 std::string plaintext_data = GetTransaction()->GetCryptographer()->
327 DecryptToString(encrypted);
328 if (plaintext_data.length() == 0 ||
329 !unencrypted_data_.ParseFromString(plaintext_data)) {
330 LOG(ERROR) << "Failed to decrypt encrypted node of type " <<
331 syncable::ModelTypeToString(GetModelType()) << ".";
332 return false;
333 }
334 VLOG(2) << "Decrypted specifics of type "
335 << syncable::ModelTypeToString(GetModelType())
336 << " with content: " << plaintext_data;
337 return true;
338 }
339
340 const sync_pb::EntitySpecifics& BaseNode::GetUnencryptedSpecifics(
341 const syncable::Entry* entry) const {
342 const sync_pb::EntitySpecifics& specifics = entry->Get(SPECIFICS);
343 if (specifics.has_encrypted()) {
344 DCHECK(syncable::GetModelTypeFromSpecifics(unencrypted_data_) !=
345 syncable::UNSPECIFIED);
346 return unencrypted_data_;
347 } else {
348 DCHECK(syncable::GetModelTypeFromSpecifics(unencrypted_data_) ==
349 syncable::UNSPECIFIED);
350 return specifics;
351 }
352 }
353
354 int64 BaseNode::GetParentId() const {
355 return IdToMetahandle(GetTransaction()->GetWrappedTrans(),
356 GetEntry()->Get(syncable::PARENT_ID));
357 }
358
359 int64 BaseNode::GetId() const {
360 return GetEntry()->Get(syncable::META_HANDLE);
361 }
362
363 int64 BaseNode::GetModificationTime() const {
364 return GetEntry()->Get(syncable::MTIME);
365 }
366
367 bool BaseNode::GetIsFolder() const {
368 return GetEntry()->Get(syncable::IS_DIR);
369 }
370
371 std::string BaseNode::GetTitle() const {
372 std::string result;
373 // TODO(zea): refactor bookmarks to not need this functionality.
374 if (syncable::BOOKMARKS == GetModelType() &&
375 GetEntry()->Get(syncable::SPECIFICS).has_encrypted()) {
376 // Special case for legacy bookmarks dealing with encryption.
377 ServerNameToSyncAPIName(GetBookmarkSpecifics().title(), &result);
378 } else {
379 ServerNameToSyncAPIName(GetEntry()->Get(syncable::NON_UNIQUE_NAME),
380 &result);
381 }
382 return result;
383 }
384
385 GURL BaseNode::GetURL() const {
386 return GURL(GetBookmarkSpecifics().url());
387 }
388
389 int64 BaseNode::GetPredecessorId() const {
390 syncable::Id id_string = GetEntry()->Get(syncable::PREV_ID);
391 if (id_string.IsRoot())
392 return kInvalidId;
393 return IdToMetahandle(GetTransaction()->GetWrappedTrans(), id_string);
394 }
395
396 int64 BaseNode::GetSuccessorId() const {
397 syncable::Id id_string = GetEntry()->Get(syncable::NEXT_ID);
398 if (id_string.IsRoot())
399 return kInvalidId;
400 return IdToMetahandle(GetTransaction()->GetWrappedTrans(), id_string);
401 }
402
403 int64 BaseNode::GetFirstChildId() const {
404 syncable::Directory* dir = GetTransaction()->GetLookup();
405 syncable::BaseTransaction* trans = GetTransaction()->GetWrappedTrans();
406 syncable::Id id_string =
407 dir->GetFirstChildId(trans, GetEntry()->Get(syncable::ID));
408 if (id_string.IsRoot())
409 return kInvalidId;
410 return IdToMetahandle(GetTransaction()->GetWrappedTrans(), id_string);
411 }
412
413 DictionaryValue* BaseNode::GetSummaryAsValue() const {
414 DictionaryValue* node_info = new DictionaryValue();
415 node_info->SetString("id", base::Int64ToString(GetId()));
416 node_info->SetBoolean("isFolder", GetIsFolder());
417 node_info->SetString("title", GetTitle());
418 node_info->Set("type", ModelTypeToValue(GetModelType()));
419 return node_info;
420 }
421
422 DictionaryValue* BaseNode::GetDetailsAsValue() const {
423 DictionaryValue* node_info = GetSummaryAsValue();
424 // TODO(akalin): Return time in a better format.
425 node_info->SetString("modificationTime",
426 base::Int64ToString(GetModificationTime()));
427 node_info->SetString("parentId", base::Int64ToString(GetParentId()));
428 // Specifics are already in the Entry value, so no need to duplicate
429 // it here.
430 node_info->SetString("externalId",
431 base::Int64ToString(GetExternalId()));
432 node_info->SetString("predecessorId",
433 base::Int64ToString(GetPredecessorId()));
434 node_info->SetString("successorId",
435 base::Int64ToString(GetSuccessorId()));
436 node_info->SetString("firstChildId",
437 base::Int64ToString(GetFirstChildId()));
438 node_info->Set("entry", GetEntry()->ToValue());
439 return node_info;
440 }
441
442 void BaseNode::GetFaviconBytes(std::vector<unsigned char>* output) const {
443 if (!output)
444 return;
445 const std::string& favicon = GetBookmarkSpecifics().favicon();
446 output->assign(reinterpret_cast<const unsigned char*>(favicon.data()),
447 reinterpret_cast<const unsigned char*>(favicon.data() +
448 favicon.length()));
449 }
450
451 int64 BaseNode::GetExternalId() const {
452 return GetEntry()->Get(syncable::LOCAL_EXTERNAL_ID);
453 }
454
455 const sync_pb::AppSpecifics& BaseNode::GetAppSpecifics() const {
456 DCHECK_EQ(syncable::APPS, GetModelType());
457 return GetEntitySpecifics().GetExtension(sync_pb::app);
458 }
459
460 const sync_pb::AutofillSpecifics& BaseNode::GetAutofillSpecifics() const {
461 DCHECK_EQ(syncable::AUTOFILL, GetModelType());
462 return GetEntitySpecifics().GetExtension(sync_pb::autofill);
463 }
464
465 const AutofillProfileSpecifics& BaseNode::GetAutofillProfileSpecifics() const {
466 DCHECK_EQ(GetModelType(), syncable::AUTOFILL_PROFILE);
467 return GetEntitySpecifics().GetExtension(sync_pb::autofill_profile);
468 }
469
470 const sync_pb::BookmarkSpecifics& BaseNode::GetBookmarkSpecifics() const {
471 DCHECK_EQ(syncable::BOOKMARKS, GetModelType());
472 return GetEntitySpecifics().GetExtension(sync_pb::bookmark);
473 }
474
475 const sync_pb::NigoriSpecifics& BaseNode::GetNigoriSpecifics() const {
476 DCHECK_EQ(syncable::NIGORI, GetModelType());
477 return GetEntitySpecifics().GetExtension(sync_pb::nigori);
478 }
479
480 const sync_pb::PasswordSpecificsData& BaseNode::GetPasswordSpecifics() const {
481 DCHECK_EQ(syncable::PASSWORDS, GetModelType());
482 return *password_data_;
483 }
484
485 const sync_pb::ThemeSpecifics& BaseNode::GetThemeSpecifics() const {
486 DCHECK_EQ(syncable::THEMES, GetModelType());
487 return GetEntitySpecifics().GetExtension(sync_pb::theme);
488 }
489
490 const sync_pb::TypedUrlSpecifics& BaseNode::GetTypedUrlSpecifics() const {
491 DCHECK_EQ(syncable::TYPED_URLS, GetModelType());
492 return GetEntitySpecifics().GetExtension(sync_pb::typed_url);
493 }
494
495 const sync_pb::ExtensionSpecifics& BaseNode::GetExtensionSpecifics() const {
496 DCHECK_EQ(syncable::EXTENSIONS, GetModelType());
497 return GetEntitySpecifics().GetExtension(sync_pb::extension);
498 }
499
500 const sync_pb::SessionSpecifics& BaseNode::GetSessionSpecifics() const {
501 DCHECK_EQ(syncable::SESSIONS, GetModelType());
502 return GetEntitySpecifics().GetExtension(sync_pb::session);
503 }
504
505 const sync_pb::EntitySpecifics& BaseNode::GetEntitySpecifics() const {
506 return GetUnencryptedSpecifics(GetEntry());
507 }
508
509 syncable::ModelType BaseNode::GetModelType() const {
510 return GetEntry()->GetModelType();
511 }
512
513 void BaseNode::SetUnencryptedSpecifics(
514 const sync_pb::EntitySpecifics& specifics) {
515 syncable::ModelType type = syncable::GetModelTypeFromSpecifics(specifics);
516 DCHECK_NE(syncable::UNSPECIFIED, type);
517 if (GetModelType() != syncable::UNSPECIFIED) {
518 DCHECK_EQ(GetModelType(), type);
519 }
520 unencrypted_data_.CopyFrom(specifics);
521 }
522
523 ////////////////////////////////////
524 // WriteNode member definitions
525 // Static.
526 bool WriteNode::UpdateEntryWithEncryption(
527 browser_sync::Cryptographer* cryptographer,
528 const sync_pb::EntitySpecifics& new_specifics,
529 syncable::MutableEntry* entry) {
530 syncable::ModelType type = syncable::GetModelTypeFromSpecifics(new_specifics);
531 DCHECK_GE(type, syncable::FIRST_REAL_MODEL_TYPE);
532 syncable::ModelTypeSet encrypted_types = cryptographer->GetEncryptedTypes();
533
534 sync_pb::EntitySpecifics generated_specifics;
535 if (type == syncable::PASSWORDS || // Has own encryption scheme.
536 type == syncable::NIGORI || // Encrypted separately.
537 encrypted_types.count(type) == 0 ||
538 new_specifics.has_encrypted()) {
539 // No encryption required.
540 generated_specifics.CopyFrom(new_specifics);
541 } else {
542 // Encrypt new_specifics into generated_specifics.
543 if (VLOG_IS_ON(2)) {
544 scoped_ptr<DictionaryValue> value(entry->ToValue());
545 std::string info;
546 base::JSONWriter::Write(value.get(), true, &info);
547 VLOG(2) << "Encrypting specifics of type "
548 << syncable::ModelTypeToString(type)
549 << " with content: "
550 << info;
551 }
552 if (!cryptographer->is_initialized())
553 return false;
554 syncable::AddDefaultExtensionValue(type, &generated_specifics);
555 if (!cryptographer->Encrypt(new_specifics,
556 generated_specifics.mutable_encrypted())) {
557 NOTREACHED() << "Could not encrypt data for node of type "
558 << syncable::ModelTypeToString(type);
559 return false;
560 }
561 }
562
563 const sync_pb::EntitySpecifics& old_specifics = entry->Get(SPECIFICS);
564 if (AreSpecificsEqual(cryptographer, old_specifics, generated_specifics)) {
565 // Even if the data is the same but the old specifics are encrypted with an
566 // old key, we should go ahead and re-encrypt with the new key.
567 if ((!old_specifics.has_encrypted() &&
568 !generated_specifics.has_encrypted()) ||
569 cryptographer->CanDecryptUsingDefaultKey(old_specifics.encrypted())) {
570 VLOG(2) << "Specifics of type " << syncable::ModelTypeToString(type)
571 << " already match, dropping change.";
572 return true;
573 }
574 // TODO(zea): Add some way to keep track of how often we're reencrypting
575 // because of a passphrase change.
576 }
577
578 if (generated_specifics.has_encrypted()) {
579 // Overwrite the possibly sensitive non-specifics data.
580 entry->Put(syncable::NON_UNIQUE_NAME, kEncryptedString);
581 // For bookmarks we actually put bogus data into the unencrypted specifics,
582 // else the server will try to do it for us.
583 if (type == syncable::BOOKMARKS) {
584 sync_pb::BookmarkSpecifics* bookmark_specifics =
585 generated_specifics.MutableExtension(sync_pb::bookmark);
586 if (!entry->Get(syncable::IS_DIR))
587 bookmark_specifics->set_url(kEncryptedString);
588 bookmark_specifics->set_title(kEncryptedString);
589 }
590 }
591 entry->Put(syncable::SPECIFICS, generated_specifics);
592 syncable::MarkForSyncing(entry);
593 return true;
594 }
595
596 void WriteNode::SetIsFolder(bool folder) {
597 if (entry_->Get(syncable::IS_DIR) == folder)
598 return; // Skip redundant changes.
599
600 entry_->Put(syncable::IS_DIR, folder);
601 MarkForSyncing();
602 }
603
604 void WriteNode::SetTitle(const std::wstring& title) {
605 std::string server_legal_name;
606 SyncAPINameToServerName(title, &server_legal_name);
607
608 string old_name = entry_->Get(syncable::NON_UNIQUE_NAME);
609
610 if (server_legal_name == old_name)
611 return; // Skip redundant changes.
612
613 // Only set NON_UNIQUE_NAME to the title if we're not encrypted.
614 if (GetEntitySpecifics().has_encrypted())
615 entry_->Put(syncable::NON_UNIQUE_NAME, kEncryptedString);
616 else
617 entry_->Put(syncable::NON_UNIQUE_NAME, server_legal_name);
618
619 // For bookmarks, we also set the title field in the specifics.
620 // TODO(zea): refactor bookmarks to not need this functionality.
621 if (GetModelType() == syncable::BOOKMARKS) {
622 sync_pb::BookmarkSpecifics new_value = GetBookmarkSpecifics();
623 new_value.set_title(server_legal_name);
624 SetBookmarkSpecifics(new_value); // Does it's own encryption checking.
625 }
626
627 MarkForSyncing();
628 }
629
630 void WriteNode::SetURL(const GURL& url) {
631 sync_pb::BookmarkSpecifics new_value = GetBookmarkSpecifics();
632 new_value.set_url(url.spec());
633 SetBookmarkSpecifics(new_value);
634 }
635
636 void WriteNode::SetAppSpecifics(
637 const sync_pb::AppSpecifics& new_value) {
638 sync_pb::EntitySpecifics entity_specifics;
639 entity_specifics.MutableExtension(sync_pb::app)->CopyFrom(new_value);
640 SetEntitySpecifics(entity_specifics);
641 }
642
643 void WriteNode::SetAutofillSpecifics(
644 const sync_pb::AutofillSpecifics& new_value) {
645 sync_pb::EntitySpecifics entity_specifics;
646 entity_specifics.MutableExtension(sync_pb::autofill)->CopyFrom(new_value);
647 SetEntitySpecifics(entity_specifics);
648 }
649
650 void WriteNode::SetAutofillProfileSpecifics(
651 const sync_pb::AutofillProfileSpecifics& new_value) {
652 sync_pb::EntitySpecifics entity_specifics;
653 entity_specifics.MutableExtension(sync_pb::autofill_profile)->
654 CopyFrom(new_value);
655 SetEntitySpecifics(entity_specifics);
656 }
657
658 void WriteNode::SetBookmarkSpecifics(
659 const sync_pb::BookmarkSpecifics& new_value) {
660 sync_pb::EntitySpecifics entity_specifics;
661 entity_specifics.MutableExtension(sync_pb::bookmark)->CopyFrom(new_value);
662 SetEntitySpecifics(entity_specifics);
663 }
664
665 void WriteNode::SetNigoriSpecifics(
666 const sync_pb::NigoriSpecifics& new_value) {
667 sync_pb::EntitySpecifics entity_specifics;
668 entity_specifics.MutableExtension(sync_pb::nigori)->CopyFrom(new_value);
669 SetEntitySpecifics(entity_specifics);
670 }
671
672 void WriteNode::SetPasswordSpecifics(
673 const sync_pb::PasswordSpecificsData& data) {
674 DCHECK_EQ(syncable::PASSWORDS, GetModelType());
675
676 Cryptographer* cryptographer = GetTransaction()->GetCryptographer();
677
678 // Idempotency check to prevent unnecessary syncing: if the plaintexts match
679 // and the old ciphertext is encrypted with the most current key, there's
680 // nothing to do here. Because each encryption is seeded with a different
681 // random value, checking for equivalence post-encryption doesn't suffice.
682 const sync_pb::EncryptedData& old_ciphertext =
683 GetEntry()->Get(SPECIFICS).GetExtension(sync_pb::password).encrypted();
684 scoped_ptr<sync_pb::PasswordSpecificsData> old_plaintext(
685 DecryptPasswordSpecifics(GetEntry()->Get(SPECIFICS), cryptographer));
686 if (old_plaintext.get() &&
687 old_plaintext->SerializeAsString() == data.SerializeAsString() &&
688 cryptographer->CanDecryptUsingDefaultKey(old_ciphertext)) {
689 return;
690 }
691
692 sync_pb::PasswordSpecifics new_value;
693 if (!cryptographer->Encrypt(data, new_value.mutable_encrypted())) {
694 NOTREACHED() << "Failed to encrypt password, possibly due to sync node "
695 << "corruption";
696 return;
697 }
698
699 sync_pb::EntitySpecifics entity_specifics;
700 entity_specifics.MutableExtension(sync_pb::password)->CopyFrom(new_value);
701 SetEntitySpecifics(entity_specifics);
702 }
703
704 void WriteNode::SetThemeSpecifics(
705 const sync_pb::ThemeSpecifics& new_value) {
706 sync_pb::EntitySpecifics entity_specifics;
707 entity_specifics.MutableExtension(sync_pb::theme)->CopyFrom(new_value);
708 SetEntitySpecifics(entity_specifics);
709 }
710
711 void WriteNode::SetSessionSpecifics(
712 const sync_pb::SessionSpecifics& new_value) {
713 sync_pb::EntitySpecifics entity_specifics;
714 entity_specifics.MutableExtension(sync_pb::session)->CopyFrom(new_value);
715 SetEntitySpecifics(entity_specifics);
716 }
717
718 void WriteNode::SetEntitySpecifics(
719 const sync_pb::EntitySpecifics& new_value) {
720 syncable::ModelType new_specifics_type =
721 syncable::GetModelTypeFromSpecifics(new_value);
722 DCHECK_NE(new_specifics_type, syncable::UNSPECIFIED);
723 VLOG(1) << "Writing entity specifics of type "
724 << syncable::ModelTypeToString(new_specifics_type);
725 // GetModelType() can be unspecified if this is the first time this
726 // node is being initialized (see PutModelType()). Otherwise, it
727 // should match |new_specifics_type|.
728 if (GetModelType() != syncable::UNSPECIFIED) {
729 DCHECK_EQ(new_specifics_type, GetModelType());
730 }
731 browser_sync::Cryptographer* cryptographer =
732 GetTransaction()->GetCryptographer();
733
734 // Preserve unknown fields.
735 const sync_pb::EntitySpecifics& old_specifics = entry_->Get(SPECIFICS);
736 sync_pb::EntitySpecifics new_specifics;
737 new_specifics.CopyFrom(new_value);
738 new_specifics.mutable_unknown_fields()->MergeFrom(
739 old_specifics.unknown_fields());
740
741 // Will update the entry if encryption was necessary.
742 if (!UpdateEntryWithEncryption(cryptographer, new_specifics, entry_)) {
743 return;
744 }
745 if (entry_->Get(SPECIFICS).has_encrypted()) {
746 // EncryptIfNecessary already updated the entry for us and marked for
747 // syncing if it was needed. Now we just make a copy of the unencrypted
748 // specifics so that if this node is updated, we do not have to decrypt the
749 // old data. Note that this only modifies the node's local data, not the
750 // entry itself.
751 SetUnencryptedSpecifics(new_value);
752 }
753
754 DCHECK_EQ(new_specifics_type, GetModelType());
755 }
756
757 void WriteNode::ResetFromSpecifics() {
758 SetEntitySpecifics(GetEntitySpecifics());
759 }
760
761 void WriteNode::SetTypedUrlSpecifics(
762 const sync_pb::TypedUrlSpecifics& new_value) {
763 sync_pb::EntitySpecifics entity_specifics;
764 entity_specifics.MutableExtension(sync_pb::typed_url)->CopyFrom(new_value);
765 SetEntitySpecifics(entity_specifics);
766 }
767
768 void WriteNode::SetExtensionSpecifics(
769 const sync_pb::ExtensionSpecifics& new_value) {
770 sync_pb::EntitySpecifics entity_specifics;
771 entity_specifics.MutableExtension(sync_pb::extension)->CopyFrom(new_value);
772 SetEntitySpecifics(entity_specifics);
773 }
774
775 void WriteNode::SetExternalId(int64 id) {
776 if (GetExternalId() != id)
777 entry_->Put(syncable::LOCAL_EXTERNAL_ID, id);
778 }
779
780 WriteNode::WriteNode(WriteTransaction* transaction)
781 : entry_(NULL), transaction_(transaction) {
782 DCHECK(transaction);
783 }
784
785 WriteNode::~WriteNode() {
786 delete entry_;
787 }
788
789 // Find an existing node matching the ID |id|, and bind this WriteNode to it.
790 // Return true on success.
791 bool WriteNode::InitByIdLookup(int64 id) {
792 DCHECK(!entry_) << "Init called twice";
793 DCHECK_NE(id, kInvalidId);
794 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
795 syncable::GET_BY_HANDLE, id);
796 return (entry_->good() && !entry_->Get(syncable::IS_DEL) &&
797 DecryptIfNecessary());
798 }
799
800 // Find a node by client tag, and bind this WriteNode to it.
801 // Return true if the write node was found, and was not deleted.
802 // Undeleting a deleted node is possible by ClientTag.
803 bool WriteNode::InitByClientTagLookup(syncable::ModelType model_type,
804 const std::string& tag) {
805 DCHECK(!entry_) << "Init called twice";
806 if (tag.empty())
807 return false;
808
809 const std::string hash = GenerateSyncableHash(model_type, tag);
810
811 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
812 syncable::GET_BY_CLIENT_TAG, hash);
813 return (entry_->good() && !entry_->Get(syncable::IS_DEL) &&
814 DecryptIfNecessary());
815 }
816
817 bool WriteNode::InitByTagLookup(const std::string& tag) {
818 DCHECK(!entry_) << "Init called twice";
819 if (tag.empty())
820 return false;
821 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
822 syncable::GET_BY_SERVER_TAG, tag);
823 if (!entry_->good())
824 return false;
825 if (entry_->Get(syncable::IS_DEL))
826 return false;
827 syncable::ModelType model_type = GetModelType();
828 DCHECK_EQ(syncable::NIGORI, model_type);
829 return true;
830 }
831
832 void WriteNode::PutModelType(syncable::ModelType model_type) {
833 // Set an empty specifics of the appropriate datatype. The presence
834 // of the specific extension will identify the model type.
835 DCHECK(GetModelType() == model_type ||
836 GetModelType() == syncable::UNSPECIFIED); // Immutable once set.
837
838 sync_pb::EntitySpecifics specifics;
839 syncable::AddDefaultExtensionValue(model_type, &specifics);
840 SetEntitySpecifics(specifics);
841 }
842
843 // Create a new node with default properties, and bind this WriteNode to it.
844 // Return true on success.
845 bool WriteNode::InitByCreation(syncable::ModelType model_type,
846 const BaseNode& parent,
847 const BaseNode* predecessor) {
848 DCHECK(!entry_) << "Init called twice";
849 // |predecessor| must be a child of |parent| or NULL.
850 if (predecessor && predecessor->GetParentId() != parent.GetId()) {
851 DCHECK(false);
852 return false;
853 }
854
855 syncable::Id parent_id = parent.GetEntry()->Get(syncable::ID);
856
857 // Start out with a dummy name. We expect
858 // the caller to set a meaningful name after creation.
859 string dummy(kDefaultNameForNewNodes);
860
861 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
862 syncable::CREATE, parent_id, dummy);
863
864 if (!entry_->good())
865 return false;
866
867 // Entries are untitled folders by default.
868 entry_->Put(syncable::IS_DIR, true);
869
870 PutModelType(model_type);
871
872 // Now set the predecessor, which sets IS_UNSYNCED as necessary.
873 PutPredecessor(predecessor);
874
875 return true;
876 }
877
878 // Create a new node with default properties and a client defined unique tag,
879 // and bind this WriteNode to it.
880 // Return true on success. If the tag exists in the database, then
881 // we will attempt to undelete the node.
882 // TODO(chron): Code datatype into hash tag.
883 // TODO(chron): Is model type ever lost?
884 bool WriteNode::InitUniqueByCreation(syncable::ModelType model_type,
885 const BaseNode& parent,
886 const std::string& tag) {
887 DCHECK(!entry_) << "Init called twice";
888
889 const std::string hash = GenerateSyncableHash(model_type, tag);
890
891 syncable::Id parent_id = parent.GetEntry()->Get(syncable::ID);
892
893 // Start out with a dummy name. We expect
894 // the caller to set a meaningful name after creation.
895 string dummy(kDefaultNameForNewNodes);
896
897 // Check if we have this locally and need to undelete it.
898 scoped_ptr<syncable::MutableEntry> existing_entry(
899 new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
900 syncable::GET_BY_CLIENT_TAG, hash));
901
902 if (existing_entry->good()) {
903 if (existing_entry->Get(syncable::IS_DEL)) {
904 // Rules for undelete:
905 // BASE_VERSION: Must keep the same.
906 // ID: Essential to keep the same.
907 // META_HANDLE: Must be the same, so we can't "split" the entry.
908 // IS_DEL: Must be set to false, will cause reindexing.
909 // This one is weird because IS_DEL is true for "update only"
910 // items. It should be OK to undelete an update only.
911 // MTIME/CTIME: Seems reasonable to just leave them alone.
912 // IS_UNSYNCED: Must set this to true or face database insurrection.
913 // We do this below this block.
914 // IS_UNAPPLIED_UPDATE: Either keep it the same or also set BASE_VERSION
915 // to SERVER_VERSION. We keep it the same here.
916 // IS_DIR: We'll leave it the same.
917 // SPECIFICS: Reset it.
918
919 existing_entry->Put(syncable::IS_DEL, false);
920
921 // Client tags are immutable and must be paired with the ID.
922 // If a server update comes down with an ID and client tag combo,
923 // and it already exists, always overwrite it and store only one copy.
924 // We have to undelete entries because we can't disassociate IDs from
925 // tags and updates.
926
927 existing_entry->Put(syncable::NON_UNIQUE_NAME, dummy);
928 existing_entry->Put(syncable::PARENT_ID, parent_id);
929 entry_ = existing_entry.release();
930 } else {
931 return false;
932 }
933 } else {
934 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
935 syncable::CREATE, parent_id, dummy);
936 if (!entry_->good()) {
937 return false;
938 }
939
940 // Only set IS_DIR for new entries. Don't bitflip undeleted ones.
941 entry_->Put(syncable::UNIQUE_CLIENT_TAG, hash);
942 }
943
944 // We don't support directory and tag combinations.
945 entry_->Put(syncable::IS_DIR, false);
946
947 // Will clear specifics data.
948 PutModelType(model_type);
949
950 // Now set the predecessor, which sets IS_UNSYNCED as necessary.
951 PutPredecessor(NULL);
952
953 return true;
954 }
955
956 bool WriteNode::SetPosition(const BaseNode& new_parent,
957 const BaseNode* predecessor) {
958 // |predecessor| must be a child of |new_parent| or NULL.
959 if (predecessor && predecessor->GetParentId() != new_parent.GetId()) {
960 DCHECK(false);
961 return false;
962 }
963
964 syncable::Id new_parent_id = new_parent.GetEntry()->Get(syncable::ID);
965
966 // Filter out redundant changes if both the parent and the predecessor match.
967 if (new_parent_id == entry_->Get(syncable::PARENT_ID)) {
968 const syncable::Id& old = entry_->Get(syncable::PREV_ID);
969 if ((!predecessor && old.IsRoot()) ||
970 (predecessor && (old == predecessor->GetEntry()->Get(syncable::ID)))) {
971 return true;
972 }
973 }
974
975 // Atomically change the parent. This will fail if it would
976 // introduce a cycle in the hierarchy.
977 if (!entry_->Put(syncable::PARENT_ID, new_parent_id))
978 return false;
979
980 // Now set the predecessor, which sets IS_UNSYNCED as necessary.
981 PutPredecessor(predecessor);
982
983 return true;
984 }
985
986 const syncable::Entry* WriteNode::GetEntry() const {
987 return entry_;
988 }
989
990 const BaseTransaction* WriteNode::GetTransaction() const {
991 return transaction_;
992 }
993
994 void WriteNode::Remove() {
995 entry_->Put(syncable::IS_DEL, true);
996 MarkForSyncing();
997 }
998
999 void WriteNode::PutPredecessor(const BaseNode* predecessor) {
1000 syncable::Id predecessor_id = predecessor ?
1001 predecessor->GetEntry()->Get(syncable::ID) : syncable::Id();
1002 entry_->PutPredecessor(predecessor_id);
1003 // Mark this entry as unsynced, to wake up the syncer.
1004 MarkForSyncing();
1005 }
1006
1007 void WriteNode::SetFaviconBytes(const vector<unsigned char>& bytes) {
1008 sync_pb::BookmarkSpecifics new_value = GetBookmarkSpecifics();
1009 new_value.set_favicon(bytes.empty() ? NULL : &bytes[0], bytes.size());
1010 SetBookmarkSpecifics(new_value);
1011 }
1012
1013 void WriteNode::MarkForSyncing() {
1014 syncable::MarkForSyncing(entry_);
1015 }
1016
1017 //////////////////////////////////////////////////////////////////////////
1018 // ReadNode member definitions
1019 ReadNode::ReadNode(const BaseTransaction* transaction)
1020 : entry_(NULL), transaction_(transaction) {
1021 DCHECK(transaction);
1022 }
1023
1024 ReadNode::ReadNode() {
1025 entry_ = NULL;
1026 transaction_ = NULL;
1027 }
1028
1029 ReadNode::~ReadNode() {
1030 delete entry_;
1031 }
1032
1033 void ReadNode::InitByRootLookup() {
1034 DCHECK(!entry_) << "Init called twice";
1035 syncable::BaseTransaction* trans = transaction_->GetWrappedTrans();
1036 entry_ = new syncable::Entry(trans, syncable::GET_BY_ID, trans->root_id());
1037 if (!entry_->good())
1038 DCHECK(false) << "Could not lookup root node for reading.";
1039 }
1040
1041 bool ReadNode::InitByIdLookup(int64 id) {
1042 DCHECK(!entry_) << "Init called twice";
1043 DCHECK_NE(id, kInvalidId);
1044 syncable::BaseTransaction* trans = transaction_->GetWrappedTrans();
1045 entry_ = new syncable::Entry(trans, syncable::GET_BY_HANDLE, id);
1046 if (!entry_->good())
1047 return false;
1048 if (entry_->Get(syncable::IS_DEL))
1049 return false;
1050 syncable::ModelType model_type = GetModelType();
1051 LOG_IF(WARNING, model_type == syncable::UNSPECIFIED ||
1052 model_type == syncable::TOP_LEVEL_FOLDER)
1053 << "SyncAPI InitByIdLookup referencing unusual object.";
1054 return DecryptIfNecessary();
1055 }
1056
1057 bool ReadNode::InitByClientTagLookup(syncable::ModelType model_type,
1058 const std::string& tag) {
1059 DCHECK(!entry_) << "Init called twice";
1060 if (tag.empty())
1061 return false;
1062
1063 const std::string hash = GenerateSyncableHash(model_type, tag);
1064
1065 entry_ = new syncable::Entry(transaction_->GetWrappedTrans(),
1066 syncable::GET_BY_CLIENT_TAG, hash);
1067 return (entry_->good() && !entry_->Get(syncable::IS_DEL) &&
1068 DecryptIfNecessary());
1069 }
1070
1071 const syncable::Entry* ReadNode::GetEntry() const {
1072 return entry_;
1073 }
1074
1075 const BaseTransaction* ReadNode::GetTransaction() const {
1076 return transaction_;
1077 }
1078
1079 bool ReadNode::InitByTagLookup(const std::string& tag) {
1080 DCHECK(!entry_) << "Init called twice";
1081 if (tag.empty())
1082 return false;
1083 syncable::BaseTransaction* trans = transaction_->GetWrappedTrans();
1084 entry_ = new syncable::Entry(trans, syncable::GET_BY_SERVER_TAG, tag);
1085 if (!entry_->good())
1086 return false;
1087 if (entry_->Get(syncable::IS_DEL))
1088 return false;
1089 syncable::ModelType model_type = GetModelType();
1090 LOG_IF(WARNING, model_type == syncable::UNSPECIFIED ||
1091 model_type == syncable::TOP_LEVEL_FOLDER)
1092 << "SyncAPI InitByTagLookup referencing unusually typed object.";
1093 return DecryptIfNecessary();
1094 }
1095
1096 //////////////////////////////////////////////////////////////////////////
1097 // ReadTransaction member definitions
1098 ReadTransaction::ReadTransaction(const tracked_objects::Location& from_here,
1099 UserShare* share)
1100 : BaseTransaction(share),
1101 transaction_(NULL),
1102 close_transaction_(true) {
1103 transaction_ = new syncable::ReadTransaction(from_here, GetLookup());
1104 }
1105
1106 ReadTransaction::ReadTransaction(UserShare* share,
1107 syncable::BaseTransaction* trans)
1108 : BaseTransaction(share),
1109 transaction_(trans),
1110 close_transaction_(false) {}
1111
1112 ReadTransaction::~ReadTransaction() {
1113 if (close_transaction_) {
1114 delete transaction_;
1115 }
1116 }
1117
1118 syncable::BaseTransaction* ReadTransaction::GetWrappedTrans() const {
1119 return transaction_;
1120 }
1121
1122 //////////////////////////////////////////////////////////////////////////
1123 // WriteTransaction member definitions
1124 WriteTransaction::WriteTransaction(const tracked_objects::Location& from_here,
1125 UserShare* share)
1126 : BaseTransaction(share),
1127 transaction_(NULL) {
1128 transaction_ = new syncable::WriteTransaction(from_here, syncable::SYNCAPI,
1129 GetLookup());
1130 }
1131
1132 WriteTransaction::~WriteTransaction() {
1133 delete transaction_;
1134 }
1135
1136 syncable::BaseTransaction* WriteTransaction::GetWrappedTrans() const {
1137 return transaction_;
1138 }
1139
1140 SyncManager::ChangeRecord::ChangeRecord()
1141 : id(kInvalidId), action(ACTION_ADD) {}
1142
1143 SyncManager::ChangeRecord::~ChangeRecord() {}
1144
1145 DictionaryValue* SyncManager::ChangeRecord::ToValue(
1146 const BaseTransaction* trans) const {
1147 DictionaryValue* value = new DictionaryValue();
1148 std::string action_str;
1149 switch (action) {
1150 case ACTION_ADD:
1151 action_str = "Add";
1152 break;
1153 case ACTION_DELETE:
1154 action_str = "Delete";
1155 break;
1156 case ACTION_UPDATE:
1157 action_str = "Update";
1158 break;
1159 default:
1160 NOTREACHED();
1161 action_str = "Unknown";
1162 break;
1163 }
1164 value->SetString("action", action_str);
1165 Value* node_value = NULL;
1166 if (action == ACTION_DELETE) {
1167 DictionaryValue* node_dict = new DictionaryValue();
1168 node_dict->SetString("id", base::Int64ToString(id));
1169 node_dict->Set("specifics",
1170 browser_sync::EntitySpecificsToValue(specifics));
1171 if (extra.get()) {
1172 node_dict->Set("extra", extra->ToValue());
1173 }
1174 node_value = node_dict;
1175 } else {
1176 ReadNode node(trans);
1177 if (node.InitByIdLookup(id)) {
1178 node_value = node.GetDetailsAsValue();
1179 }
1180 }
1181 if (!node_value) {
1182 NOTREACHED();
1183 node_value = Value::CreateNullValue();
1184 }
1185 value->Set("node", node_value);
1186 return value;
1187 }
1188
1189 SyncManager::ExtraPasswordChangeRecordData::ExtraPasswordChangeRecordData() {}
1190
1191 SyncManager::ExtraPasswordChangeRecordData::ExtraPasswordChangeRecordData(
1192 const sync_pb::PasswordSpecificsData& data)
1193 : unencrypted_(data) {
1194 }
1195
1196 SyncManager::ExtraPasswordChangeRecordData::~ExtraPasswordChangeRecordData() {}
1197
1198 DictionaryValue* SyncManager::ExtraPasswordChangeRecordData::ToValue() const {
1199 return browser_sync::PasswordSpecificsDataToValue(unencrypted_);
1200 }
1201
1202 const sync_pb::PasswordSpecificsData&
1203 SyncManager::ExtraPasswordChangeRecordData::unencrypted() const {
1204 return unencrypted_;
1205 }
1206
1207 syncable::ModelTypeSet GetEncryptedTypes(
1208 const sync_api::BaseTransaction* trans) {
1209 Cryptographer* cryptographer = trans->GetCryptographer();
1210 return cryptographer->GetEncryptedTypes();
1211 }
1212
1213 //////////////////////////////////////////////////////////////////////////
1214 // SyncManager's implementation: SyncManager::SyncInternal
1215 class SyncManager::SyncInternal
1216 : public net::NetworkChangeNotifier::IPAddressObserver,
1217 public sync_notifier::SyncNotifierObserver,
1218 public JsBackend,
1219 public SyncEngineEventListener,
1220 public ServerConnectionEventListener,
1221 public syncable::DirectoryChangeDelegate {
1222 static const int kDefaultNudgeDelayMilliseconds;
1223 static const int kPreferencesNudgeDelayMilliseconds;
1224 public:
1225 explicit SyncInternal(const std::string& name)
1226 : weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
1227 registrar_(NULL),
1228 initialized_(false),
1229 setup_for_test_mode_(false),
1230 observing_ip_address_changes_(false) {
1231 // Pre-fill |notification_info_map_|.
1232 for (int i = syncable::FIRST_REAL_MODEL_TYPE;
1233 i < syncable::MODEL_TYPE_COUNT; ++i) {
1234 notification_info_map_.insert(
1235 std::make_pair(syncable::ModelTypeFromInt(i), NotificationInfo()));
1236 }
1237
1238 // Bind message handlers.
1239 BindJsMessageHandler(
1240 "getNotificationState",
1241 &SyncManager::SyncInternal::GetNotificationState);
1242 BindJsMessageHandler(
1243 "getNotificationInfo",
1244 &SyncManager::SyncInternal::GetNotificationInfo);
1245 BindJsMessageHandler(
1246 "getRootNodeDetails",
1247 &SyncManager::SyncInternal::GetRootNodeDetails);
1248 BindJsMessageHandler(
1249 "getNodeSummariesById",
1250 &SyncManager::SyncInternal::GetNodeSummariesById);
1251 BindJsMessageHandler(
1252 "getNodeDetailsById",
1253 &SyncManager::SyncInternal::GetNodeDetailsById);
1254 BindJsMessageHandler(
1255 "getChildNodeIds",
1256 &SyncManager::SyncInternal::GetChildNodeIds);
1257 BindJsMessageHandler(
1258 "findNodesContainingString",
1259 &SyncManager::SyncInternal::FindNodesContainingString);
1260 }
1261
1262 virtual ~SyncInternal() {
1263 CHECK(!initialized_);
1264 }
1265
1266 bool Init(const FilePath& database_location,
1267 const WeakHandle<JsEventHandler>& event_handler,
1268 const std::string& sync_server_and_path,
1269 int port,
1270 bool use_ssl,
1271 HttpPostProviderFactory* post_factory,
1272 ModelSafeWorkerRegistrar* model_safe_worker_registrar,
1273 const std::string& user_agent,
1274 const SyncCredentials& credentials,
1275 sync_notifier::SyncNotifier* sync_notifier,
1276 const std::string& restored_key_for_bootstrapping,
1277 bool setup_for_test_mode);
1278
1279 // Sign into sync with given credentials.
1280 // We do not verify the tokens given. After this call, the tokens are set
1281 // and the sync DB is open. True if successful, false if something
1282 // went wrong.
1283 bool SignIn(const SyncCredentials& credentials);
1284
1285 // Update tokens that we're using in Sync. Email must stay the same.
1286 void UpdateCredentials(const SyncCredentials& credentials);
1287
1288 // Called when the user disables or enables a sync type.
1289 void UpdateEnabledTypes();
1290
1291 // Tell the sync engine to start the syncing process.
1292 void StartSyncingNormally();
1293
1294 // Whether or not the Nigori node is encrypted using an explicit passphrase.
1295 bool IsUsingExplicitPassphrase();
1296
1297 // Update the Cryptographer from the current nigori node.
1298 // Note: opens a transaction and can trigger an ON_PASSPHRASE_REQUIRED, so
1299 // should only be called after syncapi is fully initialized.
1300 // Returns true if cryptographer is ready, false otherwise.
1301 bool UpdateCryptographerFromNigori();
1302
1303 // Set the datatypes we want to encrypt and encrypt any nodes as necessary.
1304 // Note: |encrypted_types| will be unioned with the current set of encrypted
1305 // types, as we do not currently support decrypting datatypes.
1306 void EncryptDataTypes(const syncable::ModelTypeSet& encrypted_types);
1307
1308 // Try to set the current passphrase to |passphrase|, and record whether
1309 // it is an explicit passphrase or implicitly using gaia in the Nigori
1310 // node.
1311 void SetPassphrase(const std::string& passphrase, bool is_explicit);
1312
1313 // Call periodically from a database-safe thread to persist recent changes
1314 // to the syncapi model.
1315 void SaveChanges();
1316
1317 // DirectoryChangeDelegate implementation.
1318 // This listener is called upon completion of a syncable transaction, and
1319 // builds the list of sync-engine initiated changes that will be forwarded to
1320 // the SyncManager's Observers.
1321 virtual void HandleTransactionCompleteChangeEvent(
1322 const ModelTypeBitSet& models_with_changes);
1323 virtual ModelTypeBitSet HandleTransactionEndingChangeEvent(
1324 syncable::BaseTransaction* trans);
1325 virtual void HandleCalculateChangesChangeEventFromSyncApi(
1326 const EntryKernelMutationSet& mutations,
1327 syncable::BaseTransaction* trans);
1328 virtual void HandleCalculateChangesChangeEventFromSyncer(
1329 const EntryKernelMutationSet& mutations,
1330 syncable::BaseTransaction* trans);
1331
1332 // Listens for notifications from the ServerConnectionManager
1333 void HandleServerConnectionEvent(const ServerConnectionEvent& event);
1334
1335 // Open the directory named with username_for_share
1336 bool OpenDirectory();
1337
1338 // SyncNotifierObserver implementation.
1339 virtual void OnNotificationStateChange(
1340 bool notifications_enabled);
1341
1342 virtual void OnIncomingNotification(
1343 const syncable::ModelTypePayloadMap& type_payloads);
1344
1345 virtual void StoreState(const std::string& cookie);
1346
1347 // Thread-safe observers_ accessors.
1348 void CopyObservers(ObserverList<SyncManager::Observer>* observers_copy);
1349 bool HaveObservers() const;
1350 void AddObserver(SyncManager::Observer* observer);
1351 void RemoveObserver(SyncManager::Observer* observer);
1352
1353 // Accessors for the private members.
1354 DirectoryManager* dir_manager() { return share_.dir_manager.get(); }
1355 SyncAPIServerConnectionManager* connection_manager() {
1356 return connection_manager_.get();
1357 }
1358 SyncScheduler* scheduler() { return scheduler_.get(); }
1359 UserShare* GetUserShare() {
1360 DCHECK(initialized_);
1361 return &share_;
1362 }
1363
1364 // Return the currently active (validated) username for use with syncable
1365 // types.
1366 const std::string& username_for_share() const {
1367 return share_.name;
1368 }
1369
1370 Status GetStatus();
1371
1372 void RequestNudge(const tracked_objects::Location& nudge_location);
1373
1374 void RequestNudgeForDataType(
1375 const tracked_objects::Location& nudge_location,
1376 const ModelType& type);
1377
1378 void RequestEarlyExit();
1379
1380 // See SyncManager::Shutdown for information.
1381 void Shutdown();
1382
1383 // If this is a deletion for a password, sets the legacy
1384 // ExtraPasswordChangeRecordData field of |buffer|. Otherwise sets
1385 // |buffer|'s specifics field to contain the unencrypted data.
1386 void SetExtraChangeRecordData(int64 id,
1387 syncable::ModelType type,
1388 ChangeReorderBuffer* buffer,
1389 Cryptographer* cryptographer,
1390 const syncable::EntryKernel& original,
1391 bool existed_before,
1392 bool exists_now);
1393
1394 // Called only by our NetworkChangeNotifier.
1395 virtual void OnIPAddressChanged();
1396
1397 bool InitialSyncEndedForAllEnabledTypes() {
1398 syncable::ModelTypeSet types;
1399 ModelSafeRoutingInfo enabled_types;
1400 registrar_->GetModelSafeRoutingInfo(&enabled_types);
1401 for (ModelSafeRoutingInfo::const_iterator i = enabled_types.begin();
1402 i != enabled_types.end(); ++i) {
1403 types.insert(i->first);
1404 }
1405
1406 return InitialSyncEndedForTypes(types, &share_);
1407 }
1408
1409 // SyncEngineEventListener implementation.
1410 virtual void OnSyncEngineEvent(const SyncEngineEvent& event);
1411
1412 // ServerConnectionEventListener implementation.
1413 virtual void OnServerConnectionEvent(const ServerConnectionEvent& event);
1414
1415 // JsBackend implementation.
1416 virtual void SetJsEventHandler(
1417 const WeakHandle<JsEventHandler>& event_handler) OVERRIDE;
1418 virtual void ProcessJsMessage(
1419 const std::string& name, const JsArgList& args,
1420 const WeakHandle<JsReplyHandler>& reply_handler) OVERRIDE;
1421
1422 private:
1423 struct NotificationInfo {
1424 int total_count;
1425 std::string payload;
1426
1427 NotificationInfo() : total_count(0) {}
1428
1429 ~NotificationInfo() {}
1430
1431 // Returned pointer owned by the caller.
1432 DictionaryValue* ToValue() const {
1433 DictionaryValue* value = new DictionaryValue();
1434 value->SetInteger("totalCount", total_count);
1435 value->SetString("payload", payload);
1436 return value;
1437 }
1438 };
1439
1440 typedef std::map<syncable::ModelType, NotificationInfo> NotificationInfoMap;
1441 typedef JsArgList
1442 (SyncManager::SyncInternal::*UnboundJsMessageHandler)(const JsArgList&);
1443 typedef base::Callback<JsArgList(JsArgList)> JsMessageHandler;
1444 typedef std::map<std::string, JsMessageHandler> JsMessageHandlerMap;
1445
1446 // Helper to call OnAuthError when no authentication credentials are
1447 // available.
1448 void RaiseAuthNeededEvent();
1449
1450 // Determine if the parents or predecessors differ between the old and new
1451 // versions of an entry stored in |a| and |b|. Note that a node's index may
1452 // change without its NEXT_ID changing if the node at NEXT_ID also moved (but
1453 // the relative order is unchanged). To handle such cases, we rely on the
1454 // caller to treat a position update on any sibling as updating the positions
1455 // of all siblings.
1456 static bool VisiblePositionsDiffer(
1457 const syncable::EntryKernelMutation& mutation) {
1458 const syncable::EntryKernel& a = mutation.original;
1459 const syncable::EntryKernel& b = mutation.mutated;
1460 // If the datatype isn't one where the browser model cares about position,
1461 // don't bother notifying that data model of position-only changes.
1462 if (!ShouldMaintainPosition(
1463 syncable::GetModelTypeFromSpecifics(b.ref(SPECIFICS))))
1464 return false;
1465 if (a.ref(syncable::NEXT_ID) != b.ref(syncable::NEXT_ID))
1466 return true;
1467 if (a.ref(syncable::PARENT_ID) != b.ref(syncable::PARENT_ID))
1468 return true;
1469 return false;
1470 }
1471
1472 // Determine if any of the fields made visible to clients of the Sync API
1473 // differ between the versions of an entry stored in |a| and |b|. A return
1474 // value of false means that it should be OK to ignore this change.
1475 static bool VisiblePropertiesDiffer(
1476 const syncable::EntryKernelMutation& mutation,
1477 Cryptographer* cryptographer) {
1478 const syncable::EntryKernel& a = mutation.original;
1479 const syncable::EntryKernel& b = mutation.mutated;
1480 const sync_pb::EntitySpecifics& a_specifics = a.ref(SPECIFICS);
1481 const sync_pb::EntitySpecifics& b_specifics = b.ref(SPECIFICS);
1482 DCHECK_EQ(syncable::GetModelTypeFromSpecifics(a_specifics),
1483 syncable::GetModelTypeFromSpecifics(b_specifics));
1484 syncable::ModelType model_type =
1485 syncable::GetModelTypeFromSpecifics(b_specifics);
1486 // Suppress updates to items that aren't tracked by any browser model.
1487 if (model_type < syncable::FIRST_REAL_MODEL_TYPE ||
1488 !a.ref(syncable::UNIQUE_SERVER_TAG).empty()) {
1489 return false;
1490 }
1491 if (a.ref(syncable::IS_DIR) != b.ref(syncable::IS_DIR))
1492 return true;
1493 if (!AreSpecificsEqual(cryptographer,
1494 a.ref(syncable::SPECIFICS),
1495 b.ref(syncable::SPECIFICS))) {
1496 return true;
1497 }
1498 // We only care if the name has changed if neither specifics is encrypted
1499 // (encrypted nodes blow away the NON_UNIQUE_NAME).
1500 if (!a_specifics.has_encrypted() && !b_specifics.has_encrypted() &&
1501 a.ref(syncable::NON_UNIQUE_NAME) != b.ref(syncable::NON_UNIQUE_NAME))
1502 return true;
1503 if (VisiblePositionsDiffer(mutation))
1504 return true;
1505 return false;
1506 }
1507
1508 bool ChangeBuffersAreEmpty() {
1509 for (int i = 0; i < syncable::MODEL_TYPE_COUNT; ++i) {
1510 if (!change_buffers_[i].IsEmpty())
1511 return false;
1512 }
1513 return true;
1514 }
1515
1516 void CheckServerReachable() {
1517 if (connection_manager()) {
1518 connection_manager()->CheckServerReachable();
1519 } else {
1520 NOTREACHED() << "Should be valid connection manager!";
1521 }
1522 }
1523
1524 void ReEncryptEverything(WriteTransaction* trans);
1525
1526 // Initializes (bootstraps) the Cryptographer if NIGORI has finished
1527 // initial sync so that it can immediately start encrypting / decrypting.
1528 // If the restored key is incompatible with the current version of the NIGORI
1529 // node (which could happen if a restart occurred just after an update to
1530 // NIGORI was downloaded and the user must enter a new passphrase to decrypt)
1531 // then we will raise OnPassphraseRequired and set pending keys for
1532 // decryption. Otherwise, the cryptographer is made ready (is_ready()).
1533 void BootstrapEncryption(const std::string& restored_key_for_bootstrapping);
1534
1535 // Called for every notification. This updates the notification statistics
1536 // to be displayed in about:sync.
1537 void UpdateNotificationInfo(
1538 const syncable::ModelTypePayloadMap& type_payloads);
1539
1540 // Checks for server reachabilty and requests a nudge.
1541 void OnIPAddressChangedImpl();
1542
1543 // Helper function used only by the constructor.
1544 void BindJsMessageHandler(
1545 const std::string& name, UnboundJsMessageHandler unbound_message_handler);
1546
1547 // Returned pointer is owned by the caller.
1548 static DictionaryValue* NotificationInfoToValue(
1549 const NotificationInfoMap& notification_info);
1550
1551 // JS message handlers.
1552 JsArgList GetNotificationState(const JsArgList& args);
1553 JsArgList GetNotificationInfo(const JsArgList& args);
1554 JsArgList GetRootNodeDetails(const JsArgList& args);
1555 JsArgList GetNodeSummariesById(const JsArgList& args);
1556 JsArgList GetNodeDetailsById(const JsArgList& args);
1557 JsArgList GetChildNodeIds(const JsArgList& args);
1558 JsArgList FindNodesContainingString(const JsArgList& args);
1559
1560 const std::string name_;
1561
1562 base::ThreadChecker thread_checker_;
1563
1564 base::WeakPtrFactory<SyncInternal> weak_ptr_factory_;
1565
1566 // Thread-safe handle used by
1567 // HandleCalculateChangesChangeEventFromSyncApi(), which can be
1568 // called from any thread. Valid only between between calls to
1569 // Init() and Shutdown().
1570 //
1571 // TODO(akalin): Ideally, we wouldn't need to store this; instead,
1572 // we'd have another worker class which implements
1573 // HandleCalculateChangesChangeEventFromSyncApi() and we'd pass it a
1574 // WeakHandle when we construct it.
1575 WeakHandle<SyncInternal> weak_handle_this_;
1576
1577 // We couple the DirectoryManager and username together in a UserShare member
1578 // so we can return a handle to share_ to clients of the API for use when
1579 // constructing any transaction type.
1580 UserShare share_;
1581
1582 // We have to lock around every observers_ access because it can get accessed
1583 // from any thread and added to/removed from on the core thread.
1584 mutable base::Lock observers_lock_;
1585 ObserverList<SyncManager::Observer> observers_;
1586
1587 // The ServerConnectionManager used to abstract communication between the
1588 // client (the Syncer) and the sync server.
1589 scoped_ptr<SyncAPIServerConnectionManager> connection_manager_;
1590
1591 // The scheduler that runs the Syncer. Needs to be explicitly
1592 // Start()ed.
1593 scoped_ptr<SyncScheduler> scheduler_;
1594
1595 // The SyncNotifier which notifies us when updates need to be downloaded.
1596 scoped_ptr<sync_notifier::SyncNotifier> sync_notifier_;
1597
1598 // A multi-purpose status watch object that aggregates stats from various
1599 // sync components.
1600 AllStatus allstatus_;
1601
1602 // Each element of this array is a store of change records produced by
1603 // HandleChangeEvent during the CALCULATE_CHANGES step. The changes are
1604 // segregated by model type, and are stored here to be processed and
1605 // forwarded to the observer slightly later, at the TRANSACTION_ENDING
1606 // step by HandleTransactionEndingChangeEvent. The list is cleared in the
1607 // TRANSACTION_COMPLETE step by HandleTransactionCompleteChangeEvent.
1608 ChangeReorderBuffer change_buffers_[syncable::MODEL_TYPE_COUNT];
1609
1610 // The entity that provides us with information about which types to sync.
1611 // The instance is shared between the SyncManager and the Syncer.
1612 ModelSafeWorkerRegistrar* registrar_;
1613
1614 // Set to true once Init has been called.
1615 bool initialized_;
1616
1617 // True if the SyncManager should be running in test mode (no sync
1618 // scheduler actually communicating with the server).
1619 bool setup_for_test_mode_;
1620
1621 // Whether we should respond to an IP address change notification.
1622 bool observing_ip_address_changes_;
1623
1624 // Map used to store the notification info to be displayed in
1625 // about:sync page.
1626 NotificationInfoMap notification_info_map_;
1627
1628 // These are for interacting with chrome://sync-internals.
1629 JsMessageHandlerMap js_message_handlers_;
1630 WeakHandle<JsEventHandler> js_event_handler_;
1631 JsSyncManagerObserver js_sync_manager_observer_;
1632 JsTransactionObserver js_transaction_observer_;
1633 };
1634 const int SyncManager::SyncInternal::kDefaultNudgeDelayMilliseconds = 200;
1635 const int SyncManager::SyncInternal::kPreferencesNudgeDelayMilliseconds = 2000;
1636
1637 SyncManager::Observer::~Observer() {}
1638
1639 SyncManager::SyncManager(const std::string& name)
1640 : data_(new SyncInternal(name)) {}
1641
1642 SyncManager::Status::Status()
1643 : summary(INVALID),
1644 authenticated(false),
1645 server_up(false),
1646 server_reachable(false),
1647 server_broken(false),
1648 notifications_enabled(false),
1649 notifications_received(0),
1650 notifiable_commits(0),
1651 max_consecutive_errors(0),
1652 unsynced_count(0),
1653 conflicting_count(0),
1654 syncing(false),
1655 initial_sync_ended(false),
1656 syncer_stuck(false),
1657 updates_available(0),
1658 updates_received(0),
1659 tombstone_updates_received(0),
1660 disk_full(false),
1661 num_local_overwrites_total(0),
1662 num_server_overwrites_total(0),
1663 nonempty_get_updates(0),
1664 empty_get_updates(0),
1665 useless_sync_cycles(0),
1666 useful_sync_cycles(0),
1667 cryptographer_ready(false),
1668 crypto_has_pending_keys(false) {
1669 }
1670
1671 SyncManager::Status::~Status() {
1672 }
1673
1674 bool SyncManager::Init(
1675 const FilePath& database_location,
1676 const WeakHandle<JsEventHandler>& event_handler,
1677 const std::string& sync_server_and_path,
1678 int sync_server_port,
1679 bool use_ssl,
1680 HttpPostProviderFactory* post_factory,
1681 ModelSafeWorkerRegistrar* registrar,
1682 const std::string& user_agent,
1683 const SyncCredentials& credentials,
1684 sync_notifier::SyncNotifier* sync_notifier,
1685 const std::string& restored_key_for_bootstrapping,
1686 bool setup_for_test_mode) {
1687 DCHECK(post_factory);
1688 VLOG(1) << "SyncManager starting Init...";
1689 string server_string(sync_server_and_path);
1690 return data_->Init(database_location,
1691 event_handler,
1692 server_string,
1693 sync_server_port,
1694 use_ssl,
1695 post_factory,
1696 registrar,
1697 user_agent,
1698 credentials,
1699 sync_notifier,
1700 restored_key_for_bootstrapping,
1701 setup_for_test_mode);
1702 }
1703
1704 void SyncManager::UpdateCredentials(const SyncCredentials& credentials) {
1705 data_->UpdateCredentials(credentials);
1706 }
1707
1708 void SyncManager::UpdateEnabledTypes() {
1709 data_->UpdateEnabledTypes();
1710 }
1711
1712
1713 bool SyncManager::InitialSyncEndedForAllEnabledTypes() {
1714 return data_->InitialSyncEndedForAllEnabledTypes();
1715 }
1716
1717 void SyncManager::StartSyncingNormally() {
1718 data_->StartSyncingNormally();
1719 }
1720
1721 void SyncManager::SetPassphrase(const std::string& passphrase,
1722 bool is_explicit) {
1723 data_->SetPassphrase(passphrase, is_explicit);
1724 }
1725
1726 void SyncManager::EncryptDataTypes(
1727 const syncable::ModelTypeSet& encrypted_types) {
1728 data_->EncryptDataTypes(encrypted_types);
1729 }
1730
1731 bool SyncManager::IsUsingExplicitPassphrase() {
1732 return data_ && data_->IsUsingExplicitPassphrase();
1733 }
1734
1735 void SyncManager::RequestCleanupDisabledTypes() {
1736 if (data_->scheduler())
1737 data_->scheduler()->ScheduleCleanupDisabledTypes();
1738 }
1739
1740 void SyncManager::RequestClearServerData() {
1741 if (data_->scheduler())
1742 data_->scheduler()->ScheduleClearUserData();
1743 }
1744
1745 void SyncManager::RequestConfig(const syncable::ModelTypeBitSet& types,
1746 ConfigureReason reason) {
1747 if (!data_->scheduler()) {
1748 LOG(INFO)
1749 << "SyncManager::RequestConfig: bailing out because scheduler is "
1750 << "null";
1751 return;
1752 }
1753 StartConfigurationMode(NULL);
1754 data_->scheduler()->ScheduleConfig(types, reason);
1755 }
1756
1757 void SyncManager::StartConfigurationMode(ModeChangeCallback* callback) {
1758 if (!data_->scheduler()) {
1759 LOG(INFO)
1760 << "SyncManager::StartConfigurationMode: could not start "
1761 << "configuration mode because because scheduler is null";
1762 return;
1763 }
1764 data_->scheduler()->Start(
1765 browser_sync::SyncScheduler::CONFIGURATION_MODE, callback);
1766 }
1767
1768 const std::string& SyncManager::GetAuthenticatedUsername() {
1769 DCHECK(data_);
1770 return data_->username_for_share();
1771 }
1772
1773 bool SyncManager::SyncInternal::Init(
1774 const FilePath& database_location,
1775 const WeakHandle<JsEventHandler>& event_handler,
1776 const std::string& sync_server_and_path,
1777 int port,
1778 bool use_ssl,
1779 HttpPostProviderFactory* post_factory,
1780 ModelSafeWorkerRegistrar* model_safe_worker_registrar,
1781 const std::string& user_agent,
1782 const SyncCredentials& credentials,
1783 sync_notifier::SyncNotifier* sync_notifier,
1784 const std::string& restored_key_for_bootstrapping,
1785 bool setup_for_test_mode) {
1786 CHECK(!initialized_);
1787
1788 DCHECK(thread_checker_.CalledOnValidThread());
1789
1790 VLOG(1) << "Starting SyncInternal initialization.";
1791
1792 weak_handle_this_ = MakeWeakHandle(weak_ptr_factory_.GetWeakPtr());
1793
1794 registrar_ = model_safe_worker_registrar;
1795 setup_for_test_mode_ = setup_for_test_mode;
1796
1797 sync_notifier_.reset(sync_notifier);
1798
1799 AddObserver(&js_sync_manager_observer_);
1800 SetJsEventHandler(event_handler);
1801
1802 share_.dir_manager.reset(new DirectoryManager(database_location));
1803
1804 connection_manager_.reset(new SyncAPIServerConnectionManager(
1805 sync_server_and_path, port, use_ssl, user_agent, post_factory));
1806
1807 net::NetworkChangeNotifier::AddIPAddressObserver(this);
1808 observing_ip_address_changes_ = true;
1809
1810 connection_manager()->AddListener(this);
1811
1812 // TODO(akalin): CheckServerReachable() can block, which may cause jank if we
1813 // try to shut down sync. Fix this.
1814 MessageLoop::current()->PostTask(
1815 FROM_HERE, base::Bind(&SyncInternal::CheckServerReachable,
1816 weak_ptr_factory_.GetWeakPtr()));
1817
1818 // Test mode does not use a syncer context or syncer thread.
1819 if (!setup_for_test_mode_) {
1820 // Build a SyncSessionContext and store the worker in it.
1821 VLOG(1) << "Sync is bringing up SyncSessionContext.";
1822 std::vector<SyncEngineEventListener*> listeners;
1823 listeners.push_back(&allstatus_);
1824 listeners.push_back(this);
1825 SyncSessionContext* context = new SyncSessionContext(
1826 connection_manager_.get(),
1827 dir_manager(),
1828 model_safe_worker_registrar,
1829 listeners);
1830 context->set_account_name(credentials.email);
1831 // The SyncScheduler takes ownership of |context|.
1832 scheduler_.reset(new SyncScheduler(name_, context, new Syncer()));
1833 }
1834
1835 bool signed_in = SignIn(credentials);
1836
1837 if (signed_in && scheduler()) {
1838 scheduler()->Start(
1839 browser_sync::SyncScheduler::CONFIGURATION_MODE, NULL);
1840 }
1841
1842 initialized_ = true;
1843
1844 // Notify that initialization is complete.
1845 ObserverList<SyncManager::Observer> temp_obs_list;
1846 CopyObservers(&temp_obs_list);
1847 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
1848 OnInitializationComplete(
1849 WeakHandle<JsBackend>(weak_ptr_factory_.GetWeakPtr())));
1850
1851 // The following calls check that initialized_ is true.
1852
1853 BootstrapEncryption(restored_key_for_bootstrapping);
1854
1855 sync_notifier_->AddObserver(this);
1856
1857 return signed_in;
1858 }
1859
1860 void SyncManager::SyncInternal::BootstrapEncryption(
1861 const std::string& restored_key_for_bootstrapping) {
1862 // Cryptographer should only be accessed while holding a transaction.
1863 ReadTransaction trans(FROM_HERE, GetUserShare());
1864 Cryptographer* cryptographer = trans.GetCryptographer();
1865
1866 // Set the bootstrap token before bailing out if nigori node is not there.
1867 // This could happen if server asked us to migrate nigri.
1868 cryptographer->Bootstrap(restored_key_for_bootstrapping);
1869 }
1870
1871 bool SyncManager::SyncInternal::UpdateCryptographerFromNigori() {
1872 DCHECK(initialized_);
1873 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share());
1874 if (!lookup.good()) {
1875 NOTREACHED() << "BootstrapEncryption: lookup not good so bailing out";
1876 return false;
1877 }
1878 if (!lookup->initial_sync_ended_for_type(syncable::NIGORI))
1879 return false; // Should only happen during first time sync.
1880
1881 ReadTransaction trans(FROM_HERE, GetUserShare());
1882 Cryptographer* cryptographer = trans.GetCryptographer();
1883
1884 ReadNode node(&trans);
1885 if (!node.InitByTagLookup(kNigoriTag)) {
1886 NOTREACHED();
1887 return false;
1888 }
1889 Cryptographer::UpdateResult result =
1890 cryptographer->Update(node.GetNigoriSpecifics());
1891 if (result == Cryptographer::NEEDS_PASSPHRASE) {
1892 ObserverList<SyncManager::Observer> temp_obs_list;
1893 CopyObservers(&temp_obs_list);
1894 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
1895 OnPassphraseRequired(sync_api::REASON_DECRYPTION));
1896 }
1897
1898 allstatus_.SetCryptographerReady(cryptographer->is_ready());
1899 allstatus_.SetCryptoHasPendingKeys(cryptographer->has_pending_keys());
1900
1901 return cryptographer->is_ready();
1902 }
1903
1904 void SyncManager::SyncInternal::StartSyncingNormally() {
1905 // Start the sync scheduler. This won't actually result in any
1906 // syncing until at least the DirectoryManager broadcasts the OPENED
1907 // event, and a valid server connection is detected.
1908 if (scheduler()) // NULL during certain unittests.
1909 scheduler()->Start(SyncScheduler::NORMAL_MODE, NULL);
1910 }
1911
1912 bool SyncManager::SyncInternal::OpenDirectory() {
1913 DCHECK(!initialized_) << "Should only happen once";
1914
1915 bool share_opened = dir_manager()->Open(username_for_share(), this);
1916 DCHECK(share_opened);
1917 if (!share_opened) {
1918 ObserverList<SyncManager::Observer> temp_obs_list;
1919 CopyObservers(&temp_obs_list);
1920 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
1921 OnStopSyncingPermanently());
1922
1923 LOG(ERROR) << "Could not open share for:" << username_for_share();
1924 return false;
1925 }
1926
1927 // Database has to be initialized for the guid to be available.
1928 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share());
1929 if (!lookup.good()) {
1930 NOTREACHED();
1931 return false;
1932 }
1933
1934 connection_manager()->set_client_id(lookup->cache_guid());
1935 lookup->AddTransactionObserver(&js_transaction_observer_);
1936 return true;
1937 }
1938
1939 bool SyncManager::SyncInternal::SignIn(const SyncCredentials& credentials) {
1940 DCHECK(thread_checker_.CalledOnValidThread());
1941 DCHECK(share_.name.empty());
1942 share_.name = credentials.email;
1943
1944 VLOG(1) << "Signing in user: " << username_for_share();
1945 if (!OpenDirectory())
1946 return false;
1947
1948 // Retrieve and set the sync notifier state. This should be done
1949 // only after OpenDirectory is called.
1950 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share());
1951 std::string unique_id;
1952 std::string state;
1953 if (lookup.good()) {
1954 unique_id = lookup->cache_guid();
1955 state = lookup->GetNotificationState();
1956 VLOG(1) << "Read notification unique ID: " << unique_id;
1957 if (VLOG_IS_ON(1)) {
1958 std::string encoded_state;
1959 base::Base64Encode(state, &encoded_state);
1960 VLOG(1) << "Read notification state: " << encoded_state;
1961 }
1962 } else {
1963 LOG(ERROR) << "Could not read notification unique ID/state";
1964 }
1965 sync_notifier_->SetUniqueId(unique_id);
1966 sync_notifier_->SetState(state);
1967
1968 UpdateCredentials(credentials);
1969 UpdateEnabledTypes();
1970 return true;
1971 }
1972
1973 void SyncManager::SyncInternal::UpdateCredentials(
1974 const SyncCredentials& credentials) {
1975 DCHECK(thread_checker_.CalledOnValidThread());
1976 DCHECK_EQ(credentials.email, share_.name);
1977 DCHECK(!credentials.email.empty());
1978 DCHECK(!credentials.sync_token.empty());
1979
1980 observing_ip_address_changes_ = true;
1981 if (connection_manager()->set_auth_token(credentials.sync_token)) {
1982 sync_notifier_->UpdateCredentials(
1983 credentials.email, credentials.sync_token);
1984 if (!setup_for_test_mode_) {
1985 CheckServerReachable();
1986 }
1987 }
1988 }
1989
1990 void SyncManager::SyncInternal::UpdateEnabledTypes() {
1991 DCHECK(thread_checker_.CalledOnValidThread());
1992 ModelSafeRoutingInfo routes;
1993 registrar_->GetModelSafeRoutingInfo(&routes);
1994 syncable::ModelTypeSet enabled_types;
1995 for (ModelSafeRoutingInfo::const_iterator it = routes.begin();
1996 it != routes.end(); ++it) {
1997 enabled_types.insert(it->first);
1998 }
1999 sync_notifier_->UpdateEnabledTypes(enabled_types);
2000 }
2001
2002 void SyncManager::SyncInternal::RaiseAuthNeededEvent() {
2003 ObserverList<SyncManager::Observer> temp_obs_list;
2004 CopyObservers(&temp_obs_list);
2005 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
2006 OnAuthError(AuthError(AuthError::INVALID_GAIA_CREDENTIALS)));
2007 }
2008
2009 void SyncManager::SyncInternal::SetPassphrase(
2010 const std::string& passphrase, bool is_explicit) {
2011 // We do not accept empty passphrases.
2012 if (passphrase.empty()) {
2013 VLOG(1) << "Rejecting empty passphrase.";
2014 ObserverList<SyncManager::Observer> temp_obs_list;
2015 CopyObservers(&temp_obs_list);
2016 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
2017 OnPassphraseRequired(sync_api::REASON_SET_PASSPHRASE_FAILED));
2018 return;
2019 }
2020
2021 // All accesses to the cryptographer are protected by a transaction.
2022 WriteTransaction trans(FROM_HERE, GetUserShare());
2023 Cryptographer* cryptographer = trans.GetCryptographer();
2024 KeyParams params = {"localhost", "dummy", passphrase};
2025
2026 WriteNode node(&trans);
2027 if (!node.InitByTagLookup(kNigoriTag)) {
2028 // TODO(albertb): Plumb an UnrecoverableError all the way back to the PSS.
2029 NOTREACHED();
2030 return;
2031 }
2032
2033 if (cryptographer->has_pending_keys()) {
2034 bool suceeded = false;
2035
2036 // See if the explicit flag matches what is set in nigori. If not we dont
2037 // even try the passphrase. Note: This could mean that we wont try setting
2038 // the gaia password as passphrase if custom is elected by the user. Which
2039 // is fine because nigori node has all the old passwords in it.
2040 if (node.GetNigoriSpecifics().using_explicit_passphrase() == is_explicit) {
2041 if (cryptographer->DecryptPendingKeys(params)) {
2042 suceeded = true;
2043 } else {
2044 VLOG(1) << "Passphrase failed to decrypt pending keys.";
2045 }
2046 } else {
2047 VLOG(1) << "Not trying the passphrase because the explicit flags dont "
2048 << "match. Nigori node's explicit flag is "
2049 << node.GetNigoriSpecifics().using_explicit_passphrase();
2050 }
2051
2052 if (!suceeded) {
2053 ObserverList<SyncManager::Observer> temp_obs_list;
2054 CopyObservers(&temp_obs_list);
2055 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
2056 OnPassphraseRequired(sync_api::REASON_SET_PASSPHRASE_FAILED));
2057 return;
2058 }
2059
2060 // Nudge the syncer so that encrypted datatype updates that were waiting for
2061 // this passphrase get applied as soon as possible.
2062 RequestNudge(FROM_HERE);
2063 } else {
2064 VLOG(1) << "No pending keys, adding provided passphrase.";
2065
2066 // Prevent an implicit SetPassphrase request from changing an explicitly
2067 // set passphrase.
2068 if (!is_explicit && node.GetNigoriSpecifics().using_explicit_passphrase())
2069 return;
2070
2071 cryptographer->AddKey(params);
2072
2073 // TODO(tim): Bug 58231. It would be nice if SetPassphrase didn't require
2074 // messing with the Nigori node, because we can't call SetPassphrase until
2075 // download conditions are met vs Cryptographer init. It seems like it's
2076 // safe to defer this work.
2077 sync_pb::NigoriSpecifics specifics(node.GetNigoriSpecifics());
2078 specifics.clear_encrypted();
2079 cryptographer->GetKeys(specifics.mutable_encrypted());
2080 specifics.set_using_explicit_passphrase(is_explicit);
2081 node.SetNigoriSpecifics(specifics);
2082 ReEncryptEverything(&trans);
2083 }
2084
2085 VLOG(1) << "Passphrase accepted, bootstrapping encryption.";
2086 std::string bootstrap_token;
2087 cryptographer->GetBootstrapToken(&bootstrap_token);
2088 ObserverList<SyncManager::Observer> temp_obs_list;
2089 CopyObservers(&temp_obs_list);
2090 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
2091 OnPassphraseAccepted(bootstrap_token));
2092 }
2093
2094 bool SyncManager::SyncInternal::IsUsingExplicitPassphrase() {
2095 ReadTransaction trans(FROM_HERE, &share_);
2096 ReadNode node(&trans);
2097 if (!node.InitByTagLookup(kNigoriTag)) {
2098 // TODO(albertb): Plumb an UnrecoverableError all the way back to the PSS.
2099 NOTREACHED();
2100 return false;
2101 }
2102
2103 return node.GetNigoriSpecifics().using_explicit_passphrase();
2104 }
2105
2106 void SyncManager::SyncInternal::EncryptDataTypes(
2107 const syncable::ModelTypeSet& encrypted_types) {
2108 DCHECK(initialized_);
2109 VLOG(1) << "Attempting to encrypt datatypes "
2110 << syncable::ModelTypeSetToString(encrypted_types);
2111
2112 WriteTransaction trans(FROM_HERE, GetUserShare());
2113 WriteNode node(&trans);
2114 if (!node.InitByTagLookup(kNigoriTag)) {
2115 NOTREACHED() << "Unable to set encrypted datatypes because Nigori node not "
2116 << "found.";
2117 return;
2118 }
2119
2120 Cryptographer* cryptographer = trans.GetCryptographer();
2121
2122 if (!cryptographer->is_initialized()) {
2123 VLOG(1) << "Attempting to encrypt datatypes when cryptographer not "
2124 << "initialized, prompting for passphrase.";
2125 ObserverList<SyncManager::Observer> temp_obs_list;
2126 CopyObservers(&temp_obs_list);
2127 // TODO(zea): this isn't really decryption, but that's the only way we have
2128 // to prompt the user for a passsphrase. See http://crbug.com/91379.
2129 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
2130 OnPassphraseRequired(sync_api::REASON_DECRYPTION));
2131 return;
2132 }
2133
2134 // Update the Nigori node's set of encrypted datatypes.
2135 // Note, we merge the current encrypted types with those requested. Once a
2136 // datatypes is marked as needing encryption, it is never unmarked.
2137 sync_pb::NigoriSpecifics nigori;
2138 nigori.CopyFrom(node.GetNigoriSpecifics());
2139 syncable::ModelTypeSet current_encrypted_types = GetEncryptedTypes(&trans);
2140 syncable::ModelTypeSet newly_encrypted_types;
2141 std::set_union(current_encrypted_types.begin(), current_encrypted_types.end(),
2142 encrypted_types.begin(), encrypted_types.end(),
2143 std::inserter(newly_encrypted_types,
2144 newly_encrypted_types.begin()));
2145 allstatus_.SetEncryptedTypes(newly_encrypted_types);
2146 if (newly_encrypted_types == current_encrypted_types) {
2147 // Set of encrypted types has not changed, just notify and return.
2148 ObserverList<SyncManager::Observer> temp_obs_list;
2149 CopyObservers(&temp_obs_list);
2150 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
2151 OnEncryptionComplete(current_encrypted_types));
2152 return;
2153 }
2154 syncable::FillNigoriEncryptedTypes(newly_encrypted_types, &nigori);
2155 node.SetNigoriSpecifics(nigori);
2156
2157 cryptographer->SetEncryptedTypes(nigori);
2158
2159 // TODO(zea): only reencrypt this datatype? ReEncrypting everything is a
2160 // safer approach, and should not impact anything that is already encrypted
2161 // (redundant changes are ignored).
2162 ReEncryptEverything(&trans);
2163 return;
2164 }
2165
2166 // TODO(zea): Add unit tests that ensure no sync changes are made when not
2167 // needed.
2168 void SyncManager::SyncInternal::ReEncryptEverything(WriteTransaction* trans) {
2169 syncable::ModelTypeSet encrypted_types =
2170 GetEncryptedTypes(trans);
2171 ModelSafeRoutingInfo routes;
2172 registrar_->GetModelSafeRoutingInfo(&routes);
2173 std::string tag;
2174 for (syncable::ModelTypeSet::iterator iter = encrypted_types.begin();
2175 iter != encrypted_types.end(); ++iter) {
2176 if (*iter == syncable::PASSWORDS || routes.count(*iter) == 0)
2177 continue;
2178 ReadNode type_root(trans);
2179 tag = syncable::ModelTypeToRootTag(*iter);
2180 if (!type_root.InitByTagLookup(tag)) {
2181 NOTREACHED();
2182 return;
2183 }
2184
2185 // Iterate through all children of this datatype.
2186 std::queue<int64> to_visit;
2187 int64 child_id = type_root.GetFirstChildId();
2188 to_visit.push(child_id);
2189 while (!to_visit.empty()) {
2190 child_id = to_visit.front();
2191 to_visit.pop();
2192 if (child_id == kInvalidId)
2193 continue;
2194
2195 WriteNode child(trans);
2196 if (!child.InitByIdLookup(child_id)) {
2197 NOTREACHED();
2198 continue;
2199 }
2200 if (child.GetIsFolder()) {
2201 to_visit.push(child.GetFirstChildId());
2202 }
2203 if (child.GetEntry()->Get(syncable::UNIQUE_SERVER_TAG).empty()) {
2204 // Rewrite the specifics of the node with encrypted data if necessary
2205 // (only rewrite the non-unique folders).
2206 child.ResetFromSpecifics();
2207 }
2208 to_visit.push(child.GetSuccessorId());
2209 }
2210 }
2211
2212 if (routes.count(syncable::PASSWORDS) > 0) {
2213 // Passwords are encrypted with their own legacy scheme.
2214 ReadNode passwords_root(trans);
2215 std::string passwords_tag =
2216 syncable::ModelTypeToRootTag(syncable::PASSWORDS);
2217 // It's possible we'll have the password routing info and not the password
2218 // root if we attempted to SetPassphrase before passwords was enabled.
2219 if (passwords_root.InitByTagLookup(passwords_tag)) {
2220 int64 child_id = passwords_root.GetFirstChildId();
2221 while (child_id != kInvalidId) {
2222 WriteNode child(trans);
2223 if (!child.InitByIdLookup(child_id)) {
2224 NOTREACHED();
2225 return;
2226 }
2227 child.SetPasswordSpecifics(child.GetPasswordSpecifics());
2228 child_id = child.GetSuccessorId();
2229 }
2230 }
2231 }
2232
2233 ObserverList<SyncManager::Observer> temp_obs_list;
2234 CopyObservers(&temp_obs_list);
2235 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
2236 OnEncryptionComplete(encrypted_types));
2237 }
2238
2239 SyncManager::~SyncManager() {
2240 delete data_;
2241 }
2242
2243 void SyncManager::AddObserver(Observer* observer) {
2244 data_->AddObserver(observer);
2245 }
2246
2247 void SyncManager::RemoveObserver(Observer* observer) {
2248 data_->RemoveObserver(observer);
2249 }
2250
2251 void SyncManager::RequestEarlyExit() {
2252 data_->RequestEarlyExit();
2253 }
2254
2255 void SyncManager::SyncInternal::RequestEarlyExit() {
2256 if (scheduler()) {
2257 scheduler()->RequestEarlyExit();
2258 }
2259 }
2260
2261 void SyncManager::Shutdown() {
2262 data_->Shutdown();
2263 }
2264
2265 void SyncManager::SyncInternal::Shutdown() {
2266 DCHECK(thread_checker_.CalledOnValidThread());
2267
2268 // Prevent any in-flight method calls from running. Also
2269 // invalidates |weak_handle_this_|.
2270 weak_ptr_factory_.InvalidateWeakPtrs();
2271
2272 // Automatically stops the scheduler.
2273 scheduler_.reset();
2274
2275 SetJsEventHandler(WeakHandle<JsEventHandler>());
2276 RemoveObserver(&js_sync_manager_observer_);
2277
2278 if (sync_notifier_.get()) {
2279 sync_notifier_->RemoveObserver(this);
2280 }
2281 sync_notifier_.reset();
2282
2283 if (connection_manager_.get()) {
2284 connection_manager_->RemoveListener(this);
2285 }
2286 connection_manager_.reset();
2287
2288 net::NetworkChangeNotifier::RemoveIPAddressObserver(this);
2289 observing_ip_address_changes_ = false;
2290
2291 if (dir_manager()) {
2292 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share());
2293 if (lookup.good()) {
2294 lookup->RemoveTransactionObserver(&js_transaction_observer_);
2295 } else {
2296 NOTREACHED();
2297 }
2298 dir_manager()->FinalSaveChangesForAll();
2299 dir_manager()->Close(username_for_share());
2300 }
2301
2302 // Reset the DirectoryManager and UserSettings so they relinquish sqlite
2303 // handles to backing files.
2304 share_.dir_manager.reset();
2305
2306 setup_for_test_mode_ = false;
2307 registrar_ = NULL;
2308
2309 initialized_ = false;
2310
2311 // We reset this here, since only now we know it will not be
2312 // accessed from other threads (since we shut down everything).
2313 weak_handle_this_.Reset();
2314 }
2315
2316 void SyncManager::SyncInternal::OnIPAddressChanged() {
2317 VLOG(1) << "IP address change detected";
2318 if (!observing_ip_address_changes_) {
2319 VLOG(1) << "IP address change dropped.";
2320 return;
2321 }
2322
2323 #if defined (OS_CHROMEOS)
2324 // TODO(tim): This is a hack to intentionally lose a race with flimflam at
2325 // shutdown, so we don't cause shutdown to wait for our http request.
2326 // http://crosbug.com/8429
2327 MessageLoop::current()->PostDelayedTask(
2328 FROM_HERE,
2329 base::Bind(&SyncInternal::OnIPAddressChangedImpl,
2330 weak_ptr_factory_.GetWeakPtr()),
2331 kChromeOSNetworkChangeReactionDelayHackMsec);
2332 #else
2333 OnIPAddressChangedImpl();
2334 #endif // defined(OS_CHROMEOS)
2335 }
2336
2337 void SyncManager::SyncInternal::OnIPAddressChangedImpl() {
2338 // TODO(akalin): CheckServerReachable() can block, which may cause
2339 // jank if we try to shut down sync. Fix this.
2340 connection_manager()->CheckServerReachable();
2341 }
2342
2343 void SyncManager::SyncInternal::OnServerConnectionEvent(
2344 const ServerConnectionEvent& event) {
2345 allstatus_.HandleServerConnectionEvent(event);
2346 if (event.connection_code ==
2347 browser_sync::HttpResponse::SERVER_CONNECTION_OK) {
2348 ObserverList<SyncManager::Observer> temp_obs_list;
2349 CopyObservers(&temp_obs_list);
2350 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
2351 OnAuthError(AuthError::None()));
2352 }
2353
2354 if (event.connection_code == browser_sync::HttpResponse::SYNC_AUTH_ERROR) {
2355 observing_ip_address_changes_ = false;
2356 ObserverList<SyncManager::Observer> temp_obs_list;
2357 CopyObservers(&temp_obs_list);
2358 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
2359 OnAuthError(AuthError(AuthError::INVALID_GAIA_CREDENTIALS)));
2360 }
2361
2362 if (event.connection_code ==
2363 browser_sync::HttpResponse::SYNC_SERVER_ERROR) {
2364 ObserverList<SyncManager::Observer> temp_obs_list;
2365 CopyObservers(&temp_obs_list);
2366 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
2367 OnAuthError(AuthError(AuthError::CONNECTION_FAILED)));
2368 }
2369 }
2370
2371 void SyncManager::SyncInternal::HandleTransactionCompleteChangeEvent(
2372 const syncable::ModelTypeBitSet& models_with_changes) {
2373 // This notification happens immediately after the transaction mutex is
2374 // released. This allows work to be performed without blocking other threads
2375 // from acquiring a transaction.
2376 if (!HaveObservers())
2377 return;
2378
2379 // Call commit.
2380 for (int i = 0; i < syncable::MODEL_TYPE_COUNT; ++i) {
2381 if (models_with_changes.test(i)) {
2382 ObserverList<SyncManager::Observer> temp_obs_list;
2383 CopyObservers(&temp_obs_list);
2384 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
2385 OnChangesComplete(syncable::ModelTypeFromInt(i)));
2386 }
2387 }
2388 }
2389
2390 ModelTypeBitSet SyncManager::SyncInternal::HandleTransactionEndingChangeEvent(
2391 syncable::BaseTransaction* trans) {
2392 // This notification happens immediately before a syncable WriteTransaction
2393 // falls out of scope. It happens while the channel mutex is still held,
2394 // and while the transaction mutex is held, so it cannot be re-entrant.
2395 if (!HaveObservers() || ChangeBuffersAreEmpty())
2396 return ModelTypeBitSet();
2397
2398 // This will continue the WriteTransaction using a read only wrapper.
2399 // This is the last chance for read to occur in the WriteTransaction
2400 // that's closing. This special ReadTransaction will not close the
2401 // underlying transaction.
2402 ReadTransaction read_trans(GetUserShare(), trans);
2403
2404 syncable::ModelTypeBitSet models_with_changes;
2405 for (int i = 0; i < syncable::MODEL_TYPE_COUNT; ++i) {
2406 if (change_buffers_[i].IsEmpty())
2407 continue;
2408
2409 vector<ChangeRecord> ordered_changes;
2410 change_buffers_[i].GetAllChangesInTreeOrder(&read_trans, &ordered_changes);
2411 if (!ordered_changes.empty()) {
2412 ObserverList<SyncManager::Observer> temp_obs_list;
2413 CopyObservers(&temp_obs_list);
2414 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
2415 OnChangesApplied(syncable::ModelTypeFromInt(i), &read_trans,
2416 &ordered_changes[0], ordered_changes.size()));
2417 models_with_changes.set(i, true);
2418 }
2419 change_buffers_[i].Clear();
2420 }
2421 return models_with_changes;
2422 }
2423
2424 void SyncManager::SyncInternal::HandleCalculateChangesChangeEventFromSyncApi(
2425 const EntryKernelMutationSet& mutations,
2426 syncable::BaseTransaction* trans) {
2427 if (!scheduler()) {
2428 return;
2429 }
2430
2431 // We have been notified about a user action changing a sync model.
2432 LOG_IF(WARNING, !ChangeBuffersAreEmpty()) <<
2433 "CALCULATE_CHANGES called with unapplied old changes.";
2434
2435 // The mutated model type, or UNSPECIFIED if nothing was mutated.
2436 syncable::ModelType mutated_model_type = syncable::UNSPECIFIED;
2437
2438 // Find the first real mutation. We assume that only a single model
2439 // type is mutated per transaction.
2440 for (syncable::EntryKernelMutationSet::const_iterator it =
2441 mutations.begin(); it != mutations.end(); ++it) {
2442 if (!it->mutated.ref(syncable::IS_UNSYNCED)) {
2443 continue;
2444 }
2445
2446 syncable::ModelType model_type =
2447 syncable::GetModelTypeFromSpecifics(it->mutated.ref(SPECIFICS));
2448 if (model_type < syncable::FIRST_REAL_MODEL_TYPE) {
2449 NOTREACHED() << "Permanent or underspecified item changed via syncapi.";
2450 continue;
2451 }
2452
2453 // Found real mutation.
2454 if (mutated_model_type == syncable::UNSPECIFIED) {
2455 mutated_model_type = model_type;
2456 break;
2457 }
2458 }
2459
2460 // Nudge if necessary.
2461 if (mutated_model_type != syncable::UNSPECIFIED) {
2462 if (weak_handle_this_.IsInitialized()) {
2463 weak_handle_this_.Call(FROM_HERE,
2464 &SyncInternal::RequestNudgeForDataType,
2465 FROM_HERE,
2466 mutated_model_type);
2467 } else {
2468 NOTREACHED();
2469 }
2470 }
2471 }
2472
2473 void SyncManager::SyncInternal::SetExtraChangeRecordData(int64 id,
2474 syncable::ModelType type, ChangeReorderBuffer* buffer,
2475 Cryptographer* cryptographer, const syncable::EntryKernel& original,
2476 bool existed_before, bool exists_now) {
2477 // If this is a deletion and the datatype was encrypted, we need to decrypt it
2478 // and attach it to the buffer.
2479 if (!exists_now && existed_before) {
2480 sync_pb::EntitySpecifics original_specifics(original.ref(SPECIFICS));
2481 if (type == syncable::PASSWORDS) {
2482 // Passwords must use their own legacy ExtraPasswordChangeRecordData.
2483 scoped_ptr<sync_pb::PasswordSpecificsData> data(
2484 DecryptPasswordSpecifics(original_specifics, cryptographer));
2485 if (!data.get()) {
2486 NOTREACHED();
2487 return;
2488 }
2489 buffer->SetExtraDataForId(id, new ExtraPasswordChangeRecordData(*data));
2490 } else if (original_specifics.has_encrypted()) {
2491 // All other datatypes can just create a new unencrypted specifics and
2492 // attach it.
2493 const sync_pb::EncryptedData& encrypted = original_specifics.encrypted();
2494 if (!cryptographer->Decrypt(encrypted, &original_specifics)) {
2495 NOTREACHED();
2496 return;
2497 }
2498 }
2499 buffer->SetSpecificsForId(id, original_specifics);
2500 }
2501 }
2502
2503 void SyncManager::SyncInternal::HandleCalculateChangesChangeEventFromSyncer(
2504 const EntryKernelMutationSet& mutations,
2505 syncable::BaseTransaction* trans) {
2506 // We only expect one notification per sync step, so change_buffers_ should
2507 // contain no pending entries.
2508 LOG_IF(WARNING, !ChangeBuffersAreEmpty()) <<
2509 "CALCULATE_CHANGES called with unapplied old changes.";
2510
2511 Cryptographer* crypto = dir_manager()->GetCryptographer(trans);
2512 for (syncable::EntryKernelMutationSet::const_iterator it =
2513 mutations.begin(); it != mutations.end(); ++it) {
2514 bool existed_before = !it->original.ref(syncable::IS_DEL);
2515 bool exists_now = !it->mutated.ref(syncable::IS_DEL);
2516
2517 // Omit items that aren't associated with a model.
2518 syncable::ModelType type =
2519 syncable::GetModelTypeFromSpecifics(it->mutated.ref(SPECIFICS));
2520 if (type < syncable::FIRST_REAL_MODEL_TYPE)
2521 continue;
2522
2523 int64 id = it->original.ref(syncable::META_HANDLE);
2524 if (exists_now && !existed_before)
2525 change_buffers_[type].PushAddedItem(id);
2526 else if (!exists_now && existed_before)
2527 change_buffers_[type].PushDeletedItem(id);
2528 else if (exists_now && existed_before &&
2529 VisiblePropertiesDiffer(*it, crypto)) {
2530 change_buffers_[type].PushUpdatedItem(
2531 id, VisiblePositionsDiffer(*it));
2532 }
2533
2534 SetExtraChangeRecordData(id, type, &change_buffers_[type], crypto,
2535 it->original, existed_before, exists_now);
2536 }
2537 }
2538
2539 SyncManager::Status SyncManager::SyncInternal::GetStatus() {
2540 return allstatus_.status();
2541 }
2542
2543 void SyncManager::SyncInternal::RequestNudge(
2544 const tracked_objects::Location& location) {
2545 if (scheduler())
2546 scheduler()->ScheduleNudge(
2547 TimeDelta::FromMilliseconds(0), browser_sync::NUDGE_SOURCE_LOCAL,
2548 ModelTypeBitSet(), location);
2549 }
2550
2551
2552 void SyncManager::SyncInternal::RequestNudgeForDataType(
2553 const tracked_objects::Location& nudge_location,
2554 const ModelType& type) {
2555 if (!scheduler()) {
2556 NOTREACHED();
2557 return;
2558 }
2559 base::TimeDelta nudge_delay;
2560 switch (type) {
2561 case syncable::PREFERENCES:
2562 nudge_delay =
2563 TimeDelta::FromMilliseconds(kPreferencesNudgeDelayMilliseconds);
2564 break;
2565 case syncable::SESSIONS:
2566 nudge_delay = scheduler()->sessions_commit_delay();
2567 break;
2568 default:
2569 nudge_delay =
2570 TimeDelta::FromMilliseconds(kDefaultNudgeDelayMilliseconds);
2571 break;
2572 }
2573 syncable::ModelTypeBitSet types;
2574 types.set(type);
2575 scheduler()->ScheduleNudge(nudge_delay,
2576 browser_sync::NUDGE_SOURCE_LOCAL,
2577 types,
2578 nudge_location);
2579 }
2580
2581 void SyncManager::SyncInternal::OnSyncEngineEvent(
2582 const SyncEngineEvent& event) {
2583 DCHECK(thread_checker_.CalledOnValidThread());
2584 if (!HaveObservers()) {
2585 LOG(INFO)
2586 << "OnSyncEngineEvent returning because observers_.size() is zero";
2587 return;
2588 }
2589
2590 // Only send an event if this is due to a cycle ending and this cycle
2591 // concludes a canonical "sync" process; that is, based on what is known
2592 // locally we are "all happy" and up-to-date. There may be new changes on
2593 // the server, but we'll get them on a subsequent sync.
2594 //
2595 // Notifications are sent at the end of every sync cycle, regardless of
2596 // whether we should sync again.
2597 if (event.what_happened == SyncEngineEvent::SYNC_CYCLE_ENDED) {
2598 ModelSafeRoutingInfo enabled_types;
2599 registrar_->GetModelSafeRoutingInfo(&enabled_types);
2600 {
2601 // Check to see if we need to notify the frontend that we have newly
2602 // encrypted types or that we require a passphrase.
2603 sync_api::ReadTransaction trans(FROM_HERE, GetUserShare());
2604 Cryptographer* cryptographer = trans.GetCryptographer();
2605 // If we've completed a sync cycle and the cryptographer isn't ready
2606 // yet, prompt the user for a passphrase.
2607 if (cryptographer->has_pending_keys()) {
2608 VLOG(1) << "OnPassPhraseRequired Sent";
2609 ObserverList<SyncManager::Observer> temp_obs_list;
2610 CopyObservers(&temp_obs_list);
2611 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
2612 OnPassphraseRequired(sync_api::REASON_DECRYPTION));
2613 } else if (!cryptographer->is_ready() &&
2614 event.snapshot->initial_sync_ended.test(syncable::NIGORI)) {
2615 VLOG(1) << "OnPassphraseRequired sent because cryptographer is not "
2616 << "ready";
2617 ObserverList<SyncManager::Observer> temp_obs_list;
2618 CopyObservers(&temp_obs_list);
2619 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
2620 OnPassphraseRequired(sync_api::REASON_ENCRYPTION));
2621 }
2622
2623 allstatus_.SetCryptographerReady(cryptographer->is_ready());
2624 allstatus_.SetCryptoHasPendingKeys(cryptographer->has_pending_keys());
2625 allstatus_.SetEncryptedTypes(cryptographer->GetEncryptedTypes());
2626
2627 // If everything is in order(we have the passphrase) then there is no
2628 // need to inform the listeners. They will just wait for sync
2629 // completion event and if no errors have been raised it means
2630 // encryption was succesful.
2631 }
2632
2633 if (!initialized_) {
2634 LOG(INFO) << "OnSyncCycleCompleted not sent because sync api is not "
2635 << "initialized";
2636 return;
2637 }
2638
2639 if (!event.snapshot->has_more_to_sync) {
2640 VLOG(1) << "OnSyncCycleCompleted sent";
2641 ObserverList<SyncManager::Observer> temp_obs_list;
2642 CopyObservers(&temp_obs_list);
2643 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
2644 OnSyncCycleCompleted(event.snapshot));
2645 }
2646
2647 // This is here for tests, which are still using p2p notifications.
2648 //
2649 // TODO(chron): Consider changing this back to track has_more_to_sync
2650 // only notify peers if a successful commit has occurred.
2651 bool is_notifiable_commit =
2652 (event.snapshot->syncer_status.num_successful_commits > 0);
2653 if (is_notifiable_commit) {
2654 allstatus_.IncrementNotifiableCommits();
2655 if (sync_notifier_.get()) {
2656 sync_notifier_->SendNotification();
2657 } else {
2658 VLOG(1) << "Not sending notification: sync_notifier_ is NULL";
2659 }
2660 }
2661 }
2662
2663 if (event.what_happened == SyncEngineEvent::STOP_SYNCING_PERMANENTLY) {
2664 ObserverList<SyncManager::Observer> temp_obs_list;
2665 CopyObservers(&temp_obs_list);
2666 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
2667 OnStopSyncingPermanently());
2668 return;
2669 }
2670
2671 if (event.what_happened == SyncEngineEvent::CLEAR_SERVER_DATA_SUCCEEDED) {
2672 ObserverList<SyncManager::Observer> temp_obs_list;
2673 CopyObservers(&temp_obs_list);
2674 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
2675 OnClearServerDataSucceeded());
2676 return;
2677 }
2678
2679 if (event.what_happened == SyncEngineEvent::CLEAR_SERVER_DATA_FAILED) {
2680 ObserverList<SyncManager::Observer> temp_obs_list;
2681 CopyObservers(&temp_obs_list);
2682 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
2683 OnClearServerDataFailed());
2684 return;
2685 }
2686
2687 if (event.what_happened == SyncEngineEvent::UPDATED_TOKEN) {
2688 ObserverList<SyncManager::Observer> temp_obs_list;
2689 CopyObservers(&temp_obs_list);
2690 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
2691 OnUpdatedToken(event.updated_token));
2692 return;
2693 }
2694 }
2695
2696 void SyncManager::SyncInternal::SetJsEventHandler(
2697 const WeakHandle<JsEventHandler>& event_handler) {
2698 js_event_handler_ = event_handler;
2699 js_sync_manager_observer_.SetJsEventHandler(js_event_handler_);
2700 js_transaction_observer_.SetJsEventHandler(js_event_handler_);
2701 }
2702
2703 void SyncManager::SyncInternal::ProcessJsMessage(
2704 const std::string& name, const JsArgList& args,
2705 const WeakHandle<JsReplyHandler>& reply_handler) {
2706 if (!initialized_) {
2707 NOTREACHED();
2708 return;
2709 }
2710
2711 if (!reply_handler.IsInitialized()) {
2712 VLOG(1) << "Uninitialized reply handler; dropping unknown message "
2713 << name << " with args " << args.ToString();
2714 return;
2715 }
2716
2717 JsMessageHandler js_message_handler = js_message_handlers_[name];
2718 if (js_message_handler.is_null()) {
2719 VLOG(1) << "Dropping unknown message " << name
2720 << " with args " << args.ToString();
2721 return;
2722 }
2723
2724 reply_handler.Call(FROM_HERE,
2725 &JsReplyHandler::HandleJsReply,
2726 name, js_message_handler.Run(args));
2727 }
2728
2729 void SyncManager::SyncInternal::BindJsMessageHandler(
2730 const std::string& name,
2731 UnboundJsMessageHandler unbound_message_handler) {
2732 js_message_handlers_[name] =
2733 base::Bind(unbound_message_handler, base::Unretained(this));
2734 }
2735
2736 DictionaryValue* SyncManager::SyncInternal::NotificationInfoToValue(
2737 const NotificationInfoMap& notification_info) {
2738 DictionaryValue* value = new DictionaryValue();
2739
2740 for (NotificationInfoMap::const_iterator it = notification_info.begin();
2741 it != notification_info.end(); ++it) {
2742 const std::string& model_type_str =
2743 syncable::ModelTypeToString(it->first);
2744 value->Set(model_type_str, it->second.ToValue());
2745 }
2746
2747 return value;
2748 }
2749
2750 JsArgList SyncManager::SyncInternal::GetNotificationState(
2751 const JsArgList& args) {
2752 bool notifications_enabled = allstatus_.status().notifications_enabled;
2753 ListValue return_args;
2754 return_args.Append(Value::CreateBooleanValue(notifications_enabled));
2755 return JsArgList(&return_args);
2756 }
2757
2758 JsArgList SyncManager::SyncInternal::GetNotificationInfo(
2759 const JsArgList& args) {
2760 ListValue return_args;
2761 return_args.Append(NotificationInfoToValue(notification_info_map_));
2762 return JsArgList(&return_args);
2763 }
2764
2765 JsArgList SyncManager::SyncInternal::GetRootNodeDetails(
2766 const JsArgList& args) {
2767 ReadTransaction trans(FROM_HERE, GetUserShare());
2768 ReadNode root(&trans);
2769 root.InitByRootLookup();
2770 ListValue return_args;
2771 return_args.Append(root.GetDetailsAsValue());
2772 return JsArgList(&return_args);
2773 }
2774
2775 namespace {
2776
2777 int64 GetId(const ListValue& ids, int i) {
2778 std::string id_str;
2779 if (!ids.GetString(i, &id_str)) {
2780 return kInvalidId;
2781 }
2782 int64 id = kInvalidId;
2783 if (!base::StringToInt64(id_str, &id)) {
2784 return kInvalidId;
2785 }
2786 return id;
2787 }
2788
2789 JsArgList GetNodeInfoById(const JsArgList& args,
2790 UserShare* user_share,
2791 DictionaryValue* (BaseNode::*info_getter)() const) {
2792 CHECK(info_getter);
2793 ListValue return_args;
2794 ListValue* node_summaries = new ListValue();
2795 return_args.Append(node_summaries);
2796 ListValue* id_list = NULL;
2797 ReadTransaction trans(FROM_HERE, user_share);
2798 if (args.Get().GetList(0, &id_list)) {
2799 CHECK(id_list);
2800 for (size_t i = 0; i < id_list->GetSize(); ++i) {
2801 int64 id = GetId(*id_list, i);
2802 if (id == kInvalidId) {
2803 continue;
2804 }
2805 ReadNode node(&trans);
2806 if (!node.InitByIdLookup(id)) {
2807 continue;
2808 }
2809 node_summaries->Append((node.*info_getter)());
2810 }
2811 }
2812 return JsArgList(&return_args);
2813 }
2814
2815 } // namespace
2816
2817 JsArgList SyncManager::SyncInternal::GetNodeSummariesById(
2818 const JsArgList& args) {
2819 return GetNodeInfoById(args, GetUserShare(), &BaseNode::GetSummaryAsValue);
2820 }
2821
2822 JsArgList SyncManager::SyncInternal::GetNodeDetailsById(
2823 const JsArgList& args) {
2824 return GetNodeInfoById(args, GetUserShare(), &BaseNode::GetDetailsAsValue);
2825 }
2826
2827 JsArgList SyncManager::SyncInternal::GetChildNodeIds(
2828 const JsArgList& args) {
2829 ListValue return_args;
2830 ListValue* child_ids = new ListValue();
2831 return_args.Append(child_ids);
2832 int64 id = GetId(args.Get(), 0);
2833 if (id != kInvalidId) {
2834 ReadTransaction trans(FROM_HERE, GetUserShare());
2835 syncable::Directory::ChildHandles child_handles;
2836 trans.GetLookup()->GetChildHandlesByHandle(trans.GetWrappedTrans(),
2837 id, &child_handles);
2838 for (syncable::Directory::ChildHandles::const_iterator it =
2839 child_handles.begin(); it != child_handles.end(); ++it) {
2840 child_ids->Append(Value::CreateStringValue(
2841 base::Int64ToString(*it)));
2842 }
2843 }
2844 return JsArgList(&return_args);
2845 }
2846
2847 JsArgList SyncManager::SyncInternal::FindNodesContainingString(
2848 const JsArgList& args) {
2849 std::string query;
2850 ListValue return_args;
2851 if (!args.Get().GetString(0, &query)) {
2852 return_args.Append(new ListValue());
2853 return JsArgList(&return_args);
2854 }
2855
2856 // Convert the query string to lower case to perform case insensitive
2857 // searches.
2858 std::string lowercase_query = query;
2859 StringToLowerASCII(&lowercase_query);
2860
2861 ListValue* result = new ListValue();
2862 return_args.Append(result);
2863
2864 ReadTransaction trans(FROM_HERE, GetUserShare());
2865 std::vector<const syncable::EntryKernel*> entry_kernels;
2866 trans.GetLookup()->GetAllEntryKernels(trans.GetWrappedTrans(),
2867 &entry_kernels);
2868
2869 for (std::vector<const syncable::EntryKernel*>::const_iterator it =
2870 entry_kernels.begin(); it != entry_kernels.end(); ++it) {
2871 if ((*it)->ContainsString(lowercase_query)) {
2872 result->Append(new StringValue(base::Int64ToString(
2873 (*it)->ref(syncable::META_HANDLE))));
2874 }
2875 }
2876
2877 return JsArgList(&return_args);
2878 }
2879
2880 void SyncManager::SyncInternal::OnNotificationStateChange(
2881 bool notifications_enabled) {
2882 VLOG(1) << "P2P: Notifications enabled = "
2883 << (notifications_enabled ? "true" : "false");
2884 allstatus_.SetNotificationsEnabled(notifications_enabled);
2885 if (scheduler()) {
2886 scheduler()->set_notifications_enabled(notifications_enabled);
2887 }
2888 if (js_event_handler_.IsInitialized()) {
2889 DictionaryValue details;
2890 details.Set("enabled", Value::CreateBooleanValue(notifications_enabled));
2891 js_event_handler_.Call(FROM_HERE,
2892 &JsEventHandler::HandleJsEvent,
2893 "onNotificationStateChange",
2894 JsEventDetails(&details));
2895 }
2896 }
2897
2898 void SyncManager::SyncInternal::UpdateNotificationInfo(
2899 const syncable::ModelTypePayloadMap& type_payloads) {
2900 for (syncable::ModelTypePayloadMap::const_iterator it = type_payloads.begin();
2901 it != type_payloads.end(); ++it) {
2902 NotificationInfo* info = &notification_info_map_[it->first];
2903 info->total_count++;
2904 info->payload = it->second;
2905 }
2906 }
2907
2908 void SyncManager::SyncInternal::OnIncomingNotification(
2909 const syncable::ModelTypePayloadMap& type_payloads) {
2910 if (!type_payloads.empty()) {
2911 if (scheduler()) {
2912 scheduler()->ScheduleNudgeWithPayloads(
2913 TimeDelta::FromMilliseconds(kSyncSchedulerDelayMsec),
2914 browser_sync::NUDGE_SOURCE_NOTIFICATION,
2915 type_payloads, FROM_HERE);
2916 }
2917 allstatus_.IncrementNotificationsReceived();
2918 UpdateNotificationInfo(type_payloads);
2919 } else {
2920 LOG(WARNING) << "Sync received notification without any type information.";
2921 }
2922
2923 if (js_event_handler_.IsInitialized()) {
2924 DictionaryValue details;
2925 ListValue* changed_types = new ListValue();
2926 details.Set("changedTypes", changed_types);
2927 for (syncable::ModelTypePayloadMap::const_iterator
2928 it = type_payloads.begin();
2929 it != type_payloads.end(); ++it) {
2930 const std::string& model_type_str =
2931 syncable::ModelTypeToString(it->first);
2932 changed_types->Append(Value::CreateStringValue(model_type_str));
2933 }
2934 js_event_handler_.Call(FROM_HERE,
2935 &JsEventHandler::HandleJsEvent,
2936 "onIncomingNotification",
2937 JsEventDetails(&details));
2938 }
2939 }
2940
2941 void SyncManager::SyncInternal::StoreState(
2942 const std::string& state) {
2943 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share());
2944 if (!lookup.good()) {
2945 LOG(ERROR) << "Could not write notification state";
2946 // TODO(akalin): Propagate result callback all the way to this
2947 // function and call it with "false" to signal failure.
2948 return;
2949 }
2950 if (VLOG_IS_ON(1)) {
2951 std::string encoded_state;
2952 base::Base64Encode(state, &encoded_state);
2953 VLOG(1) << "Writing notification state: " << encoded_state;
2954 }
2955 lookup->SetNotificationState(state);
2956 lookup->SaveChanges();
2957 }
2958
2959
2960 // Note: it is possible that an observer will remove itself after we have made
2961 // a copy, but before the copy is consumed. This could theoretically result
2962 // in accessing a garbage pointer, but can only occur when an about:sync window
2963 // is closed in the middle of a notification.
2964 // See crbug.com/85481.
2965 void SyncManager::SyncInternal::CopyObservers(
2966 ObserverList<SyncManager::Observer>* observers_copy) {
2967 DCHECK_EQ(0U, observers_copy->size());
2968 base::AutoLock lock(observers_lock_);
2969 if (observers_.size() == 0)
2970 return;
2971 ObserverListBase<SyncManager::Observer>::Iterator it(observers_);
2972 SyncManager::Observer* obs;
2973 while ((obs = it.GetNext()) != NULL)
2974 observers_copy->AddObserver(obs);
2975 }
2976
2977 bool SyncManager::SyncInternal::HaveObservers() const {
2978 base::AutoLock lock(observers_lock_);
2979 return observers_.size() > 0;
2980 }
2981
2982 void SyncManager::SyncInternal::AddObserver(
2983 SyncManager::Observer* observer) {
2984 base::AutoLock lock(observers_lock_);
2985 observers_.AddObserver(observer);
2986 }
2987
2988 void SyncManager::SyncInternal::RemoveObserver(
2989 SyncManager::Observer* observer) {
2990 base::AutoLock lock(observers_lock_);
2991 observers_.RemoveObserver(observer);
2992 }
2993
2994 SyncManager::Status::Summary SyncManager::GetStatusSummary() const {
2995 return data_->GetStatus().summary;
2996 }
2997
2998 SyncManager::Status SyncManager::GetDetailedStatus() const {
2999 return data_->GetStatus();
3000 }
3001
3002 SyncManager::SyncInternal* SyncManager::GetImpl() const { return data_; }
3003
3004 void SyncManager::SaveChanges() {
3005 data_->SaveChanges();
3006 }
3007
3008 void SyncManager::SyncInternal::SaveChanges() {
3009 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share());
3010 if (!lookup.good()) {
3011 DCHECK(false) << "ScopedDirLookup creation failed; Unable to SaveChanges";
3012 return;
3013 }
3014 lookup->SaveChanges();
3015 }
3016
3017 //////////////////////////////////////////////////////////////////////////
3018 // BaseTransaction member definitions
3019 BaseTransaction::BaseTransaction(UserShare* share)
3020 : lookup_(NULL) {
3021 DCHECK(share && share->dir_manager.get());
3022 lookup_ = new syncable::ScopedDirLookup(share->dir_manager.get(),
3023 share->name);
3024 cryptographer_ = share->dir_manager->GetCryptographer(this);
3025 if (!(lookup_->good()))
3026 DCHECK(false) << "ScopedDirLookup failed on valid DirManager.";
3027 }
3028 BaseTransaction::~BaseTransaction() {
3029 delete lookup_;
3030 }
3031
3032 UserShare* SyncManager::GetUserShare() const {
3033 return data_->GetUserShare();
3034 }
3035
3036 void SyncManager::RefreshEncryption() {
3037 if (data_->UpdateCryptographerFromNigori())
3038 data_->EncryptDataTypes(syncable::ModelTypeSet());
3039 }
3040
3041 syncable::ModelTypeSet SyncManager::GetEncryptedDataTypes() const {
3042 sync_api::ReadTransaction trans(FROM_HERE, GetUserShare());
3043 return GetEncryptedTypes(&trans);
3044 }
3045
3046 bool SyncManager::HasUnsyncedItems() const {
3047 sync_api::ReadTransaction trans(FROM_HERE, GetUserShare());
3048 return (trans.GetWrappedTrans()->directory()->unsynced_entity_count() != 0);
3049 }
3050
3051 void SyncManager::LogUnsyncedItems(int level) const {
3052 std::vector<int64> unsynced_handles;
3053 sync_api::ReadTransaction trans(FROM_HERE, GetUserShare());
3054 trans.GetWrappedTrans()->directory()->GetUnsyncedMetaHandles(
3055 trans.GetWrappedTrans(), &unsynced_handles);
3056
3057 for (std::vector<int64>::const_iterator it = unsynced_handles.begin();
3058 it != unsynced_handles.end(); ++it) {
3059 ReadNode node(&trans);
3060 if (node.InitByIdLookup(*it)) {
3061 scoped_ptr<DictionaryValue> value(node.GetDetailsAsValue());
3062 std::string info;
3063 base::JSONWriter::Write(value.get(), true, &info);
3064 VLOG(level) << info;
3065 }
3066 }
3067 }
3068
3069 void SyncManager::TriggerOnNotificationStateChangeForTest(
3070 bool notifications_enabled) {
3071 data_->OnNotificationStateChange(notifications_enabled);
3072 }
3073
3074 void SyncManager::TriggerOnIncomingNotificationForTest(
3075 const syncable::ModelTypeBitSet& model_types) {
3076 syncable::ModelTypePayloadMap model_types_with_payloads =
3077 syncable::ModelTypePayloadMapFromBitSet(model_types,
3078 std::string());
3079
3080 data_->OnIncomingNotification(model_types_with_payloads);
3081 }
3082
3083 } // namespace sync_api
OLDNEW
« no previous file with comments | « chrome/browser/sync/engine/syncapi.h ('k') | chrome/browser/sync/engine/syncapi_internal.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698