1 // Copyright 2014 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_driver/generic_change_processor.h"
6
7 #include "base/location.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "components/sync_driver/sync_api_component_factory.h"
11 #include "sync/api/sync_change.h"
12 #include "sync/api/sync_error.h"
13 #include "sync/api/syncable_service.h"
14 #include "sync/internal_api/public/base_node.h"
15 #include "sync/internal_api/public/change_record.h"
16 #include "sync/internal_api/public/read_node.h"
17 #include "sync/internal_api/public/read_transaction.h"
18 #include "sync/internal_api/public/util/unrecoverable_error_handler.h"
19 #include "sync/internal_api/public/write_node.h"
20 #include "sync/internal_api/public/write_transaction.h"
21 #include "sync/syncable/entry.h" // TODO(tim): Bug 123674.
22
23 namespace browser_sync {
24
25 namespace {
26
27 const int kContextSizeLimit = 1024; // Datatype context size limit.
28
SetNodeSpecifics(const sync_pb::EntitySpecifics & entity_specifics,syncer::WriteNode * write_node)29 void SetNodeSpecifics(const sync_pb::EntitySpecifics& entity_specifics,
30 syncer::WriteNode* write_node) {
31 if (syncer::GetModelTypeFromSpecifics(entity_specifics) ==
32 syncer::PASSWORDS) {
33 write_node->SetPasswordSpecifics(
34 entity_specifics.password().client_only_encrypted_data());
35 } else {
36 write_node->SetEntitySpecifics(entity_specifics);
37 }
38 }
39
40 // Helper function to convert AttachmentId to AttachmentMetadataRecord.
AttachmentIdToRecord(const syncer::AttachmentId & attachment_id)41 sync_pb::AttachmentMetadataRecord AttachmentIdToRecord(
42 const syncer::AttachmentId& attachment_id) {
43 sync_pb::AttachmentMetadataRecord record;
44 *record.mutable_id() = attachment_id.GetProto();
45 return record;
46 }
47
48 // Replace |write_nodes|'s attachment ids with |attachment_ids|.
SetAttachmentMetadata(const syncer::AttachmentIdList & attachment_ids,syncer::WriteNode * write_node)49 void SetAttachmentMetadata(const syncer::AttachmentIdList& attachment_ids,
50 syncer::WriteNode* write_node) {
51 DCHECK(write_node);
52 sync_pb::AttachmentMetadata attachment_metadata;
53 std::transform(
54 attachment_ids.begin(),
55 attachment_ids.end(),
56 RepeatedFieldBackInserter(attachment_metadata.mutable_record()),
57 AttachmentIdToRecord);
58 write_node->SetAttachmentMetadata(attachment_metadata);
59 }
60
BuildRemoteSyncData(int64 sync_id,const syncer::BaseNode & read_node,const syncer::AttachmentServiceProxy & attachment_service_proxy)61 syncer::SyncData BuildRemoteSyncData(
62 int64 sync_id,
63 const syncer::BaseNode& read_node,
64 const syncer::AttachmentServiceProxy& attachment_service_proxy) {
65 const syncer::AttachmentIdList& attachment_ids = read_node.GetAttachmentIds();
66 // Use the specifics of non-password datatypes directly (encryption has
67 // already been handled).
68 if (read_node.GetModelType() != syncer::PASSWORDS) {
69 return syncer::SyncData::CreateRemoteData(sync_id,
70 read_node.GetEntitySpecifics(),
71 read_node.GetModificationTime(),
72 attachment_ids,
73 attachment_service_proxy);
74 }
75
76 // Passwords must be accessed differently, to account for their encryption,
77 // and stored into a temporary EntitySpecifics.
78 sync_pb::EntitySpecifics password_holder;
79 password_holder.mutable_password()->mutable_client_only_encrypted_data()->
80 CopyFrom(read_node.GetPasswordSpecifics());
81 return syncer::SyncData::CreateRemoteData(sync_id,
82 password_holder,
83 read_node.GetModificationTime(),
84 attachment_ids,
85 attachment_service_proxy);
86 }
87
88 } // namespace
89
GenericChangeProcessor(DataTypeErrorHandler * error_handler,const base::WeakPtr<syncer::SyncableService> & local_service,const base::WeakPtr<syncer::SyncMergeResult> & merge_result,syncer::UserShare * user_share,SyncApiComponentFactory * sync_factory)90 GenericChangeProcessor::GenericChangeProcessor(
91 DataTypeErrorHandler* error_handler,
92 const base::WeakPtr<syncer::SyncableService>& local_service,
93 const base::WeakPtr<syncer::SyncMergeResult>& merge_result,
94 syncer::UserShare* user_share,
95 SyncApiComponentFactory* sync_factory)
96 : ChangeProcessor(error_handler),
97 local_service_(local_service),
98 merge_result_(merge_result),
99 share_handle_(user_share),
100 attachment_service_(sync_factory->CreateAttachmentService(this)),
101 attachment_service_weak_ptr_factory_(attachment_service_.get()),
102 attachment_service_proxy_(
103 base::MessageLoopProxy::current(),
104 attachment_service_weak_ptr_factory_.GetWeakPtr()) {
105 DCHECK(CalledOnValidThread());
106 DCHECK(attachment_service_);
107 }
108
~GenericChangeProcessor()109 GenericChangeProcessor::~GenericChangeProcessor() {
110 DCHECK(CalledOnValidThread());
111 }
112
ApplyChangesFromSyncModel(const syncer::BaseTransaction * trans,int64 model_version,const syncer::ImmutableChangeRecordList & changes)113 void GenericChangeProcessor::ApplyChangesFromSyncModel(
114 const syncer::BaseTransaction* trans,
115 int64 model_version,
116 const syncer::ImmutableChangeRecordList& changes) {
117 DCHECK(CalledOnValidThread());
118 DCHECK(syncer_changes_.empty());
119 for (syncer::ChangeRecordList::const_iterator it =
120 changes.Get().begin(); it != changes.Get().end(); ++it) {
121 if (it->action == syncer::ChangeRecord::ACTION_DELETE) {
122 scoped_ptr<sync_pb::EntitySpecifics> specifics;
123 if (it->specifics.has_password()) {
124 DCHECK(it->extra.get());
125 specifics.reset(new sync_pb::EntitySpecifics(it->specifics));
126 specifics->mutable_password()->mutable_client_only_encrypted_data()->
127 CopyFrom(it->extra->unencrypted());
128 }
129 const syncer::AttachmentIdList empty_list_of_attachment_ids;
130 syncer_changes_.push_back(
131 syncer::SyncChange(FROM_HERE,
132 syncer::SyncChange::ACTION_DELETE,
133 syncer::SyncData::CreateRemoteData(
134 it->id,
135 specifics ? *specifics : it->specifics,
136 base::Time(),
137 empty_list_of_attachment_ids,
138 attachment_service_proxy_)));
139 } else {
140 syncer::SyncChange::SyncChangeType action =
141 (it->action == syncer::ChangeRecord::ACTION_ADD) ?
142 syncer::SyncChange::ACTION_ADD : syncer::SyncChange::ACTION_UPDATE;
143 // Need to load specifics from node.
144 syncer::ReadNode read_node(trans);
145 if (read_node.InitByIdLookup(it->id) != syncer::BaseNode::INIT_OK) {
146 error_handler()->OnSingleDatatypeUnrecoverableError(
147 FROM_HERE,
148 "Failed to look up data for received change with id " +
149 base::Int64ToString(it->id));
150 return;
151 }
152 syncer_changes_.push_back(syncer::SyncChange(
153 FROM_HERE,
154 action,
155 BuildRemoteSyncData(it->id, read_node, attachment_service_proxy_)));
156 }
157 }
158 }
159
CommitChangesFromSyncModel()160 void GenericChangeProcessor::CommitChangesFromSyncModel() {
161 DCHECK(CalledOnValidThread());
162 if (syncer_changes_.empty())
163 return;
164 if (!local_service_.get()) {
165 syncer::ModelType type = syncer_changes_[0].sync_data().GetDataType();
166 syncer::SyncError error(FROM_HERE,
167 syncer::SyncError::DATATYPE_ERROR,
168 "Local service destroyed.",
169 type);
170 error_handler()->OnSingleDatatypeUnrecoverableError(error.location(),
171 error.message());
172 return;
173 }
174 syncer::SyncError error = local_service_->ProcessSyncChanges(FROM_HERE,
175 syncer_changes_);
176 syncer_changes_.clear();
177 if (error.IsSet()) {
178 error_handler()->OnSingleDatatypeUnrecoverableError(
179 error.location(), error.message());
180 }
181 }
182
GetAllSyncData(syncer::ModelType type) const183 syncer::SyncDataList GenericChangeProcessor::GetAllSyncData(
184 syncer::ModelType type) const {
185 // This is slow / memory intensive. Should be used sparingly by datatypes.
186 syncer::SyncDataList data;
187 GetAllSyncDataReturnError(type, &data);
188 return data;
189 }
190
UpdateDataTypeContext(syncer::ModelType type,syncer::SyncChangeProcessor::ContextRefreshStatus refresh_status,const std::string & context)191 syncer::SyncError GenericChangeProcessor::UpdateDataTypeContext(
192 syncer::ModelType type,
193 syncer::SyncChangeProcessor::ContextRefreshStatus refresh_status,
194 const std::string& context) {
195 DCHECK(syncer::ProtocolTypes().Has(type));
196
197 if (context.size() > static_cast<size_t>(kContextSizeLimit)) {
198 return syncer::SyncError(FROM_HERE,
199 syncer::SyncError::DATATYPE_ERROR,
200 "Context size limit exceeded.",
201 type);
202 }
203
204 syncer::WriteTransaction trans(FROM_HERE, share_handle());
205 trans.SetDataTypeContext(type, refresh_status, context);
206
207 // TODO(zea): plumb a pointer to the PSS or SyncManagerImpl here so we can
208 // trigger a datatype nudge if |refresh_status == REFRESH_NEEDED|.
209
210 return syncer::SyncError();
211 }
212
OnAttachmentUploaded(const syncer::AttachmentId & attachment_id)213 void GenericChangeProcessor::OnAttachmentUploaded(
214 const syncer::AttachmentId& attachment_id) {
215 syncer::WriteTransaction trans(FROM_HERE, share_handle());
216 trans.UpdateEntriesWithAttachmentId(attachment_id);
217 }
218
GetAllSyncDataReturnError(syncer::ModelType type,syncer::SyncDataList * current_sync_data) const219 syncer::SyncError GenericChangeProcessor::GetAllSyncDataReturnError(
220 syncer::ModelType type,
221 syncer::SyncDataList* current_sync_data) const {
222 DCHECK(CalledOnValidThread());
223 std::string type_name = syncer::ModelTypeToString(type);
224 syncer::ReadTransaction trans(FROM_HERE, share_handle());
225 syncer::ReadNode root(&trans);
226 if (root.InitTypeRoot(type) != syncer::BaseNode::INIT_OK) {
227 syncer::SyncError error(FROM_HERE,
228 syncer::SyncError::DATATYPE_ERROR,
229 "Server did not create the top-level " + type_name +
230 " node. We might be running against an out-of-"
231 "date server.",
232 type);
233 return error;
234 }
235
236 // TODO(akalin): We'll have to do a tree traversal for bookmarks.
237 DCHECK_NE(type, syncer::BOOKMARKS);
238
239 std::vector<int64> child_ids;
240 root.GetChildIds(&child_ids);
241
242 for (std::vector<int64>::iterator it = child_ids.begin();
243 it != child_ids.end(); ++it) {
244 syncer::ReadNode sync_child_node(&trans);
245 if (sync_child_node.InitByIdLookup(*it) !=
246 syncer::BaseNode::INIT_OK) {
247 syncer::SyncError error(FROM_HERE,
248 syncer::SyncError::DATATYPE_ERROR,
249 "Failed to fetch child node for type " +
250 type_name + ".",
251 type);
252 return error;
253 }
254 current_sync_data->push_back(BuildRemoteSyncData(
255 sync_child_node.GetId(), sync_child_node, attachment_service_proxy_));
256 }
257 return syncer::SyncError();
258 }
259
GetDataTypeContext(syncer::ModelType type,std::string * context) const260 bool GenericChangeProcessor::GetDataTypeContext(syncer::ModelType type,
261 std::string* context) const {
262 syncer::ReadTransaction trans(FROM_HERE, share_handle());
263 sync_pb::DataTypeContext context_proto;
264 trans.GetDataTypeContext(type, &context_proto);
265 if (!context_proto.has_context())
266 return false;
267
268 DCHECK_EQ(type,
269 syncer::GetModelTypeFromSpecificsFieldNumber(
270 context_proto.data_type_id()));
271 *context = context_proto.context();
272 return true;
273 }
274
GetSyncCountForType(syncer::ModelType type)275 int GenericChangeProcessor::GetSyncCountForType(syncer::ModelType type) {
276 syncer::ReadTransaction trans(FROM_HERE, share_handle());
277 syncer::ReadNode root(&trans);
278 if (root.InitTypeRoot(type) != syncer::BaseNode::INIT_OK)
279 return 0;
280
281 // Subtract one to account for type's root node.
282 return root.GetTotalNodeCount() - 1;
283 }
284
285 namespace {
286
287 // TODO(isherman): Investigating http://crbug.com/121592
288 // WARNING: this code is sensitive to compiler optimizations. Be careful
289 // modifying any code around an OnSingleDatatypeUnrecoverableError call, else
290 // the compiler attempts to merge it with other calls, losing useful information
291 // in breakpad uploads.
LogLookupFailure(syncer::BaseNode::InitByLookupResult lookup_result,const tracked_objects::Location & from_here,const std::string & error_prefix,syncer::ModelType type,DataTypeErrorHandler * error_handler)292 syncer::SyncError LogLookupFailure(
293 syncer::BaseNode::InitByLookupResult lookup_result,
294 const tracked_objects::Location& from_here,
295 const std::string& error_prefix,
296 syncer::ModelType type,
297 DataTypeErrorHandler* error_handler) {
298 switch (lookup_result) {
299 case syncer::BaseNode::INIT_FAILED_ENTRY_NOT_GOOD: {
300 syncer::SyncError error;
301 error.Reset(from_here,
302 error_prefix +
303 "could not find entry matching the lookup criteria.",
304 type);
305 error_handler->OnSingleDatatypeUnrecoverableError(FROM_HERE,
306 error.message());
307 LOG(ERROR) << "Delete: Bad entry.";
308 return error;
309 }
310 case syncer::BaseNode::INIT_FAILED_ENTRY_IS_DEL: {
311 syncer::SyncError error;
312 error.Reset(from_here, error_prefix + "entry is already deleted.", type);
313 error_handler->OnSingleDatatypeUnrecoverableError(FROM_HERE,
314 error.message());
315 LOG(ERROR) << "Delete: Deleted entry.";
316 return error;
317 }
318 case syncer::BaseNode::INIT_FAILED_DECRYPT_IF_NECESSARY: {
319 syncer::SyncError error;
320 error.Reset(from_here, error_prefix + "unable to decrypt", type);
321 error_handler->OnSingleDatatypeUnrecoverableError(FROM_HERE,
322 error.message());
323 LOG(ERROR) << "Delete: Undecryptable entry.";
324 return error;
325 }
326 case syncer::BaseNode::INIT_FAILED_PRECONDITION: {
327 syncer::SyncError error;
328 error.Reset(from_here,
329 error_prefix + "a precondition was not met for calling init.",
330 type);
331 error_handler->OnSingleDatatypeUnrecoverableError(FROM_HERE,
332 error.message());
333 LOG(ERROR) << "Delete: Failed precondition.";
334 return error;
335 }
336 default: {
337 syncer::SyncError error;
338 // Should have listed all the possible error cases above.
339 error.Reset(from_here, error_prefix + "unknown error", type);
340 error_handler->OnSingleDatatypeUnrecoverableError(FROM_HERE,
341 error.message());
342 LOG(ERROR) << "Delete: Unknown error.";
343 return error;
344 }
345 }
346 }
347
AttemptDelete(const syncer::SyncChange & change,syncer::ModelType type,const std::string & type_str,syncer::WriteNode * node,DataTypeErrorHandler * error_handler)348 syncer::SyncError AttemptDelete(const syncer::SyncChange& change,
349 syncer::ModelType type,
350 const std::string& type_str,
351 syncer::WriteNode* node,
352 DataTypeErrorHandler* error_handler) {
353 DCHECK_EQ(change.change_type(), syncer::SyncChange::ACTION_DELETE);
354 if (change.sync_data().IsLocal()) {
355 const std::string& tag = syncer::SyncDataLocal(change.sync_data()).GetTag();
356 if (tag.empty()) {
357 syncer::SyncError error(
358 FROM_HERE,
359 syncer::SyncError::DATATYPE_ERROR,
360 "Failed to delete " + type_str + " node. Local data, empty tag. " +
361 change.location().ToString(),
362 type);
363 error_handler->OnSingleDatatypeUnrecoverableError(error.location(),
364 error.message());
365 NOTREACHED();
366 return error;
367 }
368
369 syncer::BaseNode::InitByLookupResult result =
370 node->InitByClientTagLookup(change.sync_data().GetDataType(), tag);
371 if (result != syncer::BaseNode::INIT_OK) {
372 return LogLookupFailure(
373 result, FROM_HERE,
374 "Failed to delete " + type_str + " node. Local data. " +
375 change.location().ToString(),
376 type, error_handler);
377 }
378 } else {
379 syncer::BaseNode::InitByLookupResult result = node->InitByIdLookup(
380 syncer::SyncDataRemote(change.sync_data()).GetId());
381 if (result != syncer::BaseNode::INIT_OK) {
382 return LogLookupFailure(
383 result, FROM_HERE,
384 "Failed to delete " + type_str + " node. Non-local data. " +
385 change.location().ToString(),
386 type, error_handler);
387 }
388 }
389 if (IsActOnceDataType(type))
390 node->Drop();
391 else
392 node->Tombstone();
393 return syncer::SyncError();
394 }
395
396 // A callback invoked on completion of AttachmentService::StoreAttachment.
IgnoreStoreResult(const syncer::AttachmentService::StoreResult &)397 void IgnoreStoreResult(const syncer::AttachmentService::StoreResult&) {
398 // TODO(maniscalco): Here is where we're going to update the in-directory
399 // entry to indicate that the attachments have been successfully stored on
400 // disk. Why do we care? Because we might crash after persisting the
401 // directory to disk, but before we have persisted its attachments, leaving us
402 // with danging attachment ids. Having a flag that indicates we've stored the
403 // entry will allow us to detect and filter entries with dangling attachment
404 // ids (bug 368353).
405 }
406
StoreAttachments(syncer::AttachmentService * attachment_service,const syncer::AttachmentList & attachments)407 void StoreAttachments(syncer::AttachmentService* attachment_service,
408 const syncer::AttachmentList& attachments) {
409 DCHECK(attachment_service);
410 syncer::AttachmentService::StoreCallback ignore_store_result =
411 base::Bind(&IgnoreStoreResult);
412 attachment_service->StoreAttachments(attachments, ignore_store_result);
413 }
414
415 } // namespace
416
ProcessSyncChanges(const tracked_objects::Location & from_here,const syncer::SyncChangeList & list_of_changes)417 syncer::SyncError GenericChangeProcessor::ProcessSyncChanges(
418 const tracked_objects::Location& from_here,
419 const syncer::SyncChangeList& list_of_changes) {
420 DCHECK(CalledOnValidThread());
421
422 // Keep track of brand new attachments so we can persist them on this device
423 // and upload them to the server.
424 syncer::AttachmentList new_attachments;
425
426 syncer::WriteTransaction trans(from_here, share_handle());
427
428 for (syncer::SyncChangeList::const_iterator iter = list_of_changes.begin();
429 iter != list_of_changes.end();
430 ++iter) {
431 const syncer::SyncChange& change = *iter;
432 DCHECK_NE(change.sync_data().GetDataType(), syncer::UNSPECIFIED);
433 syncer::ModelType type = change.sync_data().GetDataType();
434 std::string type_str = syncer::ModelTypeToString(type);
435 syncer::WriteNode sync_node(&trans);
436 if (change.change_type() == syncer::SyncChange::ACTION_DELETE) {
437 syncer::SyncError error =
438 AttemptDelete(change, type, type_str, &sync_node, error_handler());
439 if (error.IsSet()) {
440 NOTREACHED();
441 return error;
442 }
443 if (merge_result_.get()) {
444 merge_result_->set_num_items_deleted(
445 merge_result_->num_items_deleted() + 1);
446 }
447 } else if (change.change_type() == syncer::SyncChange::ACTION_ADD) {
448 syncer::SyncError error = HandleActionAdd(
449 change, type_str, type, trans, &sync_node, &new_attachments);
450 if (error.IsSet()) {
451 return error;
452 }
453 } else if (change.change_type() == syncer::SyncChange::ACTION_UPDATE) {
454 syncer::SyncError error = HandleActionUpdate(
455 change, type_str, type, trans, &sync_node, &new_attachments);
456 if (error.IsSet()) {
457 return error;
458 }
459 } else {
460 syncer::SyncError error(
461 FROM_HERE,
462 syncer::SyncError::DATATYPE_ERROR,
463 "Received unset SyncChange in the change processor, " +
464 change.location().ToString(),
465 type);
466 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
467 error.message());
468 NOTREACHED();
469 LOG(ERROR) << "Unset sync change.";
470 return error;
471 }
472 }
473
474 if (!new_attachments.empty()) {
475 StoreAttachments(attachment_service_.get(), new_attachments);
476 }
477
478 return syncer::SyncError();
479 }
480
481 // WARNING: this code is sensitive to compiler optimizations. Be careful
482 // modifying any code around an OnSingleDatatypeUnrecoverableError call, else
483 // the compiler attempts to merge it with other calls, losing useful information
484 // in breakpad uploads.
HandleActionAdd(const syncer::SyncChange & change,const std::string & type_str,const syncer::ModelType & type,const syncer::WriteTransaction & trans,syncer::WriteNode * sync_node,syncer::AttachmentList * new_attachments)485 syncer::SyncError GenericChangeProcessor::HandleActionAdd(
486 const syncer::SyncChange& change,
487 const std::string& type_str,
488 const syncer::ModelType& type,
489 const syncer::WriteTransaction& trans,
490 syncer::WriteNode* sync_node,
491 syncer::AttachmentList* new_attachments) {
492 // TODO(sync): Handle other types of creation (custom parents, folders,
493 // etc.).
494 syncer::ReadNode root_node(&trans);
495 const syncer::SyncDataLocal sync_data_local(change.sync_data());
496 if (root_node.InitTypeRoot(sync_data_local.GetDataType()) !=
497 syncer::BaseNode::INIT_OK) {
498 syncer::SyncError error(FROM_HERE,
499 syncer::SyncError::DATATYPE_ERROR,
500 "Failed to look up root node for type " + type_str,
501 type);
502 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
503 error.message());
504 NOTREACHED();
505 LOG(ERROR) << "Create: no root node.";
506 return error;
507 }
508 syncer::WriteNode::InitUniqueByCreationResult result =
509 sync_node->InitUniqueByCreation(
510 sync_data_local.GetDataType(), root_node, sync_data_local.GetTag());
511 if (result != syncer::WriteNode::INIT_SUCCESS) {
512 std::string error_prefix = "Failed to create " + type_str + " node: " +
513 change.location().ToString() + ", ";
514 switch (result) {
515 case syncer::WriteNode::INIT_FAILED_EMPTY_TAG: {
516 syncer::SyncError error;
517 error.Reset(FROM_HERE, error_prefix + "empty tag", type);
518 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
519 error.message());
520 LOG(ERROR) << "Create: Empty tag.";
521 return error;
522 }
523 case syncer::WriteNode::INIT_FAILED_ENTRY_ALREADY_EXISTS: {
524 syncer::SyncError error;
525 error.Reset(FROM_HERE, error_prefix + "entry already exists", type);
526 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
527 error.message());
528 LOG(ERROR) << "Create: Entry exists.";
529 return error;
530 }
531 case syncer::WriteNode::INIT_FAILED_COULD_NOT_CREATE_ENTRY: {
532 syncer::SyncError error;
533 error.Reset(FROM_HERE, error_prefix + "failed to create entry", type);
534 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
535 error.message());
536 LOG(ERROR) << "Create: Could not create entry.";
537 return error;
538 }
539 case syncer::WriteNode::INIT_FAILED_SET_PREDECESSOR: {
540 syncer::SyncError error;
541 error.Reset(
542 FROM_HERE, error_prefix + "failed to set predecessor", type);
543 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
544 error.message());
545 LOG(ERROR) << "Create: Bad predecessor.";
546 return error;
547 }
548 default: {
549 syncer::SyncError error;
550 error.Reset(FROM_HERE, error_prefix + "unknown error", type);
551 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
552 error.message());
553 LOG(ERROR) << "Create: Unknown error.";
554 return error;
555 }
556 }
557 }
558 sync_node->SetTitle(change.sync_data().GetTitle());
559 SetNodeSpecifics(sync_data_local.GetSpecifics(), sync_node);
560
561 syncer::AttachmentIdList attachment_ids = sync_data_local.GetAttachmentIds();
562 SetAttachmentMetadata(attachment_ids, sync_node);
563
564 // Return any newly added attachments.
565 const syncer::AttachmentList& local_attachments_for_upload =
566 sync_data_local.GetLocalAttachmentsForUpload();
567 new_attachments->insert(new_attachments->end(),
568 local_attachments_for_upload.begin(),
569 local_attachments_for_upload.end());
570
571 if (merge_result_.get()) {
572 merge_result_->set_num_items_added(merge_result_->num_items_added() + 1);
573 }
574 return syncer::SyncError();
575 }
576 // WARNING: this code is sensitive to compiler optimizations. Be careful
577 // modifying any code around an OnSingleDatatypeUnrecoverableError call, else
578 // the compiler attempts to merge it with other calls, losing useful information
579 // in breakpad uploads.
HandleActionUpdate(const syncer::SyncChange & change,const std::string & type_str,const syncer::ModelType & type,const syncer::WriteTransaction & trans,syncer::WriteNode * sync_node,syncer::AttachmentList * new_attachments)580 syncer::SyncError GenericChangeProcessor::HandleActionUpdate(
581 const syncer::SyncChange& change,
582 const std::string& type_str,
583 const syncer::ModelType& type,
584 const syncer::WriteTransaction& trans,
585 syncer::WriteNode* sync_node,
586 syncer::AttachmentList* new_attachments) {
587 // TODO(zea): consider having this logic for all possible changes?
588
589 const syncer::SyncDataLocal sync_data_local(change.sync_data());
590 syncer::BaseNode::InitByLookupResult result =
591 sync_node->InitByClientTagLookup(sync_data_local.GetDataType(),
592 sync_data_local.GetTag());
593 if (result != syncer::BaseNode::INIT_OK) {
594 std::string error_prefix = "Failed to load " + type_str + " node. " +
595 change.location().ToString() + ", ";
596 if (result == syncer::BaseNode::INIT_FAILED_PRECONDITION) {
597 syncer::SyncError error;
598 error.Reset(FROM_HERE, error_prefix + "empty tag", type);
599 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
600 error.message());
601 LOG(ERROR) << "Update: Empty tag.";
602 return error;
603 } else if (result == syncer::BaseNode::INIT_FAILED_ENTRY_NOT_GOOD) {
604 syncer::SyncError error;
605 error.Reset(FROM_HERE, error_prefix + "bad entry", type);
606 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
607 error.message());
608 LOG(ERROR) << "Update: bad entry.";
609 return error;
610 } else if (result == syncer::BaseNode::INIT_FAILED_ENTRY_IS_DEL) {
611 syncer::SyncError error;
612 error.Reset(FROM_HERE, error_prefix + "deleted entry", type);
613 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
614 error.message());
615 LOG(ERROR) << "Update: deleted entry.";
616 return error;
617 } else {
618 syncer::Cryptographer* crypto = trans.GetCryptographer();
619 syncer::ModelTypeSet encrypted_types(trans.GetEncryptedTypes());
620 const sync_pb::EntitySpecifics& specifics =
621 sync_node->GetEntry()->GetSpecifics();
622 CHECK(specifics.has_encrypted());
623 const bool can_decrypt = crypto->CanDecrypt(specifics.encrypted());
624 const bool agreement = encrypted_types.Has(type);
625 if (!agreement && !can_decrypt) {
626 syncer::SyncError error;
627 error.Reset(FROM_HERE,
628 "Failed to load encrypted entry, missing key and "
629 "nigori mismatch for " +
630 type_str + ".",
631 type);
632 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
633 error.message());
634 LOG(ERROR) << "Update: encr case 1.";
635 return error;
636 } else if (agreement && can_decrypt) {
637 syncer::SyncError error;
638 error.Reset(FROM_HERE,
639 "Failed to load encrypted entry, we have the key "
640 "and the nigori matches (?!) for " +
641 type_str + ".",
642 type);
643 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
644 error.message());
645 LOG(ERROR) << "Update: encr case 2.";
646 return error;
647 } else if (agreement) {
648 syncer::SyncError error;
649 error.Reset(FROM_HERE,
650 "Failed to load encrypted entry, missing key and "
651 "the nigori matches for " +
652 type_str + ".",
653 type);
654 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
655 error.message());
656 LOG(ERROR) << "Update: encr case 3.";
657 return error;
658 } else {
659 syncer::SyncError error;
660 error.Reset(FROM_HERE,
661 "Failed to load encrypted entry, we have the key"
662 "(?!) and nigori mismatch for " +
663 type_str + ".",
664 type);
665 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
666 error.message());
667 LOG(ERROR) << "Update: encr case 4.";
668 return error;
669 }
670 }
671 }
672
673 sync_node->SetTitle(change.sync_data().GetTitle());
674 SetNodeSpecifics(sync_data_local.GetSpecifics(), sync_node);
675 SetAttachmentMetadata(sync_data_local.GetAttachmentIds(), sync_node);
676
677 // Return any newly added attachments.
678 const syncer::AttachmentList& local_attachments_for_upload =
679 sync_data_local.GetLocalAttachmentsForUpload();
680 new_attachments->insert(new_attachments->end(),
681 local_attachments_for_upload.begin(),
682 local_attachments_for_upload.end());
683
684 if (merge_result_.get()) {
685 merge_result_->set_num_items_modified(merge_result_->num_items_modified() +
686 1);
687 }
688 // TODO(sync): Support updating other parts of the sync node (title,
689 // successor, parent, etc.).
690 return syncer::SyncError();
691 }
692
SyncModelHasUserCreatedNodes(syncer::ModelType type,bool * has_nodes)693 bool GenericChangeProcessor::SyncModelHasUserCreatedNodes(
694 syncer::ModelType type,
695 bool* has_nodes) {
696 DCHECK(CalledOnValidThread());
697 DCHECK(has_nodes);
698 DCHECK_NE(type, syncer::UNSPECIFIED);
699 std::string type_name = syncer::ModelTypeToString(type);
700 std::string err_str = "Server did not create the top-level " + type_name +
701 " node. We might be running against an out-of-date server.";
702 *has_nodes = false;
703 syncer::ReadTransaction trans(FROM_HERE, share_handle());
704 syncer::ReadNode type_root_node(&trans);
705 if (type_root_node.InitTypeRoot(type) != syncer::BaseNode::INIT_OK) {
706 LOG(ERROR) << err_str;
707 return false;
708 }
709
710 // The sync model has user created nodes if the type's root node has any
711 // children.
712 *has_nodes = type_root_node.HasChildren();
713 return true;
714 }
715
CryptoReadyIfNecessary(syncer::ModelType type)716 bool GenericChangeProcessor::CryptoReadyIfNecessary(syncer::ModelType type) {
717 DCHECK(CalledOnValidThread());
718 DCHECK_NE(type, syncer::UNSPECIFIED);
719 // We only access the cryptographer while holding a transaction.
720 syncer::ReadTransaction trans(FROM_HERE, share_handle());
721 const syncer::ModelTypeSet encrypted_types = trans.GetEncryptedTypes();
722 return !encrypted_types.Has(type) ||
723 trans.GetCryptographer()->is_ready();
724 }
725
StartImpl()726 void GenericChangeProcessor::StartImpl() {
727 }
728
share_handle() const729 syncer::UserShare* GenericChangeProcessor::share_handle() const {
730 DCHECK(CalledOnValidThread());
731 return share_handle_;
732 }
733
734 } // namespace browser_sync
735