15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "sync/internal_api/public/write_node.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/string_util.h"
8868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/values.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "sync/internal_api/public/base_transaction.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "sync/internal_api/public/write_transaction.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "sync/internal_api/syncapi_internal.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "sync/protocol/app_specifics.pb.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "sync/protocol/autofill_specifics.pb.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "sync/protocol/bookmark_specifics.pb.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "sync/protocol/extension_specifics.pb.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "sync/protocol/password_specifics.pb.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "sync/protocol/session_specifics.pb.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "sync/protocol/theme_specifics.pb.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "sync/protocol/typed_url_specifics.pb.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "sync/syncable/mutable_entry.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "sync/syncable/nigori_util.h"
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "sync/syncable/syncable_util.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "sync/util/cryptographer.h"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using std::string;
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using std::vector;
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace syncer {
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using syncable::kEncryptedString;
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using syncable::SPECIFICS;
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const char kDefaultNameForNewNodes[] = " ";
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WriteNode::SetIsFolder(bool folder) {
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (entry_->Get(syncable::IS_DIR) == folder)
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;  // Skip redundant changes.
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  entry_->Put(syncable::IS_DIR, folder);
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MarkForSyncing();
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WriteNode::SetTitle(const std::wstring& title) {
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_NE(GetModelType(), UNSPECIFIED);
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ModelType type = GetModelType();
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // It's possible the nigori lost the set of encrypted types. If the current
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // specifics are already encrypted, we want to ensure we continue encrypting.
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool needs_encryption = GetTransaction()->GetEncryptedTypes().Has(type) ||
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          entry_->Get(SPECIFICS).has_encrypted();
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If this datatype is encrypted and is not a bookmark, we disregard the
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // specified title in favor of kEncryptedString. For encrypted bookmarks the
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // NON_UNIQUE_NAME will still be kEncryptedString, but we store the real title
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // into the specifics. All strings compared are server legal strings.
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string new_legal_title;
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (type != BOOKMARKS && needs_encryption) {
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    new_legal_title = kEncryptedString;
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SyncAPINameToServerName(WideToUTF8(title), &new_legal_title);
61c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    TruncateUTF8ToByteSize(new_legal_title, 255, &new_legal_title);
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string current_legal_title;
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (BOOKMARKS == type &&
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      entry_->Get(syncable::SPECIFICS).has_encrypted()) {
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Encrypted bookmarks only have their title in the unencrypted specifics.
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    current_legal_title = GetBookmarkSpecifics().title();
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Non-bookmarks and legacy bookmarks (those with no title in their
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // specifics) store their title in NON_UNIQUE_NAME. Non-legacy bookmarks
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // store their title in specifics as well as NON_UNIQUE_NAME.
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    current_legal_title = entry_->Get(syncable::NON_UNIQUE_NAME);
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool title_matches = (current_legal_title == new_legal_title);
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool encrypted_without_overwriting_name = (needs_encryption &&
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      entry_->Get(syncable::NON_UNIQUE_NAME) != kEncryptedString);
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If the title matches and the NON_UNIQUE_NAME is properly overwritten as
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // necessary, nothing needs to change.
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (title_matches && !encrypted_without_overwriting_name) {
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DVLOG(2) << "Title matches, dropping change.";
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // For bookmarks, we also set the title field in the specifics.
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(zea): refactor bookmarks to not need this functionality.
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (GetModelType() == BOOKMARKS) {
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sync_pb::EntitySpecifics specifics = GetEntitySpecifics();
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    specifics.mutable_bookmark()->set_title(new_legal_title);
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SetEntitySpecifics(specifics);  // Does it's own encryption checking.
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // For bookmarks, this has to happen after we set the title in the specifics,
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // because the presence of a title in the NON_UNIQUE_NAME is what controls
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the logic deciding whether this is an empty node or a legacy bookmark.
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // See BaseNode::GetUnencryptedSpecific(..).
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (needs_encryption)
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    entry_->Put(syncable::NON_UNIQUE_NAME, kEncryptedString);
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    entry_->Put(syncable::NON_UNIQUE_NAME, new_legal_title);
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DVLOG(1) << "Overwriting title of type "
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           << ModelTypeToString(type)
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           << " and marking for syncing.";
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MarkForSyncing();
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WriteNode::SetAppSpecifics(
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const sync_pb::AppSpecifics& new_value) {
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sync_pb::EntitySpecifics entity_specifics;
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  entity_specifics.mutable_app()->CopyFrom(new_value);
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetEntitySpecifics(entity_specifics);
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WriteNode::SetAutofillSpecifics(
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const sync_pb::AutofillSpecifics& new_value) {
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sync_pb::EntitySpecifics entity_specifics;
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  entity_specifics.mutable_autofill()->CopyFrom(new_value);
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetEntitySpecifics(entity_specifics);
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WriteNode::SetAutofillProfileSpecifics(
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const sync_pb::AutofillProfileSpecifics& new_value) {
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sync_pb::EntitySpecifics entity_specifics;
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  entity_specifics.mutable_autofill_profile()->
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      CopyFrom(new_value);
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetEntitySpecifics(entity_specifics);
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WriteNode::SetBookmarkSpecifics(
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const sync_pb::BookmarkSpecifics& new_value) {
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sync_pb::EntitySpecifics entity_specifics;
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  entity_specifics.mutable_bookmark()->CopyFrom(new_value);
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetEntitySpecifics(entity_specifics);
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WriteNode::SetNigoriSpecifics(
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const sync_pb::NigoriSpecifics& new_value) {
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sync_pb::EntitySpecifics entity_specifics;
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  entity_specifics.mutable_nigori()->CopyFrom(new_value);
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetEntitySpecifics(entity_specifics);
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WriteNode::SetPasswordSpecifics(
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const sync_pb::PasswordSpecificsData& data) {
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(GetModelType(), PASSWORDS);
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Cryptographer* cryptographer = GetTransaction()->GetCryptographer();
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We have to do the idempotency check here (vs in UpdateEntryWithEncryption)
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // because Passwords have their encrypted data within the PasswordSpecifics,
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // vs within the EntitySpecifics like all the other types.
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const sync_pb::EntitySpecifics& old_specifics = GetEntry()->Get(SPECIFICS);
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sync_pb::EntitySpecifics entity_specifics;
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Copy over the old specifics if they exist.
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (GetModelTypeFromSpecifics(old_specifics) == PASSWORDS) {
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    entity_specifics.CopyFrom(old_specifics);
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AddDefaultFieldValue(PASSWORDS, &entity_specifics);
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sync_pb::PasswordSpecifics* password_specifics =
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      entity_specifics.mutable_password();
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This will only update password_specifics if the underlying unencrypted blob
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // was different from |data| or was not encrypted with the proper passphrase.
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!cryptographer->Encrypt(data, password_specifics->mutable_encrypted())) {
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED() << "Failed to encrypt password, possibly due to sync node "
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 << "corruption";
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetEntitySpecifics(entity_specifics);
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WriteNode::SetThemeSpecifics(
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const sync_pb::ThemeSpecifics& new_value) {
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sync_pb::EntitySpecifics entity_specifics;
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  entity_specifics.mutable_theme()->CopyFrom(new_value);
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetEntitySpecifics(entity_specifics);
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WriteNode::SetSessionSpecifics(
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const sync_pb::SessionSpecifics& new_value) {
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sync_pb::EntitySpecifics entity_specifics;
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  entity_specifics.mutable_session()->CopyFrom(new_value);
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetEntitySpecifics(entity_specifics);
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
189c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void WriteNode::SetManagedUserSettingSpecifics(
190c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const sync_pb::ManagedUserSettingSpecifics& new_value) {
191c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  sync_pb::EntitySpecifics entity_specifics;
192c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  entity_specifics.mutable_managed_user_setting()->CopyFrom(new_value);
193c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  SetEntitySpecifics(entity_specifics);
194c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
195c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
19690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)void WriteNode::SetManagedUserSpecifics(
19790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    const sync_pb::ManagedUserSpecifics& new_value) {
19890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  sync_pb::EntitySpecifics entity_specifics;
19990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  entity_specifics.mutable_managed_user()->CopyFrom(new_value);
20090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  SetEntitySpecifics(entity_specifics);
20190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}
20290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WriteNode::SetDeviceInfoSpecifics(
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const sync_pb::DeviceInfoSpecifics& new_value) {
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sync_pb::EntitySpecifics entity_specifics;
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  entity_specifics.mutable_device_info()->CopyFrom(new_value);
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetEntitySpecifics(entity_specifics);
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WriteNode::SetExperimentsSpecifics(
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const sync_pb::ExperimentsSpecifics& new_value) {
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sync_pb::EntitySpecifics entity_specifics;
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  entity_specifics.mutable_experiments()->CopyFrom(new_value);
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetEntitySpecifics(entity_specifics);
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void WriteNode::SetPriorityPreferenceSpecifics(
2182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const sync_pb::PriorityPreferenceSpecifics& new_value) {
2192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  sync_pb::EntitySpecifics entity_specifics;
2202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  entity_specifics.mutable_priority_preference()->CopyFrom(new_value);
2212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SetEntitySpecifics(entity_specifics);
2222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WriteNode::SetEntitySpecifics(
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const sync_pb::EntitySpecifics& new_value) {
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ModelType new_specifics_type =
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GetModelTypeFromSpecifics(new_value);
2287dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  CHECK(!new_value.password().has_client_only_encrypted_data());
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_NE(new_specifics_type, UNSPECIFIED);
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DVLOG(1) << "Writing entity specifics of type "
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           << ModelTypeToString(new_specifics_type);
2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK_EQ(new_specifics_type, GetModelType());
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Preserve unknown fields.
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const sync_pb::EntitySpecifics& old_specifics = entry_->Get(SPECIFICS);
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sync_pb::EntitySpecifics new_specifics;
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  new_specifics.CopyFrom(new_value);
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  new_specifics.mutable_unknown_fields()->MergeFrom(
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      old_specifics.unknown_fields());
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Will update the entry if encryption was necessary.
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!UpdateEntryWithEncryption(GetTransaction()->GetWrappedTrans(),
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 new_specifics,
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 entry_)) {
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (entry_->Get(SPECIFICS).has_encrypted()) {
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // EncryptIfNecessary already updated the entry for us and marked for
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // syncing if it was needed. Now we just make a copy of the unencrypted
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // specifics so that if this node is updated, we do not have to decrypt the
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // old data. Note that this only modifies the node's local data, not the
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // entry itself.
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SetUnencryptedSpecifics(new_value);
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(new_specifics_type, GetModelType());
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WriteNode::ResetFromSpecifics() {
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetEntitySpecifics(GetEntitySpecifics());
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WriteNode::SetTypedUrlSpecifics(
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const sync_pb::TypedUrlSpecifics& new_value) {
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sync_pb::EntitySpecifics entity_specifics;
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  entity_specifics.mutable_typed_url()->CopyFrom(new_value);
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetEntitySpecifics(entity_specifics);
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WriteNode::SetExtensionSpecifics(
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const sync_pb::ExtensionSpecifics& new_value) {
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sync_pb::EntitySpecifics entity_specifics;
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  entity_specifics.mutable_extension()->CopyFrom(new_value);
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetEntitySpecifics(entity_specifics);
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WriteNode::SetExternalId(int64 id) {
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (GetExternalId() != id)
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    entry_->Put(syncable::LOCAL_EXTERNAL_ID, id);
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)WriteNode::WriteNode(WriteTransaction* transaction)
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : entry_(NULL), transaction_(transaction) {
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(transaction);
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)WriteNode::~WriteNode() {
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  delete entry_;
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Find an existing node matching the ID |id|, and bind this WriteNode to it.
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Return true on success.
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BaseNode::InitByLookupResult WriteNode::InitByIdLookup(int64 id) {
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!entry_) << "Init called twice";
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_NE(id, kInvalidId);
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      syncable::GET_BY_HANDLE, id);
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!entry_->good())
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return INIT_FAILED_ENTRY_NOT_GOOD;
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (entry_->Get(syncable::IS_DEL))
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return INIT_FAILED_ENTRY_IS_DEL;
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return DecryptIfNecessary() ? INIT_OK : INIT_FAILED_DECRYPT_IF_NECESSARY;
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Find a node by client tag, and bind this WriteNode to it.
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Return true if the write node was found, and was not deleted.
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Undeleting a deleted node is possible by ClientTag.
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BaseNode::InitByLookupResult WriteNode::InitByClientTagLookup(
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ModelType model_type,
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& tag) {
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!entry_) << "Init called twice";
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tag.empty())
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return INIT_FAILED_PRECONDITION;
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const std::string hash = syncable::GenerateSyncableHash(model_type, tag);
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      syncable::GET_BY_CLIENT_TAG, hash);
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!entry_->good())
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return INIT_FAILED_ENTRY_NOT_GOOD;
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (entry_->Get(syncable::IS_DEL))
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return INIT_FAILED_ENTRY_IS_DEL;
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return DecryptIfNecessary() ? INIT_OK : INIT_FAILED_DECRYPT_IF_NECESSARY;
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BaseNode::InitByLookupResult WriteNode::InitByTagLookup(
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& tag) {
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!entry_) << "Init called twice";
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tag.empty())
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return INIT_FAILED_PRECONDITION;
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      syncable::GET_BY_SERVER_TAG, tag);
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!entry_->good())
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return INIT_FAILED_ENTRY_NOT_GOOD;
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (entry_->Get(syncable::IS_DEL))
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return INIT_FAILED_ENTRY_IS_DEL;
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ModelType model_type = GetModelType();
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(model_type, NIGORI);
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return INIT_OK;
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Create a new node with default properties, and bind this WriteNode to it.
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Return true on success.
3442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool WriteNode::InitBookmarkByCreation(const BaseNode& parent,
3452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                       const BaseNode* predecessor) {
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!entry_) << "Init called twice";
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // |predecessor| must be a child of |parent| or NULL.
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (predecessor && predecessor->GetParentId() != parent.GetId()) {
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(false);
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  syncable::Id parent_id = parent.GetEntry()->Get(syncable::ID);
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Start out with a dummy name.  We expect
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the caller to set a meaningful name after creation.
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string dummy(kDefaultNameForNewNodes);
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
3602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                      syncable::CREATE, BOOKMARKS,
3612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                      parent_id, dummy);
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!entry_->good())
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Entries are untitled folders by default.
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  entry_->Put(syncable::IS_DIR, true);
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Now set the predecessor, which sets IS_UNSYNCED as necessary.
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return PutPredecessor(predecessor);
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Create a new node with default properties and a client defined unique tag,
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// and bind this WriteNode to it.
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Return true on success. If the tag exists in the database, then
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// we will attempt to undelete the node.
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TODO(chron): Code datatype into hash tag.
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TODO(chron): Is model type ever lost?
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)WriteNode::InitUniqueByCreationResult WriteNode::InitUniqueByCreation(
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ModelType model_type,
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const BaseNode& parent,
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& tag) {
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This DCHECK will only fail if init is called twice.
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!entry_);
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tag.empty()) {
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(WARNING) << "InitUniqueByCreation failed due to empty tag.";
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return INIT_FAILED_EMPTY_TAG;
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const std::string hash = syncable::GenerateSyncableHash(model_type, tag);
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  syncable::Id parent_id = parent.GetEntry()->Get(syncable::ID);
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Start out with a dummy name.  We expect
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the caller to set a meaningful name after creation.
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string dummy(kDefaultNameForNewNodes);
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Check if we have this locally and need to undelete it.
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<syncable::MutableEntry> existing_entry(
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 syncable::GET_BY_CLIENT_TAG, hash));
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (existing_entry->good()) {
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (existing_entry->Get(syncable::IS_DEL)) {
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Rules for undelete:
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // BASE_VERSION: Must keep the same.
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // ID: Essential to keep the same.
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // META_HANDLE: Must be the same, so we can't "split" the entry.
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // IS_DEL: Must be set to false, will cause reindexing.
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      //         This one is weird because IS_DEL is true for "update only"
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      //         items. It should be OK to undelete an update only.
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // MTIME/CTIME: Seems reasonable to just leave them alone.
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // IS_UNSYNCED: Must set this to true or face database insurrection.
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      //              We do this below this block.
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // IS_UNAPPLIED_UPDATE: Either keep it the same or also set BASE_VERSION
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      //                      to SERVER_VERSION. We keep it the same here.
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // IS_DIR: We'll leave it the same.
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // SPECIFICS: Reset it.
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      existing_entry->Put(syncable::IS_DEL, false);
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Client tags are immutable and must be paired with the ID.
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // If a server update comes down with an ID and client tag combo,
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // and it already exists, always overwrite it and store only one copy.
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // We have to undelete entries because we can't disassociate IDs from
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // tags and updates.
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      existing_entry->Put(syncable::NON_UNIQUE_NAME, dummy);
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      existing_entry->Put(syncable::PARENT_ID, parent_id);
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      entry_ = existing_entry.release();
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return INIT_FAILED_ENTRY_ALREADY_EXISTS;
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
4362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                        syncable::CREATE,
4372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                        model_type, parent_id, dummy);
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!entry_->good())
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return INIT_FAILED_COULD_NOT_CREATE_ENTRY;
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Only set IS_DIR for new entries. Don't bitflip undeleted ones.
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    entry_->Put(syncable::UNIQUE_CLIENT_TAG, hash);
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We don't support directory and tag combinations.
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  entry_->Put(syncable::IS_DIR, false);
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Now set the predecessor, which sets IS_UNSYNCED as necessary.
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool success = PutPredecessor(NULL);
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!success)
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return INIT_FAILED_SET_PREDECESSOR;
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return INIT_SUCCESS;
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool WriteNode::SetPosition(const BaseNode& new_parent,
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            const BaseNode* predecessor) {
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // |predecessor| must be a child of |new_parent| or NULL.
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (predecessor && predecessor->GetParentId() != new_parent.GetId()) {
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(false);
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  syncable::Id new_parent_id = new_parent.GetEntry()->Get(syncable::ID);
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Filter out redundant changes if both the parent and the predecessor match.
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (new_parent_id == entry_->Get(syncable::PARENT_ID)) {
4682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const syncable::Id& old = entry_->GetPredecessorId();
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ((!predecessor && old.IsRoot()) ||
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        (predecessor && (old == predecessor->GetEntry()->Get(syncable::ID)))) {
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return true;
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Atomically change the parent. This will fail if it would
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // introduce a cycle in the hierarchy.
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!entry_->Put(syncable::PARENT_ID, new_parent_id))
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Now set the predecessor, which sets IS_UNSYNCED as necessary.
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return PutPredecessor(predecessor);
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const syncable::Entry* WriteNode::GetEntry() const {
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return entry_;
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const BaseTransaction* WriteNode::GetTransaction() const {
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return transaction_;
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)syncable::MutableEntry* WriteNode::GetMutableEntryForTest() {
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return entry_;
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void WriteNode::Tombstone() {
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // These lines must be in this order.  The call to Put(IS_DEL) might choose to
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // unset the IS_UNSYNCED bit if the item was not known to the server at the
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // time of deletion.  It's important that the bit not be reset in that case.
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MarkForSyncing();
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  entry_->Put(syncable::IS_DEL, true);
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void WriteNode::Drop() {
5052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (entry_->Get(syncable::ID).ServerKnows()) {
5062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    entry_->Put(syncable::IS_DEL, true);
5072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
5082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
5092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool WriteNode::PutPredecessor(const BaseNode* predecessor) {
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  syncable::Id predecessor_id = predecessor ?
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      predecessor->GetEntry()->Get(syncable::ID) : syncable::Id();
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!entry_->PutPredecessor(predecessor_id))
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Mark this entry as unsynced, to wake up the syncer.
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MarkForSyncing();
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WriteNode::MarkForSyncing() {
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  syncable::MarkForSyncing(entry_);
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace syncer
526