Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / sync / internal_api / write_node.cc
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.
4
5 #include "sync/internal_api/public/write_node.h"
6
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"
25
26 using std::string;
27 using std::vector;
28
29 namespace syncer {
30
31 using syncable::kEncryptedString;
32 using syncable::SPECIFICS;
33
34 static const char kDefaultNameForNewNodes[] = " ";
35
36 void WriteNode::SetIsFolder(bool folder) {
37   if (entry_->GetIsDir() == folder)
38     return;  // Skip redundant changes.
39
40   entry_->PutIsDir(folder);
41   MarkForSyncing();
42 }
43
44 void WriteNode::SetTitle(const std::wstring& 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();
51
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     SyncAPINameToServerName(base::WideToUTF8(title), &new_legal_title);
61     base::TruncateUTF8ToByteSize(new_legal_title, 255, &new_legal_title);
62   }
63
64   std::string current_legal_title;
65   if (BOOKMARKS == type &&
66       entry_->GetSpecifics().has_encrypted()) {
67     // Encrypted bookmarks only have their title in the unencrypted specifics.
68     current_legal_title = GetBookmarkSpecifics().title();
69   } else {
70     // Non-bookmarks and legacy bookmarks (those with no title in their
71     // specifics) store their title in NON_UNIQUE_NAME. Non-legacy bookmarks
72     // store their title in specifics as well as NON_UNIQUE_NAME.
73     current_legal_title = entry_->GetNonUniqueName();
74   }
75
76   bool title_matches = (current_legal_title == new_legal_title);
77   bool encrypted_without_overwriting_name = (needs_encryption &&
78       entry_->GetNonUniqueName() != kEncryptedString);
79
80   // If the title matches and the NON_UNIQUE_NAME is properly overwritten as
81   // necessary, nothing needs to change.
82   if (title_matches && !encrypted_without_overwriting_name) {
83     DVLOG(2) << "Title matches, dropping change.";
84     return;
85   }
86
87   // For bookmarks, we also set the title field in the specifics.
88   // TODO(zea): refactor bookmarks to not need this functionality.
89   if (GetModelType() == BOOKMARKS) {
90     sync_pb::EntitySpecifics specifics = GetEntitySpecifics();
91     specifics.mutable_bookmark()->set_title(new_legal_title);
92     SetEntitySpecifics(specifics);  // Does it's own encryption checking.
93   }
94
95   // For bookmarks, this has to happen after we set the title in the specifics,
96   // because the presence of a title in the NON_UNIQUE_NAME is what controls
97   // the logic deciding whether this is an empty node or a legacy bookmark.
98   // See BaseNode::GetUnencryptedSpecific(..).
99   if (needs_encryption)
100     entry_->PutNonUniqueName(kEncryptedString);
101   else
102     entry_->PutNonUniqueName(new_legal_title);
103
104   DVLOG(1) << "Overwriting title of type "
105            << ModelTypeToString(type)
106            << " and marking for syncing.";
107   MarkForSyncing();
108 }
109
110 void WriteNode::SetAppSpecifics(
111     const sync_pb::AppSpecifics& new_value) {
112   sync_pb::EntitySpecifics entity_specifics;
113   entity_specifics.mutable_app()->CopyFrom(new_value);
114   SetEntitySpecifics(entity_specifics);
115 }
116
117 void WriteNode::SetAutofillSpecifics(
118     const sync_pb::AutofillSpecifics& new_value) {
119   sync_pb::EntitySpecifics entity_specifics;
120   entity_specifics.mutable_autofill()->CopyFrom(new_value);
121   SetEntitySpecifics(entity_specifics);
122 }
123
124 void WriteNode::SetAutofillProfileSpecifics(
125     const sync_pb::AutofillProfileSpecifics& new_value) {
126   sync_pb::EntitySpecifics entity_specifics;
127   entity_specifics.mutable_autofill_profile()->
128       CopyFrom(new_value);
129   SetEntitySpecifics(entity_specifics);
130 }
131
132 void WriteNode::SetBookmarkSpecifics(
133     const sync_pb::BookmarkSpecifics& new_value) {
134   sync_pb::EntitySpecifics entity_specifics;
135   entity_specifics.mutable_bookmark()->CopyFrom(new_value);
136   SetEntitySpecifics(entity_specifics);
137 }
138
139 void WriteNode::SetNigoriSpecifics(
140     const sync_pb::NigoriSpecifics& new_value) {
141   sync_pb::EntitySpecifics entity_specifics;
142   entity_specifics.mutable_nigori()->CopyFrom(new_value);
143   SetEntitySpecifics(entity_specifics);
144 }
145
146 void WriteNode::SetPasswordSpecifics(
147     const sync_pb::PasswordSpecificsData& data) {
148   DCHECK_EQ(GetModelType(), PASSWORDS);
149
150   Cryptographer* cryptographer = GetTransaction()->GetCryptographer();
151
152   // We have to do the idempotency check here (vs in UpdateEntryWithEncryption)
153   // because Passwords have their encrypted data within the PasswordSpecifics,
154   // vs within the EntitySpecifics like all the other types.
155   const sync_pb::EntitySpecifics& old_specifics = GetEntry()->GetSpecifics();
156   sync_pb::EntitySpecifics entity_specifics;
157   // Copy over the old specifics if they exist.
158   if (GetModelTypeFromSpecifics(old_specifics) == PASSWORDS) {
159     entity_specifics.CopyFrom(old_specifics);
160   } else {
161     AddDefaultFieldValue(PASSWORDS, &entity_specifics);
162   }
163   sync_pb::PasswordSpecifics* password_specifics =
164       entity_specifics.mutable_password();
165   // This will only update password_specifics if the underlying unencrypted blob
166   // was different from |data| or was not encrypted with the proper passphrase.
167   if (!cryptographer->Encrypt(data, password_specifics->mutable_encrypted())) {
168     NOTREACHED() << "Failed to encrypt password, possibly due to sync node "
169                  << "corruption";
170     return;
171   }
172   SetEntitySpecifics(entity_specifics);
173 }
174
175 void WriteNode::SetThemeSpecifics(
176     const sync_pb::ThemeSpecifics& new_value) {
177   sync_pb::EntitySpecifics entity_specifics;
178   entity_specifics.mutable_theme()->CopyFrom(new_value);
179   SetEntitySpecifics(entity_specifics);
180 }
181
182 void WriteNode::SetSessionSpecifics(
183     const sync_pb::SessionSpecifics& new_value) {
184   sync_pb::EntitySpecifics entity_specifics;
185   entity_specifics.mutable_session()->CopyFrom(new_value);
186   SetEntitySpecifics(entity_specifics);
187 }
188
189 void WriteNode::SetDeviceInfoSpecifics(
190     const sync_pb::DeviceInfoSpecifics& new_value) {
191   sync_pb::EntitySpecifics entity_specifics;
192   entity_specifics.mutable_device_info()->CopyFrom(new_value);
193   SetEntitySpecifics(entity_specifics);
194 }
195
196 void WriteNode::SetExperimentsSpecifics(
197     const sync_pb::ExperimentsSpecifics& new_value) {
198   sync_pb::EntitySpecifics entity_specifics;
199   entity_specifics.mutable_experiments()->CopyFrom(new_value);
200   SetEntitySpecifics(entity_specifics);
201 }
202
203 void WriteNode::SetPriorityPreferenceSpecifics(
204     const sync_pb::PriorityPreferenceSpecifics& new_value) {
205   sync_pb::EntitySpecifics entity_specifics;
206   entity_specifics.mutable_priority_preference()->CopyFrom(new_value);
207   SetEntitySpecifics(entity_specifics);
208 }
209
210 void WriteNode::SetEntitySpecifics(
211     const sync_pb::EntitySpecifics& new_value) {
212   ModelType new_specifics_type =
213       GetModelTypeFromSpecifics(new_value);
214   CHECK(!new_value.password().has_client_only_encrypted_data());
215   DCHECK_NE(new_specifics_type, UNSPECIFIED);
216   DVLOG(1) << "Writing entity specifics of type "
217            << ModelTypeToString(new_specifics_type);
218   DCHECK_EQ(new_specifics_type, GetModelType());
219
220   // Preserve unknown fields.
221   const sync_pb::EntitySpecifics& old_specifics = entry_->GetSpecifics();
222   sync_pb::EntitySpecifics new_specifics;
223   new_specifics.CopyFrom(new_value);
224   new_specifics.mutable_unknown_fields()->MergeFrom(
225       old_specifics.unknown_fields());
226
227   // Will update the entry if encryption was necessary.
228   if (!UpdateEntryWithEncryption(GetTransaction()->GetWrappedTrans(),
229                                  new_specifics,
230                                  entry_)) {
231     return;
232   }
233   if (entry_->GetSpecifics().has_encrypted()) {
234     // EncryptIfNecessary already updated the entry for us and marked for
235     // syncing if it was needed. Now we just make a copy of the unencrypted
236     // specifics so that if this node is updated, we do not have to decrypt the
237     // old data. Note that this only modifies the node's local data, not the
238     // entry itself.
239     SetUnencryptedSpecifics(new_value);
240   }
241
242   DCHECK_EQ(new_specifics_type, GetModelType());
243 }
244
245 void WriteNode::ResetFromSpecifics() {
246   SetEntitySpecifics(GetEntitySpecifics());
247 }
248
249 void WriteNode::SetTypedUrlSpecifics(
250     const sync_pb::TypedUrlSpecifics& new_value) {
251   sync_pb::EntitySpecifics entity_specifics;
252   entity_specifics.mutable_typed_url()->CopyFrom(new_value);
253   SetEntitySpecifics(entity_specifics);
254 }
255
256 void WriteNode::SetExtensionSpecifics(
257     const sync_pb::ExtensionSpecifics& new_value) {
258   sync_pb::EntitySpecifics entity_specifics;
259   entity_specifics.mutable_extension()->CopyFrom(new_value);
260   SetEntitySpecifics(entity_specifics);
261 }
262
263 void WriteNode::SetExternalId(int64 id) {
264   if (GetExternalId() != id)
265     entry_->PutLocalExternalId(id);
266 }
267
268 WriteNode::WriteNode(WriteTransaction* transaction)
269     : entry_(NULL), transaction_(transaction) {
270   DCHECK(transaction);
271 }
272
273 WriteNode::~WriteNode() {
274   delete entry_;
275 }
276
277 // Find an existing node matching the ID |id|, and bind this WriteNode to it.
278 // Return true on success.
279 BaseNode::InitByLookupResult WriteNode::InitByIdLookup(int64 id) {
280   DCHECK(!entry_) << "Init called twice";
281   DCHECK_NE(id, kInvalidId);
282   entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
283                                       syncable::GET_BY_HANDLE, id);
284   if (!entry_->good())
285     return INIT_FAILED_ENTRY_NOT_GOOD;
286   if (entry_->GetIsDel())
287     return INIT_FAILED_ENTRY_IS_DEL;
288   return DecryptIfNecessary() ? INIT_OK : INIT_FAILED_DECRYPT_IF_NECESSARY;
289 }
290
291 // Find a node by client tag, and bind this WriteNode to it.
292 // Return true if the write node was found, and was not deleted.
293 // Undeleting a deleted node is possible by ClientTag.
294 BaseNode::InitByLookupResult WriteNode::InitByClientTagLookup(
295     ModelType model_type,
296     const std::string& tag) {
297   DCHECK(!entry_) << "Init called twice";
298   if (tag.empty())
299     return INIT_FAILED_PRECONDITION;
300
301   const std::string hash = syncable::GenerateSyncableHash(model_type, tag);
302
303   entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
304                                       syncable::GET_BY_CLIENT_TAG, hash);
305   if (!entry_->good())
306     return INIT_FAILED_ENTRY_NOT_GOOD;
307   if (entry_->GetIsDel())
308     return INIT_FAILED_ENTRY_IS_DEL;
309   return DecryptIfNecessary() ? INIT_OK : INIT_FAILED_DECRYPT_IF_NECESSARY;
310 }
311
312 BaseNode::InitByLookupResult WriteNode::InitByTagLookup(
313     const std::string& tag) {
314   DCHECK(!entry_) << "Init called twice";
315   if (tag.empty())
316     return INIT_FAILED_PRECONDITION;
317   entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
318                                       syncable::GET_BY_SERVER_TAG, tag);
319   if (!entry_->good())
320     return INIT_FAILED_ENTRY_NOT_GOOD;
321   if (entry_->GetIsDel())
322     return INIT_FAILED_ENTRY_IS_DEL;
323   ModelType model_type = GetModelType();
324   DCHECK_EQ(model_type, NIGORI);
325   return INIT_OK;
326 }
327
328 // Create a new node with default properties, and bind this WriteNode to it.
329 // Return true on success.
330 bool WriteNode::InitBookmarkByCreation(const BaseNode& parent,
331                                        const BaseNode* predecessor) {
332   DCHECK(!entry_) << "Init called twice";
333   // |predecessor| must be a child of |parent| or NULL.
334   if (predecessor && predecessor->GetParentId() != parent.GetId()) {
335     DCHECK(false);
336     return false;
337   }
338
339   syncable::Id parent_id = parent.GetEntry()->GetId();
340
341   // Start out with a dummy name.  We expect
342   // the caller to set a meaningful name after creation.
343   string dummy(kDefaultNameForNewNodes);
344
345   entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
346                                       syncable::CREATE, BOOKMARKS,
347                                       parent_id, dummy);
348
349   if (!entry_->good())
350     return false;
351
352   // Entries are untitled folders by default.
353   entry_->PutIsDir(true);
354
355   // Now set the predecessor, which sets IS_UNSYNCED as necessary.
356   return PutPredecessor(predecessor);
357 }
358
359 // Create a new node with default properties and a client defined unique tag,
360 // and bind this WriteNode to it.
361 // Return true on success. If the tag exists in the database, then
362 // we will attempt to undelete the node.
363 // TODO(chron): Code datatype into hash tag.
364 // TODO(chron): Is model type ever lost?
365 WriteNode::InitUniqueByCreationResult WriteNode::InitUniqueByCreation(
366     ModelType model_type,
367     const BaseNode& parent,
368     const std::string& tag) {
369   // This DCHECK will only fail if init is called twice.
370   DCHECK(!entry_);
371   if (tag.empty()) {
372     LOG(WARNING) << "InitUniqueByCreation failed due to empty tag.";
373     return INIT_FAILED_EMPTY_TAG;
374   }
375
376   const std::string hash = syncable::GenerateSyncableHash(model_type, tag);
377
378   syncable::Id parent_id = parent.GetEntry()->GetId();
379
380   // Start out with a dummy name.  We expect
381   // the caller to set a meaningful name after creation.
382   string dummy(kDefaultNameForNewNodes);
383
384   // Check if we have this locally and need to undelete it.
385   scoped_ptr<syncable::MutableEntry> existing_entry(
386       new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
387                                  syncable::GET_BY_CLIENT_TAG, hash));
388
389   if (existing_entry->good()) {
390     if (existing_entry->GetIsDel()) {
391       // Rules for undelete:
392       // BASE_VERSION: Must keep the same.
393       // ID: Essential to keep the same.
394       // META_HANDLE: Must be the same, so we can't "split" the entry.
395       // IS_DEL: Must be set to false, will cause reindexing.
396       //         This one is weird because IS_DEL is true for "update only"
397       //         items. It should be OK to undelete an update only.
398       // MTIME/CTIME: Seems reasonable to just leave them alone.
399       // IS_UNSYNCED: Must set this to true or face database insurrection.
400       //              We do this below this block.
401       // IS_UNAPPLIED_UPDATE: Either keep it the same or also set BASE_VERSION
402       //                      to SERVER_VERSION. We keep it the same here.
403       // IS_DIR: We'll leave it the same.
404       // SPECIFICS: Reset it.
405
406       existing_entry->PutIsDel(false);
407
408       // Client tags are immutable and must be paired with the ID.
409       // If a server update comes down with an ID and client tag combo,
410       // and it already exists, always overwrite it and store only one copy.
411       // We have to undelete entries because we can't disassociate IDs from
412       // tags and updates.
413
414       existing_entry->PutNonUniqueName(dummy);
415       existing_entry->PutParentId(parent_id);
416       entry_ = existing_entry.release();
417     } else {
418       return INIT_FAILED_ENTRY_ALREADY_EXISTS;
419     }
420   } else {
421     entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
422                                         syncable::CREATE,
423                                         model_type, parent_id, dummy);
424     if (!entry_->good())
425       return INIT_FAILED_COULD_NOT_CREATE_ENTRY;
426
427     // Only set IS_DIR for new entries. Don't bitflip undeleted ones.
428     entry_->PutUniqueClientTag(hash);
429   }
430
431   // We don't support directory and tag combinations.
432   entry_->PutIsDir(false);
433
434   // Now set the predecessor, which sets IS_UNSYNCED as necessary.
435   bool success = PutPredecessor(NULL);
436   if (!success)
437     return INIT_FAILED_SET_PREDECESSOR;
438
439   return INIT_SUCCESS;
440 }
441
442 bool WriteNode::SetPosition(const BaseNode& new_parent,
443                             const BaseNode* predecessor) {
444   // |predecessor| must be a child of |new_parent| or NULL.
445   if (predecessor && predecessor->GetParentId() != new_parent.GetId()) {
446     DCHECK(false);
447     return false;
448   }
449
450   syncable::Id new_parent_id = new_parent.GetEntry()->GetId();
451
452   // Filter out redundant changes if both the parent and the predecessor match.
453   if (new_parent_id == entry_->GetParentId()) {
454     const syncable::Id& old = entry_->GetPredecessorId();
455     if ((!predecessor && old.IsRoot()) ||
456         (predecessor && (old == predecessor->GetEntry()->GetId()))) {
457       return true;
458     }
459   }
460
461   entry_->PutParentId(new_parent_id);
462
463   // Now set the predecessor, which sets IS_UNSYNCED as necessary.
464   return PutPredecessor(predecessor);
465 }
466
467 const syncable::Entry* WriteNode::GetEntry() const {
468   return entry_;
469 }
470
471 const BaseTransaction* WriteNode::GetTransaction() const {
472   return transaction_;
473 }
474
475 syncable::MutableEntry* WriteNode::GetMutableEntryForTest() {
476   return entry_;
477 }
478
479 void WriteNode::Tombstone() {
480   // These lines must be in this order.  The call to Put(IS_DEL) might choose to
481   // unset the IS_UNSYNCED bit if the item was not known to the server at the
482   // time of deletion.  It's important that the bit not be reset in that case.
483   MarkForSyncing();
484   entry_->PutIsDel(true);
485 }
486
487 void WriteNode::Drop() {
488   if (entry_->GetId().ServerKnows()) {
489     entry_->PutIsDel(true);
490   }
491 }
492
493 bool WriteNode::PutPredecessor(const BaseNode* predecessor) {
494   syncable::Id predecessor_id = predecessor ?
495       predecessor->GetEntry()->GetId() : syncable::Id();
496   if (!entry_->PutPredecessor(predecessor_id))
497     return false;
498   // Mark this entry as unsynced, to wake up the syncer.
499   MarkForSyncing();
500
501   return true;
502 }
503
504 void WriteNode::MarkForSyncing() {
505   syncable::MarkForSyncing(entry_);
506 }
507
508 }  // namespace syncer