1 // Copyright 2017 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/chromeos/printing/printers_sync_bridge.h"
6 
7 #include <set>
8 #include <utility>
9 
10 #include "base/bind.h"
11 #include "base/metrics/histogram_functions.h"
12 #include "base/stl_util.h"
13 #include "base/task/post_task.h"
14 #include "chrome/browser/chromeos/printing/specifics_translation.h"
15 #include "chromeos/printing/printer_configuration.h"
16 #include "components/sync/base/report_unrecoverable_error.h"
17 #include "components/sync/model/model_type_change_processor.h"
18 #include "components/sync/model/mutable_data_batch.h"
19 #include "components/sync/model_impl/client_tag_based_model_type_processor.h"
20 #include "components/sync/protocol/model_type_state.pb.h"
21 #include "components/sync/protocol/sync.pb.h"
22 
23 namespace chromeos {
24 
25 namespace {
26 
27 using syncer::ClientTagBasedModelTypeProcessor;
28 using syncer::ConflictResolution;
29 using syncer::EntityChange;
30 using syncer::EntityChangeList;
31 using syncer::EntityData;
32 using syncer::MetadataChangeList;
33 using syncer::ModelTypeChangeProcessor;
34 using syncer::ModelTypeStore;
35 
CopyToEntityData(const sync_pb::PrinterSpecifics & specifics)36 std::unique_ptr<EntityData> CopyToEntityData(
37     const sync_pb::PrinterSpecifics& specifics) {
38   auto entity_data = std::make_unique<EntityData>();
39   *entity_data->specifics.mutable_printer() = specifics;
40   entity_data->name =
41       specifics.display_name().empty() ? "PRINTER" : specifics.display_name();
42   return entity_data;
43 }
44 
45 // Computes the make_and_model field for old |specifics| where it is missing.
46 // Returns true if an update was made.  make_and_model is computed from the
47 // manufacturer and model strings.
MigrateMakeAndModel(sync_pb::PrinterSpecifics * specifics)48 bool MigrateMakeAndModel(sync_pb::PrinterSpecifics* specifics) {
49   if (specifics->has_make_and_model()) {
50     base::UmaHistogramBoolean("Printing.CUPS.MigratedMakeAndModel", false);
51     return false;
52   }
53 
54   specifics->set_make_and_model(
55       chromeos::MakeAndModel(specifics->manufacturer(), specifics->model()));
56   base::UmaHistogramBoolean("Printing.CUPS.MigratedMakeAndModel", true);
57   return true;
58 }
59 
60 // If |specifics|'s PPD reference has both autoconf and another option selected,
61 // we strip the autoconf flag and return true, false otherwise.
ResolveInvalidPpdReference(sync_pb::PrinterSpecifics * specifics)62 bool ResolveInvalidPpdReference(sync_pb::PrinterSpecifics* specifics) {
63   auto* ppd_ref = specifics->mutable_ppd_reference();
64 
65   if (!ppd_ref->autoconf())
66     return false;
67 
68   if (!ppd_ref->has_user_supplied_ppd_url() &&
69       !ppd_ref->has_effective_make_and_model()) {
70     return false;
71   }
72 
73   ppd_ref->clear_autoconf();
74   return true;
75 }
76 
77 }  // namespace
78 
79 // Delegate class which helps to manage the ModelTypeStore.
80 class PrintersSyncBridge::StoreProxy {
81  public:
StoreProxy(PrintersSyncBridge * owner,syncer::OnceModelTypeStoreFactory callback)82   StoreProxy(PrintersSyncBridge* owner,
83              syncer::OnceModelTypeStoreFactory callback)
84       : owner_(owner) {
85     std::move(callback).Run(syncer::PRINTERS,
86                             base::BindOnce(&StoreProxy::OnStoreCreated,
87                                            weak_ptr_factory_.GetWeakPtr()));
88   }
89 
90   // Returns true if the store has been initialized.
Ready()91   bool Ready() { return store_.get() != nullptr; }
92 
93   // Returns a new WriteBatch.
CreateWriteBatch()94   std::unique_ptr<ModelTypeStore::WriteBatch> CreateWriteBatch() {
95     DCHECK(store_);
96     return store_->CreateWriteBatch();
97   }
98 
99   // Commits writes to the database and updates metadata.
Commit(std::unique_ptr<ModelTypeStore::WriteBatch> batch)100   void Commit(std::unique_ptr<ModelTypeStore::WriteBatch> batch) {
101     DCHECK(store_);
102     store_->CommitWriteBatch(
103         std::move(batch),
104         base::BindOnce(&StoreProxy::OnCommit, weak_ptr_factory_.GetWeakPtr()));
105     owner_->NotifyPrintersUpdated();
106   }
107 
108  private:
109   // Callback for ModelTypeStore initialization.
OnStoreCreated(const base::Optional<syncer::ModelError> & error,std::unique_ptr<ModelTypeStore> store)110   void OnStoreCreated(const base::Optional<syncer::ModelError>& error,
111                       std::unique_ptr<ModelTypeStore> store) {
112     if (error) {
113       owner_->change_processor()->ReportError(*error);
114       return;
115     }
116 
117     store_ = std::move(store);
118     store_->ReadAllData(base::BindOnce(&StoreProxy::OnReadAllData,
119                                        weak_ptr_factory_.GetWeakPtr()));
120   }
121 
OnReadAllData(const base::Optional<syncer::ModelError> & error,std::unique_ptr<ModelTypeStore::RecordList> record_list)122   void OnReadAllData(const base::Optional<syncer::ModelError>& error,
123                      std::unique_ptr<ModelTypeStore::RecordList> record_list) {
124     if (error) {
125       owner_->change_processor()->ReportError(*error);
126       return;
127     }
128 
129     bool parse_error = false;
130     {
131       base::AutoLock lock(owner_->data_lock_);
132       for (const ModelTypeStore::Record& r : *record_list) {
133         auto specifics = std::make_unique<sync_pb::PrinterSpecifics>();
134         if (specifics->ParseFromString(r.value)) {
135           auto& dest = owner_->all_data_[specifics->id()];
136           dest = std::move(specifics);
137         } else {
138           parse_error = true;
139         }
140       }
141     }
142     owner_->NotifyPrintersUpdated();
143 
144     if (parse_error) {
145       owner_->change_processor()->ReportError(
146           {FROM_HERE, "Failed to deserialize all specifics."});
147       return;
148     }
149 
150     // Data loaded.  Load metadata.
151     store_->ReadAllMetadata(base::BindOnce(&StoreProxy::OnReadAllMetadata,
152                                            weak_ptr_factory_.GetWeakPtr()));
153   }
154 
155   // Callback to handle commit errors.
OnCommit(const base::Optional<syncer::ModelError> & error)156   void OnCommit(const base::Optional<syncer::ModelError>& error) {
157     if (error) {
158       LOG(WARNING) << "Failed to commit operation to store";
159       owner_->change_processor()->ReportError(*error);
160       return;
161     }
162   }
163 
OnReadAllMetadata(const base::Optional<syncer::ModelError> & error,std::unique_ptr<syncer::MetadataBatch> metadata_batch)164   void OnReadAllMetadata(
165       const base::Optional<syncer::ModelError>& error,
166       std::unique_ptr<syncer::MetadataBatch> metadata_batch) {
167     if (error) {
168       owner_->change_processor()->ReportError(*error);
169       return;
170     }
171 
172     owner_->change_processor()->ModelReadyToSync(std::move(metadata_batch));
173   }
174 
175   PrintersSyncBridge* owner_;
176 
177   std::unique_ptr<ModelTypeStore> store_;
178   base::WeakPtrFactory<StoreProxy> weak_ptr_factory_{this};
179 };
180 
PrintersSyncBridge(syncer::OnceModelTypeStoreFactory callback,base::RepeatingClosure error_callback)181 PrintersSyncBridge::PrintersSyncBridge(
182     syncer::OnceModelTypeStoreFactory callback,
183     base::RepeatingClosure error_callback)
184     : ModelTypeSyncBridge(std::make_unique<ClientTagBasedModelTypeProcessor>(
185           syncer::PRINTERS,
186           std::move(error_callback))),
187       store_delegate_(std::make_unique<StoreProxy>(this, std::move(callback))),
188       observers_(new base::ObserverListThreadSafe<Observer>()) {}
189 
190 PrintersSyncBridge::~PrintersSyncBridge() = default;
191 
192 std::unique_ptr<MetadataChangeList>
CreateMetadataChangeList()193 PrintersSyncBridge::CreateMetadataChangeList() {
194   return ModelTypeStore::WriteBatch::CreateMetadataChangeList();
195 }
196 
MergeSyncData(std::unique_ptr<MetadataChangeList> metadata_change_list,syncer::EntityChangeList entity_data)197 base::Optional<syncer::ModelError> PrintersSyncBridge::MergeSyncData(
198     std::unique_ptr<MetadataChangeList> metadata_change_list,
199     syncer::EntityChangeList entity_data) {
200   DCHECK(change_processor()->IsTrackingMetadata());
201 
202   std::unique_ptr<ModelTypeStore::WriteBatch> batch =
203       store_delegate_->CreateWriteBatch();
204   std::set<std::string> sync_entity_ids;
205   {
206     base::AutoLock lock(data_lock_);
207     // Store the new data locally.
208     for (const auto& change : entity_data) {
209       const sync_pb::PrinterSpecifics& specifics =
210           change->data().specifics.printer();
211 
212       DCHECK_EQ(change->storage_key(), specifics.id());
213       sync_entity_ids.insert(specifics.id());
214 
215       // Write the update to local storage even if we already have it.
216       StoreSpecifics(std::make_unique<sync_pb::PrinterSpecifics>(specifics),
217                      batch.get());
218     }
219 
220     // Inform the change processor of the new local entities and generate
221     // appropriate metadata.
222     for (const auto& entry : all_data_) {
223       const std::string& local_entity_id = entry.first;
224 
225       // TODO(crbug.com/737809): Remove when all data is expected to have been
226       // migrated.
227       bool migrated = MigrateMakeAndModel(entry.second.get());
228 
229       // TODO(crbug.com/987869): Remove when all data is expected to have been
230       // resolved.
231       bool resolved = ResolveInvalidPpdReference(entry.second.get());
232 
233       if (migrated || resolved ||
234           !base::Contains(sync_entity_ids, local_entity_id)) {
235         // Only local objects which were not updated are uploaded.  Objects for
236         // which there was a remote copy are overwritten.
237         change_processor()->Put(local_entity_id,
238                                 CopyToEntityData(*entry.second),
239                                 metadata_change_list.get());
240       }
241     }
242   }
243 
244   NotifyPrintersUpdated();
245   batch->TakeMetadataChangesFrom(std::move(metadata_change_list));
246   store_delegate_->Commit(std::move(batch));
247   return {};
248 }
249 
ApplySyncChanges(std::unique_ptr<MetadataChangeList> metadata_change_list,EntityChangeList entity_changes)250 base::Optional<syncer::ModelError> PrintersSyncBridge::ApplySyncChanges(
251     std::unique_ptr<MetadataChangeList> metadata_change_list,
252     EntityChangeList entity_changes) {
253   std::unique_ptr<ModelTypeStore::WriteBatch> batch =
254       store_delegate_->CreateWriteBatch();
255   {
256     base::AutoLock lock(data_lock_);
257     // For all the entities from the server, apply changes.
258     for (const std::unique_ptr<EntityChange>& change : entity_changes) {
259       // We register the entity's storage key as our printer ids since they're
260       // globally unique.
261       const std::string& id = change->storage_key();
262       if (change->type() == EntityChange::ACTION_DELETE) {
263         // Server says delete, try to remove locally.
264         DeleteSpecifics(id, batch.get());
265       } else {
266         // Server says update, overwrite whatever is local.  Conflict resolution
267         // guarantees that this will be the newest version of the object.
268         const sync_pb::PrinterSpecifics& specifics =
269             change->data().specifics.printer();
270         DCHECK_EQ(id, specifics.id());
271         StoreSpecifics(std::make_unique<sync_pb::PrinterSpecifics>(specifics),
272                        batch.get());
273       }
274     }
275   }
276 
277   NotifyPrintersUpdated();
278   // Update the local database with metadata for the incoming changes.
279   batch->TakeMetadataChangesFrom(std::move(metadata_change_list));
280 
281   store_delegate_->Commit(std::move(batch));
282   return {};
283 }
284 
GetData(StorageKeyList storage_keys,DataCallback callback)285 void PrintersSyncBridge::GetData(StorageKeyList storage_keys,
286                                  DataCallback callback) {
287   auto batch = std::make_unique<syncer::MutableDataBatch>();
288   {
289     base::AutoLock lock(data_lock_);
290     for (const auto& key : storage_keys) {
291       auto found = all_data_.find(key);
292       if (found != all_data_.end()) {
293         batch->Put(key, CopyToEntityData(*found->second));
294       }
295     }
296   }
297   std::move(callback).Run(std::move(batch));
298 }
299 
GetAllDataForDebugging(DataCallback callback)300 void PrintersSyncBridge::GetAllDataForDebugging(DataCallback callback) {
301   auto batch = std::make_unique<syncer::MutableDataBatch>();
302   {
303     base::AutoLock lock(data_lock_);
304     for (const auto& entry : all_data_) {
305       batch->Put(entry.first, CopyToEntityData(*entry.second));
306     }
307   }
308   std::move(callback).Run(std::move(batch));
309 }
310 
GetClientTag(const EntityData & entity_data)311 std::string PrintersSyncBridge::GetClientTag(const EntityData& entity_data) {
312   // Printers were never synced prior to USS so this can match GetStorageKey.
313   return GetStorageKey(entity_data);
314 }
315 
GetStorageKey(const EntityData & entity_data)316 std::string PrintersSyncBridge::GetStorageKey(const EntityData& entity_data) {
317   DCHECK(entity_data.specifics.has_printer());
318   return entity_data.specifics.printer().id();
319 }
320 
321 // Picks the entity with the most recent updated time as the canonical version.
ResolveConflict(const std::string & storage_key,const EntityData & remote_data) const322 ConflictResolution PrintersSyncBridge::ResolveConflict(
323     const std::string& storage_key,
324     const EntityData& remote_data) const {
325   DCHECK(remote_data.specifics.has_printer());
326 
327   auto iter = all_data_.find(storage_key);
328   // If the local printer doesn't exist, it must have been deleted. In this
329   // case, use the remote one.
330   if (iter == all_data_.end()) {
331     return ConflictResolution::kUseRemote;
332   }
333   const sync_pb::PrinterSpecifics& local_printer = *iter->second;
334 
335   const sync_pb::PrinterSpecifics& remote_printer =
336       remote_data.specifics.printer();
337 
338   if (local_printer.updated_timestamp() > remote_printer.updated_timestamp()) {
339     return ConflictResolution::kUseLocal;
340   }
341 
342   return ConflictResolution::kUseRemote;
343 }
344 
AddPrinter(std::unique_ptr<sync_pb::PrinterSpecifics> printer)345 void PrintersSyncBridge::AddPrinter(
346     std::unique_ptr<sync_pb::PrinterSpecifics> printer) {
347   {
348     base::AutoLock lock(data_lock_);
349     AddPrinterLocked(std::move(printer));
350   }
351   NotifyPrintersUpdated();
352 }
353 
UpdatePrinter(std::unique_ptr<sync_pb::PrinterSpecifics> printer)354 bool PrintersSyncBridge::UpdatePrinter(
355     std::unique_ptr<sync_pb::PrinterSpecifics> printer) {
356   bool res;
357   {
358     base::AutoLock lock(data_lock_);
359     res = UpdatePrinterLocked(std::move(printer));
360   }
361   NotifyPrintersUpdated();
362   return res;
363 }
364 
UpdatePrinterLocked(std::unique_ptr<sync_pb::PrinterSpecifics> printer)365 bool PrintersSyncBridge::UpdatePrinterLocked(
366     std::unique_ptr<sync_pb::PrinterSpecifics> printer) {
367   data_lock_.AssertAcquired();
368   DCHECK(printer->has_id());
369   auto iter = all_data_.find(printer->id());
370   if (iter == all_data_.end()) {
371     AddPrinterLocked(std::move(printer));
372     return true;
373   }
374 
375   // Modify the printer in-place then notify the change processor.
376   sync_pb::PrinterSpecifics* merged = iter->second.get();
377   MergePrinterToSpecifics(*SpecificsToPrinter(*printer), merged);
378   merged->set_updated_timestamp(base::Time::Now().ToJavaTime());
379   CommitPrinterPut(*merged);
380 
381   return false;
382 }
383 
RemovePrinter(const std::string & id)384 bool PrintersSyncBridge::RemovePrinter(const std::string& id) {
385   DCHECK(store_delegate_->Ready());
386 
387   std::unique_ptr<ModelTypeStore::WriteBatch> batch =
388       store_delegate_->CreateWriteBatch();
389   {
390     base::AutoLock lock(data_lock_);
391     if (!DeleteSpecifics(id, batch.get())) {
392       LOG(WARNING) << "Could not find printer" << id;
393       return false;
394     }
395   }
396 
397   if (change_processor()->IsTrackingMetadata()) {
398     change_processor()->Delete(id, batch->GetMetadataChangeList());
399   }
400   store_delegate_->Commit(std::move(batch));
401 
402   return true;
403 }
404 
GetAllPrinters() const405 std::vector<sync_pb::PrinterSpecifics> PrintersSyncBridge::GetAllPrinters()
406     const {
407   base::AutoLock lock(data_lock_);
408   std::vector<sync_pb::PrinterSpecifics> printers;
409   for (auto& entry : all_data_) {
410     printers.push_back(*entry.second);
411   }
412 
413   return printers;
414 }
415 
GetPrinter(const std::string & id) const416 base::Optional<sync_pb::PrinterSpecifics> PrintersSyncBridge::GetPrinter(
417     const std::string& id) const {
418   base::AutoLock lock(data_lock_);
419   auto iter = all_data_.find(id);
420   if (iter == all_data_.end()) {
421     return {};
422   }
423 
424   return {*iter->second};
425 }
426 
HasPrinter(const std::string & id) const427 bool PrintersSyncBridge::HasPrinter(const std::string& id) const {
428   base::AutoLock lock(data_lock_);
429   return all_data_.find(id) != all_data_.end();
430 }
431 
CommitPrinterPut(const sync_pb::PrinterSpecifics & printer)432 void PrintersSyncBridge::CommitPrinterPut(
433     const sync_pb::PrinterSpecifics& printer) {
434   std::unique_ptr<ModelTypeStore::WriteBatch> batch =
435       store_delegate_->CreateWriteBatch();
436   if (change_processor()->IsTrackingMetadata()) {
437     change_processor()->Put(printer.id(), CopyToEntityData(printer),
438                             batch->GetMetadataChangeList());
439   }
440   batch->WriteData(printer.id(), printer.SerializeAsString());
441 
442   store_delegate_->Commit(std::move(batch));
443 }
444 
AddPrinterLocked(std::unique_ptr<sync_pb::PrinterSpecifics> printer)445 void PrintersSyncBridge::AddPrinterLocked(
446     std::unique_ptr<sync_pb::PrinterSpecifics> printer) {
447   // TODO(skau): Benchmark this code.  Make sure it doesn't hold onto the lock
448   // for too long.
449   data_lock_.AssertAcquired();
450   printer->set_updated_timestamp(base::Time::Now().ToJavaTime());
451 
452   CommitPrinterPut(*printer);
453   auto& dest = all_data_[printer->id()];
454   dest = std::move(printer);
455 }
456 
StoreSpecifics(std::unique_ptr<sync_pb::PrinterSpecifics> specifics,ModelTypeStore::WriteBatch * batch)457 void PrintersSyncBridge::StoreSpecifics(
458     std::unique_ptr<sync_pb::PrinterSpecifics> specifics,
459     ModelTypeStore::WriteBatch* batch) {
460   data_lock_.AssertAcquired();
461   const std::string id = specifics->id();
462   batch->WriteData(id, specifics->SerializeAsString());
463   all_data_[id] = std::move(specifics);
464 }
465 
DeleteSpecifics(const std::string & id,ModelTypeStore::WriteBatch * batch)466 bool PrintersSyncBridge::DeleteSpecifics(const std::string& id,
467                                          ModelTypeStore::WriteBatch* batch) {
468   data_lock_.AssertAcquired();
469   auto iter = all_data_.find(id);
470   if (iter != all_data_.end()) {
471     batch->DeleteData(id);
472     all_data_.erase(iter);
473     return true;
474   }
475 
476   return false;
477 }
478 
AddObserver(Observer * obs)479 void PrintersSyncBridge::AddObserver(Observer* obs) {
480   observers_->AddObserver(obs);
481 }
482 
RemoveObserver(Observer * obs)483 void PrintersSyncBridge::RemoveObserver(Observer* obs) {
484   observers_->RemoveObserver(obs);
485 }
486 
NotifyPrintersUpdated()487 void PrintersSyncBridge::NotifyPrintersUpdated() {
488   observers_->Notify(FROM_HERE,
489                      &PrintersSyncBridge::Observer::OnPrintersUpdated);
490 }
491 
492 }  // namespace chromeos
493