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 27using sync_pb::AutofillProfileSpecifics; 28 29namespace syncer { 30 31using syncable::SPECIFICS; 32 33// Helper function to look up the int64 metahandle of an object given the ID 34// string. 35static 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 43BaseNode::BaseNode() : password_data_(new sync_pb::PasswordSpecificsData) {} 44 45BaseNode::~BaseNode() {} 46 47bool 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 109const 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 141int64 BaseNode::GetParentId() const { 142 return IdToMetahandle(GetTransaction()->GetWrappedTrans(), 143 GetEntry()->GetParentId()); 144} 145 146int64 BaseNode::GetId() const { 147 return GetEntry()->GetMetahandle(); 148} 149 150base::Time BaseNode::GetModificationTime() const { 151 return GetEntry()->GetMtime(); 152} 153 154bool BaseNode::GetIsFolder() const { 155 return GetEntry()->GetIsDir(); 156} 157 158std::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 172bool BaseNode::HasChildren() const { 173 syncable::Directory* dir = GetTransaction()->GetDirectory(); 174 syncable::BaseTransaction* trans = GetTransaction()->GetWrappedTrans(); 175 return dir->HasChildren(trans, GetEntry()->GetId()); 176} 177 178int64 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 185int64 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 192int64 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 199void BaseNode::GetChildIds(std::vector<int64>* result) const { 200 GetEntry()->GetChildHandles(result); 201} 202 203int BaseNode::GetTotalNodeCount() const { 204 return GetEntry()->GetTotalNodeCount(); 205} 206 207int BaseNode::GetPositionIndex() const { 208 return GetEntry()->GetPositionIndex(); 209} 210 211base::DictionaryValue* BaseNode::ToValue() const { 212 return GetEntry()->ToValue(GetTransaction()->GetCryptographer()); 213} 214 215int64 BaseNode::GetExternalId() const { 216 return GetEntry()->GetLocalExternalId(); 217} 218 219const sync_pb::AppSpecifics& BaseNode::GetAppSpecifics() const { 220 DCHECK_EQ(GetModelType(), APPS); 221 return GetEntitySpecifics().app(); 222} 223 224const sync_pb::AutofillSpecifics& BaseNode::GetAutofillSpecifics() const { 225 DCHECK_EQ(GetModelType(), AUTOFILL); 226 return GetEntitySpecifics().autofill(); 227} 228 229const AutofillProfileSpecifics& BaseNode::GetAutofillProfileSpecifics() const { 230 DCHECK_EQ(GetModelType(), AUTOFILL_PROFILE); 231 return GetEntitySpecifics().autofill_profile(); 232} 233 234const sync_pb::BookmarkSpecifics& BaseNode::GetBookmarkSpecifics() const { 235 DCHECK_EQ(GetModelType(), BOOKMARKS); 236 return GetEntitySpecifics().bookmark(); 237} 238 239const sync_pb::NigoriSpecifics& BaseNode::GetNigoriSpecifics() const { 240 DCHECK_EQ(GetModelType(), NIGORI); 241 return GetEntitySpecifics().nigori(); 242} 243 244const sync_pb::PasswordSpecificsData& BaseNode::GetPasswordSpecifics() const { 245 DCHECK_EQ(GetModelType(), PASSWORDS); 246 return *password_data_; 247} 248 249const sync_pb::ThemeSpecifics& BaseNode::GetThemeSpecifics() const { 250 DCHECK_EQ(GetModelType(), THEMES); 251 return GetEntitySpecifics().theme(); 252} 253 254const sync_pb::TypedUrlSpecifics& BaseNode::GetTypedUrlSpecifics() const { 255 DCHECK_EQ(GetModelType(), TYPED_URLS); 256 return GetEntitySpecifics().typed_url(); 257} 258 259const sync_pb::ExtensionSpecifics& BaseNode::GetExtensionSpecifics() const { 260 DCHECK_EQ(GetModelType(), EXTENSIONS); 261 return GetEntitySpecifics().extension(); 262} 263 264const sync_pb::SessionSpecifics& BaseNode::GetSessionSpecifics() const { 265 DCHECK_EQ(GetModelType(), SESSIONS); 266 return GetEntitySpecifics().session(); 267} 268 269const sync_pb::DeviceInfoSpecifics& BaseNode::GetDeviceInfoSpecifics() const { 270 DCHECK_EQ(GetModelType(), DEVICE_INFO); 271 return GetEntitySpecifics().device_info(); 272} 273 274const sync_pb::ExperimentsSpecifics& BaseNode::GetExperimentsSpecifics() const { 275 DCHECK_EQ(GetModelType(), EXPERIMENTS); 276 return GetEntitySpecifics().experiments(); 277} 278 279const sync_pb::PriorityPreferenceSpecifics& 280 BaseNode::GetPriorityPreferenceSpecifics() const { 281 DCHECK_EQ(GetModelType(), PRIORITY_PREFERENCES); 282 return GetEntitySpecifics().priority_preference(); 283} 284 285const sync_pb::EntitySpecifics& BaseNode::GetEntitySpecifics() const { 286 return GetUnencryptedSpecifics(GetEntry()); 287} 288 289ModelType BaseNode::GetModelType() const { 290 return GetEntry()->GetModelType(); 291} 292 293const 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 303void 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