1 // Copyright 2014 The Chromium OS 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 "chromeos-dbus-bindings/dbus_signature.h"
6 
7 #include <base/logging.h>
8 #include <base/strings/stringprintf.h>
9 #include <brillo/strings/string_utils.h>
10 #include <dbus/dbus-protocol.h>
11 
12 using base::StringPrintf;
13 using std::string;
14 using std::vector;
15 
16 namespace chromeos_dbus_bindings {
17 
18 // static
19 const char DbusSignature::kArrayTypename[] = "std::vector";
20 const char DbusSignature::kBooleanTypename[] = "bool";
21 const char DbusSignature::kByteTypename[] = "uint8_t";
22 const char DbusSignature::kDefaultObjectPathTypename[] = "dbus::ObjectPath";
23 const char DbusSignature::kDictTypename[] = "std::map";
24 const char DbusSignature::kDoubleTypename[] = "double";
25 const char DbusSignature::kSigned16Typename[] = "int16_t";
26 const char DbusSignature::kSigned32Typename[] = "int32_t";
27 const char DbusSignature::kSigned64Typename[] = "int64_t";
28 const char DbusSignature::kStringTypename[] = "std::string";
29 const char DbusSignature::kUnixFdTypename[] = "dbus::FileDescriptor";
30 const char DbusSignature::kUnsigned16Typename[] = "uint16_t";
31 const char DbusSignature::kUnsigned32Typename[] = "uint32_t";
32 const char DbusSignature::kUnsigned64Typename[] = "uint64_t";
33 const char DbusSignature::kVariantTypename[] = "brillo::Any";
34 const char DbusSignature::kVariantDictTypename[] = "brillo::VariantDictionary";
35 const char DbusSignature::kTupleTypename[] = "std::tuple";
36 
DbusSignature()37 DbusSignature::DbusSignature()
38     : object_path_typename_(kDefaultObjectPathTypename) {}
39 
Parse(const string & signature,string * output)40 bool DbusSignature::Parse(const string& signature, string* output) {
41   string::const_iterator end;
42   if (!GetTypenameForSignature(
43           signature.begin(), signature.end(), &end, output)) {
44     LOG(ERROR) << "Parse failed for signature " << signature;
45     return false;
46   }
47   if (end != signature.end()) {
48     LOG(WARNING) << "A portion of signature " << signature
49                  << " is left unparsed: " << string(end, signature.end());
50   }
51   return true;
52 }
53 
GetTypenameForSignature(string::const_iterator signature,string::const_iterator end,string::const_iterator * next,string * output)54 bool DbusSignature::GetTypenameForSignature(
55     string::const_iterator signature,
56     string::const_iterator end,
57     string::const_iterator* next,
58     string* output) {
59   if (signature == end) {
60     LOG(ERROR) << "Signature is empty";
61     return false;
62   }
63 
64   string::const_iterator cur = signature;
65   int signature_value = *cur++;
66   switch (signature_value) {
67     case DBUS_STRUCT_BEGIN_CHAR:
68       if (!GetStructTypenameForSignature(cur, end, &cur, output)) {
69         return false;
70       }
71       break;
72 
73     case DBUS_TYPE_ARRAY:
74       if (!GetArrayTypenameForSignature(cur, end, &cur, output)) {
75         return false;
76       }
77       break;
78 
79     case DBUS_TYPE_BOOLEAN:
80       *output = kBooleanTypename;
81       break;
82 
83     case DBUS_TYPE_BYTE:
84       *output = kByteTypename;
85       break;
86 
87     case DBUS_TYPE_DOUBLE:
88       *output = kDoubleTypename;
89       break;
90 
91     case DBUS_TYPE_OBJECT_PATH:
92       *output = object_path_typename_;
93       break;
94 
95     case DBUS_TYPE_INT16:
96       *output = kSigned16Typename;
97       break;
98 
99     case DBUS_TYPE_INT32:
100       *output = kSigned32Typename;
101       break;
102 
103     case DBUS_TYPE_INT64:
104       *output = kSigned64Typename;
105       break;
106 
107     case DBUS_TYPE_STRING:
108       *output = kStringTypename;
109       break;
110 
111     case DBUS_TYPE_UNIX_FD:
112       *output = kUnixFdTypename;
113       break;
114 
115     case DBUS_TYPE_UINT16:
116       *output = kUnsigned16Typename;
117       break;
118 
119     case DBUS_TYPE_UINT32:
120       *output = kUnsigned32Typename;
121       break;
122 
123     case DBUS_TYPE_UINT64:
124       *output = kUnsigned64Typename;
125       break;
126 
127     case DBUS_TYPE_VARIANT:
128       *output = kVariantTypename;
129       break;
130 
131     default:
132       LOG(ERROR) << "Unexpected token " << *signature;
133       return false;
134   }
135 
136   if (next) {
137     *next = cur;
138   }
139 
140   return true;
141 }
142 
GetArrayTypenameForSignature(string::const_iterator signature,string::const_iterator end,string::const_iterator * next,string * output)143 bool DbusSignature::GetArrayTypenameForSignature(
144     string::const_iterator signature,
145     string::const_iterator end,
146     string::const_iterator* next,
147     string* output) {
148   string::const_iterator cur = signature;
149   if (cur == end) {
150     LOG(ERROR) << "At end of string while reading array parameter";
151     return false;
152   }
153 
154   if (*cur == DBUS_DICT_ENTRY_BEGIN_CHAR) {
155     vector<string> children;
156     ++cur;
157     while (cur != end && *cur != DBUS_DICT_ENTRY_END_CHAR) {
158       children.emplace_back();
159       if (!GetTypenameForSignature(cur, end, &cur, &children.back())) {
160         LOG(ERROR) << "Unable to decode child elements starting at "
161                    << string(cur, end);
162         return false;
163       }
164     }
165     if (cur == end) {
166       LOG(ERROR) << "At end of string while processing dict "
167                  << "starting at " << string(signature, end);
168       return false;
169     }
170 
171     DCHECK_EQ(DBUS_DICT_ENTRY_END_CHAR, *cur);
172     ++cur;
173 
174     if (children.size() != 2) {
175       LOG(ERROR) << "Dict entry contains " << children.size()
176                  << " members starting at " << string(signature, end)
177                  << " where only 2 children is valid.";
178       return false;
179     }
180     string dict_signature{signature, cur};
181     if (dict_signature == "{sv}") {
182       *output = kVariantDictTypename;
183     } else {
184       *output = StringPrintf("%s<%s, %s>", kDictTypename,
185                              children[0].c_str(), children[1].c_str());
186     }
187   } else {
188     string child;
189     if (!GetTypenameForSignature(cur, end, &cur, &child)) {
190       LOG(ERROR) << "Unable to decode child element starting at "
191                  << string(cur, end);
192       return false;
193     }
194     *output = StringPrintf("%s<%s>", kArrayTypename, child.c_str());
195   }
196 
197   if (next) {
198     *next = cur;
199   }
200 
201   return true;
202 }
203 
GetStructTypenameForSignature(string::const_iterator signature,string::const_iterator end,string::const_iterator * next,string * output)204 bool DbusSignature::GetStructTypenameForSignature(
205     string::const_iterator signature,
206     string::const_iterator end,
207     string::const_iterator* next,
208     string* output) {
209   string::const_iterator cur = signature;
210   if (cur == end) {
211     LOG(ERROR) << "At end of string while reading struct parameter";
212     return false;
213   }
214 
215   vector<string> children;
216   while (cur != end && *cur != DBUS_STRUCT_END_CHAR) {
217     children.emplace_back();
218     if (!GetTypenameForSignature(cur, end, &cur, &children.back())) {
219       LOG(ERROR) << "Unable to decode child elements starting at "
220                  << string(cur, end);
221       return false;
222     }
223   }
224   if (cur == end) {
225     LOG(ERROR) << "At end of string while processing struct "
226                << "starting at " << string(signature, end);
227     return false;
228   }
229 
230   DCHECK_EQ(DBUS_STRUCT_END_CHAR, *cur);
231   ++cur;
232 
233   *output = StringPrintf("%s<%s>", kTupleTypename,
234                          brillo::string_utils::Join(", ", children).c_str());
235 
236   if (next) {
237     *next = cur;
238   }
239 
240   return true;
241 }
242 
243 }  // namespace chromeos_dbus_bindings
244