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/base_node.h"
      6 
      7 #include <stack>
      8 
      9 #include "base/strings/string_number_conversions.h"
     10 #include "base/strings/utf_string_conversions.h"
     11 #include "sync/internal_api/public/base_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/nigori_specifics.pb.h"
     18 #include "sync/protocol/password_specifics.pb.h"
     19 #include "sync/protocol/session_specifics.pb.h"
     20 #include "sync/protocol/theme_specifics.pb.h"
     21 #include "sync/protocol/typed_url_specifics.pb.h"
     22 #include "sync/syncable/directory.h"
     23 #include "sync/syncable/entry.h"
     24 #include "sync/syncable/syncable_id.h"
     25 #include "sync/util/time.h"
     26 
     27 using sync_pb::AutofillProfileSpecifics;
     28 
     29 namespace syncer {
     30 
     31 using syncable::SPECIFICS;
     32 
     33 // Helper function to look up the int64 metahandle of an object given the ID
     34 // string.
     35 static int64 IdToMetahandle(syncable::BaseTransaction* trans,
     36                             const syncable::Id& id) {
     37   syncable::Entry entry(trans, syncable::GET_BY_ID, id);
     38   if (!entry.good())
     39     return kInvalidId;
     40   return entry.GetMetahandle();
     41 }
     42 
     43 BaseNode::BaseNode() : password_data_(new sync_pb::PasswordSpecificsData) {}
     44 
     45 BaseNode::~BaseNode() {}
     46 
     47 bool BaseNode::DecryptIfNecessary() {
     48   if (!GetEntry()->GetUniqueServerTag().empty())
     49       return true;  // Ignore unique folders.
     50   const sync_pb::EntitySpecifics& specifics =
     51       GetEntry()->GetSpecifics();
     52   if (specifics.has_password()) {
     53     // Passwords have their own legacy encryption structure.
     54     scoped_ptr<sync_pb::PasswordSpecificsData> data(DecryptPasswordSpecifics(
     55         specifics, GetTransaction()->GetCryptographer()));
     56     if (!data) {
     57       LOG(ERROR) << "Failed to decrypt password specifics.";
     58       return false;
     59     }
     60     password_data_.swap(data);
     61     return true;
     62   }
     63 
     64   // We assume any node with the encrypted field set has encrypted data and if
     65   // not we have no work to do, with the exception of bookmarks. For bookmarks
     66   // we must make sure the bookmarks data has the title field supplied. If not,
     67   // we fill the unencrypted_data_ with a copy of the bookmark specifics that
     68   // follows the new bookmarks format.
     69   if (!specifics.has_encrypted()) {
     70     if (GetModelType() == BOOKMARKS &&
     71         !specifics.bookmark().has_title() &&
     72         !GetTitle().empty()) {  // Last check ensures this isn't a new node.
     73       // We need to fill in the title.
     74       std::string title = GetTitle();
     75       std::string server_legal_title;
     76       SyncAPINameToServerName(title, &server_legal_title);
     77       DVLOG(1) << "Reading from legacy bookmark, manually returning title "
     78                << title;
     79       unencrypted_data_.CopyFrom(specifics);
     80       unencrypted_data_.mutable_bookmark()->set_title(
     81           server_legal_title);
     82     }
     83     return true;
     84   }
     85 
     86   const sync_pb::EncryptedData& encrypted = specifics.encrypted();
     87   std::string plaintext_data = GetTransaction()->GetCryptographer()->
     88       DecryptToString(encrypted);
     89   if (plaintext_data.length() == 0) {
     90     LOG(ERROR) << "Failed to decrypt encrypted node of type "
     91                << ModelTypeToString(GetModelType()) << ".";
     92     // Debugging for crbug.com/123223. We failed to decrypt the data, which
     93     // means we applied an update without having the key or lost the key at a
     94     // later point.
     95     CHECK(false);
     96     return false;
     97   } else if (!unencrypted_data_.ParseFromString(plaintext_data)) {
     98     // Debugging for crbug.com/123223. We should never succeed in decrypting
     99     // but fail to parse into a protobuf.
    100     CHECK(false);
    101     return false;
    102   }
    103   DVLOG(2) << "Decrypted specifics of type "
    104            << ModelTypeToString(GetModelType())
    105            << " with content: " << plaintext_data;
    106   return true;
    107 }
    108 
    109 const sync_pb::EntitySpecifics& BaseNode::GetUnencryptedSpecifics(
    110     const syncable::Entry* entry) const {
    111   const sync_pb::EntitySpecifics& specifics = entry->GetSpecifics();
    112   if (specifics.has_encrypted()) {
    113     DCHECK_NE(GetModelTypeFromSpecifics(unencrypted_data_), UNSPECIFIED);
    114     return unencrypted_data_;
    115   } else {
    116     // Due to the change in bookmarks format, we need to check to see if this is
    117     // a legacy bookmarks (and has no title field in the proto). If it is, we
    118     // return the unencrypted_data_, which was filled in with the title by
    119     // DecryptIfNecessary().
    120     if (GetModelType() == BOOKMARKS) {
    121       const sync_pb::BookmarkSpecifics& bookmark_specifics =
    122           specifics.bookmark();
    123       if (bookmark_specifics.has_title() ||
    124           GetTitle().empty() ||  // For the empty node case
    125           !GetEntry()->GetUniqueServerTag().empty()) {
    126         // It's possible we previously had to convert and set
    127         // |unencrypted_data_| but then wrote our own data, so we allow
    128         // |unencrypted_data_| to be non-empty.
    129         return specifics;
    130       } else {
    131         DCHECK_EQ(GetModelTypeFromSpecifics(unencrypted_data_), BOOKMARKS);
    132         return unencrypted_data_;
    133       }
    134     } else {
    135       DCHECK_EQ(GetModelTypeFromSpecifics(unencrypted_data_), UNSPECIFIED);
    136       return specifics;
    137     }
    138   }
    139 }
    140 
    141 int64 BaseNode::GetParentId() const {
    142   return IdToMetahandle(GetTransaction()->GetWrappedTrans(),
    143                         GetEntry()->GetParentId());
    144 }
    145 
    146 int64 BaseNode::GetId() const {
    147   return GetEntry()->GetMetahandle();
    148 }
    149 
    150 base::Time BaseNode::GetModificationTime() const {
    151   return GetEntry()->GetMtime();
    152 }
    153 
    154 bool BaseNode::GetIsFolder() const {
    155   return GetEntry()->GetIsDir();
    156 }
    157 
    158 std::string BaseNode::GetTitle() const {
    159   std::string result;
    160   // TODO(zea): refactor bookmarks to not need this functionality.
    161   if (BOOKMARKS == GetModelType() &&
    162       GetEntry()->GetSpecifics().has_encrypted()) {
    163     // Special case for legacy bookmarks dealing with encryption.
    164     ServerNameToSyncAPIName(GetBookmarkSpecifics().title(), &result);
    165   } else {
    166     ServerNameToSyncAPIName(GetEntry()->GetNonUniqueName(),
    167                             &result);
    168   }
    169   return result;
    170 }
    171 
    172 bool BaseNode::HasChildren() const {
    173   syncable::Directory* dir = GetTransaction()->GetDirectory();
    174   syncable::BaseTransaction* trans = GetTransaction()->GetWrappedTrans();
    175   return dir->HasChildren(trans, GetEntry()->GetId());
    176 }
    177 
    178 int64 BaseNode::GetPredecessorId() const {
    179   syncable::Id id_string = GetEntry()->GetPredecessorId();
    180   if (id_string.IsRoot())
    181     return kInvalidId;
    182   return IdToMetahandle(GetTransaction()->GetWrappedTrans(), id_string);
    183 }
    184 
    185 int64 BaseNode::GetSuccessorId() const {
    186   syncable::Id id_string = GetEntry()->GetSuccessorId();
    187   if (id_string.IsRoot())
    188     return kInvalidId;
    189   return IdToMetahandle(GetTransaction()->GetWrappedTrans(), id_string);
    190 }
    191 
    192 int64 BaseNode::GetFirstChildId() const {
    193   syncable::Id id_string = GetEntry()->GetFirstChildId();
    194   if (id_string.IsRoot())
    195     return kInvalidId;
    196   return IdToMetahandle(GetTransaction()->GetWrappedTrans(), id_string);
    197 }
    198 
    199 void BaseNode::GetChildIds(std::vector<int64>* result) const {
    200   GetEntry()->GetChildHandles(result);
    201 }
    202 
    203 int BaseNode::GetTotalNodeCount() const {
    204   return GetEntry()->GetTotalNodeCount();
    205 }
    206 
    207 int BaseNode::GetPositionIndex() const {
    208   return GetEntry()->GetPositionIndex();
    209 }
    210 
    211 base::DictionaryValue* BaseNode::ToValue() const {
    212   return GetEntry()->ToValue(GetTransaction()->GetCryptographer());
    213 }
    214 
    215 int64 BaseNode::GetExternalId() const {
    216   return GetEntry()->GetLocalExternalId();
    217 }
    218 
    219 const sync_pb::AppSpecifics& BaseNode::GetAppSpecifics() const {
    220   DCHECK_EQ(GetModelType(), APPS);
    221   return GetEntitySpecifics().app();
    222 }
    223 
    224 const sync_pb::AutofillSpecifics& BaseNode::GetAutofillSpecifics() const {
    225   DCHECK_EQ(GetModelType(), AUTOFILL);
    226   return GetEntitySpecifics().autofill();
    227 }
    228 
    229 const AutofillProfileSpecifics& BaseNode::GetAutofillProfileSpecifics() const {
    230   DCHECK_EQ(GetModelType(), AUTOFILL_PROFILE);
    231   return GetEntitySpecifics().autofill_profile();
    232 }
    233 
    234 const sync_pb::BookmarkSpecifics& BaseNode::GetBookmarkSpecifics() const {
    235   DCHECK_EQ(GetModelType(), BOOKMARKS);
    236   return GetEntitySpecifics().bookmark();
    237 }
    238 
    239 const sync_pb::NigoriSpecifics& BaseNode::GetNigoriSpecifics() const {
    240   DCHECK_EQ(GetModelType(), NIGORI);
    241   return GetEntitySpecifics().nigori();
    242 }
    243 
    244 const sync_pb::PasswordSpecificsData& BaseNode::GetPasswordSpecifics() const {
    245   DCHECK_EQ(GetModelType(), PASSWORDS);
    246   return *password_data_;
    247 }
    248 
    249 const sync_pb::ThemeSpecifics& BaseNode::GetThemeSpecifics() const {
    250   DCHECK_EQ(GetModelType(), THEMES);
    251   return GetEntitySpecifics().theme();
    252 }
    253 
    254 const sync_pb::TypedUrlSpecifics& BaseNode::GetTypedUrlSpecifics() const {
    255   DCHECK_EQ(GetModelType(), TYPED_URLS);
    256   return GetEntitySpecifics().typed_url();
    257 }
    258 
    259 const sync_pb::ExtensionSpecifics& BaseNode::GetExtensionSpecifics() const {
    260   DCHECK_EQ(GetModelType(), EXTENSIONS);
    261   return GetEntitySpecifics().extension();
    262 }
    263 
    264 const sync_pb::SessionSpecifics& BaseNode::GetSessionSpecifics() const {
    265   DCHECK_EQ(GetModelType(), SESSIONS);
    266   return GetEntitySpecifics().session();
    267 }
    268 
    269 const sync_pb::DeviceInfoSpecifics& BaseNode::GetDeviceInfoSpecifics() const {
    270   DCHECK_EQ(GetModelType(), DEVICE_INFO);
    271   return GetEntitySpecifics().device_info();
    272 }
    273 
    274 const sync_pb::ExperimentsSpecifics& BaseNode::GetExperimentsSpecifics() const {
    275   DCHECK_EQ(GetModelType(), EXPERIMENTS);
    276   return GetEntitySpecifics().experiments();
    277 }
    278 
    279 const sync_pb::PriorityPreferenceSpecifics&
    280     BaseNode::GetPriorityPreferenceSpecifics() const {
    281   DCHECK_EQ(GetModelType(), PRIORITY_PREFERENCES);
    282   return GetEntitySpecifics().priority_preference();
    283 }
    284 
    285 const sync_pb::EntitySpecifics& BaseNode::GetEntitySpecifics() const {
    286   return GetUnencryptedSpecifics(GetEntry());
    287 }
    288 
    289 ModelType BaseNode::GetModelType() const {
    290   return GetEntry()->GetModelType();
    291 }
    292 
    293 const syncer::AttachmentIdList BaseNode::GetAttachmentIds() const {
    294   AttachmentIdList result;
    295   const sync_pb::AttachmentMetadata& metadata =
    296       GetEntry()->GetAttachmentMetadata();
    297   for (int i = 0; i < metadata.record_size(); ++i) {
    298     result.push_back(AttachmentId::CreateFromProto(metadata.record(i).id()));
    299   }
    300   return result;
    301 }
    302 
    303 void BaseNode::SetUnencryptedSpecifics(
    304     const sync_pb::EntitySpecifics& specifics) {
    305   ModelType type = GetModelTypeFromSpecifics(specifics);
    306   DCHECK_NE(UNSPECIFIED, type);
    307   if (GetModelType() != UNSPECIFIED) {
    308     DCHECK_EQ(GetModelType(), type);
    309   }
    310   unencrypted_data_.CopyFrom(specifics);
    311 }
    312 
    313 }  // namespace syncer
    314