- add sources.
[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(WideToUTF8(title), &new_legal_title);
61     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::SetManagedUserSettingSpecifics(
190     const sync_pb::ManagedUserSettingSpecifics& new_value) {
191   sync_pb::EntitySpecifics entity_specifics;
192   entity_specifics.mutable_managed_user_setting()->CopyFrom(new_value);
193   SetEntitySpecifics(entity_specifics);
194 }
195
196 void WriteNode::SetManagedUserSpecifics(
197     const sync_pb::ManagedUserSpecifics& new_value) {
198   sync_pb::EntitySpecifics entity_specifics;
199   entity_specifics.mutable_managed_user()->CopyFrom(new_value);
200   SetEntitySpecifics(entity_specifics);
201 }
202
203 void WriteNode::SetDeviceInfoSpecifics(
204     const sync_pb::DeviceInfoSpecifics& new_value) {
205   sync_pb::EntitySpecifics entity_specifics;
206   entity_specifics.mutable_device_info()->CopyFrom(new_value);
207   SetEntitySpecifics(entity_specifics);
208 }
209
210 void WriteNode::SetExperimentsSpecifics(
211     const sync_pb::ExperimentsSpecifics& new_value) {
212   sync_pb::EntitySpecifics entity_specifics;
213   entity_specifics.mutable_experiments()->CopyFrom(new_value);
214   SetEntitySpecifics(entity_specifics);
215 }
216
217 void WriteNode::SetPriorityPreferenceSpecifics(
218     const sync_pb::PriorityPreferenceSpecifics& new_value) {
219   sync_pb::EntitySpecifics entity_specifics;
220   entity_specifics.mutable_priority_preference()->CopyFrom(new_value);
221   SetEntitySpecifics(entity_specifics);
222 }
223
224 void WriteNode::SetEntitySpecifics(
225     const sync_pb::EntitySpecifics& new_value) {
226   ModelType new_specifics_type =
227       GetModelTypeFromSpecifics(new_value);
228   CHECK(!new_value.password().has_client_only_encrypted_data());
229   DCHECK_NE(new_specifics_type, UNSPECIFIED);
230   DVLOG(1) << "Writing entity specifics of type "
231            << ModelTypeToString(new_specifics_type);
232   DCHECK_EQ(new_specifics_type, GetModelType());
233
234   // Preserve unknown fields.
235   const sync_pb::EntitySpecifics& old_specifics = entry_->GetSpecifics();
236   sync_pb::EntitySpecifics new_specifics;
237   new_specifics.CopyFrom(new_value);
238   new_specifics.mutable_unknown_fields()->MergeFrom(
239       old_specifics.unknown_fields());
240
241   // Will update the entry if encryption was necessary.
242   if (!UpdateEntryWithEncryption(GetTransaction()->GetWrappedTrans(),
243                                  new_specifics,
244                                  entry_)) {
245     return;
246   }
247   if (entry_->GetSpecifics().has_encrypted()) {
248     // EncryptIfNecessary already updated the entry for us and marked for
249     // syncing if it was needed. Now we just make a copy of the unencrypted
250     // specifics so that if this node is updated, we do not have to decrypt the
251     // old data. Note that this only modifies the node's local data, not the
252     // entry itself.
253     SetUnencryptedSpecifics(new_value);
254   }
255
256   DCHECK_EQ(new_specifics_type, GetModelType());
257 }
258
259 void WriteNode::ResetFromSpecifics() {
260   SetEntitySpecifics(GetEntitySpecifics());
261 }
262
263 void WriteNode::SetTypedUrlSpecifics(
264     const sync_pb::TypedUrlSpecifics& new_value) {
265   sync_pb::EntitySpecifics entity_specifics;
266   entity_specifics.mutable_typed_url()->CopyFrom(new_value);
267   SetEntitySpecifics(entity_specifics);
268 }
269
270 void WriteNode::SetExtensionSpecifics(
271     const sync_pb::ExtensionSpecifics& new_value) {
272   sync_pb::EntitySpecifics entity_specifics;
273   entity_specifics.mutable_extension()->CopyFrom(new_value);
274   SetEntitySpecifics(entity_specifics);
275 }
276
277 void WriteNode::SetExternalId(int64 id) {
278   if (GetExternalId() != id)
279     entry_->PutLocalExternalId(id);
280 }
281
282 WriteNode::WriteNode(WriteTransaction* transaction)
283     : entry_(NULL), transaction_(transaction) {
284   DCHECK(transaction);
285 }
286
287 WriteNode::~WriteNode() {
288   delete entry_;
289 }
290
291 // Find an existing node matching the ID |id|, and bind this WriteNode to it.
292 // Return true on success.
293 BaseNode::InitByLookupResult WriteNode::InitByIdLookup(int64 id) {
294   DCHECK(!entry_) << "Init called twice";
295   DCHECK_NE(id, kInvalidId);
296   entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
297                                       syncable::GET_BY_HANDLE, id);
298   if (!entry_->good())
299     return INIT_FAILED_ENTRY_NOT_GOOD;
300   if (entry_->GetIsDel())
301     return INIT_FAILED_ENTRY_IS_DEL;
302   return DecryptIfNecessary() ? INIT_OK : INIT_FAILED_DECRYPT_IF_NECESSARY;
303 }
304
305 // Find a node by client tag, and bind this WriteNode to it.
306 // Return true if the write node was found, and was not deleted.
307 // Undeleting a deleted node is possible by ClientTag.
308 BaseNode::InitByLookupResult WriteNode::InitByClientTagLookup(
309     ModelType model_type,
310     const std::string& tag) {
311   DCHECK(!entry_) << "Init called twice";
312   if (tag.empty())
313     return INIT_FAILED_PRECONDITION;
314
315   const std::string hash = syncable::GenerateSyncableHash(model_type, tag);
316
317   entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
318                                       syncable::GET_BY_CLIENT_TAG, hash);
319   if (!entry_->good())
320     return INIT_FAILED_ENTRY_NOT_GOOD;
321   if (entry_->GetIsDel())
322     return INIT_FAILED_ENTRY_IS_DEL;
323   return DecryptIfNecessary() ? INIT_OK : INIT_FAILED_DECRYPT_IF_NECESSARY;
324 }
325
326 BaseNode::InitByLookupResult WriteNode::InitByTagLookup(
327     const std::string& tag) {
328   DCHECK(!entry_) << "Init called twice";
329   if (tag.empty())
330     return INIT_FAILED_PRECONDITION;
331   entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
332                                       syncable::GET_BY_SERVER_TAG, tag);
333   if (!entry_->good())
334     return INIT_FAILED_ENTRY_NOT_GOOD;
335   if (entry_->GetIsDel())
336     return INIT_FAILED_ENTRY_IS_DEL;
337   ModelType model_type = GetModelType();
338   DCHECK_EQ(model_type, NIGORI);
339   return INIT_OK;
340 }
341
342 // Create a new node with default properties, and bind this WriteNode to it.
343 // Return true on success.
344 bool WriteNode::InitBookmarkByCreation(const BaseNode& parent,
345                                        const BaseNode* predecessor) {
346   DCHECK(!entry_) << "Init called twice";
347   // |predecessor| must be a child of |parent| or NULL.
348   if (predecessor && predecessor->GetParentId() != parent.GetId()) {
349     DCHECK(false);
350     return false;
351   }
352
353   syncable::Id parent_id = parent.GetEntry()->GetId();
354
355   // Start out with a dummy name.  We expect
356   // the caller to set a meaningful name after creation.
357   string dummy(kDefaultNameForNewNodes);
358
359   entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
360                                       syncable::CREATE, BOOKMARKS,
361                                       parent_id, dummy);
362
363   if (!entry_->good())
364     return false;
365
366   // Entries are untitled folders by default.
367   entry_->PutIsDir(true);
368
369   // Now set the predecessor, which sets IS_UNSYNCED as necessary.
370   return PutPredecessor(predecessor);
371 }
372
373 // Create a new node with default properties and a client defined unique tag,
374 // and bind this WriteNode to it.
375 // Return true on success. If the tag exists in the database, then
376 // we will attempt to undelete the node.
377 // TODO(chron): Code datatype into hash tag.
378 // TODO(chron): Is model type ever lost?
379 WriteNode::InitUniqueByCreationResult WriteNode::InitUniqueByCreation(
380     ModelType model_type,
381     const BaseNode& parent,
382     const std::string& tag) {
383   // This DCHECK will only fail if init is called twice.
384   DCHECK(!entry_);
385   if (tag.empty()) {
386     LOG(WARNING) << "InitUniqueByCreation failed due to empty tag.";
387     return INIT_FAILED_EMPTY_TAG;
388   }
389
390   const std::string hash = syncable::GenerateSyncableHash(model_type, tag);
391
392   syncable::Id parent_id = parent.GetEntry()->GetId();
393
394   // Start out with a dummy name.  We expect
395   // the caller to set a meaningful name after creation.
396   string dummy(kDefaultNameForNewNodes);
397
398   // Check if we have this locally and need to undelete it.
399   scoped_ptr<syncable::MutableEntry> existing_entry(
400       new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
401                                  syncable::GET_BY_CLIENT_TAG, hash));
402
403   if (existing_entry->good()) {
404     if (existing_entry->GetIsDel()) {
405       // Rules for undelete:
406       // BASE_VERSION: Must keep the same.
407       // ID: Essential to keep the same.
408       // META_HANDLE: Must be the same, so we can't "split" the entry.
409       // IS_DEL: Must be set to false, will cause reindexing.
410       //         This one is weird because IS_DEL is true for "update only"
411       //         items. It should be OK to undelete an update only.
412       // MTIME/CTIME: Seems reasonable to just leave them alone.
413       // IS_UNSYNCED: Must set this to true or face database insurrection.
414       //              We do this below this block.
415       // IS_UNAPPLIED_UPDATE: Either keep it the same or also set BASE_VERSION
416       //                      to SERVER_VERSION. We keep it the same here.
417       // IS_DIR: We'll leave it the same.
418       // SPECIFICS: Reset it.
419
420       existing_entry->PutIsDel(false);
421
422       // Client tags are immutable and must be paired with the ID.
423       // If a server update comes down with an ID and client tag combo,
424       // and it already exists, always overwrite it and store only one copy.
425       // We have to undelete entries because we can't disassociate IDs from
426       // tags and updates.
427
428       existing_entry->PutNonUniqueName(dummy);
429       existing_entry->PutParentId(parent_id);
430       entry_ = existing_entry.release();
431     } else {
432       return INIT_FAILED_ENTRY_ALREADY_EXISTS;
433     }
434   } else {
435     entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
436                                         syncable::CREATE,
437                                         model_type, parent_id, dummy);
438     if (!entry_->good())
439       return INIT_FAILED_COULD_NOT_CREATE_ENTRY;
440
441     // Only set IS_DIR for new entries. Don't bitflip undeleted ones.
442     entry_->PutUniqueClientTag(hash);
443   }
444
445   // We don't support directory and tag combinations.
446   entry_->PutIsDir(false);
447
448   // Now set the predecessor, which sets IS_UNSYNCED as necessary.
449   bool success = PutPredecessor(NULL);
450   if (!success)
451     return INIT_FAILED_SET_PREDECESSOR;
452
453   return INIT_SUCCESS;
454 }
455
456 bool WriteNode::SetPosition(const BaseNode& new_parent,
457                             const BaseNode* predecessor) {
458   // |predecessor| must be a child of |new_parent| or NULL.
459   if (predecessor && predecessor->GetParentId() != new_parent.GetId()) {
460     DCHECK(false);
461     return false;
462   }
463
464   syncable::Id new_parent_id = new_parent.GetEntry()->GetId();
465
466   // Filter out redundant changes if both the parent and the predecessor match.
467   if (new_parent_id == entry_->GetParentId()) {
468     const syncable::Id& old = entry_->GetPredecessorId();
469     if ((!predecessor && old.IsRoot()) ||
470         (predecessor && (old == predecessor->GetEntry()->GetId()))) {
471       return true;
472     }
473   }
474
475   entry_->PutParentId(new_parent_id);
476
477   // Now set the predecessor, which sets IS_UNSYNCED as necessary.
478   return PutPredecessor(predecessor);
479 }
480
481 const syncable::Entry* WriteNode::GetEntry() const {
482   return entry_;
483 }
484
485 const BaseTransaction* WriteNode::GetTransaction() const {
486   return transaction_;
487 }
488
489 syncable::MutableEntry* WriteNode::GetMutableEntryForTest() {
490   return entry_;
491 }
492
493 void WriteNode::Tombstone() {
494   // These lines must be in this order.  The call to Put(IS_DEL) might choose to
495   // unset the IS_UNSYNCED bit if the item was not known to the server at the
496   // time of deletion.  It's important that the bit not be reset in that case.
497   MarkForSyncing();
498   entry_->PutIsDel(true);
499 }
500
501 void WriteNode::Drop() {
502   if (entry_->GetId().ServerKnows()) {
503     entry_->PutIsDel(true);
504   }
505 }
506
507 bool WriteNode::PutPredecessor(const BaseNode* predecessor) {
508   syncable::Id predecessor_id = predecessor ?
509       predecessor->GetEntry()->GetId() : syncable::Id();
510   if (!entry_->PutPredecessor(predecessor_id))
511     return false;
512   // Mark this entry as unsynced, to wake up the syncer.
513   MarkForSyncing();
514
515   return true;
516 }
517
518 void WriteNode::MarkForSyncing() {
519   syncable::MarkForSyncing(entry_);
520 }
521
522 }  // namespace syncer