Upstream version 11.40.277.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::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();
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     DCHECK(base::IsStringUTF8(title));
61     SyncAPINameToServerName(title, &new_legal_title);
62     base::TruncateUTF8ToByteSize(new_legal_title, 255, &new_legal_title);
63   }
64
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();
75   }
76
77   bool title_matches = (current_legal_title == new_legal_title);
78   bool encrypted_without_overwriting_name = (needs_encryption &&
79       entry_->GetNonUniqueName() != kEncryptedString);
80
81   // If the title matches and the NON_UNIQUE_NAME is properly overwritten as
82   // necessary, nothing needs to change.
83   if (title_matches && !encrypted_without_overwriting_name) {
84     DVLOG(2) << "Title matches, dropping change.";
85     return;
86   }
87
88   // For bookmarks, we also set the title field in the specifics.
89   // TODO(zea): refactor bookmarks to not need this functionality.
90   if (GetModelType() == BOOKMARKS) {
91     sync_pb::EntitySpecifics specifics = GetEntitySpecifics();
92     specifics.mutable_bookmark()->set_title(new_legal_title);
93     SetEntitySpecifics(specifics);  // Does it's own encryption checking.
94   }
95
96   // For bookmarks, this has to happen after we set the title in the specifics,
97   // because the presence of a title in the NON_UNIQUE_NAME is what controls
98   // the logic deciding whether this is an empty node or a legacy bookmark.
99   // See BaseNode::GetUnencryptedSpecific(..).
100   if (needs_encryption)
101     entry_->PutNonUniqueName(kEncryptedString);
102   else
103     entry_->PutNonUniqueName(new_legal_title);
104
105   DVLOG(1) << "Overwriting title of type "
106            << ModelTypeToString(type)
107            << " and marking for syncing.";
108   MarkForSyncing();
109 }
110
111 void WriteNode::SetAppSpecifics(
112     const sync_pb::AppSpecifics& new_value) {
113   sync_pb::EntitySpecifics entity_specifics;
114   entity_specifics.mutable_app()->CopyFrom(new_value);
115   SetEntitySpecifics(entity_specifics);
116 }
117
118 void WriteNode::SetAutofillSpecifics(
119     const sync_pb::AutofillSpecifics& new_value) {
120   sync_pb::EntitySpecifics entity_specifics;
121   entity_specifics.mutable_autofill()->CopyFrom(new_value);
122   SetEntitySpecifics(entity_specifics);
123 }
124
125 void WriteNode::SetAutofillProfileSpecifics(
126     const sync_pb::AutofillProfileSpecifics& new_value) {
127   sync_pb::EntitySpecifics entity_specifics;
128   entity_specifics.mutable_autofill_profile()->
129       CopyFrom(new_value);
130   SetEntitySpecifics(entity_specifics);
131 }
132
133 void WriteNode::SetBookmarkSpecifics(
134     const sync_pb::BookmarkSpecifics& new_value) {
135   sync_pb::EntitySpecifics entity_specifics;
136   entity_specifics.mutable_bookmark()->CopyFrom(new_value);
137   SetEntitySpecifics(entity_specifics);
138 }
139
140 void WriteNode::SetNigoriSpecifics(
141     const sync_pb::NigoriSpecifics& new_value) {
142   sync_pb::EntitySpecifics entity_specifics;
143   entity_specifics.mutable_nigori()->CopyFrom(new_value);
144   SetEntitySpecifics(entity_specifics);
145 }
146
147 void WriteNode::SetPasswordSpecifics(
148     const sync_pb::PasswordSpecificsData& data) {
149   DCHECK_EQ(GetModelType(), PASSWORDS);
150
151   Cryptographer* cryptographer = GetTransaction()->GetCryptographer();
152
153   // We have to do the idempotency check here (vs in UpdateEntryWithEncryption)
154   // because Passwords have their encrypted data within the PasswordSpecifics,
155   // vs within the EntitySpecifics like all the other types.
156   const sync_pb::EntitySpecifics& old_specifics = GetEntry()->GetSpecifics();
157   sync_pb::EntitySpecifics entity_specifics;
158   // Copy over the old specifics if they exist.
159   if (GetModelTypeFromSpecifics(old_specifics) == PASSWORDS) {
160     entity_specifics.CopyFrom(old_specifics);
161   } else {
162     AddDefaultFieldValue(PASSWORDS, &entity_specifics);
163   }
164   sync_pb::PasswordSpecifics* password_specifics =
165       entity_specifics.mutable_password();
166   // This will only update password_specifics if the underlying unencrypted blob
167   // was different from |data| or was not encrypted with the proper passphrase.
168   if (!cryptographer->Encrypt(data, password_specifics->mutable_encrypted())) {
169     NOTREACHED() << "Failed to encrypt password, possibly due to sync node "
170                  << "corruption";
171     return;
172   }
173   SetEntitySpecifics(entity_specifics);
174 }
175
176 void WriteNode::SetThemeSpecifics(
177     const sync_pb::ThemeSpecifics& new_value) {
178   sync_pb::EntitySpecifics entity_specifics;
179   entity_specifics.mutable_theme()->CopyFrom(new_value);
180   SetEntitySpecifics(entity_specifics);
181 }
182
183 void WriteNode::SetSessionSpecifics(
184     const sync_pb::SessionSpecifics& new_value) {
185   sync_pb::EntitySpecifics entity_specifics;
186   entity_specifics.mutable_session()->CopyFrom(new_value);
187   SetEntitySpecifics(entity_specifics);
188 }
189
190 void WriteNode::SetDeviceInfoSpecifics(
191     const sync_pb::DeviceInfoSpecifics& new_value) {
192   sync_pb::EntitySpecifics entity_specifics;
193   entity_specifics.mutable_device_info()->CopyFrom(new_value);
194   SetEntitySpecifics(entity_specifics);
195 }
196
197 void WriteNode::SetExperimentsSpecifics(
198     const sync_pb::ExperimentsSpecifics& new_value) {
199   sync_pb::EntitySpecifics entity_specifics;
200   entity_specifics.mutable_experiments()->CopyFrom(new_value);
201   SetEntitySpecifics(entity_specifics);
202 }
203
204 void WriteNode::SetPriorityPreferenceSpecifics(
205     const sync_pb::PriorityPreferenceSpecifics& new_value) {
206   sync_pb::EntitySpecifics entity_specifics;
207   entity_specifics.mutable_priority_preference()->CopyFrom(new_value);
208   SetEntitySpecifics(entity_specifics);
209 }
210
211 void WriteNode::SetEntitySpecifics(
212     const sync_pb::EntitySpecifics& new_value) {
213   ModelType new_specifics_type =
214       GetModelTypeFromSpecifics(new_value);
215   CHECK(!new_value.password().has_client_only_encrypted_data());
216   DCHECK_NE(new_specifics_type, UNSPECIFIED);
217   DVLOG(1) << "Writing entity specifics of type "
218            << ModelTypeToString(new_specifics_type);
219   DCHECK_EQ(new_specifics_type, GetModelType());
220
221   // Preserve unknown fields.
222   const sync_pb::EntitySpecifics& old_specifics = entry_->GetSpecifics();
223   sync_pb::EntitySpecifics new_specifics;
224   new_specifics.CopyFrom(new_value);
225   new_specifics.mutable_unknown_fields()->MergeFrom(
226       old_specifics.unknown_fields());
227
228   // Will update the entry if encryption was necessary.
229   if (!UpdateEntryWithEncryption(GetTransaction()->GetWrappedTrans(),
230                                  new_specifics,
231                                  entry_)) {
232     return;
233   }
234   if (entry_->GetSpecifics().has_encrypted()) {
235     // EncryptIfNecessary already updated the entry for us and marked for
236     // syncing if it was needed. Now we just make a copy of the unencrypted
237     // specifics so that if this node is updated, we do not have to decrypt the
238     // old data. Note that this only modifies the node's local data, not the
239     // entry itself.
240     SetUnencryptedSpecifics(new_value);
241   }
242
243   DCHECK_EQ(new_specifics_type, GetModelType());
244 }
245
246 void WriteNode::ResetFromSpecifics() {
247   SetEntitySpecifics(GetEntitySpecifics());
248 }
249
250 void WriteNode::SetTypedUrlSpecifics(
251     const sync_pb::TypedUrlSpecifics& new_value) {
252   sync_pb::EntitySpecifics entity_specifics;
253   entity_specifics.mutable_typed_url()->CopyFrom(new_value);
254   SetEntitySpecifics(entity_specifics);
255 }
256
257 void WriteNode::SetExtensionSpecifics(
258     const sync_pb::ExtensionSpecifics& new_value) {
259   sync_pb::EntitySpecifics entity_specifics;
260   entity_specifics.mutable_extension()->CopyFrom(new_value);
261   SetEntitySpecifics(entity_specifics);
262 }
263
264 void WriteNode::SetExternalId(int64 id) {
265   if (GetExternalId() != id)
266     entry_->PutLocalExternalId(id);
267 }
268
269 WriteNode::WriteNode(WriteTransaction* transaction)
270     : entry_(NULL), transaction_(transaction) {
271   DCHECK(transaction);
272 }
273
274 WriteNode::~WriteNode() {
275   delete entry_;
276 }
277
278 // Find an existing node matching the ID |id|, and bind this WriteNode to it.
279 // Return true on success.
280 BaseNode::InitByLookupResult WriteNode::InitByIdLookup(int64 id) {
281   DCHECK(!entry_) << "Init called twice";
282   DCHECK_NE(id, kInvalidId);
283   entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
284                                       syncable::GET_BY_HANDLE, id);
285   if (!entry_->good())
286     return INIT_FAILED_ENTRY_NOT_GOOD;
287   if (entry_->GetIsDel())
288     return INIT_FAILED_ENTRY_IS_DEL;
289   return DecryptIfNecessary() ? INIT_OK : INIT_FAILED_DECRYPT_IF_NECESSARY;
290 }
291
292 // Find a node by client tag, and bind this WriteNode to it.
293 // Return true if the write node was found, and was not deleted.
294 // Undeleting a deleted node is possible by ClientTag.
295 BaseNode::InitByLookupResult WriteNode::InitByClientTagLookup(
296     ModelType model_type,
297     const std::string& tag) {
298   DCHECK(!entry_) << "Init called twice";
299   if (tag.empty())
300     return INIT_FAILED_PRECONDITION;
301
302   const std::string hash = syncable::GenerateSyncableHash(model_type, tag);
303
304   entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
305                                       syncable::GET_BY_CLIENT_TAG, hash);
306   if (!entry_->good())
307     return INIT_FAILED_ENTRY_NOT_GOOD;
308   if (entry_->GetIsDel())
309     return INIT_FAILED_ENTRY_IS_DEL;
310   return DecryptIfNecessary() ? INIT_OK : INIT_FAILED_DECRYPT_IF_NECESSARY;
311 }
312
313 BaseNode::InitByLookupResult WriteNode::InitTypeRoot(ModelType type) {
314   DCHECK(!entry_) << "Init called twice";
315   if (!IsRealDataType(type))
316     return INIT_FAILED_PRECONDITION;
317   entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
318                                       syncable::GET_TYPE_ROOT, type);
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
417       // Put specifics to handle the case where this is not actually an
418       // undeletion, but instead a collision with a newly downloaded,
419       // processed, and unapplied server update.  This is a fix for
420       // http://crbug.com/397766.
421       sync_pb::EntitySpecifics specifics;
422       AddDefaultFieldValue(model_type, &specifics);
423       existing_entry->PutSpecifics(specifics);
424
425       entry_ = existing_entry.release();
426     } else {
427       return INIT_FAILED_ENTRY_ALREADY_EXISTS;
428     }
429   } else {
430     entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
431                                         syncable::CREATE,
432                                         model_type, parent_id, dummy);
433     if (!entry_->good())
434       return INIT_FAILED_COULD_NOT_CREATE_ENTRY;
435
436     // Only set IS_DIR for new entries. Don't bitflip undeleted ones.
437     entry_->PutUniqueClientTag(hash);
438   }
439
440   // We don't support directory and tag combinations.
441   entry_->PutIsDir(false);
442
443   // Now set the predecessor, which sets IS_UNSYNCED as necessary.
444   bool success = PutPredecessor(NULL);
445   if (!success)
446     return INIT_FAILED_SET_PREDECESSOR;
447
448   return INIT_SUCCESS;
449 }
450
451 bool WriteNode::SetPosition(const BaseNode& new_parent,
452                             const BaseNode* predecessor) {
453   // |predecessor| must be a child of |new_parent| or NULL.
454   if (predecessor && predecessor->GetParentId() != new_parent.GetId()) {
455     DCHECK(false);
456     return false;
457   }
458
459   syncable::Id new_parent_id = new_parent.GetEntry()->GetId();
460
461   // Filter out redundant changes if both the parent and the predecessor match.
462   if (new_parent_id == entry_->GetParentId()) {
463     const syncable::Id& old = entry_->GetPredecessorId();
464     if ((!predecessor && old.IsRoot()) ||
465         (predecessor && (old == predecessor->GetEntry()->GetId()))) {
466       return true;
467     }
468   }
469
470   entry_->PutParentId(new_parent_id);
471
472   // Now set the predecessor, which sets IS_UNSYNCED as necessary.
473   return PutPredecessor(predecessor);
474 }
475
476 void WriteNode::SetAttachmentMetadata(
477     const sync_pb::AttachmentMetadata& attachment_metadata) {
478   entry_->PutAttachmentMetadata(attachment_metadata);
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