1// Copyright 2013 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 "components/policy/core/common/registry_dict_win.h"
6
7#include "base/json/json_reader.h"
8#include "base/stl_util.h"
9#include "base/strings/string_number_conversions.h"
10#include "base/strings/string_util.h"
11#include "base/strings/utf_string_conversions.h"
12#include "base/sys_byteorder.h"
13#include "base/values.h"
14#include "base/win/registry.h"
15#include "components/policy/core/common/schema.h"
16
17using base::win::RegistryKeyIterator;
18using base::win::RegistryValueIterator;
19
20namespace policy {
21
22namespace {
23
24// Converts a value (as read from the registry) to meet |schema|, converting
25// types as necessary. Unconvertible types will show up as NULL values in the
26// result.
27scoped_ptr<base::Value> ConvertValue(const base::Value& value,
28                                     const Schema& schema) {
29  if (!schema.valid())
30    return make_scoped_ptr(value.DeepCopy());
31
32  // If the type is good already, go with it.
33  if (value.IsType(schema.type())) {
34    // Recurse for complex types.
35    const base::DictionaryValue* dict = NULL;
36    const base::ListValue* list = NULL;
37    if (value.GetAsDictionary(&dict)) {
38      scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue());
39      for (base::DictionaryValue::Iterator entry(*dict); !entry.IsAtEnd();
40           entry.Advance()) {
41        scoped_ptr<base::Value> converted =
42            ConvertValue(entry.value(), schema.GetProperty(entry.key()));
43        if (converted)
44          result->SetWithoutPathExpansion(entry.key(), converted.release());
45      }
46      return result.Pass();
47    } else if (value.GetAsList(&list)) {
48      scoped_ptr<base::ListValue> result(new base::ListValue());
49      for (base::ListValue::const_iterator entry(list->begin());
50           entry != list->end(); ++entry) {
51        scoped_ptr<base::Value> converted =
52            ConvertValue(**entry, schema.GetItems());
53        if (converted)
54          result->Append(converted.release());
55      }
56      return result.Pass();
57    }
58    return make_scoped_ptr(value.DeepCopy());
59  }
60
61  // Else, do some conversions to map windows registry data types to JSON types.
62  std::string string_value;
63  int int_value = 0;
64  switch (schema.type()) {
65    case base::Value::TYPE_NULL: {
66      return make_scoped_ptr(base::Value::CreateNullValue());
67    }
68    case base::Value::TYPE_BOOLEAN: {
69      // Accept booleans encoded as either string or integer.
70      if (value.GetAsInteger(&int_value) ||
71          (value.GetAsString(&string_value) &&
72           base::StringToInt(string_value, &int_value))) {
73        return make_scoped_ptr(base::Value::CreateBooleanValue(int_value != 0));
74      }
75      break;
76    }
77    case base::Value::TYPE_INTEGER: {
78      // Integers may be string-encoded.
79      if (value.GetAsString(&string_value) &&
80          base::StringToInt(string_value, &int_value)) {
81        return make_scoped_ptr(base::Value::CreateIntegerValue(int_value));
82      }
83      break;
84    }
85    case base::Value::TYPE_DOUBLE: {
86      // Doubles may be string-encoded or integer-encoded.
87      double double_value = 0;
88      if (value.GetAsInteger(&int_value)) {
89        return make_scoped_ptr(base::Value::CreateDoubleValue(int_value));
90      } else if (value.GetAsString(&string_value) &&
91                 base::StringToDouble(string_value, &double_value)) {
92        return make_scoped_ptr(base::Value::CreateDoubleValue(double_value));
93      }
94      break;
95    }
96    case base::Value::TYPE_LIST: {
97      // Lists are encoded as subkeys with numbered value in the registry.
98      const base::DictionaryValue* dict = NULL;
99      if (value.GetAsDictionary(&dict)) {
100        scoped_ptr<base::ListValue> result(new base::ListValue());
101        for (int i = 1; ; ++i) {
102          const base::Value* entry = NULL;
103          if (!dict->Get(base::IntToString(i), &entry))
104            break;
105          scoped_ptr<base::Value> converted =
106              ConvertValue(*entry, schema.GetItems());
107          if (converted)
108            result->Append(converted.release());
109        }
110        return result.Pass();
111      }
112      // Fall through in order to accept lists encoded as JSON strings.
113    }
114    case base::Value::TYPE_DICTIONARY: {
115      // Dictionaries may be encoded as JSON strings.
116      if (value.GetAsString(&string_value)) {
117        scoped_ptr<base::Value> result(base::JSONReader::Read(string_value));
118        if (result && result->IsType(schema.type()))
119          return result.Pass();
120      }
121      break;
122    }
123    case base::Value::TYPE_STRING:
124    case base::Value::TYPE_BINARY:
125      // No conversion possible.
126      break;
127  }
128
129  LOG(WARNING) << "Failed to convert " << value.GetType()
130               << " to " << schema.type();
131  return scoped_ptr<base::Value>();
132}
133
134}  // namespace
135
136bool CaseInsensitiveStringCompare::operator()(const std::string& a,
137                                              const std::string& b) const {
138  return base::strcasecmp(a.c_str(), b.c_str()) < 0;
139}
140
141RegistryDict::RegistryDict() {}
142
143RegistryDict::~RegistryDict() {
144  ClearKeys();
145  ClearValues();
146}
147
148RegistryDict* RegistryDict::GetKey(const std::string& name) {
149  KeyMap::iterator entry = keys_.find(name);
150  return entry != keys_.end() ? entry->second : NULL;
151}
152
153const RegistryDict* RegistryDict::GetKey(const std::string& name) const {
154  KeyMap::const_iterator entry = keys_.find(name);
155  return entry != keys_.end() ? entry->second : NULL;
156}
157
158void RegistryDict::SetKey(const std::string& name,
159                          scoped_ptr<RegistryDict> dict) {
160  if (!dict) {
161    RemoveKey(name);
162    return;
163  }
164
165  RegistryDict*& entry = keys_[name];
166  delete entry;
167  entry = dict.release();
168}
169
170scoped_ptr<RegistryDict> RegistryDict::RemoveKey(const std::string& name) {
171  scoped_ptr<RegistryDict> result;
172  KeyMap::iterator entry = keys_.find(name);
173  if (entry != keys_.end()) {
174    result.reset(entry->second);
175    keys_.erase(entry);
176  }
177  return result.Pass();
178}
179
180void RegistryDict::ClearKeys() {
181  STLDeleteValues(&keys_);
182}
183
184base::Value* RegistryDict::GetValue(const std::string& name) {
185  ValueMap::iterator entry = values_.find(name);
186  return entry != values_.end() ? entry->second : NULL;
187}
188
189const base::Value* RegistryDict::GetValue(const std::string& name) const {
190  ValueMap::const_iterator entry = values_.find(name);
191  return entry != values_.end() ? entry->second : NULL;
192}
193
194void RegistryDict::SetValue(const std::string& name,
195                            scoped_ptr<base::Value> dict) {
196  if (!dict) {
197    RemoveValue(name);
198    return;
199  }
200
201  base::Value*& entry = values_[name];
202  delete entry;
203  entry = dict.release();
204}
205
206scoped_ptr<base::Value> RegistryDict::RemoveValue(const std::string& name) {
207  scoped_ptr<base::Value> result;
208  ValueMap::iterator entry = values_.find(name);
209  if (entry != values_.end()) {
210    result.reset(entry->second);
211    values_.erase(entry);
212  }
213  return result.Pass();
214}
215
216void RegistryDict::ClearValues() {
217  STLDeleteValues(&values_);
218}
219
220void RegistryDict::Merge(const RegistryDict& other) {
221  for (KeyMap::const_iterator entry(other.keys_.begin());
222       entry != other.keys_.end(); ++entry) {
223    RegistryDict*& subdict = keys_[entry->first];
224    if (!subdict)
225      subdict = new RegistryDict();
226    subdict->Merge(*entry->second);
227  }
228
229  for (ValueMap::const_iterator entry(other.values_.begin());
230       entry != other.values_.end(); ++entry) {
231    SetValue(entry->first, make_scoped_ptr(entry->second->DeepCopy()));
232  }
233}
234
235void RegistryDict::Swap(RegistryDict* other) {
236  keys_.swap(other->keys_);
237  values_.swap(other->values_);
238}
239
240void RegistryDict::ReadRegistry(HKEY hive, const base::string16& root) {
241  ClearKeys();
242  ClearValues();
243
244  // First, read all the values of the key.
245  for (RegistryValueIterator it(hive, root.c_str()); it.Valid(); ++it) {
246    const std::string name = base::UTF16ToUTF8(it.Name());
247    switch (it.Type()) {
248      case REG_SZ:
249      case REG_EXPAND_SZ:
250        SetValue(
251            name,
252            make_scoped_ptr(
253                new base::StringValue(base::UTF16ToUTF8(it.Value()))));
254        continue;
255      case REG_DWORD_LITTLE_ENDIAN:
256      case REG_DWORD_BIG_ENDIAN:
257        if (it.ValueSize() == sizeof(DWORD)) {
258          DWORD dword_value = *(reinterpret_cast<const DWORD*>(it.Value()));
259          if (it.Type() == REG_DWORD_BIG_ENDIAN)
260            dword_value = base::NetToHost32(dword_value);
261          else
262            dword_value = base::ByteSwapToLE32(dword_value);
263          SetValue(
264              name,
265              make_scoped_ptr(base::Value::CreateIntegerValue(dword_value)));
266          continue;
267        }
268      case REG_NONE:
269      case REG_LINK:
270      case REG_MULTI_SZ:
271      case REG_RESOURCE_LIST:
272      case REG_FULL_RESOURCE_DESCRIPTOR:
273      case REG_RESOURCE_REQUIREMENTS_LIST:
274      case REG_QWORD_LITTLE_ENDIAN:
275        // Unsupported type, message gets logged below.
276        break;
277    }
278
279    LOG(WARNING) << "Failed to read hive " << hive << " at "
280                 << root << "\\" << name
281                 << " type " << it.Type();
282  }
283
284  // Recurse for all subkeys.
285  for (RegistryKeyIterator it(hive, root.c_str()); it.Valid(); ++it) {
286    std::string name(base::UTF16ToUTF8(it.Name()));
287    scoped_ptr<RegistryDict> subdict(new RegistryDict());
288    subdict->ReadRegistry(hive, root + L"\\" + it.Name());
289    SetKey(name, subdict.Pass());
290  }
291}
292
293scoped_ptr<base::Value> RegistryDict::ConvertToJSON(
294    const Schema& schema) const {
295  base::Value::Type type =
296      schema.valid() ? schema.type() : base::Value::TYPE_DICTIONARY;
297  switch (type) {
298    case base::Value::TYPE_DICTIONARY: {
299      scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue());
300      for (RegistryDict::ValueMap::const_iterator entry(values_.begin());
301           entry != values_.end(); ++entry) {
302        Schema subschema =
303            schema.valid() ? schema.GetProperty(entry->first) : Schema();
304        scoped_ptr<base::Value> converted =
305            ConvertValue(*entry->second, subschema);
306        if (converted)
307          result->SetWithoutPathExpansion(entry->first, converted.release());
308      }
309      for (RegistryDict::KeyMap::const_iterator entry(keys_.begin());
310           entry != keys_.end(); ++entry) {
311        Schema subschema =
312            schema.valid() ? schema.GetProperty(entry->first) : Schema();
313        scoped_ptr<base::Value> converted =
314            entry->second->ConvertToJSON(subschema);
315        if (converted)
316          result->SetWithoutPathExpansion(entry->first, converted.release());
317      }
318      return result.Pass();
319    }
320    case base::Value::TYPE_LIST: {
321      scoped_ptr<base::ListValue> result(new base::ListValue());
322      Schema item_schema = schema.valid() ? schema.GetItems() : Schema();
323      for (int i = 1; ; ++i) {
324        const std::string name(base::IntToString(i));
325        const RegistryDict* key = GetKey(name);
326        if (key) {
327          scoped_ptr<base::Value> converted = key->ConvertToJSON(item_schema);
328          if (converted)
329            result->Append(converted.release());
330          continue;
331        }
332        const base::Value* value = GetValue(name);
333        if (value) {
334          scoped_ptr<base::Value> converted = ConvertValue(*value, item_schema);
335          if (converted)
336            result->Append(converted.release());
337          continue;
338        }
339        break;
340      }
341      return result.Pass();
342    }
343    default:
344      LOG(WARNING) << "Can't convert registry key to schema type " << type;
345  }
346
347  return scoped_ptr<base::Value>();
348}
349
350}  // namespace policy
351