1 // Copyright 2016 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 "components/sync/engine_impl/loopback_server/loopback_server.h"
6 
7 #include <algorithm>
8 #include <limits>
9 #include <set>
10 #include <utility>
11 
12 #include "base/files/file_util.h"
13 #include "base/format_macros.h"
14 #include "base/guid.h"
15 #include "base/logging.h"
16 #include "base/metrics/histogram_macros.h"
17 #include "base/rand_util.h"
18 #include "base/sequence_checker.h"
19 #include "base/sequenced_task_runner.h"
20 #include "base/stl_util.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "base/strings/string_split.h"
23 #include "base/strings/string_util.h"
24 #include "base/strings/stringprintf.h"
25 #include "base/task/post_task.h"
26 #include "base/task/task_traits.h"
27 #include "base/task/thread_pool.h"
28 #include "components/sync/engine_impl/loopback_server/persistent_bookmark_entity.h"
29 #include "components/sync/engine_impl/loopback_server/persistent_permanent_entity.h"
30 #include "components/sync/engine_impl/loopback_server/persistent_tombstone_entity.h"
31 #include "components/sync/engine_impl/loopback_server/persistent_unique_client_entity.h"
32 #include "net/base/net_errors.h"
33 #include "net/http/http_status_code.h"
34 
35 using std::string;
36 using std::vector;
37 
38 using syncer::GetModelType;
39 using syncer::GetModelTypeFromSpecifics;
40 using syncer::ModelType;
41 using syncer::ModelTypeSet;
42 
43 namespace syncer {
44 
45 class LoopbackServerEntity;
46 
47 namespace {
48 
49 static const int kCurrentLoopbackServerProtoVersion = 1;
50 static const int kKeystoreKeyLength = 16;
51 
52 // Properties of the bookmark bar permanent folders.
53 static const char kBookmarkBarFolderServerTag[] = "bookmark_bar";
54 static const char kBookmarkBarFolderName[] = "Bookmark Bar";
55 static const char kOtherBookmarksFolderServerTag[] = "other_bookmarks";
56 static const char kOtherBookmarksFolderName[] = "Other Bookmarks";
57 static const char kSyncedBookmarksFolderServerTag[] = "synced_bookmarks";
58 static const char kSyncedBookmarksFolderName[] = "Synced Bookmarks";
59 
GetServerMigrationVersion(const std::map<ModelType,int> & server_migration_versions,ModelType type)60 int GetServerMigrationVersion(
61     const std::map<ModelType, int>& server_migration_versions,
62     ModelType type) {
63   auto server_it = server_migration_versions.find(type);
64   return server_it == server_migration_versions.end() ? 0 : server_it->second;
65 }
66 
67 class ProgressMarkerToken {
68  public:
FromEmpty(int migration_version)69   static ProgressMarkerToken FromEmpty(int migration_version) {
70     ProgressMarkerToken token;
71     token.migration_version_ = migration_version;
72     return token;
73   }
74 
FromString(const std::string & s)75   static ProgressMarkerToken FromString(const std::string& s) {
76     DCHECK(!s.empty());
77     const vector<base::StringPiece> splits = base::SplitStringPiece(
78         s, "/", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
79     if (splits.size() != 2) {
80       ProgressMarkerToken token;
81       base::StringToInt64(s, &token.entity_version_);
82       return token;
83     }
84     ProgressMarkerToken token;
85     if (!base::StringToInt(splits[0], &token.migration_version_) ||
86         !base::StringToInt64(splits[1], &token.entity_version_)) {
87       return ProgressMarkerToken();
88     }
89     return token;
90   }
91 
ToString() const92   std::string ToString() const {
93     if (migration_version_ == 0) {
94       return base::NumberToString(entity_version_);
95     } else {
96       return base::StringPrintf("%d/%" PRId64, migration_version_,
97                                 entity_version_);
98     }
99   }
100 
migration_version() const101   int migration_version() const { return migration_version_; }
entity_version() const102   int64_t entity_version() const { return entity_version_; }
103 
UpdateWithEntity(int64_t other_entity_version)104   void UpdateWithEntity(int64_t other_entity_version) {
105     entity_version_ = std::max(entity_version_, other_entity_version);
106   }
107 
108  private:
109   int migration_version_ = 0;
110   int64_t entity_version_ = 0;
111 };
112 
113 // A filter used during GetUpdates calls to determine what information to
114 // send back to the client; filtering out old entities and tracking versions to
115 // use in response progress markers. Note that only the GetUpdatesMessage's
116 // from_progress_marker is used to determine this; legacy fields are ignored.
117 class UpdateSieve {
118  public:
UpdateSieve(const sync_pb::GetUpdatesMessage & message,const std::map<ModelType,int> & server_migration_versions)119   UpdateSieve(const sync_pb::GetUpdatesMessage& message,
120               const std::map<ModelType, int>& server_migration_versions)
121       : UpdateSieve(MessageToVersionMap(message, server_migration_versions)) {}
~UpdateSieve()122   ~UpdateSieve() {}
123 
124   // Verifies if MIGRATION_DONE should be exercised. It intentionally returns
125   // migrations in the order that they were triggered.  Doing it this way
126   // allows the client to queue up two migrations in a row, so the second one
127   // is received while responding to the first.
ShouldTriggerMigration(const std::map<ModelType,int> & server_migration_versions,std::vector<ModelType> * datatypes_to_migrate) const128   bool ShouldTriggerMigration(
129       const std::map<ModelType, int>& server_migration_versions,
130       std::vector<ModelType>* datatypes_to_migrate) const {
131     DCHECK(datatypes_to_migrate);
132     datatypes_to_migrate->clear();
133 
134     for (const auto& request_version : request_version_map_) {
135       const ModelType type = request_version.first;
136       const int client_migration_version =
137           request_version.second.migration_version();
138 
139       const int server_migration_version =
140           GetServerMigrationVersion(server_migration_versions, type);
141 
142       if (client_migration_version < server_migration_version) {
143         datatypes_to_migrate->push_back(type);
144       }
145     }
146 
147     return !datatypes_to_migrate->empty();
148   }
149 
150   // Sets the progress markers in |get_updates_response| based on the highest
151   // version between request progress markers and response entities.
SetProgressMarkers(sync_pb::GetUpdatesResponse * get_updates_response) const152   void SetProgressMarkers(
153       sync_pb::GetUpdatesResponse* get_updates_response) const {
154     for (const auto& kv : response_version_map_) {
155       sync_pb::DataTypeProgressMarker* new_marker =
156           get_updates_response->add_new_progress_marker();
157       new_marker->set_data_type_id(
158           GetSpecificsFieldNumberFromModelType(kv.first));
159       new_marker->set_token(kv.second.ToString());
160     }
161   }
162 
163   // Determines whether the server should send an |entity| to the client as
164   // part of a GetUpdatesResponse.
ClientWantsItem(const LoopbackServerEntity & entity) const165   bool ClientWantsItem(const LoopbackServerEntity& entity) const {
166     ModelType type = entity.GetModelType();
167     auto it = request_version_map_.find(type);
168     if (it == request_version_map_.end())
169       return false;
170     DCHECK_NE(0U, response_version_map_.count(type));
171     return it->second.entity_version() < entity.GetVersion();
172   }
173 
174   // Updates internal tracking of max versions to later be used to set response
175   // progress markers.
UpdateProgressMarker(const LoopbackServerEntity & entity)176   void UpdateProgressMarker(const LoopbackServerEntity& entity) {
177     DCHECK(ClientWantsItem(entity));
178     ModelType type = entity.GetModelType();
179     response_version_map_[type].UpdateWithEntity(entity.GetVersion());
180   }
181 
182  private:
183   using ModelTypeToVersionMap = std::map<ModelType, ProgressMarkerToken>;
184 
MessageToVersionMap(const sync_pb::GetUpdatesMessage & get_updates_message,const std::map<ModelType,int> & server_migration_versions)185   static UpdateSieve::ModelTypeToVersionMap MessageToVersionMap(
186       const sync_pb::GetUpdatesMessage& get_updates_message,
187       const std::map<ModelType, int>& server_migration_versions) {
188     DCHECK_GT(get_updates_message.from_progress_marker_size(), 0)
189         << "A GetUpdates request must have at least one progress marker.";
190     ModelTypeToVersionMap request_version_map;
191 
192     for (int i = 0; i < get_updates_message.from_progress_marker_size(); i++) {
193       const sync_pb::DataTypeProgressMarker& marker =
194           get_updates_message.from_progress_marker(i);
195 
196       const ModelType model_type =
197           syncer::GetModelTypeFromSpecificsFieldNumber(marker.data_type_id());
198       const int server_migration_version =
199           GetServerMigrationVersion(server_migration_versions, model_type);
200       const ProgressMarkerToken version =
201           marker.token().empty()
202               ? ProgressMarkerToken::FromEmpty(server_migration_version)
203               : ProgressMarkerToken::FromString(marker.token());
204 
205       DCHECK(request_version_map.find(model_type) == request_version_map.end());
206       request_version_map[model_type] = version;
207     }
208     return request_version_map;
209   }
210 
UpdateSieve(const ModelTypeToVersionMap request_version_map)211   explicit UpdateSieve(const ModelTypeToVersionMap request_version_map)
212       : request_version_map_(request_version_map),
213         response_version_map_(request_version_map) {}
214 
215   // The largest versions the client has seen before this request, and is used
216   // to filter entities to send back to clients. The values in this map are not
217   // updated after being initially set. The presence of a type in this map is a
218   // proxy for the desire to receive results about this type.
219   const ModelTypeToVersionMap request_version_map_;
220 
221   // The largest versions seen between client and server, ultimately used to
222   // send progress markers back to the client.
223   ModelTypeToVersionMap response_version_map_;
224 };
225 
SortByVersion(const LoopbackServerEntity * lhs,const LoopbackServerEntity * rhs)226 bool SortByVersion(const LoopbackServerEntity* lhs,
227                    const LoopbackServerEntity* rhs) {
228   return lhs->GetVersion() < rhs->GetVersion();
229 }
230 
231 }  // namespace
232 
LoopbackServer(const base::FilePath & persistent_file)233 LoopbackServer::LoopbackServer(const base::FilePath& persistent_file)
234     : strong_consistency_model_enabled_(false),
235       version_(0),
236       store_birthday_(0),
237       persistent_file_(persistent_file),
238       writer_(
239           persistent_file_,
240           base::ThreadPool::CreateSequencedTaskRunner(
241               {base::MayBlock(), base::TaskShutdownBehavior::BLOCK_SHUTDOWN})),
242       observer_for_tests_(nullptr) {
243   DCHECK(!persistent_file_.empty());
244   Init();
245 }
246 
~LoopbackServer()247 LoopbackServer::~LoopbackServer() {
248   if (writer_.HasPendingWrite())
249     writer_.DoScheduledWrite();
250 }
251 
Init()252 void LoopbackServer::Init() {
253   if (LoadStateFromFile())
254     return;
255 
256   store_birthday_ = base::Time::Now().ToJavaTime();
257   keystore_keys_.push_back(GenerateNewKeystoreKey());
258 
259   const bool create_result = CreateDefaultPermanentItems();
260   DCHECK(create_result) << "Permanent items were not created successfully.";
261 }
262 
GenerateNewKeystoreKey() const263 std::vector<uint8_t> LoopbackServer::GenerateNewKeystoreKey() const {
264   std::vector<uint8_t> generated_key(kKeystoreKeyLength);
265   base::RandBytes(generated_key.data(), generated_key.size());
266   return generated_key;
267 }
268 
CreatePermanentBookmarkFolder(const std::string & server_tag,const std::string & name)269 bool LoopbackServer::CreatePermanentBookmarkFolder(
270     const std::string& server_tag,
271     const std::string& name) {
272   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
273   std::unique_ptr<LoopbackServerEntity> entity =
274       PersistentPermanentEntity::CreateNew(
275           syncer::BOOKMARKS, server_tag, name,
276           ModelTypeToRootTag(syncer::BOOKMARKS));
277   if (!entity)
278     return false;
279 
280   SaveEntity(std::move(entity));
281   return true;
282 }
283 
CreateDefaultPermanentItems()284 bool LoopbackServer::CreateDefaultPermanentItems() {
285   // Permanent folders are always required for Bookmarks (hierarchical
286   // structure) and Nigori (data stored in permanent root folder).
287   ModelTypeSet permanent_folder_types =
288       ModelTypeSet(syncer::BOOKMARKS, syncer::NIGORI);
289 
290   for (ModelType model_type : permanent_folder_types) {
291     std::unique_ptr<LoopbackServerEntity> top_level_entity =
292         PersistentPermanentEntity::CreateTopLevel(model_type);
293     if (!top_level_entity) {
294       return false;
295     }
296     SaveEntity(std::move(top_level_entity));
297   }
298 
299   return true;
300 }
301 
UpdateEntityVersion(LoopbackServerEntity * entity)302 void LoopbackServer::UpdateEntityVersion(LoopbackServerEntity* entity) {
303   entity->SetVersion(++version_);
304 }
305 
SaveEntity(std::unique_ptr<LoopbackServerEntity> entity)306 void LoopbackServer::SaveEntity(std::unique_ptr<LoopbackServerEntity> entity) {
307   UpdateEntityVersion(entity.get());
308   entities_[entity->GetId()] = std::move(entity);
309 }
310 
HandleCommand(const sync_pb::ClientToServerMessage & message,sync_pb::ClientToServerResponse * response)311 net::HttpStatusCode LoopbackServer::HandleCommand(
312     const sync_pb::ClientToServerMessage& message,
313     sync_pb::ClientToServerResponse* response) {
314   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
315   DCHECK(response);
316 
317   response->Clear();
318 
319   if (bag_of_chips_.has_value()) {
320     *response->mutable_new_bag_of_chips() = *bag_of_chips_;
321   }
322 
323   if (message.has_store_birthday() &&
324       message.store_birthday() != GetStoreBirthday()) {
325     response->set_error_code(sync_pb::SyncEnums::NOT_MY_BIRTHDAY);
326   } else {
327     bool success = false;
328     std::vector<ModelType> datatypes_to_migrate;
329     ModelTypeSet throttled_datatypes_in_request;
330     switch (message.message_contents()) {
331       case sync_pb::ClientToServerMessage::GET_UPDATES:
332         success = HandleGetUpdatesRequest(
333             message.get_updates(), message.store_birthday(),
334             message.invalidator_client_id(), response->mutable_get_updates(),
335             &datatypes_to_migrate);
336         break;
337       case sync_pb::ClientToServerMessage::COMMIT:
338         success = HandleCommitRequest(
339             message.commit(), message.invalidator_client_id(),
340             response->mutable_commit(), &throttled_datatypes_in_request);
341         break;
342       case sync_pb::ClientToServerMessage::CLEAR_SERVER_DATA:
343         ClearServerData();
344         response->mutable_clear_server_data();
345         success = true;
346         break;
347       default:
348         response->Clear();
349         return net::HTTP_BAD_REQUEST;
350     }
351 
352     if (success) {
353       response->set_error_code(sync_pb::SyncEnums::SUCCESS);
354     } else if (!datatypes_to_migrate.empty()) {
355       DLOG(WARNING) << "Migration required for " << datatypes_to_migrate.size()
356                     << " datatypes";
357       response->set_error_code(sync_pb::SyncEnums::MIGRATION_DONE);
358       for (ModelType type : datatypes_to_migrate) {
359         response->add_migrated_data_type_id(
360             GetSpecificsFieldNumberFromModelType(type));
361       }
362     } else if (!throttled_datatypes_in_request.Empty()) {
363       DLOG(WARNING) << "Throttled datatypes: "
364                     << ModelTypeSetToString(throttled_datatypes_in_request);
365       response->set_error_code(sync_pb::SyncEnums::PARTIAL_FAILURE);
366       response->mutable_error()->set_error_type(
367           sync_pb::SyncEnums::PARTIAL_FAILURE);
368       for (ModelType type : throttled_datatypes_in_request) {
369         response->mutable_error()->add_error_data_type_ids(
370             syncer::GetSpecificsFieldNumberFromModelType(type));
371       }
372     } else {
373       UMA_HISTOGRAM_ENUMERATION(
374           "Sync.Local.RequestTypeOnError", message.message_contents(),
375           sync_pb::ClientToServerMessage_Contents_Contents_MAX);
376       return net::HTTP_INTERNAL_SERVER_ERROR;
377     }
378   }
379 
380   response->set_store_birthday(GetStoreBirthday());
381 
382   ScheduleSaveStateToFile();
383   return net::HTTP_OK;
384 }
385 
EnableStrongConsistencyWithConflictDetectionModel()386 void LoopbackServer::EnableStrongConsistencyWithConflictDetectionModel() {
387   strong_consistency_model_enabled_ = true;
388 }
389 
AddNewKeystoreKeyForTesting()390 void LoopbackServer::AddNewKeystoreKeyForTesting() {
391   keystore_keys_.push_back(GenerateNewKeystoreKey());
392 }
393 
HandleGetUpdatesRequest(const sync_pb::GetUpdatesMessage & get_updates,const std::string & store_birthday,const std::string & invalidator_client_id,sync_pb::GetUpdatesResponse * response,std::vector<ModelType> * datatypes_to_migrate)394 bool LoopbackServer::HandleGetUpdatesRequest(
395     const sync_pb::GetUpdatesMessage& get_updates,
396     const std::string& store_birthday,
397     const std::string& invalidator_client_id,
398     sync_pb::GetUpdatesResponse* response,
399     std::vector<ModelType>* datatypes_to_migrate) {
400   response->set_changes_remaining(0);
401 
402   bool is_initial_bookmark_sync = false;
403   for (const sync_pb::DataTypeProgressMarker& marker :
404        get_updates.from_progress_marker()) {
405     if (GetModelTypeFromSpecificsFieldNumber(marker.data_type_id()) !=
406         syncer::BOOKMARKS) {
407       continue;
408     }
409     if (!marker.has_token() || marker.token().empty()) {
410       is_initial_bookmark_sync = true;
411       break;
412     }
413   }
414 
415   if (is_initial_bookmark_sync) {
416     if (!CreatePermanentBookmarkFolder(kBookmarkBarFolderServerTag,
417                                        kBookmarkBarFolderName)) {
418       return false;
419     }
420     if (!CreatePermanentBookmarkFolder(kOtherBookmarksFolderServerTag,
421                                        kOtherBookmarksFolderName)) {
422       return false;
423     }
424     // This folder is called "Synced Bookmarks" by sync and is renamed
425     // "Mobile Bookmarks" by the mobile client UIs.
426     if (!CreatePermanentBookmarkFolder(kSyncedBookmarksFolderServerTag,
427                                        kSyncedBookmarksFolderName)) {
428       return false;
429     }
430   }
431 
432   // It's a protocol-level contract that the birthday should only be empty
433   // during the initial sync cycle, which requires all progress markers to be
434   // empty. This is also DCHECK-ed on the client, inside syncer_proto_util.cc,
435   // but we guard against client-side code changes here.
436   if (store_birthday.empty()) {
437     for (const sync_pb::DataTypeProgressMarker& marker :
438          get_updates.from_progress_marker()) {
439       if (!marker.token().empty()) {
440         DLOG(WARNING) << "Non-empty progress marker without birthday";
441         return false;
442       }
443     }
444   }
445 
446   auto sieve = std::make_unique<UpdateSieve>(get_updates, migration_versions_);
447 
448   if (sieve->ShouldTriggerMigration(migration_versions_,
449                                     datatypes_to_migrate)) {
450     DCHECK(!datatypes_to_migrate->empty());
451     return false;
452   }
453 
454   std::vector<const LoopbackServerEntity*> wanted_entities;
455   for (const auto& id_and_entity : entities_) {
456     if (sieve->ClientWantsItem(*id_and_entity.second)) {
457       wanted_entities.push_back(id_and_entity.second.get());
458     }
459   }
460 
461   int max_batch_size = max_get_updates_batch_size_;
462   if (get_updates.batch_size() > 0)
463     max_batch_size = std::min(max_batch_size, get_updates.batch_size());
464 
465   if (static_cast<int>(wanted_entities.size()) > max_batch_size) {
466     response->set_changes_remaining(wanted_entities.size() - max_batch_size);
467     std::partial_sort(wanted_entities.begin(),
468                       wanted_entities.begin() + max_batch_size,
469                       wanted_entities.end(), SortByVersion);
470     wanted_entities.resize(max_batch_size);
471   }
472 
473   bool send_encryption_keys_based_on_nigori = false;
474   for (const LoopbackServerEntity* entity : wanted_entities) {
475     sieve->UpdateProgressMarker(*entity);
476 
477     sync_pb::SyncEntity* response_entity = response->add_entries();
478     entity->SerializeAsProto(response_entity);
479 
480     if (entity->GetModelType() == syncer::NIGORI) {
481       send_encryption_keys_based_on_nigori =
482           response_entity->specifics().nigori().passphrase_type() ==
483           sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE;
484     }
485   }
486 
487   if (send_encryption_keys_based_on_nigori ||
488       get_updates.need_encryption_key()) {
489     for (const auto& key : keystore_keys_) {
490       response->add_encryption_keys(key.data(), key.size());
491     }
492   }
493 
494   sieve->SetProgressMarkers(response);
495   // During initial bookmark sync, we create new entities for bookmark permanent
496   // folders, and hence we should inform the observers.
497   if (is_initial_bookmark_sync && observer_for_tests_) {
498     observer_for_tests_->OnCommit(invalidator_client_id, {syncer::BOOKMARKS});
499   }
500 
501   return true;
502 }
503 
CommitEntity(const sync_pb::SyncEntity & client_entity,sync_pb::CommitResponse_EntryResponse * entry_response,const string & client_guid,const string & parent_id)504 string LoopbackServer::CommitEntity(
505     const sync_pb::SyncEntity& client_entity,
506     sync_pb::CommitResponse_EntryResponse* entry_response,
507     const string& client_guid,
508     const string& parent_id) {
509   if (client_entity.version() == 0 && client_entity.deleted()) {
510     return string();
511   }
512 
513   // If strong consistency model is enabled (usually on a per-datatype level,
514   // but implemented here as a global state), the server detects version
515   // mismatches and responds with CONFLICT.
516   if (strong_consistency_model_enabled_) {
517     EntityMap::const_iterator iter = entities_.find(client_entity.id_string());
518     if (iter != entities_.end()) {
519       const LoopbackServerEntity* server_entity = iter->second.get();
520       if (server_entity->GetVersion() != client_entity.version()) {
521         entry_response->set_response_type(sync_pb::CommitResponse::CONFLICT);
522         return client_entity.id_string();
523       }
524     }
525   }
526 
527   std::unique_ptr<LoopbackServerEntity> entity;
528   syncer::ModelType type = GetModelType(client_entity);
529   if (client_entity.deleted()) {
530     entity = PersistentTombstoneEntity::CreateFromEntity(client_entity);
531     if (entity) {
532       DeleteChildren(client_entity.id_string());
533     }
534   } else if (type == syncer::NIGORI) {
535     // NIGORI is the only permanent item type that should be updated by the
536     // client.
537     EntityMap::const_iterator iter = entities_.find(client_entity.id_string());
538     DCHECK(iter != entities_.end());
539     entity = PersistentPermanentEntity::CreateUpdatedNigoriEntity(
540         client_entity, *iter->second);
541   } else if (type == syncer::BOOKMARKS) {
542     // TODO(pvalenzuela): Validate entity's parent ID.
543     EntityMap::const_iterator iter = entities_.find(client_entity.id_string());
544     if (iter != entities_.end()) {
545       entity = PersistentBookmarkEntity::CreateUpdatedVersion(
546           client_entity, *iter->second, parent_id, client_guid);
547     } else {
548       entity = PersistentBookmarkEntity::CreateNew(client_entity, parent_id,
549                                                    client_guid);
550     }
551   } else {
552     entity = PersistentUniqueClientEntity::CreateFromEntity(client_entity);
553   }
554 
555   if (!entity)
556     return string();
557 
558   const std::string id = entity->GetId();
559   SaveEntity(std::move(entity));
560   BuildEntryResponseForSuccessfulCommit(id, entry_response);
561   return id;
562 }
563 
OverrideResponseType(ResponseTypeProvider response_type_override)564 void LoopbackServer::OverrideResponseType(
565     ResponseTypeProvider response_type_override) {
566   response_type_override_ = std::move(response_type_override);
567 }
568 
BuildEntryResponseForSuccessfulCommit(const std::string & entity_id,sync_pb::CommitResponse_EntryResponse * entry_response)569 void LoopbackServer::BuildEntryResponseForSuccessfulCommit(
570     const std::string& entity_id,
571     sync_pb::CommitResponse_EntryResponse* entry_response) {
572   EntityMap::const_iterator iter = entities_.find(entity_id);
573   DCHECK(iter != entities_.end());
574   const LoopbackServerEntity& entity = *iter->second;
575   entry_response->set_response_type(response_type_override_
576                                         ? response_type_override_.Run(entity)
577                                         : sync_pb::CommitResponse::SUCCESS);
578   entry_response->set_id_string(entity.GetId());
579 
580   if (entity.IsDeleted()) {
581     entry_response->set_version(entity.GetVersion() + 1);
582   } else {
583     entry_response->set_version(entity.GetVersion());
584     entry_response->set_name(entity.GetName());
585   }
586 }
587 
IsChild(const string & id,const string & potential_parent_id)588 bool LoopbackServer::IsChild(const string& id,
589                              const string& potential_parent_id) {
590   EntityMap::const_iterator iter = entities_.find(id);
591   if (iter == entities_.end()) {
592     // We've hit an ID (probably the imaginary root entity) that isn't stored
593     // by the server, so it can't be a child.
594     return false;
595   }
596 
597   const LoopbackServerEntity& entity = *iter->second;
598   if (entity.GetParentId() == potential_parent_id)
599     return true;
600 
601   // Recursively look up the tree.
602   return IsChild(entity.GetParentId(), potential_parent_id);
603 }
604 
DeleteChildren(const string & parent_id)605 void LoopbackServer::DeleteChildren(const string& parent_id) {
606   std::vector<sync_pb::SyncEntity> tombstones;
607   // Find all the children of |parent_id|.
608   for (auto& entity : entities_) {
609     if (IsChild(entity.first, parent_id)) {
610       sync_pb::SyncEntity proto;
611       entity.second->SerializeAsProto(&proto);
612       tombstones.emplace_back(proto);
613     }
614   }
615 
616   for (auto& tombstone : tombstones) {
617     SaveEntity(PersistentTombstoneEntity::CreateFromEntity(tombstone));
618   }
619 }
620 
HandleCommitRequest(const sync_pb::CommitMessage & commit,const std::string & invalidator_client_id,sync_pb::CommitResponse * response,ModelTypeSet * throttled_datatypes_in_request)621 bool LoopbackServer::HandleCommitRequest(
622     const sync_pb::CommitMessage& commit,
623     const std::string& invalidator_client_id,
624     sync_pb::CommitResponse* response,
625     ModelTypeSet* throttled_datatypes_in_request) {
626   std::map<string, string> client_to_server_ids;
627   string guid = commit.cache_guid();
628   ModelTypeSet committed_model_types;
629 
630   ModelTypeSet enabled_types;
631   for (int field_number : commit.config_params().enabled_type_ids()) {
632     enabled_types.Put(GetModelTypeFromSpecificsFieldNumber(field_number));
633   }
634 
635   // TODO(pvalenzuela): Add validation of CommitMessage.entries.
636   for (const sync_pb::SyncEntity& client_entity : commit.entries()) {
637     sync_pb::CommitResponse_EntryResponse* entry_response =
638         response->add_entryresponse();
639 
640     string parent_id = client_entity.parent_id_string();
641     if (client_to_server_ids.find(parent_id) != client_to_server_ids.end()) {
642       parent_id = client_to_server_ids[parent_id];
643     }
644 
645     const ModelType entity_model_type = GetModelType(client_entity);
646     if (throttled_types_.Has(entity_model_type)) {
647       entry_response->set_response_type(sync_pb::CommitResponse::OVER_QUOTA);
648       throttled_datatypes_in_request->Put(entity_model_type);
649       continue;
650     }
651 
652     const string entity_id =
653         CommitEntity(client_entity, entry_response, guid, parent_id);
654     if (entity_id.empty()) {
655       return false;
656     }
657 
658     // Record the ID if it was renamed.
659     if (entity_id != client_entity.id_string()) {
660       client_to_server_ids[client_entity.id_string()] = entity_id;
661     }
662 
663     EntityMap::const_iterator iter = entities_.find(entity_id);
664     DCHECK(iter != entities_.end());
665     committed_model_types.Put(iter->second->GetModelType());
666 
667     // Notify observers about history having been synced. "History" sync is
668     // guarded by the user's selection in the settings page. This also excludes
669     // custom passphrase users who, in addition to HISTORY_DELETE_DIRECTIVES not
670     // being enabled, will commit encrypted specifics and hence cannot be
671     // iterated over.
672     if (observer_for_tests_ && iter->second->GetModelType() == SESSIONS &&
673         enabled_types.Has(HISTORY_DELETE_DIRECTIVES) &&
674         enabled_types.Has(TYPED_URLS)) {
675       for (const sync_pb::TabNavigation& navigation :
676            client_entity.specifics().session().tab().navigation()) {
677         observer_for_tests_->OnHistoryCommit(navigation.virtual_url());
678       }
679     }
680   }
681 
682   if (observer_for_tests_)
683     observer_for_tests_->OnCommit(invalidator_client_id, committed_model_types);
684 
685   return throttled_datatypes_in_request->Empty();
686 }
687 
ClearServerData()688 void LoopbackServer::ClearServerData() {
689   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
690   entities_.clear();
691   keystore_keys_.clear();
692   store_birthday_ = base::Time::Now().ToJavaTime();
693   base::DeleteFile(persistent_file_);
694   Init();
695 }
696 
GetStoreBirthday() const697 std::string LoopbackServer::GetStoreBirthday() const {
698   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
699   return base::NumberToString(store_birthday_);
700 }
701 
GetSyncEntitiesByModelType(ModelType model_type)702 std::vector<sync_pb::SyncEntity> LoopbackServer::GetSyncEntitiesByModelType(
703     ModelType model_type) {
704   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
705   std::vector<sync_pb::SyncEntity> sync_entities;
706   for (const auto& kv : entities_) {
707     const LoopbackServerEntity& entity = *kv.second;
708     if (!(entity.IsDeleted() || entity.IsPermanent()) &&
709         entity.GetModelType() == model_type) {
710       sync_pb::SyncEntity sync_entity;
711       entity.SerializeAsProto(&sync_entity);
712       sync_entities.push_back(sync_entity);
713     }
714   }
715   return sync_entities;
716 }
717 
718 std::vector<sync_pb::SyncEntity>
GetPermanentSyncEntitiesByModelType(ModelType model_type)719 LoopbackServer::GetPermanentSyncEntitiesByModelType(ModelType model_type) {
720   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
721   std::vector<sync_pb::SyncEntity> sync_entities;
722   for (const auto& kv : entities_) {
723     const LoopbackServerEntity& entity = *kv.second;
724     if (!entity.IsDeleted() && entity.IsPermanent() &&
725         entity.GetModelType() == model_type) {
726       sync_pb::SyncEntity sync_entity;
727       entity.SerializeAsProto(&sync_entity);
728       sync_entities.push_back(sync_entity);
729     }
730   }
731   return sync_entities;
732 }
733 
734 std::unique_ptr<base::DictionaryValue>
GetEntitiesAsDictionaryValue()735 LoopbackServer::GetEntitiesAsDictionaryValue() {
736   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
737   std::unique_ptr<base::DictionaryValue> dictionary(
738       new base::DictionaryValue());
739 
740   // Initialize an empty ListValue for all ModelTypes.
741   ModelTypeSet all_types = ModelTypeSet::All();
742   for (ModelType type : all_types) {
743     dictionary->Set(ModelTypeToString(type),
744                     std::make_unique<base::ListValue>());
745   }
746 
747   for (const auto& kv : entities_) {
748     const LoopbackServerEntity& entity = *kv.second;
749     if (entity.IsDeleted() || entity.IsPermanent()) {
750       // Tombstones are ignored as they don't represent current data. Folders
751       // are also ignored as current verification infrastructure does not
752       // consider them.
753       continue;
754     }
755     base::ListValue* list_value;
756     if (!dictionary->GetList(ModelTypeToString(entity.GetModelType()),
757                              &list_value)) {
758       return std::unique_ptr<base::DictionaryValue>();
759     }
760     // TODO(pvalenzuela): Store more data for each entity so additional
761     // verification can be performed. One example of additional verification
762     // is checking the correctness of the bookmark hierarchy.
763     list_value->AppendString(entity.GetName());
764   }
765 
766   return dictionary;
767 }
768 
ModifyEntitySpecifics(const std::string & id,const sync_pb::EntitySpecifics & updated_specifics)769 bool LoopbackServer::ModifyEntitySpecifics(
770     const std::string& id,
771     const sync_pb::EntitySpecifics& updated_specifics) {
772   EntityMap::const_iterator iter = entities_.find(id);
773   if (iter == entities_.end() ||
774       iter->second->GetModelType() !=
775           GetModelTypeFromSpecifics(updated_specifics)) {
776     return false;
777   }
778 
779   LoopbackServerEntity* entity = iter->second.get();
780   entity->SetSpecifics(updated_specifics);
781   UpdateEntityVersion(entity);
782   return true;
783 }
784 
ModifyBookmarkEntity(const std::string & id,const std::string & parent_id,const sync_pb::EntitySpecifics & updated_specifics)785 bool LoopbackServer::ModifyBookmarkEntity(
786     const std::string& id,
787     const std::string& parent_id,
788     const sync_pb::EntitySpecifics& updated_specifics) {
789   EntityMap::const_iterator iter = entities_.find(id);
790   if (iter == entities_.end() ||
791       iter->second->GetModelType() != syncer::BOOKMARKS ||
792       GetModelTypeFromSpecifics(updated_specifics) != syncer::BOOKMARKS) {
793     return false;
794   }
795 
796   PersistentBookmarkEntity* entity =
797       static_cast<PersistentBookmarkEntity*>(iter->second.get());
798 
799   entity->SetParentId(parent_id);
800   entity->SetSpecifics(updated_specifics);
801   if (updated_specifics.has_bookmark()) {
802     entity->SetName(updated_specifics.bookmark().legacy_canonicalized_title());
803   }
804   UpdateEntityVersion(entity);
805   return true;
806 }
807 
SerializeState(sync_pb::LoopbackServerProto * proto) const808 void LoopbackServer::SerializeState(sync_pb::LoopbackServerProto* proto) const {
809   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
810 
811   proto->set_version(kCurrentLoopbackServerProtoVersion);
812   proto->set_store_birthday(store_birthday_);
813   proto->set_last_version_assigned(version_);
814   for (const auto& key : keystore_keys_)
815     proto->add_keystore_keys(key.data(), key.size());
816   for (const auto& entity : entities_) {
817     auto* new_entity = proto->mutable_entities()->Add();
818     entity.second->SerializeAsLoopbackServerEntity(new_entity);
819   }
820 }
821 
DeSerializeState(const sync_pb::LoopbackServerProto & proto)822 bool LoopbackServer::DeSerializeState(
823     const sync_pb::LoopbackServerProto& proto) {
824   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
825   DCHECK_EQ(proto.version(), kCurrentLoopbackServerProtoVersion);
826 
827   store_birthday_ = proto.store_birthday();
828   version_ = proto.last_version_assigned();
829   for (int i = 0; i < proto.keystore_keys_size(); ++i) {
830     const auto& key = proto.keystore_keys(i);
831     keystore_keys_.emplace_back(key.begin(), key.end());
832   }
833   for (int i = 0; i < proto.entities_size(); ++i) {
834     std::unique_ptr<LoopbackServerEntity> entity =
835         LoopbackServerEntity::CreateEntityFromProto(proto.entities(i));
836     // Silently drop entities that cannot be successfully deserialized.
837     if (entity)
838       entities_[proto.entities(i).entity().id_string()] = std::move(entity);
839   }
840 
841   // Report success regardless of if some entities were dropped.
842   return true;
843 }
844 
SerializeData(std::string * data)845 bool LoopbackServer::SerializeData(std::string* data) {
846   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
847   sync_pb::LoopbackServerProto proto;
848   SerializeState(&proto);
849   if (!proto.SerializeToString(data)) {
850     LOG(ERROR) << "Loopback sync proto could not be serialized";
851     return false;
852   }
853   UMA_HISTOGRAM_MEMORY_KB(
854       "Sync.Local.FileSizeKB",
855       base::saturated_cast<base::Histogram::Sample>(
856           base::ClampDiv(base::ClampAdd(data->size(), 512), 1024)));
857   return true;
858 }
859 
ScheduleSaveStateToFile()860 bool LoopbackServer::ScheduleSaveStateToFile() {
861   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
862   if (!base::CreateDirectory(persistent_file_.DirName())) {
863     LOG(ERROR) << "Loopback sync could not create the storage directory.";
864     return false;
865   }
866 
867   writer_.ScheduleWrite(this);
868   return true;
869 }
870 
LoadStateFromFile()871 bool LoopbackServer::LoadStateFromFile() {
872   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
873   if (!base::PathExists(persistent_file_)) {
874     LOG(WARNING) << "Loopback sync persistent state file does not exist.";
875     return false;
876   }
877   std::string serialized;
878   if (base::ReadFileToString(persistent_file_, &serialized)) {
879     sync_pb::LoopbackServerProto proto;
880     if (serialized.length() > 0 && proto.ParseFromString(serialized)) {
881       return DeSerializeState(proto);
882     }
883     LOG(ERROR) << "Loopback sync can not parse the persistent state file.";
884     return false;
885   }
886   // TODO(pastarmovj): Try to understand what is the issue e.g. file already
887   // open, no access rights etc. and decide if better course of action is
888   // available instead of giving up and wiping the global state on the next
889   // write.
890   LOG(ERROR) << "Loopback sync can not read the persistent state file.";
891   return false;
892 }
893 
894 }  // namespace syncer
895