| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/sync/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 = ¬ification_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 | |
| OLD | NEW |