diff options
Diffstat (limited to 'chrome/browser/sync/internal_api/base_node.cc')
| -rw-r--r-- | chrome/browser/sync/internal_api/base_node.cc | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/chrome/browser/sync/internal_api/base_node.cc b/chrome/browser/sync/internal_api/base_node.cc new file mode 100644 index 0000000..002006c --- /dev/null +++ b/chrome/browser/sync/internal_api/base_node.cc @@ -0,0 +1,298 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/sync/internal_api/base_node.h" + +#include "base/base64.h" +#include "base/sha1.h" +#include "base/string_number_conversions.h" +#include "base/values.h" +#include "chrome/browser/sync/engine/syncapi_internal.h" +#include "chrome/browser/sync/internal_api/base_transaction.h" +#include "chrome/browser/sync/protocol/app_specifics.pb.h" +#include "chrome/browser/sync/protocol/autofill_specifics.pb.h" +#include "chrome/browser/sync/protocol/bookmark_specifics.pb.h" +#include "chrome/browser/sync/protocol/extension_specifics.pb.h" +#include "chrome/browser/sync/protocol/nigori_specifics.pb.h" +#include "chrome/browser/sync/protocol/password_specifics.pb.h" +#include "chrome/browser/sync/protocol/session_specifics.pb.h" +#include "chrome/browser/sync/protocol/theme_specifics.pb.h" +#include "chrome/browser/sync/protocol/typed_url_specifics.pb.h" +#include "chrome/browser/sync/syncable/directory_manager.h" +#include "chrome/browser/sync/syncable/syncable.h" +#include "chrome/browser/sync/syncable/syncable_id.h" + +using syncable::SPECIFICS; +using sync_pb::AutofillProfileSpecifics; + +namespace sync_api { + +// Helper function to look up the int64 metahandle of an object given the ID +// string. +static int64 IdToMetahandle(syncable::BaseTransaction* trans, + const syncable::Id& id) { + syncable::Entry entry(trans, syncable::GET_BY_ID, id); + if (!entry.good()) + return kInvalidId; + return entry.Get(syncable::META_HANDLE); +} + +static bool EndsWithSpace(const std::string& string) { + return !string.empty() && *string.rbegin() == ' '; +} + +// In the reverse direction, if a server name matches the pattern of a +// server-illegal name followed by one or more spaces, remove the trailing +// space. +static void ServerNameToSyncAPIName(const std::string& server_name, + std::string* out) { + CHECK(out); + int length_to_copy = server_name.length(); + if (IsNameServerIllegalAfterTrimming(server_name) && + EndsWithSpace(server_name)) { + --length_to_copy; + } + *out = std::string(server_name.c_str(), length_to_copy); +} + +BaseNode::BaseNode() : password_data_(new sync_pb::PasswordSpecificsData) {} + +BaseNode::~BaseNode() {} + +std::string BaseNode::GenerateSyncableHash( + syncable::ModelType model_type, const std::string& client_tag) { + // blank PB with just the extension in it has termination symbol, + // handy for delimiter + sync_pb::EntitySpecifics serialized_type; + syncable::AddDefaultExtensionValue(model_type, &serialized_type); + std::string hash_input; + serialized_type.AppendToString(&hash_input); + hash_input.append(client_tag); + + std::string encode_output; + CHECK(base::Base64Encode(base::SHA1HashString(hash_input), &encode_output)); + return encode_output; +} + +bool BaseNode::DecryptIfNecessary() { + if (!GetEntry()->Get(syncable::UNIQUE_SERVER_TAG).empty()) + return true; // Ignore unique folders. + const sync_pb::EntitySpecifics& specifics = + GetEntry()->Get(syncable::SPECIFICS); + if (specifics.HasExtension(sync_pb::password)) { + // Passwords have their own legacy encryption structure. + scoped_ptr<sync_pb::PasswordSpecificsData> data(DecryptPasswordSpecifics( + specifics, GetTransaction()->GetCryptographer())); + if (!data.get()) { + LOG(ERROR) << "Failed to decrypt password specifics."; + return false; + } + password_data_.swap(data); + return true; + } + + // We assume any node with the encrypted field set has encrypted data. + if (!specifics.has_encrypted()) + return true; + + const sync_pb::EncryptedData& encrypted = + specifics.encrypted(); + std::string plaintext_data = GetTransaction()->GetCryptographer()-> + DecryptToString(encrypted); + if (plaintext_data.length() == 0 || + !unencrypted_data_.ParseFromString(plaintext_data)) { + LOG(ERROR) << "Failed to decrypt encrypted node of type " << + syncable::ModelTypeToString(GetModelType()) << "."; + return false; + } + VLOG(2) << "Decrypted specifics of type " + << syncable::ModelTypeToString(GetModelType()) + << " with content: " << plaintext_data; + return true; +} + +const sync_pb::EntitySpecifics& BaseNode::GetUnencryptedSpecifics( + const syncable::Entry* entry) const { + const sync_pb::EntitySpecifics& specifics = entry->Get(SPECIFICS); + if (specifics.has_encrypted()) { + DCHECK(syncable::GetModelTypeFromSpecifics(unencrypted_data_) != + syncable::UNSPECIFIED); + return unencrypted_data_; + } else { + DCHECK(syncable::GetModelTypeFromSpecifics(unencrypted_data_) == + syncable::UNSPECIFIED); + return specifics; + } +} + +int64 BaseNode::GetParentId() const { + return IdToMetahandle(GetTransaction()->GetWrappedTrans(), + GetEntry()->Get(syncable::PARENT_ID)); +} + +int64 BaseNode::GetId() const { + return GetEntry()->Get(syncable::META_HANDLE); +} + +int64 BaseNode::GetModificationTime() const { + return GetEntry()->Get(syncable::MTIME); +} + +bool BaseNode::GetIsFolder() const { + return GetEntry()->Get(syncable::IS_DIR); +} + +std::string BaseNode::GetTitle() const { + std::string result; + // TODO(zea): refactor bookmarks to not need this functionality. + if (syncable::BOOKMARKS == GetModelType() && + GetEntry()->Get(syncable::SPECIFICS).has_encrypted()) { + // Special case for legacy bookmarks dealing with encryption. + ServerNameToSyncAPIName(GetBookmarkSpecifics().title(), &result); + } else { + ServerNameToSyncAPIName(GetEntry()->Get(syncable::NON_UNIQUE_NAME), + &result); + } + return result; +} + +GURL BaseNode::GetURL() const { + return GURL(GetBookmarkSpecifics().url()); +} + +int64 BaseNode::GetPredecessorId() const { + syncable::Id id_string = GetEntry()->Get(syncable::PREV_ID); + if (id_string.IsRoot()) + return kInvalidId; + return IdToMetahandle(GetTransaction()->GetWrappedTrans(), id_string); +} + +int64 BaseNode::GetSuccessorId() const { + syncable::Id id_string = GetEntry()->Get(syncable::NEXT_ID); + if (id_string.IsRoot()) + return kInvalidId; + return IdToMetahandle(GetTransaction()->GetWrappedTrans(), id_string); +} + +int64 BaseNode::GetFirstChildId() const { + syncable::Directory* dir = GetTransaction()->GetLookup(); + syncable::BaseTransaction* trans = GetTransaction()->GetWrappedTrans(); + syncable::Id id_string = + dir->GetFirstChildId(trans, GetEntry()->Get(syncable::ID)); + if (id_string.IsRoot()) + return kInvalidId; + return IdToMetahandle(GetTransaction()->GetWrappedTrans(), id_string); +} + +DictionaryValue* BaseNode::GetSummaryAsValue() const { + DictionaryValue* node_info = new DictionaryValue(); + node_info->SetString("id", base::Int64ToString(GetId())); + node_info->SetBoolean("isFolder", GetIsFolder()); + node_info->SetString("title", GetTitle()); + node_info->Set("type", ModelTypeToValue(GetModelType())); + return node_info; +} + +DictionaryValue* BaseNode::GetDetailsAsValue() const { + DictionaryValue* node_info = GetSummaryAsValue(); + // TODO(akalin): Return time in a better format. + node_info->SetString("modificationTime", + base::Int64ToString(GetModificationTime())); + node_info->SetString("parentId", base::Int64ToString(GetParentId())); + // Specifics are already in the Entry value, so no need to duplicate + // it here. + node_info->SetString("externalId", + base::Int64ToString(GetExternalId())); + node_info->SetString("predecessorId", + base::Int64ToString(GetPredecessorId())); + node_info->SetString("successorId", + base::Int64ToString(GetSuccessorId())); + node_info->SetString("firstChildId", + base::Int64ToString(GetFirstChildId())); + node_info->Set("entry", GetEntry()->ToValue()); + return node_info; +} + +void BaseNode::GetFaviconBytes(std::vector<unsigned char>* output) const { + if (!output) + return; + const std::string& favicon = GetBookmarkSpecifics().favicon(); + output->assign(reinterpret_cast<const unsigned char*>(favicon.data()), + reinterpret_cast<const unsigned char*>(favicon.data() + + favicon.length())); +} + +int64 BaseNode::GetExternalId() const { + return GetEntry()->Get(syncable::LOCAL_EXTERNAL_ID); +} + +const sync_pb::AppSpecifics& BaseNode::GetAppSpecifics() const { + DCHECK_EQ(syncable::APPS, GetModelType()); + return GetEntitySpecifics().GetExtension(sync_pb::app); +} + +const sync_pb::AutofillSpecifics& BaseNode::GetAutofillSpecifics() const { + DCHECK_EQ(syncable::AUTOFILL, GetModelType()); + return GetEntitySpecifics().GetExtension(sync_pb::autofill); +} + +const AutofillProfileSpecifics& BaseNode::GetAutofillProfileSpecifics() const { + DCHECK_EQ(GetModelType(), syncable::AUTOFILL_PROFILE); + return GetEntitySpecifics().GetExtension(sync_pb::autofill_profile); +} + +const sync_pb::BookmarkSpecifics& BaseNode::GetBookmarkSpecifics() const { + DCHECK_EQ(syncable::BOOKMARKS, GetModelType()); + return GetEntitySpecifics().GetExtension(sync_pb::bookmark); +} + +const sync_pb::NigoriSpecifics& BaseNode::GetNigoriSpecifics() const { + DCHECK_EQ(syncable::NIGORI, GetModelType()); + return GetEntitySpecifics().GetExtension(sync_pb::nigori); +} + +const sync_pb::PasswordSpecificsData& BaseNode::GetPasswordSpecifics() const { + DCHECK_EQ(syncable::PASSWORDS, GetModelType()); + return *password_data_; +} + +const sync_pb::ThemeSpecifics& BaseNode::GetThemeSpecifics() const { + DCHECK_EQ(syncable::THEMES, GetModelType()); + return GetEntitySpecifics().GetExtension(sync_pb::theme); +} + +const sync_pb::TypedUrlSpecifics& BaseNode::GetTypedUrlSpecifics() const { + DCHECK_EQ(syncable::TYPED_URLS, GetModelType()); + return GetEntitySpecifics().GetExtension(sync_pb::typed_url); +} + +const sync_pb::ExtensionSpecifics& BaseNode::GetExtensionSpecifics() const { + DCHECK_EQ(syncable::EXTENSIONS, GetModelType()); + return GetEntitySpecifics().GetExtension(sync_pb::extension); +} + +const sync_pb::SessionSpecifics& BaseNode::GetSessionSpecifics() const { + DCHECK_EQ(syncable::SESSIONS, GetModelType()); + return GetEntitySpecifics().GetExtension(sync_pb::session); +} + +const sync_pb::EntitySpecifics& BaseNode::GetEntitySpecifics() const { + return GetUnencryptedSpecifics(GetEntry()); +} + +syncable::ModelType BaseNode::GetModelType() const { + return GetEntry()->GetModelType(); +} + +void BaseNode::SetUnencryptedSpecifics( + const sync_pb::EntitySpecifics& specifics) { + syncable::ModelType type = syncable::GetModelTypeFromSpecifics(specifics); + DCHECK_NE(syncable::UNSPECIFIED, type); + if (GetModelType() != syncable::UNSPECIFIED) { + DCHECK_EQ(GetModelType(), type); + } + unencrypted_data_.CopyFrom(specifics); +} + +} // namespace sync_api |
