ac047686a5f1c0b9d83555230c3c12453a548f62
[platform/framework/web/crosswalk.git] / src / sync / internal_api / base_node.cc
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 void BaseNode::SetUnencryptedSpecifics(
294     const sync_pb::EntitySpecifics& specifics) {
295   ModelType type = GetModelTypeFromSpecifics(specifics);
296   DCHECK_NE(UNSPECIFIED, type);
297   if (GetModelType() != UNSPECIFIED) {
298     DCHECK_EQ(GetModelType(), type);
299   }
300   unencrypted_data_.CopyFrom(specifics);
301 }
302
303 }  // namespace syncer