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.
5 #include "sync/internal_api/public/base_node.h"
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"
27 using sync_pb::AutofillProfileSpecifics
;
31 using syncable::SPECIFICS
;
33 // Helper function to look up the int64 metahandle of an object given the ID
35 static int64
IdToMetahandle(syncable::BaseTransaction
* trans
,
36 const syncable::Id
& id
) {
37 syncable::Entry
entry(trans
, syncable::GET_BY_ID
, id
);
40 return entry
.GetMetahandle();
43 static bool EndsWithSpace(const std::string
& string
) {
44 return !string
.empty() && *string
.rbegin() == ' ';
47 // In the reverse direction, if a server name matches the pattern of a
48 // server-illegal name followed by one or more spaces, remove the trailing
50 static void ServerNameToSyncAPIName(const std::string
& server_name
,
53 int length_to_copy
= server_name
.length();
54 if (IsNameServerIllegalAfterTrimming(server_name
) &&
55 EndsWithSpace(server_name
)) {
58 *out
= std::string(server_name
.c_str(), length_to_copy
);
61 BaseNode::BaseNode() : password_data_(new sync_pb::PasswordSpecificsData
) {}
63 BaseNode::~BaseNode() {}
65 bool BaseNode::DecryptIfNecessary() {
66 if (!GetEntry()->GetUniqueServerTag().empty())
67 return true; // Ignore unique folders.
68 const sync_pb::EntitySpecifics
& specifics
=
69 GetEntry()->GetSpecifics();
70 if (specifics
.has_password()) {
71 // Passwords have their own legacy encryption structure.
72 scoped_ptr
<sync_pb::PasswordSpecificsData
> data(DecryptPasswordSpecifics(
73 specifics
, GetTransaction()->GetCryptographer()));
75 LOG(ERROR
) << "Failed to decrypt password specifics.";
78 password_data_
.swap(data
);
82 // We assume any node with the encrypted field set has encrypted data and if
83 // not we have no work to do, with the exception of bookmarks. For bookmarks
84 // we must make sure the bookmarks data has the title field supplied. If not,
85 // we fill the unencrypted_data_ with a copy of the bookmark specifics that
86 // follows the new bookmarks format.
87 if (!specifics
.has_encrypted()) {
88 if (GetModelType() == BOOKMARKS
&&
89 !specifics
.bookmark().has_title() &&
90 !GetTitle().empty()) { // Last check ensures this isn't a new node.
91 // We need to fill in the title.
92 std::string title
= GetTitle();
93 std::string server_legal_title
;
94 SyncAPINameToServerName(title
, &server_legal_title
);
95 DVLOG(1) << "Reading from legacy bookmark, manually returning title "
97 unencrypted_data_
.CopyFrom(specifics
);
98 unencrypted_data_
.mutable_bookmark()->set_title(
104 const sync_pb::EncryptedData
& encrypted
= specifics
.encrypted();
105 std::string plaintext_data
= GetTransaction()->GetCryptographer()->
106 DecryptToString(encrypted
);
107 if (plaintext_data
.length() == 0) {
108 LOG(ERROR
) << "Failed to decrypt encrypted node of type "
109 << ModelTypeToString(GetModelType()) << ".";
110 // Debugging for crbug.com/123223. We failed to decrypt the data, which
111 // means we applied an update without having the key or lost the key at a
115 } else if (!unencrypted_data_
.ParseFromString(plaintext_data
)) {
116 // Debugging for crbug.com/123223. We should never succeed in decrypting
117 // but fail to parse into a protobuf.
121 DVLOG(2) << "Decrypted specifics of type "
122 << ModelTypeToString(GetModelType())
123 << " with content: " << plaintext_data
;
127 const sync_pb::EntitySpecifics
& BaseNode::GetUnencryptedSpecifics(
128 const syncable::Entry
* entry
) const {
129 const sync_pb::EntitySpecifics
& specifics
= entry
->GetSpecifics();
130 if (specifics
.has_encrypted()) {
131 DCHECK_NE(GetModelTypeFromSpecifics(unencrypted_data_
), UNSPECIFIED
);
132 return unencrypted_data_
;
134 // Due to the change in bookmarks format, we need to check to see if this is
135 // a legacy bookmarks (and has no title field in the proto). If it is, we
136 // return the unencrypted_data_, which was filled in with the title by
137 // DecryptIfNecessary().
138 if (GetModelType() == BOOKMARKS
) {
139 const sync_pb::BookmarkSpecifics
& bookmark_specifics
=
140 specifics
.bookmark();
141 if (bookmark_specifics
.has_title() ||
142 GetTitle().empty() || // For the empty node case
143 !GetEntry()->GetUniqueServerTag().empty()) {
144 // It's possible we previously had to convert and set
145 // |unencrypted_data_| but then wrote our own data, so we allow
146 // |unencrypted_data_| to be non-empty.
149 DCHECK_EQ(GetModelTypeFromSpecifics(unencrypted_data_
), BOOKMARKS
);
150 return unencrypted_data_
;
153 DCHECK_EQ(GetModelTypeFromSpecifics(unencrypted_data_
), UNSPECIFIED
);
159 int64
BaseNode::GetParentId() const {
160 return IdToMetahandle(GetTransaction()->GetWrappedTrans(),
161 GetEntry()->GetParentId());
164 int64
BaseNode::GetId() const {
165 return GetEntry()->GetMetahandle();
168 base::Time
BaseNode::GetModificationTime() const {
169 return GetEntry()->GetMtime();
172 bool BaseNode::GetIsFolder() const {
173 return GetEntry()->GetIsDir();
176 std::string
BaseNode::GetTitle() const {
178 // TODO(zea): refactor bookmarks to not need this functionality.
179 if (BOOKMARKS
== GetModelType() &&
180 GetEntry()->GetSpecifics().has_encrypted()) {
181 // Special case for legacy bookmarks dealing with encryption.
182 ServerNameToSyncAPIName(GetBookmarkSpecifics().title(), &result
);
184 ServerNameToSyncAPIName(GetEntry()->GetNonUniqueName(),
190 bool BaseNode::HasChildren() const {
191 syncable::Directory
* dir
= GetTransaction()->GetDirectory();
192 syncable::BaseTransaction
* trans
= GetTransaction()->GetWrappedTrans();
193 return dir
->HasChildren(trans
, GetEntry()->GetId());
196 int64
BaseNode::GetPredecessorId() const {
197 syncable::Id id_string
= GetEntry()->GetPredecessorId();
198 if (id_string
.IsRoot())
200 return IdToMetahandle(GetTransaction()->GetWrappedTrans(), id_string
);
203 int64
BaseNode::GetSuccessorId() const {
204 syncable::Id id_string
= GetEntry()->GetSuccessorId();
205 if (id_string
.IsRoot())
207 return IdToMetahandle(GetTransaction()->GetWrappedTrans(), id_string
);
210 int64
BaseNode::GetFirstChildId() const {
211 syncable::Id id_string
= GetEntry()->GetFirstChildId();
212 if (id_string
.IsRoot())
214 return IdToMetahandle(GetTransaction()->GetWrappedTrans(), id_string
);
217 void BaseNode::GetChildIds(std::vector
<int64
>* result
) const {
218 GetEntry()->GetChildHandles(result
);
221 int BaseNode::GetTotalNodeCount() const {
222 return GetEntry()->GetTotalNodeCount();
225 int BaseNode::GetPositionIndex() const {
226 return GetEntry()->GetPositionIndex();
229 base::DictionaryValue
* BaseNode::ToValue() const {
230 return GetEntry()->ToValue(GetTransaction()->GetCryptographer());
233 int64
BaseNode::GetExternalId() const {
234 return GetEntry()->GetLocalExternalId();
237 const sync_pb::AppSpecifics
& BaseNode::GetAppSpecifics() const {
238 DCHECK_EQ(GetModelType(), APPS
);
239 return GetEntitySpecifics().app();
242 const sync_pb::AutofillSpecifics
& BaseNode::GetAutofillSpecifics() const {
243 DCHECK_EQ(GetModelType(), AUTOFILL
);
244 return GetEntitySpecifics().autofill();
247 const AutofillProfileSpecifics
& BaseNode::GetAutofillProfileSpecifics() const {
248 DCHECK_EQ(GetModelType(), AUTOFILL_PROFILE
);
249 return GetEntitySpecifics().autofill_profile();
252 const sync_pb::BookmarkSpecifics
& BaseNode::GetBookmarkSpecifics() const {
253 DCHECK_EQ(GetModelType(), BOOKMARKS
);
254 return GetEntitySpecifics().bookmark();
257 const sync_pb::NigoriSpecifics
& BaseNode::GetNigoriSpecifics() const {
258 DCHECK_EQ(GetModelType(), NIGORI
);
259 return GetEntitySpecifics().nigori();
262 const sync_pb::PasswordSpecificsData
& BaseNode::GetPasswordSpecifics() const {
263 DCHECK_EQ(GetModelType(), PASSWORDS
);
264 return *password_data_
;
267 const sync_pb::ThemeSpecifics
& BaseNode::GetThemeSpecifics() const {
268 DCHECK_EQ(GetModelType(), THEMES
);
269 return GetEntitySpecifics().theme();
272 const sync_pb::TypedUrlSpecifics
& BaseNode::GetTypedUrlSpecifics() const {
273 DCHECK_EQ(GetModelType(), TYPED_URLS
);
274 return GetEntitySpecifics().typed_url();
277 const sync_pb::ExtensionSpecifics
& BaseNode::GetExtensionSpecifics() const {
278 DCHECK_EQ(GetModelType(), EXTENSIONS
);
279 return GetEntitySpecifics().extension();
282 const sync_pb::SessionSpecifics
& BaseNode::GetSessionSpecifics() const {
283 DCHECK_EQ(GetModelType(), SESSIONS
);
284 return GetEntitySpecifics().session();
287 const sync_pb::DeviceInfoSpecifics
& BaseNode::GetDeviceInfoSpecifics() const {
288 DCHECK_EQ(GetModelType(), DEVICE_INFO
);
289 return GetEntitySpecifics().device_info();
292 const sync_pb::ExperimentsSpecifics
& BaseNode::GetExperimentsSpecifics() const {
293 DCHECK_EQ(GetModelType(), EXPERIMENTS
);
294 return GetEntitySpecifics().experiments();
297 const sync_pb::PriorityPreferenceSpecifics
&
298 BaseNode::GetPriorityPreferenceSpecifics() const {
299 DCHECK_EQ(GetModelType(), PRIORITY_PREFERENCES
);
300 return GetEntitySpecifics().priority_preference();
303 const sync_pb::EntitySpecifics
& BaseNode::GetEntitySpecifics() const {
304 return GetUnencryptedSpecifics(GetEntry());
307 ModelType
BaseNode::GetModelType() const {
308 return GetEntry()->GetModelType();
311 void BaseNode::SetUnencryptedSpecifics(
312 const sync_pb::EntitySpecifics
& specifics
) {
313 ModelType type
= GetModelTypeFromSpecifics(specifics
);
314 DCHECK_NE(UNSPECIFIED
, type
);
315 if (GetModelType() != UNSPECIFIED
) {
316 DCHECK_EQ(GetModelType(), type
);
318 unencrypted_data_
.CopyFrom(specifics
);
321 } // namespace syncer