// Copyright (c) 2012 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 "dbus/values_util.h" #include #include #include "base/json/json_writer.h" #include "base/logging.h" #include "base/notreached.h" #include "base/values.h" #include "dbus/message.h" namespace dbus { namespace { // Returns whether |value| is exactly representable by double or not. template bool IsExactlyRepresentableByDouble(T value) { return value == static_cast(static_cast(value)); } // Pops values from |reader| and appends them to |list_value|. bool PopListElements(MessageReader* reader, base::Value* list_value) { DCHECK(list_value->is_list()); while (reader->HasMoreData()) { base::Value element_value = PopDataAsValue(reader); if (element_value.is_none()) return false; list_value->Append(std::move(element_value)); } return true; } // Pops dict-entries from |reader| and sets them to |dictionary_value| bool PopDictionaryEntries(MessageReader* reader, base::Value* dictionary_value) { DCHECK(dictionary_value->is_dict()); while (reader->HasMoreData()) { DCHECK_EQ(Message::DICT_ENTRY, reader->GetDataType()); MessageReader entry_reader(nullptr); if (!reader->PopDictEntry(&entry_reader)) return false; // Get key as a string. std::string key_string; if (entry_reader.GetDataType() == Message::STRING) { // If the type of keys is STRING, pop it directly. if (!entry_reader.PopString(&key_string)) return false; } else { // If the type of keys is not STRING, convert it to string. base::Value key = PopDataAsValue(&entry_reader); if (key.is_none()) return false; // Use JSONWriter to convert an arbitrary value to a string. base::JSONWriter::Write(key, &key_string); } // Get the value and set the key-value pair. base::Value value = PopDataAsValue(&entry_reader); if (value.is_none()) return false; dictionary_value->SetKey(key_string, std::move(value)); } return true; } // Gets the D-Bus type signature for the value. std::string GetTypeSignature(base::ValueView value) { struct Visitor { std::string operator()(absl::monostate) { DLOG(ERROR) << "Unexpected type " << base::Value::Type::NONE; return std::string(); } std::string operator()(bool) { return "b"; } std::string operator()(int) { return "i"; } std::string operator()(double) { return "d"; } std::string operator()(base::StringPiece) { return "s"; } std::string operator()(const base::Value::BlobStorage&) { return "ay"; } std::string operator()(const base::Value::Dict&) { return "a{sv}"; } std::string operator()(const base::Value::List&) { return "av"; } }; return value.Visit(Visitor()); } } // namespace base::Value PopDataAsValue(MessageReader* reader) { base::Value result; switch (reader->GetDataType()) { case Message::INVALID_DATA: // Do nothing. break; case Message::BYTE: { uint8_t value = 0; if (reader->PopByte(&value)) result = base::Value(value); break; } case Message::BOOL: { bool value = false; if (reader->PopBool(&value)) result = base::Value(value); break; } case Message::INT16: { int16_t value = 0; if (reader->PopInt16(&value)) result = base::Value(value); break; } case Message::UINT16: { uint16_t value = 0; if (reader->PopUint16(&value)) result = base::Value(value); break; } case Message::INT32: { int32_t value = 0; if (reader->PopInt32(&value)) result = base::Value(value); break; } case Message::UINT32: { uint32_t value = 0; if (reader->PopUint32(&value)) { result = base::Value(static_cast(value)); } break; } case Message::INT64: { int64_t value = 0; if (reader->PopInt64(&value)) { DLOG_IF(WARNING, !IsExactlyRepresentableByDouble(value)) << value << " is not exactly representable by double"; result = base::Value(static_cast(value)); } break; } case Message::UINT64: { uint64_t value = 0; if (reader->PopUint64(&value)) { DLOG_IF(WARNING, !IsExactlyRepresentableByDouble(value)) << value << " is not exactly representable by double"; result = base::Value(static_cast(value)); } break; } case Message::DOUBLE: { double value = 0; if (reader->PopDouble(&value)) result = base::Value(value); break; } case Message::STRING: { std::string value; if (reader->PopString(&value)) result = base::Value(value); break; } case Message::OBJECT_PATH: { ObjectPath value; if (reader->PopObjectPath(&value)) result = base::Value(value.value()); break; } case Message::UNIX_FD: { // Cannot distinguish a file descriptor from an int NOTREACHED(); break; } case Message::ARRAY: { MessageReader sub_reader(nullptr); if (reader->PopArray(&sub_reader)) { // If the type of the array's element is DICT_ENTRY, create a // Value with type base::Value::Type::DICTIONARY, otherwise create a // Value with type base::Value::Type::LIST. if (sub_reader.GetDataType() == Message::DICT_ENTRY) { auto dictionary_value = base::Value(base::Value::Type::DICTIONARY); if (PopDictionaryEntries(&sub_reader, &dictionary_value)) result = std::move(dictionary_value); } else { auto list_value = base::Value(base::Value::Type::LIST); if (PopListElements(&sub_reader, &list_value)) result = std::move(list_value); } } break; } case Message::STRUCT: { MessageReader sub_reader(nullptr); if (reader->PopStruct(&sub_reader)) { auto list_value = base::Value(base::Value::Type::LIST); if (PopListElements(&sub_reader, &list_value)) result = std::move(list_value); } break; } case Message::DICT_ENTRY: // DICT_ENTRY must be popped as an element of an array. NOTREACHED(); break; case Message::VARIANT: { MessageReader sub_reader(nullptr); if (reader->PopVariant(&sub_reader)) result = PopDataAsValue(&sub_reader); break; } } return result; } void AppendBasicTypeValueData(MessageWriter* writer, base::ValueView value) { struct Visitor { MessageWriter* writer; void operator()(absl::monostate) { DLOG(ERROR) << "Unexpected type: " << base::Value::Type::NONE; } void operator()(bool value) { writer->AppendBool(value); } void operator()(int value) { writer->AppendInt32(value); } void operator()(double value) { writer->AppendDouble(value); } void operator()(base::StringPiece value) { writer->AppendString(value); } void operator()(const base::Value::BlobStorage&) { DLOG(ERROR) << "Unexpected type: " << base::Value::Type::BINARY; } void operator()(const base::Value::Dict&) { DLOG(ERROR) << "Unexpected type: " << base::Value::Type::DICT; } void operator()(const base::Value::List&) { DLOG(ERROR) << "Unexpected type: " << base::Value::Type::LIST; } }; value.Visit(Visitor{.writer = writer}); } void AppendBasicTypeValueDataAsVariant(MessageWriter* writer, base::ValueView value) { MessageWriter sub_writer(nullptr); writer->OpenVariant(GetTypeSignature(value), &sub_writer); AppendBasicTypeValueData(&sub_writer, value); writer->CloseContainer(&sub_writer); } void AppendValueData(MessageWriter* writer, base::ValueView value) { struct Visitor { MessageWriter* writer; void operator()(absl::monostate) { DLOG(ERROR) << "Unexpected type: " << base::Value::Type::NONE; } void operator()(bool value) { return AppendBasicTypeValueData(writer, value); } void operator()(int value) { return AppendBasicTypeValueData(writer, value); } void operator()(double value) { return AppendBasicTypeValueData(writer, value); } void operator()(base::StringPiece value) { return AppendBasicTypeValueData(writer, value); } void operator()(const base::Value::BlobStorage& value) { DLOG(ERROR) << "Unexpected type: " << base::Value::Type::BINARY; } void operator()(const base::Value::Dict& value) { dbus::MessageWriter array_writer(nullptr); writer->OpenArray("{sv}", &array_writer); for (auto item : value) { dbus::MessageWriter dict_entry_writer(nullptr); array_writer.OpenDictEntry(&dict_entry_writer); dict_entry_writer.AppendString(item.first); AppendValueDataAsVariant(&dict_entry_writer, item.second); array_writer.CloseContainer(&dict_entry_writer); } writer->CloseContainer(&array_writer); } void operator()(const base::Value::List& value) { dbus::MessageWriter array_writer(nullptr); writer->OpenArray("v", &array_writer); for (const auto& value_in_list : value) { AppendValueDataAsVariant(&array_writer, value_in_list); } writer->CloseContainer(&array_writer); } }; value.Visit(Visitor{.writer = writer}); } void AppendValueDataAsVariant(MessageWriter* writer, base::ValueView value) { MessageWriter variant_writer(nullptr); writer->OpenVariant(GetTypeSignature(value), &variant_writer); AppendValueData(&variant_writer, value); writer->CloseContainer(&variant_writer); } } // namespace dbus