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