Don't show supervised user as "already on this device" while they're being imported.
[chromium-blink-merge.git] / sync / internal_api / write_node.cc
blobcefe8547e12108a4200fd466a59312798bf5496a
1 // Copyright (c) 2012 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.
5 #include "sync/internal_api/public/write_node.h"
7 #include "base/strings/string_util.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "base/values.h"
10 #include "sync/internal_api/public/base_transaction.h"
11 #include "sync/internal_api/public/write_transaction.h"
12 #include "sync/internal_api/syncapi_internal.h"
13 #include "sync/protocol/app_specifics.pb.h"
14 #include "sync/protocol/autofill_specifics.pb.h"
15 #include "sync/protocol/bookmark_specifics.pb.h"
16 #include "sync/protocol/extension_specifics.pb.h"
17 #include "sync/protocol/password_specifics.pb.h"
18 #include "sync/protocol/session_specifics.pb.h"
19 #include "sync/protocol/theme_specifics.pb.h"
20 #include "sync/protocol/typed_url_specifics.pb.h"
21 #include "sync/syncable/mutable_entry.h"
22 #include "sync/syncable/nigori_util.h"
23 #include "sync/syncable/syncable_util.h"
24 #include "sync/util/cryptographer.h"
26 using std::string;
27 using std::vector;
29 namespace syncer {
31 using syncable::kEncryptedString;
32 using syncable::SPECIFICS;
34 static const char kDefaultNameForNewNodes[] = " ";
36 void WriteNode::SetIsFolder(bool folder) {
37 if (entry_->GetIsDir() == folder)
38 return; // Skip redundant changes.
40 entry_->PutIsDir(folder);
41 MarkForSyncing();
44 void WriteNode::SetTitle(const std::string& title) {
45 DCHECK_NE(GetModelType(), UNSPECIFIED);
46 ModelType type = GetModelType();
47 // It's possible the nigori lost the set of encrypted types. If the current
48 // specifics are already encrypted, we want to ensure we continue encrypting.
49 bool needs_encryption = GetTransaction()->GetEncryptedTypes().Has(type) ||
50 entry_->GetSpecifics().has_encrypted();
52 // If this datatype is encrypted and is not a bookmark, we disregard the
53 // specified title in favor of kEncryptedString. For encrypted bookmarks the
54 // NON_UNIQUE_NAME will still be kEncryptedString, but we store the real title
55 // into the specifics. All strings compared are server legal strings.
56 std::string new_legal_title;
57 if (type != BOOKMARKS && needs_encryption) {
58 new_legal_title = kEncryptedString;
59 } else {
60 DCHECK(base::IsStringUTF8(title));
61 SyncAPINameToServerName(title, &new_legal_title);
62 base::TruncateUTF8ToByteSize(new_legal_title, 255, &new_legal_title);
65 std::string current_legal_title;
66 if (BOOKMARKS == type &&
67 entry_->GetSpecifics().has_encrypted()) {
68 // Encrypted bookmarks only have their title in the unencrypted specifics.
69 current_legal_title = GetBookmarkSpecifics().title();
70 } else {
71 // Non-bookmarks and legacy bookmarks (those with no title in their
72 // specifics) store their title in NON_UNIQUE_NAME. Non-legacy bookmarks
73 // store their title in specifics as well as NON_UNIQUE_NAME.
74 current_legal_title = entry_->GetNonUniqueName();
77 bool title_matches = (current_legal_title == new_legal_title);
78 bool encrypted_without_overwriting_name = (needs_encryption &&
79 entry_->GetNonUniqueName() != kEncryptedString);
81 // For bookmarks, we also set the title field in the specifics.
82 // TODO(zea): refactor bookmarks to not need this functionality.
83 sync_pb::EntitySpecifics specifics = GetEntitySpecifics();
84 if (GetModelType() == BOOKMARKS &&
85 specifics.bookmark().title() != new_legal_title) {
86 specifics.mutable_bookmark()->set_title(new_legal_title);
87 SetEntitySpecifics(specifics); // Does it's own encryption checking.
88 title_matches = false;
91 // If the title matches and the NON_UNIQUE_NAME is properly overwritten as
92 // necessary, nothing needs to change.
93 if (title_matches && !encrypted_without_overwriting_name) {
94 DVLOG(2) << "Title matches, dropping change.";
95 return;
98 // For bookmarks, this has to happen after we set the title in the specifics,
99 // because the presence of a title in the NON_UNIQUE_NAME is what controls
100 // the logic deciding whether this is an empty node or a legacy bookmark.
101 // See BaseNode::GetUnencryptedSpecific(..).
102 if (needs_encryption)
103 entry_->PutNonUniqueName(kEncryptedString);
104 else
105 entry_->PutNonUniqueName(new_legal_title);
107 DVLOG(1) << "Overwriting title of type "
108 << ModelTypeToString(type)
109 << " and marking for syncing.";
110 MarkForSyncing();
113 void WriteNode::SetAppSpecifics(
114 const sync_pb::AppSpecifics& new_value) {
115 sync_pb::EntitySpecifics entity_specifics;
116 entity_specifics.mutable_app()->CopyFrom(new_value);
117 SetEntitySpecifics(entity_specifics);
120 void WriteNode::SetAutofillSpecifics(
121 const sync_pb::AutofillSpecifics& new_value) {
122 sync_pb::EntitySpecifics entity_specifics;
123 entity_specifics.mutable_autofill()->CopyFrom(new_value);
124 SetEntitySpecifics(entity_specifics);
127 void WriteNode::SetAutofillProfileSpecifics(
128 const sync_pb::AutofillProfileSpecifics& new_value) {
129 sync_pb::EntitySpecifics entity_specifics;
130 entity_specifics.mutable_autofill_profile()->
131 CopyFrom(new_value);
132 SetEntitySpecifics(entity_specifics);
135 void WriteNode::SetBookmarkSpecifics(
136 const sync_pb::BookmarkSpecifics& new_value) {
137 sync_pb::EntitySpecifics entity_specifics;
138 entity_specifics.mutable_bookmark()->CopyFrom(new_value);
139 SetEntitySpecifics(entity_specifics);
142 void WriteNode::SetNigoriSpecifics(
143 const sync_pb::NigoriSpecifics& new_value) {
144 sync_pb::EntitySpecifics entity_specifics;
145 entity_specifics.mutable_nigori()->CopyFrom(new_value);
146 SetEntitySpecifics(entity_specifics);
149 void WriteNode::SetPasswordSpecifics(
150 const sync_pb::PasswordSpecificsData& data) {
151 DCHECK_EQ(GetModelType(), PASSWORDS);
153 Cryptographer* cryptographer = GetTransaction()->GetCryptographer();
155 // We have to do the idempotency check here (vs in UpdateEntryWithEncryption)
156 // because Passwords have their encrypted data within the PasswordSpecifics,
157 // vs within the EntitySpecifics like all the other types.
158 const sync_pb::EntitySpecifics& old_specifics = GetEntry()->GetSpecifics();
159 sync_pb::EntitySpecifics entity_specifics;
160 // Copy over the old specifics if they exist.
161 if (GetModelTypeFromSpecifics(old_specifics) == PASSWORDS) {
162 entity_specifics.CopyFrom(old_specifics);
163 } else {
164 AddDefaultFieldValue(PASSWORDS, &entity_specifics);
166 sync_pb::PasswordSpecifics* password_specifics =
167 entity_specifics.mutable_password();
168 // This will only update password_specifics if the underlying unencrypted blob
169 // was different from |data| or was not encrypted with the proper passphrase.
170 if (!cryptographer->Encrypt(data, password_specifics->mutable_encrypted())) {
171 NOTREACHED() << "Failed to encrypt password, possibly due to sync node "
172 << "corruption";
173 return;
175 SetEntitySpecifics(entity_specifics);
178 void WriteNode::SetThemeSpecifics(
179 const sync_pb::ThemeSpecifics& new_value) {
180 sync_pb::EntitySpecifics entity_specifics;
181 entity_specifics.mutable_theme()->CopyFrom(new_value);
182 SetEntitySpecifics(entity_specifics);
185 void WriteNode::SetSessionSpecifics(
186 const sync_pb::SessionSpecifics& new_value) {
187 sync_pb::EntitySpecifics entity_specifics;
188 entity_specifics.mutable_session()->CopyFrom(new_value);
189 SetEntitySpecifics(entity_specifics);
192 void WriteNode::SetDeviceInfoSpecifics(
193 const sync_pb::DeviceInfoSpecifics& new_value) {
194 sync_pb::EntitySpecifics entity_specifics;
195 entity_specifics.mutable_device_info()->CopyFrom(new_value);
196 SetEntitySpecifics(entity_specifics);
199 void WriteNode::SetExperimentsSpecifics(
200 const sync_pb::ExperimentsSpecifics& new_value) {
201 sync_pb::EntitySpecifics entity_specifics;
202 entity_specifics.mutable_experiments()->CopyFrom(new_value);
203 SetEntitySpecifics(entity_specifics);
206 void WriteNode::SetPriorityPreferenceSpecifics(
207 const sync_pb::PriorityPreferenceSpecifics& new_value) {
208 sync_pb::EntitySpecifics entity_specifics;
209 entity_specifics.mutable_priority_preference()->CopyFrom(new_value);
210 SetEntitySpecifics(entity_specifics);
213 void WriteNode::SetEntitySpecifics(
214 const sync_pb::EntitySpecifics& new_value) {
215 ModelType new_specifics_type =
216 GetModelTypeFromSpecifics(new_value);
217 CHECK(!new_value.password().has_client_only_encrypted_data());
218 DCHECK_NE(new_specifics_type, UNSPECIFIED);
219 DVLOG(1) << "Writing entity specifics of type "
220 << ModelTypeToString(new_specifics_type);
221 DCHECK_EQ(new_specifics_type, GetModelType());
223 // Preserve unknown fields.
224 const sync_pb::EntitySpecifics& old_specifics = entry_->GetSpecifics();
225 sync_pb::EntitySpecifics new_specifics;
226 new_specifics.CopyFrom(new_value);
227 new_specifics.mutable_unknown_fields()->MergeFrom(
228 old_specifics.unknown_fields());
230 // Will update the entry if encryption was necessary.
231 if (!UpdateEntryWithEncryption(GetTransaction()->GetWrappedTrans(),
232 new_specifics,
233 entry_)) {
234 return;
236 if (entry_->GetSpecifics().has_encrypted()) {
237 // EncryptIfNecessary already updated the entry for us and marked for
238 // syncing if it was needed. Now we just make a copy of the unencrypted
239 // specifics so that if this node is updated, we do not have to decrypt the
240 // old data. Note that this only modifies the node's local data, not the
241 // entry itself.
242 SetUnencryptedSpecifics(new_value);
245 DCHECK_EQ(new_specifics_type, GetModelType());
248 void WriteNode::ResetFromSpecifics() {
249 SetEntitySpecifics(GetEntitySpecifics());
252 void WriteNode::SetTypedUrlSpecifics(
253 const sync_pb::TypedUrlSpecifics& new_value) {
254 sync_pb::EntitySpecifics entity_specifics;
255 entity_specifics.mutable_typed_url()->CopyFrom(new_value);
256 SetEntitySpecifics(entity_specifics);
259 void WriteNode::SetExtensionSpecifics(
260 const sync_pb::ExtensionSpecifics& new_value) {
261 sync_pb::EntitySpecifics entity_specifics;
262 entity_specifics.mutable_extension()->CopyFrom(new_value);
263 SetEntitySpecifics(entity_specifics);
266 void WriteNode::SetExternalId(int64 id) {
267 if (GetExternalId() != id)
268 entry_->PutLocalExternalId(id);
271 WriteNode::WriteNode(WriteTransaction* transaction)
272 : entry_(NULL), transaction_(transaction) {
273 DCHECK(transaction);
276 WriteNode::~WriteNode() {
277 delete entry_;
280 // Find an existing node matching the ID |id|, and bind this WriteNode to it.
281 // Return true on success.
282 BaseNode::InitByLookupResult WriteNode::InitByIdLookup(int64 id) {
283 DCHECK(!entry_) << "Init called twice";
284 DCHECK_NE(id, kInvalidId);
285 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
286 syncable::GET_BY_HANDLE, id);
287 if (!entry_->good())
288 return INIT_FAILED_ENTRY_NOT_GOOD;
289 if (entry_->GetIsDel())
290 return INIT_FAILED_ENTRY_IS_DEL;
291 return DecryptIfNecessary() ? INIT_OK : INIT_FAILED_DECRYPT_IF_NECESSARY;
294 // Find a node by client tag, and bind this WriteNode to it.
295 // Return true if the write node was found, and was not deleted.
296 // Undeleting a deleted node is possible by ClientTag.
297 BaseNode::InitByLookupResult WriteNode::InitByClientTagLookup(
298 ModelType model_type,
299 const std::string& tag) {
300 DCHECK(!entry_) << "Init called twice";
301 if (tag.empty())
302 return INIT_FAILED_PRECONDITION;
304 const std::string hash = syncable::GenerateSyncableHash(model_type, tag);
306 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
307 syncable::GET_BY_CLIENT_TAG, hash);
308 if (!entry_->good())
309 return INIT_FAILED_ENTRY_NOT_GOOD;
310 if (entry_->GetIsDel())
311 return INIT_FAILED_ENTRY_IS_DEL;
312 return DecryptIfNecessary() ? INIT_OK : INIT_FAILED_DECRYPT_IF_NECESSARY;
315 BaseNode::InitByLookupResult WriteNode::InitTypeRoot(ModelType type) {
316 DCHECK(!entry_) << "Init called twice";
317 if (!IsRealDataType(type))
318 return INIT_FAILED_PRECONDITION;
319 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
320 syncable::GET_TYPE_ROOT, type);
321 if (!entry_->good())
322 return INIT_FAILED_ENTRY_NOT_GOOD;
323 if (entry_->GetIsDel())
324 return INIT_FAILED_ENTRY_IS_DEL;
325 ModelType model_type = GetModelType();
326 DCHECK_EQ(model_type, NIGORI);
327 return INIT_OK;
330 // Create a new node with default properties, and bind this WriteNode to it.
331 // Return true on success.
332 bool WriteNode::InitBookmarkByCreation(const BaseNode& parent,
333 const BaseNode* predecessor) {
334 DCHECK(!entry_) << "Init called twice";
335 // |predecessor| must be a child of |parent| or NULL.
336 if (predecessor && predecessor->GetParentId() != parent.GetId()) {
337 DCHECK(false);
338 return false;
341 syncable::Id parent_id = parent.GetEntry()->GetId();
342 DCHECK(!parent_id.IsNull());
344 // Start out with a dummy name. We expect
345 // the caller to set a meaningful name after creation.
346 string dummy(kDefaultNameForNewNodes);
348 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
349 syncable::CREATE, BOOKMARKS,
350 parent_id, dummy);
352 if (!entry_->good())
353 return false;
355 // Entries are untitled folders by default.
356 entry_->PutIsDir(true);
358 if (!PutPredecessor(predecessor)) {
359 return false;
362 // Mark this entry as unsynced, to wake up the syncer.
363 MarkForSyncing();
364 return true;
367 WriteNode::InitUniqueByCreationResult WriteNode::InitUniqueByCreation(
368 ModelType model_type,
369 const BaseNode& parent,
370 const std::string& tag) {
371 return InitUniqueByCreationImpl(model_type, parent.GetEntry()->GetId(), tag);
374 WriteNode::InitUniqueByCreationResult WriteNode::InitUniqueByCreation(
375 ModelType model_type,
376 const std::string& tag) {
377 return InitUniqueByCreationImpl(model_type, syncable::Id(), tag);
380 // Create a new node with default properties and a client defined unique tag,
381 // and bind this WriteNode to it.
382 // Return true on success. If the tag exists in the database, then
383 // we will attempt to undelete the node.
384 // TODO(chron): Code datatype into hash tag.
385 // TODO(chron): Is model type ever lost?
386 WriteNode::InitUniqueByCreationResult WriteNode::InitUniqueByCreationImpl(
387 ModelType model_type,
388 const syncable::Id& parent_id,
389 const std::string& tag) {
390 // This DCHECK will only fail if init is called twice.
391 DCHECK(!entry_);
392 if (tag.empty()) {
393 LOG(WARNING) << "InitUniqueByCreation failed due to empty tag.";
394 return INIT_FAILED_EMPTY_TAG;
397 const std::string hash = syncable::GenerateSyncableHash(model_type, tag);
399 // Start out with a dummy name. We expect
400 // the caller to set a meaningful name after creation.
401 string dummy(kDefaultNameForNewNodes);
403 // Check if we have this locally and need to undelete it.
404 scoped_ptr<syncable::MutableEntry> existing_entry(
405 new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
406 syncable::GET_BY_CLIENT_TAG, hash));
408 if (existing_entry->good()) {
409 if (existing_entry->GetIsDel()) {
410 // Rules for undelete:
411 // BASE_VERSION: Must keep the same.
412 // ID: Essential to keep the same.
413 // META_HANDLE: Must be the same, so we can't "split" the entry.
414 // IS_DEL: Must be set to false, will cause reindexing.
415 // This one is weird because IS_DEL is true for "update only"
416 // items. It should be OK to undelete an update only.
417 // MTIME/CTIME: Seems reasonable to just leave them alone.
418 // IS_UNSYNCED: Must set this to true or face database insurrection.
419 // We do this below this block.
420 // IS_UNAPPLIED_UPDATE: Either keep it the same or also set BASE_VERSION
421 // to SERVER_VERSION. We keep it the same here.
422 // IS_DIR: We'll leave it the same.
423 // SPECIFICS: Reset it.
425 existing_entry->PutIsDel(false);
427 // Client tags are immutable and must be paired with the ID.
428 // If a server update comes down with an ID and client tag combo,
429 // and it already exists, always overwrite it and store only one copy.
430 // We have to undelete entries because we can't disassociate IDs from
431 // tags and updates.
433 existing_entry->PutNonUniqueName(dummy);
434 existing_entry->PutParentId(parent_id);
436 // Put specifics to handle the case where this is not actually an
437 // undeletion, but instead a collision with a newly downloaded,
438 // processed, and unapplied server update. This is a fix for
439 // http://crbug.com/397766.
440 sync_pb::EntitySpecifics specifics;
441 AddDefaultFieldValue(model_type, &specifics);
442 existing_entry->PutSpecifics(specifics);
444 entry_ = existing_entry.release();
445 } else {
446 return INIT_FAILED_ENTRY_ALREADY_EXISTS;
448 } else {
449 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
450 syncable::CREATE,
451 model_type, parent_id, dummy);
452 if (!entry_->good())
453 return INIT_FAILED_COULD_NOT_CREATE_ENTRY;
455 // Only set IS_DIR for new entries. Don't bitflip undeleted ones.
456 entry_->PutUniqueClientTag(hash);
459 // We don't support directory and tag combinations.
460 entry_->PutIsDir(false);
462 if (!parent_id.IsNull()) {
463 if (!PutPredecessor(NULL))
464 return INIT_FAILED_SET_PREDECESSOR;
467 // Mark this entry as unsynced, to wake up the syncer.
468 MarkForSyncing();
470 return INIT_SUCCESS;
473 bool WriteNode::SetPosition(const BaseNode& new_parent,
474 const BaseNode* predecessor) {
475 // |predecessor| must be a child of |new_parent| or NULL.
476 if (predecessor && predecessor->GetParentId() != new_parent.GetId()) {
477 DCHECK(false);
478 return false;
481 syncable::Id new_parent_id = new_parent.GetEntry()->GetId();
482 DCHECK(!new_parent_id.IsNull());
484 // Filter out redundant changes if both the parent and the predecessor match.
485 if (new_parent_id == entry_->GetParentId()) {
486 const syncable::Id& old = entry_->GetPredecessorId();
487 if ((!predecessor && old.IsNull()) ||
488 (predecessor && (old == predecessor->GetEntry()->GetId()))) {
489 return true;
493 entry_->PutParentId(new_parent_id);
495 if (!PutPredecessor(predecessor)) {
496 return false;
499 // Mark this entry as unsynced, to wake up the syncer.
500 MarkForSyncing();
501 return true;
504 void WriteNode::SetAttachmentMetadata(
505 const sync_pb::AttachmentMetadata& attachment_metadata) {
506 entry_->PutAttachmentMetadata(attachment_metadata);
509 const syncable::Entry* WriteNode::GetEntry() const {
510 return entry_;
513 const BaseTransaction* WriteNode::GetTransaction() const {
514 return transaction_;
517 syncable::MutableEntry* WriteNode::GetMutableEntryForTest() {
518 return entry_;
521 void WriteNode::Tombstone() {
522 // These lines must be in this order. The call to Put(IS_DEL) might choose to
523 // unset the IS_UNSYNCED bit if the item was not known to the server at the
524 // time of deletion. It's important that the bit not be reset in that case.
525 MarkForSyncing();
526 entry_->PutIsDel(true);
529 void WriteNode::Drop() {
530 if (entry_->GetId().ServerKnows()) {
531 entry_->PutIsDel(true);
535 bool WriteNode::PutPredecessor(const BaseNode* predecessor) {
536 DCHECK(!entry_->GetParentId().IsNull());
537 syncable::Id predecessor_id = predecessor ?
538 predecessor->GetEntry()->GetId() : syncable::Id();
539 return entry_->PutPredecessor(predecessor_id);
542 void WriteNode::MarkForSyncing() {
543 syncable::MarkForSyncing(entry_);
546 } // namespace syncer