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) {
37d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  if (entry_->GetIsDir() == folder)
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;  // Skip redundant changes.
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
40d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  entry_->PutIsDir(folder);
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MarkForSyncing();
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
440529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochvoid WriteNode::SetTitle(const std::string& 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) ||
50d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                          entry_->GetSpecifics().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 {
60cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    DCHECK(base::IsStringUTF8(title));
610529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    SyncAPINameToServerName(title, &new_legal_title);
62a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    base::TruncateUTF8ToByteSize(new_legal_title, 255, &new_legal_title);
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string current_legal_title;
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (BOOKMARKS == type &&
67d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      entry_->GetSpecifics().has_encrypted()) {
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Encrypted bookmarks only have their title in the unencrypted specifics.
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    current_legal_title = GetBookmarkSpecifics().title();
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Non-bookmarks and legacy bookmarks (those with no title in their
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // specifics) store their title in NON_UNIQUE_NAME. Non-legacy bookmarks
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // store their title in specifics as well as NON_UNIQUE_NAME.
74d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    current_legal_title = entry_->GetNonUniqueName();
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool title_matches = (current_legal_title == new_legal_title);
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool encrypted_without_overwriting_name = (needs_encryption &&
79d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      entry_->GetNonUniqueName() != kEncryptedString);
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If the title matches and the NON_UNIQUE_NAME is properly overwritten as
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // necessary, nothing needs to change.
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (title_matches && !encrypted_without_overwriting_name) {
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DVLOG(2) << "Title matches, dropping change.";
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // For bookmarks, we also set the title field in the specifics.
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(zea): refactor bookmarks to not need this functionality.
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (GetModelType() == BOOKMARKS) {
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sync_pb::EntitySpecifics specifics = GetEntitySpecifics();
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    specifics.mutable_bookmark()->set_title(new_legal_title);
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SetEntitySpecifics(specifics);  // Does it's own encryption checking.
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // For bookmarks, this has to happen after we set the title in the specifics,
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // because the presence of a title in the NON_UNIQUE_NAME is what controls
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the logic deciding whether this is an empty node or a legacy bookmark.
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // See BaseNode::GetUnencryptedSpecific(..).
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (needs_encryption)
101d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    entry_->PutNonUniqueName(kEncryptedString);
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
103d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    entry_->PutNonUniqueName(new_legal_title);
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DVLOG(1) << "Overwriting title of type "
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           << ModelTypeToString(type)
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           << " and marking for syncing.";
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MarkForSyncing();
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WriteNode::SetAppSpecifics(
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const sync_pb::AppSpecifics& new_value) {
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sync_pb::EntitySpecifics entity_specifics;
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  entity_specifics.mutable_app()->CopyFrom(new_value);
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetEntitySpecifics(entity_specifics);
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WriteNode::SetAutofillSpecifics(
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const sync_pb::AutofillSpecifics& new_value) {
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sync_pb::EntitySpecifics entity_specifics;
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  entity_specifics.mutable_autofill()->CopyFrom(new_value);
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetEntitySpecifics(entity_specifics);
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WriteNode::SetAutofillProfileSpecifics(
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const sync_pb::AutofillProfileSpecifics& new_value) {
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sync_pb::EntitySpecifics entity_specifics;
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  entity_specifics.mutable_autofill_profile()->
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      CopyFrom(new_value);
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetEntitySpecifics(entity_specifics);
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WriteNode::SetBookmarkSpecifics(
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const sync_pb::BookmarkSpecifics& new_value) {
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sync_pb::EntitySpecifics entity_specifics;
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  entity_specifics.mutable_bookmark()->CopyFrom(new_value);
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetEntitySpecifics(entity_specifics);
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WriteNode::SetNigoriSpecifics(
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const sync_pb::NigoriSpecifics& new_value) {
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sync_pb::EntitySpecifics entity_specifics;
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  entity_specifics.mutable_nigori()->CopyFrom(new_value);
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetEntitySpecifics(entity_specifics);
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WriteNode::SetPasswordSpecifics(
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const sync_pb::PasswordSpecificsData& data) {
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(GetModelType(), PASSWORDS);
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Cryptographer* cryptographer = GetTransaction()->GetCryptographer();
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We have to do the idempotency check here (vs in UpdateEntryWithEncryption)
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // because Passwords have their encrypted data within the PasswordSpecifics,
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // vs within the EntitySpecifics like all the other types.
156d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  const sync_pb::EntitySpecifics& old_specifics = GetEntry()->GetSpecifics();
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sync_pb::EntitySpecifics entity_specifics;
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Copy over the old specifics if they exist.
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (GetModelTypeFromSpecifics(old_specifics) == PASSWORDS) {
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    entity_specifics.CopyFrom(old_specifics);
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AddDefaultFieldValue(PASSWORDS, &entity_specifics);
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sync_pb::PasswordSpecifics* password_specifics =
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      entity_specifics.mutable_password();
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This will only update password_specifics if the underlying unencrypted blob
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // was different from |data| or was not encrypted with the proper passphrase.
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!cryptographer->Encrypt(data, password_specifics->mutable_encrypted())) {
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED() << "Failed to encrypt password, possibly due to sync node "
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 << "corruption";
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetEntitySpecifics(entity_specifics);
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WriteNode::SetThemeSpecifics(
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const sync_pb::ThemeSpecifics& new_value) {
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sync_pb::EntitySpecifics entity_specifics;
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  entity_specifics.mutable_theme()->CopyFrom(new_value);
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetEntitySpecifics(entity_specifics);
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WriteNode::SetSessionSpecifics(
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const sync_pb::SessionSpecifics& new_value) {
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sync_pb::EntitySpecifics entity_specifics;
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  entity_specifics.mutable_session()->CopyFrom(new_value);
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetEntitySpecifics(entity_specifics);
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WriteNode::SetDeviceInfoSpecifics(
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const sync_pb::DeviceInfoSpecifics& new_value) {
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sync_pb::EntitySpecifics entity_specifics;
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  entity_specifics.mutable_device_info()->CopyFrom(new_value);
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetEntitySpecifics(entity_specifics);
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WriteNode::SetExperimentsSpecifics(
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const sync_pb::ExperimentsSpecifics& new_value) {
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sync_pb::EntitySpecifics entity_specifics;
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  entity_specifics.mutable_experiments()->CopyFrom(new_value);
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetEntitySpecifics(entity_specifics);
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void WriteNode::SetPriorityPreferenceSpecifics(
2052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const sync_pb::PriorityPreferenceSpecifics& new_value) {
2062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  sync_pb::EntitySpecifics entity_specifics;
2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  entity_specifics.mutable_priority_preference()->CopyFrom(new_value);
2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SetEntitySpecifics(entity_specifics);
2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WriteNode::SetEntitySpecifics(
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const sync_pb::EntitySpecifics& new_value) {
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ModelType new_specifics_type =
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GetModelTypeFromSpecifics(new_value);
2157dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  CHECK(!new_value.password().has_client_only_encrypted_data());
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_NE(new_specifics_type, UNSPECIFIED);
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DVLOG(1) << "Writing entity specifics of type "
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           << ModelTypeToString(new_specifics_type);
2192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK_EQ(new_specifics_type, GetModelType());
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Preserve unknown fields.
222d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  const sync_pb::EntitySpecifics& old_specifics = entry_->GetSpecifics();
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sync_pb::EntitySpecifics new_specifics;
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  new_specifics.CopyFrom(new_value);
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  new_specifics.mutable_unknown_fields()->MergeFrom(
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      old_specifics.unknown_fields());
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Will update the entry if encryption was necessary.
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!UpdateEntryWithEncryption(GetTransaction()->GetWrappedTrans(),
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 new_specifics,
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 entry_)) {
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
234d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  if (entry_->GetSpecifics().has_encrypted()) {
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // EncryptIfNecessary already updated the entry for us and marked for
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // syncing if it was needed. Now we just make a copy of the unencrypted
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // specifics so that if this node is updated, we do not have to decrypt the
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // old data. Note that this only modifies the node's local data, not the
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // entry itself.
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SetUnencryptedSpecifics(new_value);
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(new_specifics_type, GetModelType());
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WriteNode::ResetFromSpecifics() {
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetEntitySpecifics(GetEntitySpecifics());
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WriteNode::SetTypedUrlSpecifics(
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const sync_pb::TypedUrlSpecifics& new_value) {
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sync_pb::EntitySpecifics entity_specifics;
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  entity_specifics.mutable_typed_url()->CopyFrom(new_value);
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetEntitySpecifics(entity_specifics);
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WriteNode::SetExtensionSpecifics(
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const sync_pb::ExtensionSpecifics& new_value) {
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sync_pb::EntitySpecifics entity_specifics;
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  entity_specifics.mutable_extension()->CopyFrom(new_value);
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetEntitySpecifics(entity_specifics);
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WriteNode::SetExternalId(int64 id) {
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (GetExternalId() != id)
266d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    entry_->PutLocalExternalId(id);
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)WriteNode::WriteNode(WriteTransaction* transaction)
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : entry_(NULL), transaction_(transaction) {
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(transaction);
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)WriteNode::~WriteNode() {
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  delete entry_;
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Find an existing node matching the ID |id|, and bind this WriteNode to it.
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Return true on success.
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BaseNode::InitByLookupResult WriteNode::InitByIdLookup(int64 id) {
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!entry_) << "Init called twice";
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_NE(id, kInvalidId);
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      syncable::GET_BY_HANDLE, id);
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!entry_->good())
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return INIT_FAILED_ENTRY_NOT_GOOD;
287d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  if (entry_->GetIsDel())
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return INIT_FAILED_ENTRY_IS_DEL;
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return DecryptIfNecessary() ? INIT_OK : INIT_FAILED_DECRYPT_IF_NECESSARY;
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Find a node by client tag, and bind this WriteNode to it.
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Return true if the write node was found, and was not deleted.
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Undeleting a deleted node is possible by ClientTag.
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BaseNode::InitByLookupResult WriteNode::InitByClientTagLookup(
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ModelType model_type,
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& tag) {
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!entry_) << "Init called twice";
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tag.empty())
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return INIT_FAILED_PRECONDITION;
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const std::string hash = syncable::GenerateSyncableHash(model_type, tag);
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      syncable::GET_BY_CLIENT_TAG, hash);
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!entry_->good())
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return INIT_FAILED_ENTRY_NOT_GOOD;
308d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  if (entry_->GetIsDel())
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return INIT_FAILED_ENTRY_IS_DEL;
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return DecryptIfNecessary() ? INIT_OK : INIT_FAILED_DECRYPT_IF_NECESSARY;
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
31346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)BaseNode::InitByLookupResult WriteNode::InitTypeRoot(ModelType type) {
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!entry_) << "Init called twice";
31546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  if (!IsRealDataType(type))
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return INIT_FAILED_PRECONDITION;
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
31846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)                                      syncable::GET_TYPE_ROOT, type);
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!entry_->good())
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return INIT_FAILED_ENTRY_NOT_GOOD;
321d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  if (entry_->GetIsDel())
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return INIT_FAILED_ENTRY_IS_DEL;
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ModelType model_type = GetModelType();
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(model_type, NIGORI);
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return INIT_OK;
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Create a new node with default properties, and bind this WriteNode to it.
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Return true on success.
3302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool WriteNode::InitBookmarkByCreation(const BaseNode& parent,
3312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                       const BaseNode* predecessor) {
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!entry_) << "Init called twice";
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // |predecessor| must be a child of |parent| or NULL.
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (predecessor && predecessor->GetParentId() != parent.GetId()) {
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(false);
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
339d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  syncable::Id parent_id = parent.GetEntry()->GetId();
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Start out with a dummy name.  We expect
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the caller to set a meaningful name after creation.
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string dummy(kDefaultNameForNewNodes);
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
3462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                      syncable::CREATE, BOOKMARKS,
3472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                      parent_id, dummy);
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!entry_->good())
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Entries are untitled folders by default.
353d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  entry_->PutIsDir(true);
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Now set the predecessor, which sets IS_UNSYNCED as necessary.
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return PutPredecessor(predecessor);
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Create a new node with default properties and a client defined unique tag,
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// and bind this WriteNode to it.
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Return true on success. If the tag exists in the database, then
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// we will attempt to undelete the node.
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TODO(chron): Code datatype into hash tag.
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TODO(chron): Is model type ever lost?
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)WriteNode::InitUniqueByCreationResult WriteNode::InitUniqueByCreation(
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ModelType model_type,
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const BaseNode& parent,
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& tag) {
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This DCHECK will only fail if init is called twice.
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!entry_);
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tag.empty()) {
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(WARNING) << "InitUniqueByCreation failed due to empty tag.";
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return INIT_FAILED_EMPTY_TAG;
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const std::string hash = syncable::GenerateSyncableHash(model_type, tag);
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
378d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  syncable::Id parent_id = parent.GetEntry()->GetId();
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Start out with a dummy name.  We expect
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the caller to set a meaningful name after creation.
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string dummy(kDefaultNameForNewNodes);
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Check if we have this locally and need to undelete it.
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<syncable::MutableEntry> existing_entry(
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 syncable::GET_BY_CLIENT_TAG, hash));
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (existing_entry->good()) {
390d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    if (existing_entry->GetIsDel()) {
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Rules for undelete:
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // BASE_VERSION: Must keep the same.
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // ID: Essential to keep the same.
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // META_HANDLE: Must be the same, so we can't "split" the entry.
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // IS_DEL: Must be set to false, will cause reindexing.
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      //         This one is weird because IS_DEL is true for "update only"
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      //         items. It should be OK to undelete an update only.
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // MTIME/CTIME: Seems reasonable to just leave them alone.
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // IS_UNSYNCED: Must set this to true or face database insurrection.
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      //              We do this below this block.
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // IS_UNAPPLIED_UPDATE: Either keep it the same or also set BASE_VERSION
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      //                      to SERVER_VERSION. We keep it the same here.
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // IS_DIR: We'll leave it the same.
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // SPECIFICS: Reset it.
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
406d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      existing_entry->PutIsDel(false);
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Client tags are immutable and must be paired with the ID.
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // If a server update comes down with an ID and client tag combo,
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // and it already exists, always overwrite it and store only one copy.
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // We have to undelete entries because we can't disassociate IDs from
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // tags and updates.
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
414d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      existing_entry->PutNonUniqueName(dummy);
415d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      existing_entry->PutParentId(parent_id);
4165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
4175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      // Put specifics to handle the case where this is not actually an
4185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      // undeletion, but instead a collision with a newly downloaded,
4195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      // processed, and unapplied server update.  This is a fix for
4205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      // http://crbug.com/397766.
4215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      sync_pb::EntitySpecifics specifics;
4225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      AddDefaultFieldValue(model_type, &specifics);
4235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      existing_entry->PutSpecifics(specifics);
4245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      entry_ = existing_entry.release();
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return INIT_FAILED_ENTRY_ALREADY_EXISTS;
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
4312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                        syncable::CREATE,
4322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                        model_type, parent_id, dummy);
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!entry_->good())
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return INIT_FAILED_COULD_NOT_CREATE_ENTRY;
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Only set IS_DIR for new entries. Don't bitflip undeleted ones.
437d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    entry_->PutUniqueClientTag(hash);
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We don't support directory and tag combinations.
441d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  entry_->PutIsDir(false);
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Now set the predecessor, which sets IS_UNSYNCED as necessary.
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool success = PutPredecessor(NULL);
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!success)
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return INIT_FAILED_SET_PREDECESSOR;
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return INIT_SUCCESS;
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool WriteNode::SetPosition(const BaseNode& new_parent,
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            const BaseNode* predecessor) {
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // |predecessor| must be a child of |new_parent| or NULL.
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (predecessor && predecessor->GetParentId() != new_parent.GetId()) {
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(false);
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
459d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  syncable::Id new_parent_id = new_parent.GetEntry()->GetId();
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Filter out redundant changes if both the parent and the predecessor match.
462d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  if (new_parent_id == entry_->GetParentId()) {
4632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const syncable::Id& old = entry_->GetPredecessorId();
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ((!predecessor && old.IsRoot()) ||
465d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        (predecessor && (old == predecessor->GetEntry()->GetId()))) {
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return true;
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
470d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  entry_->PutParentId(new_parent_id);
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Now set the predecessor, which sets IS_UNSYNCED as necessary.
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return PutPredecessor(predecessor);
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
476010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)void WriteNode::SetAttachmentMetadata(
477010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    const sync_pb::AttachmentMetadata& attachment_metadata) {
478010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  entry_->PutAttachmentMetadata(attachment_metadata);
479010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
480010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const syncable::Entry* WriteNode::GetEntry() const {
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return entry_;
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const BaseTransaction* WriteNode::GetTransaction() const {
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return transaction_;
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)syncable::MutableEntry* WriteNode::GetMutableEntryForTest() {
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return entry_;
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void WriteNode::Tombstone() {
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // These lines must be in this order.  The call to Put(IS_DEL) might choose to
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // unset the IS_UNSYNCED bit if the item was not known to the server at the
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // time of deletion.  It's important that the bit not be reset in that case.
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MarkForSyncing();
498d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  entry_->PutIsDel(true);
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void WriteNode::Drop() {
502d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  if (entry_->GetId().ServerKnows()) {
503d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    entry_->PutIsDel(true);
5042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
5052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
5062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool WriteNode::PutPredecessor(const BaseNode* predecessor) {
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  syncable::Id predecessor_id = predecessor ?
509d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      predecessor->GetEntry()->GetId() : syncable::Id();
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!entry_->PutPredecessor(predecessor_id))
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Mark this entry as unsynced, to wake up the syncer.
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MarkForSyncing();
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WriteNode::MarkForSyncing() {
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  syncable::MarkForSyncing(entry_);
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace syncer
523