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