• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "base/values.h"
12 #include "sync/internal_api/public/base_transaction.h"
13 #include "sync/internal_api/syncapi_internal.h"
14 #include "sync/protocol/app_specifics.pb.h"
15 #include "sync/protocol/autofill_specifics.pb.h"
16 #include "sync/protocol/bookmark_specifics.pb.h"
17 #include "sync/protocol/extension_specifics.pb.h"
18 #include "sync/protocol/nigori_specifics.pb.h"
19 #include "sync/protocol/password_specifics.pb.h"
20 #include "sync/protocol/session_specifics.pb.h"
21 #include "sync/protocol/theme_specifics.pb.h"
22 #include "sync/protocol/typed_url_specifics.pb.h"
23 #include "sync/syncable/directory.h"
24 #include "sync/syncable/entry.h"
25 #include "sync/syncable/syncable_id.h"
26 #include "sync/util/time.h"
27 
28 using sync_pb::AutofillProfileSpecifics;
29 
30 namespace syncer {
31 
32 using syncable::SPECIFICS;
33 
34 // Helper function to look up the int64 metahandle of an object given the ID
35 // string.
IdToMetahandle(syncable::BaseTransaction * trans,const syncable::Id & id)36 static int64 IdToMetahandle(syncable::BaseTransaction* trans,
37                             const syncable::Id& id) {
38   syncable::Entry entry(trans, syncable::GET_BY_ID, id);
39   if (!entry.good())
40     return kInvalidId;
41   return entry.GetMetahandle();
42 }
43 
EndsWithSpace(const std::string & string)44 static bool EndsWithSpace(const std::string& string) {
45   return !string.empty() && *string.rbegin() == ' ';
46 }
47 
48 // In the reverse direction, if a server name matches the pattern of a
49 // server-illegal name followed by one or more spaces, remove the trailing
50 // space.
ServerNameToSyncAPIName(const std::string & server_name,std::string * out)51 static void ServerNameToSyncAPIName(const std::string& server_name,
52                                     std::string* out) {
53   CHECK(out);
54   int length_to_copy = server_name.length();
55   if (IsNameServerIllegalAfterTrimming(server_name) &&
56       EndsWithSpace(server_name)) {
57     --length_to_copy;
58   }
59   *out = std::string(server_name.c_str(), length_to_copy);
60 }
61 
BaseNode()62 BaseNode::BaseNode() : password_data_(new sync_pb::PasswordSpecificsData) {}
63 
~BaseNode()64 BaseNode::~BaseNode() {}
65 
DecryptIfNecessary()66 bool BaseNode::DecryptIfNecessary() {
67   if (!GetEntry()->GetUniqueServerTag().empty())
68       return true;  // Ignore unique folders.
69   const sync_pb::EntitySpecifics& specifics =
70       GetEntry()->GetSpecifics();
71   if (specifics.has_password()) {
72     // Passwords have their own legacy encryption structure.
73     scoped_ptr<sync_pb::PasswordSpecificsData> data(DecryptPasswordSpecifics(
74         specifics, GetTransaction()->GetCryptographer()));
75     if (!data) {
76       LOG(ERROR) << "Failed to decrypt password specifics.";
77       return false;
78     }
79     password_data_.swap(data);
80     return true;
81   }
82 
83   // We assume any node with the encrypted field set has encrypted data and if
84   // not we have no work to do, with the exception of bookmarks. For bookmarks
85   // we must make sure the bookmarks data has the title field supplied. If not,
86   // we fill the unencrypted_data_ with a copy of the bookmark specifics that
87   // follows the new bookmarks format.
88   if (!specifics.has_encrypted()) {
89     if (GetModelType() == BOOKMARKS &&
90         !specifics.bookmark().has_title() &&
91         !GetTitle().empty()) {  // Last check ensures this isn't a new node.
92       // We need to fill in the title.
93       std::string title = GetTitle();
94       std::string server_legal_title;
95       SyncAPINameToServerName(title, &server_legal_title);
96       DVLOG(1) << "Reading from legacy bookmark, manually returning title "
97                << title;
98       unencrypted_data_.CopyFrom(specifics);
99       unencrypted_data_.mutable_bookmark()->set_title(
100           server_legal_title);
101     }
102     return true;
103   }
104 
105   const sync_pb::EncryptedData& encrypted = specifics.encrypted();
106   std::string plaintext_data = GetTransaction()->GetCryptographer()->
107       DecryptToString(encrypted);
108   if (plaintext_data.length() == 0) {
109     LOG(ERROR) << "Failed to decrypt encrypted node of type "
110                << ModelTypeToString(GetModelType()) << ".";
111     // Debugging for crbug.com/123223. We failed to decrypt the data, which
112     // means we applied an update without having the key or lost the key at a
113     // later point.
114     CHECK(false);
115     return false;
116   } else if (!unencrypted_data_.ParseFromString(plaintext_data)) {
117     // Debugging for crbug.com/123223. We should never succeed in decrypting
118     // but fail to parse into a protobuf.
119     CHECK(false);
120     return false;
121   }
122   DVLOG(2) << "Decrypted specifics of type "
123            << ModelTypeToString(GetModelType())
124            << " with content: " << plaintext_data;
125   return true;
126 }
127 
GetUnencryptedSpecifics(const syncable::Entry * entry) const128 const sync_pb::EntitySpecifics& BaseNode::GetUnencryptedSpecifics(
129     const syncable::Entry* entry) const {
130   const sync_pb::EntitySpecifics& specifics = entry->GetSpecifics();
131   if (specifics.has_encrypted()) {
132     DCHECK_NE(GetModelTypeFromSpecifics(unencrypted_data_), UNSPECIFIED);
133     return unencrypted_data_;
134   } else {
135     // Due to the change in bookmarks format, we need to check to see if this is
136     // a legacy bookmarks (and has no title field in the proto). If it is, we
137     // return the unencrypted_data_, which was filled in with the title by
138     // DecryptIfNecessary().
139     if (GetModelType() == BOOKMARKS) {
140       const sync_pb::BookmarkSpecifics& bookmark_specifics =
141           specifics.bookmark();
142       if (bookmark_specifics.has_title() ||
143           GetTitle().empty() ||  // For the empty node case
144           !GetEntry()->GetUniqueServerTag().empty()) {
145         // It's possible we previously had to convert and set
146         // |unencrypted_data_| but then wrote our own data, so we allow
147         // |unencrypted_data_| to be non-empty.
148         return specifics;
149       } else {
150         DCHECK_EQ(GetModelTypeFromSpecifics(unencrypted_data_), BOOKMARKS);
151         return unencrypted_data_;
152       }
153     } else {
154       DCHECK_EQ(GetModelTypeFromSpecifics(unencrypted_data_), UNSPECIFIED);
155       return specifics;
156     }
157   }
158 }
159 
GetParentId() const160 int64 BaseNode::GetParentId() const {
161   return IdToMetahandle(GetTransaction()->GetWrappedTrans(),
162                         GetEntry()->GetParentId());
163 }
164 
GetId() const165 int64 BaseNode::GetId() const {
166   return GetEntry()->GetMetahandle();
167 }
168 
GetModificationTime() const169 base::Time BaseNode::GetModificationTime() const {
170   return GetEntry()->GetMtime();
171 }
172 
GetIsFolder() const173 bool BaseNode::GetIsFolder() const {
174   return GetEntry()->GetIsDir();
175 }
176 
GetTitle() const177 std::string BaseNode::GetTitle() const {
178   std::string result;
179   // TODO(zea): refactor bookmarks to not need this functionality.
180   if (BOOKMARKS == GetModelType() &&
181       GetEntry()->GetSpecifics().has_encrypted()) {
182     // Special case for legacy bookmarks dealing with encryption.
183     ServerNameToSyncAPIName(GetBookmarkSpecifics().title(), &result);
184   } else {
185     ServerNameToSyncAPIName(GetEntry()->GetNonUniqueName(),
186                             &result);
187   }
188   return result;
189 }
190 
HasChildren() const191 bool BaseNode::HasChildren() const {
192   syncable::Directory* dir = GetTransaction()->GetDirectory();
193   syncable::BaseTransaction* trans = GetTransaction()->GetWrappedTrans();
194   return dir->HasChildren(trans, GetEntry()->GetId());
195 }
196 
GetPredecessorId() const197 int64 BaseNode::GetPredecessorId() const {
198   syncable::Id id_string = GetEntry()->GetPredecessorId();
199   if (id_string.IsRoot())
200     return kInvalidId;
201   return IdToMetahandle(GetTransaction()->GetWrappedTrans(), id_string);
202 }
203 
GetSuccessorId() const204 int64 BaseNode::GetSuccessorId() const {
205   syncable::Id id_string = GetEntry()->GetSuccessorId();
206   if (id_string.IsRoot())
207     return kInvalidId;
208   return IdToMetahandle(GetTransaction()->GetWrappedTrans(), id_string);
209 }
210 
GetFirstChildId() const211 int64 BaseNode::GetFirstChildId() const {
212   syncable::Id id_string = GetEntry()->GetFirstChildId();
213   if (id_string.IsRoot())
214     return kInvalidId;
215   return IdToMetahandle(GetTransaction()->GetWrappedTrans(), id_string);
216 }
217 
GetChildIds(std::vector<int64> * result) const218 void BaseNode::GetChildIds(std::vector<int64>* result) const {
219   GetEntry()->GetChildHandles(result);
220 }
221 
GetTotalNodeCount() const222 int BaseNode::GetTotalNodeCount() const {
223   return GetEntry()->GetTotalNodeCount();
224 }
225 
GetPositionIndex() const226 int BaseNode::GetPositionIndex() const {
227   return GetEntry()->GetPositionIndex();
228 }
229 
GetSummaryAsValue() const230 base::DictionaryValue* BaseNode::GetSummaryAsValue() const {
231   base::DictionaryValue* node_info = new base::DictionaryValue();
232   node_info->SetString("id", base::Int64ToString(GetId()));
233   node_info->SetBoolean("isFolder", GetIsFolder());
234   node_info->SetString("title", GetTitle());
235   node_info->Set("type", ModelTypeToValue(GetModelType()));
236   return node_info;
237 }
238 
GetDetailsAsValue() const239 base::DictionaryValue* BaseNode::GetDetailsAsValue() const {
240   base::DictionaryValue* node_info = GetSummaryAsValue();
241   node_info->SetString(
242       "modificationTime", GetTimeDebugString(GetModificationTime()));
243   node_info->SetString("parentId", base::Int64ToString(GetParentId()));
244   // Specifics are already in the Entry value, so no need to duplicate
245   // it here.
246   node_info->SetString("externalId", base::Int64ToString(GetExternalId()));
247   if (GetEntry()->ShouldMaintainPosition() &&
248       !GetEntry()->GetIsDel()) {
249     node_info->SetString("successorId", base::Int64ToString(GetSuccessorId()));
250     node_info->SetString(
251         "predecessorId", base::Int64ToString(GetPredecessorId()));
252   }
253   if (GetEntry()->GetIsDir()) {
254     node_info->SetString(
255         "firstChildId", base::Int64ToString(GetFirstChildId()));
256   }
257   node_info->Set(
258       "entry", GetEntry()->ToValue(GetTransaction()->GetCryptographer()));
259   return node_info;
260 }
261 
GetExternalId() const262 int64 BaseNode::GetExternalId() const {
263   return GetEntry()->GetLocalExternalId();
264 }
265 
GetAppSpecifics() const266 const sync_pb::AppSpecifics& BaseNode::GetAppSpecifics() const {
267   DCHECK_EQ(GetModelType(), APPS);
268   return GetEntitySpecifics().app();
269 }
270 
GetAutofillSpecifics() const271 const sync_pb::AutofillSpecifics& BaseNode::GetAutofillSpecifics() const {
272   DCHECK_EQ(GetModelType(), AUTOFILL);
273   return GetEntitySpecifics().autofill();
274 }
275 
GetAutofillProfileSpecifics() const276 const AutofillProfileSpecifics& BaseNode::GetAutofillProfileSpecifics() const {
277   DCHECK_EQ(GetModelType(), AUTOFILL_PROFILE);
278   return GetEntitySpecifics().autofill_profile();
279 }
280 
GetBookmarkSpecifics() const281 const sync_pb::BookmarkSpecifics& BaseNode::GetBookmarkSpecifics() const {
282   DCHECK_EQ(GetModelType(), BOOKMARKS);
283   return GetEntitySpecifics().bookmark();
284 }
285 
GetNigoriSpecifics() const286 const sync_pb::NigoriSpecifics& BaseNode::GetNigoriSpecifics() const {
287   DCHECK_EQ(GetModelType(), NIGORI);
288   return GetEntitySpecifics().nigori();
289 }
290 
GetPasswordSpecifics() const291 const sync_pb::PasswordSpecificsData& BaseNode::GetPasswordSpecifics() const {
292   DCHECK_EQ(GetModelType(), PASSWORDS);
293   return *password_data_;
294 }
295 
GetThemeSpecifics() const296 const sync_pb::ThemeSpecifics& BaseNode::GetThemeSpecifics() const {
297   DCHECK_EQ(GetModelType(), THEMES);
298   return GetEntitySpecifics().theme();
299 }
300 
GetTypedUrlSpecifics() const301 const sync_pb::TypedUrlSpecifics& BaseNode::GetTypedUrlSpecifics() const {
302   DCHECK_EQ(GetModelType(), TYPED_URLS);
303   return GetEntitySpecifics().typed_url();
304 }
305 
GetExtensionSpecifics() const306 const sync_pb::ExtensionSpecifics& BaseNode::GetExtensionSpecifics() const {
307   DCHECK_EQ(GetModelType(), EXTENSIONS);
308   return GetEntitySpecifics().extension();
309 }
310 
GetSessionSpecifics() const311 const sync_pb::SessionSpecifics& BaseNode::GetSessionSpecifics() const {
312   DCHECK_EQ(GetModelType(), SESSIONS);
313   return GetEntitySpecifics().session();
314 }
315 
316 const sync_pb::ManagedUserSettingSpecifics&
GetManagedUserSettingSpecifics() const317     BaseNode::GetManagedUserSettingSpecifics() const {
318   DCHECK_EQ(GetModelType(), MANAGED_USER_SETTINGS);
319   return GetEntitySpecifics().managed_user_setting();
320 }
321 
GetManagedUserSpecifics() const322 const sync_pb::ManagedUserSpecifics& BaseNode::GetManagedUserSpecifics() const {
323   DCHECK_EQ(GetModelType(), MANAGED_USERS);
324   return GetEntitySpecifics().managed_user();
325 }
326 
GetDeviceInfoSpecifics() const327 const sync_pb::DeviceInfoSpecifics& BaseNode::GetDeviceInfoSpecifics() const {
328   DCHECK_EQ(GetModelType(), DEVICE_INFO);
329   return GetEntitySpecifics().device_info();
330 }
331 
GetExperimentsSpecifics() const332 const sync_pb::ExperimentsSpecifics& BaseNode::GetExperimentsSpecifics() const {
333   DCHECK_EQ(GetModelType(), EXPERIMENTS);
334   return GetEntitySpecifics().experiments();
335 }
336 
337 const sync_pb::PriorityPreferenceSpecifics&
GetPriorityPreferenceSpecifics() const338     BaseNode::GetPriorityPreferenceSpecifics() const {
339   DCHECK_EQ(GetModelType(), PRIORITY_PREFERENCES);
340   return GetEntitySpecifics().priority_preference();
341 }
342 
GetEntitySpecifics() const343 const sync_pb::EntitySpecifics& BaseNode::GetEntitySpecifics() const {
344   return GetUnencryptedSpecifics(GetEntry());
345 }
346 
GetModelType() const347 ModelType BaseNode::GetModelType() const {
348   return GetEntry()->GetModelType();
349 }
350 
SetUnencryptedSpecifics(const sync_pb::EntitySpecifics & specifics)351 void BaseNode::SetUnencryptedSpecifics(
352     const sync_pb::EntitySpecifics& specifics) {
353   ModelType type = GetModelTypeFromSpecifics(specifics);
354   DCHECK_NE(UNSPECIFIED, type);
355   if (GetModelType() != UNSPECIFIED) {
356     DCHECK_EQ(GetModelType(), type);
357   }
358   unencrypted_data_.CopyFrom(specifics);
359 }
360 
361 }  // namespace syncer
362