// 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 <memory> #include <utility> #include "base/json/json_writer.h" #include "base/logging.h" #include "base/values.h" #include "dbus/message.h" namespace dbus { namespace { // Returns whether |value| is exactly representable by double or not. template<typename T> bool IsExactlyRepresentableByDouble(T value) { return value == static_cast<T>(static_cast<double>(value)); } // Pops values from |reader| and appends them to |list_value|. bool PopListElements(MessageReader* reader, base::ListValue* list_value) { while (reader->HasMoreData()) { std::unique_ptr<base::Value> element_value = PopDataAsValue(reader); if (!element_value) 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::DictionaryValue* dictionary_value) { 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. std::unique_ptr<base::Value> key(PopDataAsValue(&entry_reader)); if (!key) 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. std::unique_ptr<base::Value> value = PopDataAsValue(&entry_reader); if (!value) return false; dictionary_value->SetWithoutPathExpansion(key_string, std::move(value)); } return true; } // Gets the D-Bus type signature for the value. std::string GetTypeSignature(const base::Value& value) { switch (value.type()) { case base::Value::Type::BOOLEAN: return "b"; case base::Value::Type::INTEGER: return "i"; case base::Value::Type::DOUBLE: return "d"; case base::Value::Type::STRING: return "s"; case base::Value::Type::BINARY: return "ay"; case base::Value::Type::DICTIONARY: return "a{sv}"; case base::Value::Type::LIST: return "av"; default: DLOG(ERROR) << "Unexpected type " << value.type(); return std::string(); } } } // namespace std::unique_ptr<base::Value> PopDataAsValue(MessageReader* reader) { std::unique_ptr<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 = std::make_unique<base::Value>(value); break; } case Message::BOOL: { bool value = false; if (reader->PopBool(&value)) result = std::make_unique<base::Value>(value); break; } case Message::INT16: { int16_t value = 0; if (reader->PopInt16(&value)) result = std::make_unique<base::Value>(value); break; } case Message::UINT16: { uint16_t value = 0; if (reader->PopUint16(&value)) result = std::make_unique<base::Value>(value); break; } case Message::INT32: { int32_t value = 0; if (reader->PopInt32(&value)) result = std::make_unique<base::Value>(value); break; } case Message::UINT32: { uint32_t value = 0; if (reader->PopUint32(&value)) { result = std::make_unique<base::Value>(static_cast<double>(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 = std::make_unique<base::Value>(static_cast<double>(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 = std::make_unique<base::Value>(static_cast<double>(value)); } break; } case Message::DOUBLE: { double value = 0; if (reader->PopDouble(&value)) result = std::make_unique<base::Value>(value); break; } case Message::STRING: { std::string value; if (reader->PopString(&value)) result = std::make_unique<base::Value>(value); break; } case Message::OBJECT_PATH: { ObjectPath value; if (reader->PopObjectPath(&value)) result = std::make_unique<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 // DictionaryValue, otherwise create a ListValue. if (sub_reader.GetDataType() == Message::DICT_ENTRY) { std::unique_ptr<base::DictionaryValue> dictionary_value( new base::DictionaryValue); if (PopDictionaryEntries(&sub_reader, dictionary_value.get())) result = std::move(dictionary_value); } else { std::unique_ptr<base::ListValue> list_value(new base::ListValue); if (PopListElements(&sub_reader, list_value.get())) result = std::move(list_value); } } break; } case Message::STRUCT: { MessageReader sub_reader(nullptr); if (reader->PopStruct(&sub_reader)) { std::unique_ptr<base::ListValue> list_value(new base::ListValue); if (PopListElements(&sub_reader, list_value.get())) 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, const base::Value& value) { switch (value.type()) { case base::Value::Type::BOOLEAN: { bool bool_value = false; bool success = value.GetAsBoolean(&bool_value); DCHECK(success); writer->AppendBool(bool_value); break; } case base::Value::Type::INTEGER: { int int_value = 0; bool success = value.GetAsInteger(&int_value); DCHECK(success); writer->AppendInt32(int_value); break; } case base::Value::Type::DOUBLE: { double double_value = 0; bool success = value.GetAsDouble(&double_value); DCHECK(success); writer->AppendDouble(double_value); break; } case base::Value::Type::STRING: { std::string string_value; bool success = value.GetAsString(&string_value); DCHECK(success); writer->AppendString(string_value); break; } default: DLOG(ERROR) << "Unexpected type " << value.type(); break; } } void AppendBasicTypeValueDataAsVariant(MessageWriter* writer, const base::Value& value) { MessageWriter sub_writer(nullptr); writer->OpenVariant(GetTypeSignature(value), &sub_writer); AppendBasicTypeValueData(&sub_writer, value); writer->CloseContainer(&sub_writer); } void AppendValueData(MessageWriter* writer, const base::Value& value) { switch (value.type()) { case base::Value::Type::DICTIONARY: { const base::DictionaryValue* dictionary = nullptr; value.GetAsDictionary(&dictionary); dbus::MessageWriter array_writer(nullptr); writer->OpenArray("{sv}", &array_writer); for (base::DictionaryValue::Iterator iter(*dictionary); !iter.IsAtEnd(); iter.Advance()) { dbus::MessageWriter dict_entry_writer(nullptr); array_writer.OpenDictEntry(&dict_entry_writer); dict_entry_writer.AppendString(iter.key()); AppendValueDataAsVariant(&dict_entry_writer, iter.value()); array_writer.CloseContainer(&dict_entry_writer); } writer->CloseContainer(&array_writer); break; } case base::Value::Type::LIST: { const base::ListValue* list = nullptr; value.GetAsList(&list); dbus::MessageWriter array_writer(nullptr); writer->OpenArray("v", &array_writer); for (const auto& value : *list) { AppendValueDataAsVariant(&array_writer, value); } writer->CloseContainer(&array_writer); break; } case base::Value::Type::BOOLEAN: case base::Value::Type::INTEGER: case base::Value::Type::DOUBLE: case base::Value::Type::STRING: AppendBasicTypeValueData(writer, value); break; default: DLOG(ERROR) << "Unexpected type: " << value.type(); } } void AppendValueDataAsVariant(MessageWriter* writer, const base::Value& value) { MessageWriter variant_writer(nullptr); writer->OpenVariant(GetTypeSignature(value), &variant_writer); AppendValueData(&variant_writer, value); writer->CloseContainer(&variant_writer); } } // namespace dbus