1 // Copyright 2015 The Weave 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 "src/component_manager_impl.h"
6 
7 #include <base/strings/string_number_conversions.h>
8 #include <base/strings/string_util.h>
9 #include <base/strings/stringprintf.h>
10 
11 #include "src/commands/schema_constants.h"
12 #include "src/json_error_codes.h"
13 #include "src/string_utils.h"
14 #include "src/utils.h"
15 
16 namespace weave {
17 
18 namespace {
19 // Max of 100 state update events should be enough in the queue.
20 const size_t kMaxStateChangeQueueSize = 100;
21 
22 const EnumToStringMap<UserRole>::Map kMap[] = {
23     {UserRole::kViewer, commands::attributes::kCommand_Role_Viewer},
24     {UserRole::kUser, commands::attributes::kCommand_Role_User},
25     {UserRole::kOwner, commands::attributes::kCommand_Role_Owner},
26     {UserRole::kManager, commands::attributes::kCommand_Role_Manager},
27 };
28 }  // anonymous namespace
29 
30 template <>
EnumToStringMap()31 LIBWEAVE_EXPORT EnumToStringMap<UserRole>::EnumToStringMap()
32     : EnumToStringMap(kMap) {}
33 
ComponentManagerImpl(provider::TaskRunner * task_runner,base::Clock * clock)34 ComponentManagerImpl::ComponentManagerImpl(provider::TaskRunner* task_runner,
35                                            base::Clock* clock)
36     : clock_{clock ? clock : &default_clock_},
37       command_queue_{task_runner, clock_} {}
38 
~ComponentManagerImpl()39 ComponentManagerImpl::~ComponentManagerImpl() {}
40 
AddComponent(const std::string & path,const std::string & name,const std::vector<std::string> & traits,ErrorPtr * error)41 bool ComponentManagerImpl::AddComponent(const std::string& path,
42                                         const std::string& name,
43                                         const std::vector<std::string>& traits,
44                                         ErrorPtr* error) {
45   base::DictionaryValue* root = &components_;
46   if (!path.empty()) {
47     root = FindComponentGraftNode(path, error);
48     if (!root)
49       return false;
50   }
51   if (root->GetWithoutPathExpansion(name, nullptr)) {
52     return Error::AddToPrintf(error, FROM_HERE, errors::commands::kInvalidState,
53                               "Component '%s' already exists at path '%s'",
54                               name.c_str(), path.c_str());
55   }
56 
57   // Check to make sure the declared traits are already defined.
58   for (const std::string& trait : traits) {
59     if (!FindTraitDefinition(trait)) {
60       return Error::AddToPrintf(error, FROM_HERE,
61                                 errors::commands::kInvalidPropValue,
62                                 "Trait '%s' is undefined", trait.c_str());
63     }
64   }
65   std::unique_ptr<base::DictionaryValue> dict{new base::DictionaryValue};
66   std::unique_ptr<base::ListValue> traits_list{new base::ListValue};
67   traits_list->AppendStrings(traits);
68   dict->Set("traits", traits_list.release());
69   root->SetWithoutPathExpansion(name, dict.release());
70   for (const auto& cb : on_componet_tree_changed_)
71     cb.Run();
72   return true;
73 }
74 
AddComponentArrayItem(const std::string & path,const std::string & name,const std::vector<std::string> & traits,ErrorPtr * error)75 bool ComponentManagerImpl::AddComponentArrayItem(
76     const std::string& path,
77     const std::string& name,
78     const std::vector<std::string>& traits,
79     ErrorPtr* error) {
80   base::DictionaryValue* root = &components_;
81   if (!path.empty()) {
82     root = FindComponentGraftNode(path, error);
83     if (!root)
84       return false;
85   }
86   base::ListValue* array_value = nullptr;
87   if (!root->GetListWithoutPathExpansion(name, &array_value)) {
88     array_value = new base::ListValue;
89     root->SetWithoutPathExpansion(name, array_value);
90   }
91   std::unique_ptr<base::DictionaryValue> dict{new base::DictionaryValue};
92   std::unique_ptr<base::ListValue> traits_list{new base::ListValue};
93   traits_list->AppendStrings(traits);
94   dict->Set("traits", traits_list.release());
95   array_value->Append(dict.release());
96   for (const auto& cb : on_componet_tree_changed_)
97     cb.Run();
98   return true;
99 }
100 
RemoveComponent(const std::string & path,const std::string & name,ErrorPtr * error)101 bool ComponentManagerImpl::RemoveComponent(const std::string& path,
102                                            const std::string& name,
103                                            ErrorPtr* error) {
104   base::DictionaryValue* root = &components_;
105   if (!path.empty()) {
106     root = FindComponentGraftNode(path, error);
107     if (!root)
108       return false;
109   }
110 
111   if (!root->RemoveWithoutPathExpansion(name, nullptr)) {
112     return Error::AddToPrintf(error, FROM_HERE, errors::commands::kInvalidState,
113                               "Component '%s' does not exist at path '%s'",
114                               name.c_str(), path.c_str());
115   }
116 
117   for (const auto& cb : on_componet_tree_changed_)
118     cb.Run();
119   return true;
120 }
121 
RemoveComponentArrayItem(const std::string & path,const std::string & name,size_t index,ErrorPtr * error)122 bool ComponentManagerImpl::RemoveComponentArrayItem(const std::string& path,
123                                                     const std::string& name,
124                                                     size_t index,
125                                                     ErrorPtr* error) {
126   base::DictionaryValue* root = &components_;
127   if (!path.empty()) {
128     root = FindComponentGraftNode(path, error);
129     if (!root)
130       return false;
131   }
132 
133   base::ListValue* array_value = nullptr;
134   if (!root->GetListWithoutPathExpansion(name, &array_value)) {
135     return Error::AddToPrintf(
136         error, FROM_HERE, errors::commands::kInvalidState,
137         "There is no component array named '%s' at path '%s'", name.c_str(),
138         path.c_str());
139   }
140 
141   if (!array_value->Remove(index, nullptr)) {
142     return Error::AddToPrintf(
143         error, FROM_HERE, errors::commands::kInvalidState,
144         "Component array '%s' at path '%s' does not have an element %zu",
145         name.c_str(), path.c_str(), index);
146   }
147 
148   for (const auto& cb : on_componet_tree_changed_)
149     cb.Run();
150   return true;
151 }
152 
AddComponentTreeChangedCallback(const base::Closure & callback)153 void ComponentManagerImpl::AddComponentTreeChangedCallback(
154     const base::Closure& callback) {
155   on_componet_tree_changed_.push_back(callback);
156   callback.Run();
157 }
158 
LoadTraits(const base::DictionaryValue & dict,ErrorPtr * error)159 bool ComponentManagerImpl::LoadTraits(const base::DictionaryValue& dict,
160                                       ErrorPtr* error) {
161   bool modified = false;
162   bool result = true;
163   // Check if any of the new traits are already defined. If so, make sure the
164   // definition is exactly the same, or else this is an error.
165   for (base::DictionaryValue::Iterator it(dict); !it.IsAtEnd(); it.Advance()) {
166     if (it.value().GetType() != base::Value::TYPE_DICTIONARY) {
167       Error::AddToPrintf(error, FROM_HERE, errors::commands::kTypeMismatch,
168                          "Trait '%s' must be an object", it.key().c_str());
169       result = false;
170       break;
171     }
172     const base::DictionaryValue* existing_def = nullptr;
173     if (traits_.GetDictionary(it.key(), &existing_def)) {
174       if (!existing_def->Equals(&it.value())) {
175         Error::AddToPrintf(error, FROM_HERE, errors::commands::kTypeMismatch,
176                            "Trait '%s' cannot be redefined", it.key().c_str());
177         result = false;
178         break;
179       }
180     } else {
181       traits_.Set(it.key(), it.value().DeepCopy());
182       modified = true;
183     }
184   }
185 
186   if (modified) {
187     for (const auto& cb : on_trait_changed_)
188       cb.Run();
189   }
190   return result;
191 }
192 
LoadTraits(const std::string & json,ErrorPtr * error)193 bool ComponentManagerImpl::LoadTraits(const std::string& json,
194                                       ErrorPtr* error) {
195   std::unique_ptr<const base::DictionaryValue> dict = LoadJsonDict(json, error);
196   if (!dict)
197     return false;
198   return LoadTraits(*dict, error);
199 }
200 
AddTraitDefChangedCallback(const base::Closure & callback)201 void ComponentManagerImpl::AddTraitDefChangedCallback(
202     const base::Closure& callback) {
203   on_trait_changed_.push_back(callback);
204   callback.Run();
205 }
206 
AddCommand(std::unique_ptr<CommandInstance> command_instance)207 void ComponentManagerImpl::AddCommand(
208     std::unique_ptr<CommandInstance> command_instance) {
209   command_queue_.Add(std::move(command_instance));
210 }
211 
ParseCommandInstance(const base::DictionaryValue & command,Command::Origin command_origin,UserRole role,std::string * id,ErrorPtr * error)212 std::unique_ptr<CommandInstance> ComponentManagerImpl::ParseCommandInstance(
213     const base::DictionaryValue& command,
214     Command::Origin command_origin,
215     UserRole role,
216     std::string* id,
217     ErrorPtr* error) {
218   std::string command_id;
219   auto command_instance =
220       CommandInstance::FromJson(&command, command_origin, &command_id, error);
221   // If we fail to validate the command definition, but there was a command ID
222   // specified there, return it to the caller when requested. This will be
223   // used to abort cloud commands.
224   if (id)
225     *id = command_id;
226 
227   if (!command_instance)
228     return nullptr;
229 
230   UserRole minimal_role;
231   if (!GetMinimalRole(command_instance->GetName(), &minimal_role, error))
232     return nullptr;
233 
234   if (role < minimal_role) {
235     return Error::AddToPrintf(error, FROM_HERE, "access_denied",
236                               "User role '%s' less than minimal: '%s'",
237                               EnumToString(role).c_str(),
238                               EnumToString(minimal_role).c_str());
239   }
240 
241   std::string component_path = command_instance->GetComponent();
242   if (component_path.empty()) {
243     // Find the component to which to route this command. Get the trait name
244     // from the command name and find the first component that has this trait.
245     auto trait_name =
246         SplitAtFirst(command_instance->GetName(), ".", true).first;
247     component_path = FindComponentWithTrait(trait_name);
248     if (component_path.empty()) {
249       return Error::AddToPrintf(
250           error, FROM_HERE, "unrouted_command",
251           "Unable route command '%s' because there is no component supporting"
252           "trait '%s'",
253           command_instance->GetName().c_str(), trait_name.c_str());
254     }
255     command_instance->SetComponent(component_path);
256   }
257 
258   const base::DictionaryValue* component = FindComponent(component_path, error);
259   if (!component)
260     return nullptr;
261 
262   // Check that the command's trait is supported by the given component.
263   auto pair = SplitAtFirst(command_instance->GetName(), ".", true);
264 
265   bool trait_supported = false;
266   const base::ListValue* supported_traits = nullptr;
267   if (component->GetList("traits", &supported_traits)) {
268     for (const base::Value* value : *supported_traits) {
269       std::string trait;
270       CHECK(value->GetAsString(&trait));
271       if (trait == pair.first) {
272         trait_supported = true;
273         break;
274       }
275     }
276   }
277 
278   if (!trait_supported) {
279     return Error::AddToPrintf(error, FROM_HERE, "trait_not_supported",
280                               "Component '%s' doesn't support trait '%s'",
281                               component_path.c_str(), pair.first.c_str());
282   }
283 
284   if (command_id.empty()) {
285     command_id = std::to_string(++next_command_id_);
286     command_instance->SetID(command_id);
287     if (id)
288       *id = command_id;
289   }
290 
291   return command_instance;
292 }
293 
FindCommand(const std::string & id)294 CommandInstance* ComponentManagerImpl::FindCommand(const std::string& id) {
295   return command_queue_.Find(id);
296 }
297 
AddCommandAddedCallback(const CommandQueue::CommandCallback & callback)298 void ComponentManagerImpl::AddCommandAddedCallback(
299     const CommandQueue::CommandCallback& callback) {
300   command_queue_.AddCommandAddedCallback(callback);
301 }
302 
AddCommandRemovedCallback(const CommandQueue::CommandCallback & callback)303 void ComponentManagerImpl::AddCommandRemovedCallback(
304     const CommandQueue::CommandCallback& callback) {
305   command_queue_.AddCommandRemovedCallback(callback);
306 }
307 
AddCommandHandler(const std::string & component_path,const std::string & command_name,const Device::CommandHandlerCallback & callback)308 void ComponentManagerImpl::AddCommandHandler(
309     const std::string& component_path,
310     const std::string& command_name,
311     const Device::CommandHandlerCallback& callback) {
312   // If both component_path and command_name are empty, we are adding the
313   // default handler for all commands.
314   if (!component_path.empty() || !command_name.empty()) {
315     CHECK(FindCommandDefinition(command_name)) << "Command undefined: "
316                                                << command_name;
317   }
318   command_queue_.AddCommandHandler(component_path, command_name, callback);
319 }
320 
FindComponent(const std::string & path,ErrorPtr * error) const321 const base::DictionaryValue* ComponentManagerImpl::FindComponent(
322     const std::string& path,
323     ErrorPtr* error) const {
324   return FindComponentAt(&components_, path, error);
325 }
326 
FindTraitDefinition(const std::string & name) const327 const base::DictionaryValue* ComponentManagerImpl::FindTraitDefinition(
328     const std::string& name) const {
329   const base::DictionaryValue* trait = nullptr;
330   traits_.GetDictionaryWithoutPathExpansion(name, &trait);
331   return trait;
332 }
333 
FindCommandDefinition(const std::string & command_name) const334 const base::DictionaryValue* ComponentManagerImpl::FindCommandDefinition(
335     const std::string& command_name) const {
336   const base::DictionaryValue* definition = nullptr;
337   std::vector<std::string> components = Split(command_name, ".", true, false);
338   // Make sure the |command_name| came in form of trait_name.command_name.
339   if (components.size() != 2)
340     return definition;
341   std::string key = base::StringPrintf("%s.commands.%s", components[0].c_str(),
342                                        components[1].c_str());
343   traits_.GetDictionary(key, &definition);
344   return definition;
345 }
346 
GetMinimalRole(const std::string & command_name,UserRole * minimal_role,ErrorPtr * error) const347 bool ComponentManagerImpl::GetMinimalRole(const std::string& command_name,
348                                           UserRole* minimal_role,
349                                           ErrorPtr* error) const {
350   const base::DictionaryValue* command = FindCommandDefinition(command_name);
351   if (!command) {
352     return Error::AddToPrintf(
353         error, FROM_HERE, errors::commands::kInvalidCommandName,
354         "Command definition for '%s' not found", command_name.c_str());
355   }
356   std::string value;
357   // The JSON definition has been pre-validated already in LoadCommands, so
358   // just using CHECKs here.
359   CHECK(command->GetString(commands::attributes::kCommand_Role, &value));
360   CHECK(StringToEnum(value, minimal_role));
361   return true;
362 }
363 
AddStateChangedCallback(const base::Closure & callback)364 void ComponentManagerImpl::AddStateChangedCallback(
365     const base::Closure& callback) {
366   on_state_changed_.push_back(callback);
367   callback.Run();  // Force to read current state.
368 }
369 
SetStateProperties(const std::string & component_path,const base::DictionaryValue & dict,ErrorPtr * error)370 bool ComponentManagerImpl::SetStateProperties(const std::string& component_path,
371                                               const base::DictionaryValue& dict,
372                                               ErrorPtr* error) {
373   base::DictionaryValue* component =
374       FindMutableComponent(component_path, error);
375   if (!component)
376     return false;
377 
378   base::DictionaryValue* state = nullptr;
379   if (!component->GetDictionary("state", &state)) {
380     state = new base::DictionaryValue;
381     component->Set("state", state);
382   }
383   state->MergeDictionary(&dict);
384   last_state_change_id_++;
385   auto& queue = state_change_queues_[component_path];
386   if (!queue)
387     queue.reset(new StateChangeQueue{kMaxStateChangeQueueSize});
388   base::Time timestamp = clock_->Now();
389   queue->NotifyPropertiesUpdated(timestamp, dict);
390   for (const auto& cb : on_state_changed_)
391     cb.Run();
392   return true;
393 }
394 
SetStatePropertiesFromJson(const std::string & component_path,const std::string & json,ErrorPtr * error)395 bool ComponentManagerImpl::SetStatePropertiesFromJson(
396     const std::string& component_path,
397     const std::string& json,
398     ErrorPtr* error) {
399   std::unique_ptr<const base::DictionaryValue> dict = LoadJsonDict(json, error);
400   return dict && SetStateProperties(component_path, *dict, error);
401 }
402 
GetStateProperty(const std::string & component_path,const std::string & name,ErrorPtr * error) const403 const base::Value* ComponentManagerImpl::GetStateProperty(
404     const std::string& component_path,
405     const std::string& name,
406     ErrorPtr* error) const {
407   const base::DictionaryValue* component = FindComponent(component_path, error);
408   if (!component)
409     return nullptr;
410   auto pair = SplitAtFirst(name, ".", true);
411   if (pair.first.empty()) {
412     return Error::AddToPrintf(error, FROM_HERE,
413                               errors::commands::kPropertyMissing,
414                               "Empty state package in '%s'", name.c_str());
415   }
416   if (pair.second.empty()) {
417     return Error::AddToPrintf(
418         error, FROM_HERE, errors::commands::kPropertyMissing,
419         "State property name not specified in '%s'", name.c_str());
420   }
421   std::string key = base::StringPrintf("state.%s", name.c_str());
422   const base::Value* value = nullptr;
423   if (!component->Get(key, &value)) {
424     return Error::AddToPrintf(error, FROM_HERE,
425                               errors::commands::kPropertyMissing,
426                               "State property '%s' not found in component '%s'",
427                               name.c_str(), component_path.c_str());
428   }
429   return value;
430 }
431 
SetStateProperty(const std::string & component_path,const std::string & name,const base::Value & value,ErrorPtr * error)432 bool ComponentManagerImpl::SetStateProperty(const std::string& component_path,
433                                             const std::string& name,
434                                             const base::Value& value,
435                                             ErrorPtr* error) {
436   base::DictionaryValue dict;
437   auto pair = SplitAtFirst(name, ".", true);
438   if (pair.first.empty()) {
439     return Error::AddToPrintf(error, FROM_HERE,
440                               errors::commands::kPropertyMissing,
441                               "Empty state package in '%s'", name.c_str());
442   }
443   if (pair.second.empty()) {
444     return Error::AddToPrintf(
445         error, FROM_HERE, errors::commands::kPropertyMissing,
446         "State property name not specified in '%s'", name.c_str());
447   }
448   dict.Set(name, value.DeepCopy());
449   return SetStateProperties(component_path, dict, error);
450 }
451 
452 ComponentManager::StateSnapshot
GetAndClearRecordedStateChanges()453 ComponentManagerImpl::GetAndClearRecordedStateChanges() {
454   StateSnapshot snapshot;
455   snapshot.update_id = GetLastStateChangeId();
456   for (auto& pair : state_change_queues_) {
457     auto changes = pair.second->GetAndClearRecordedStateChanges();
458     auto component = pair.first;
459     auto conv = [component](weave::StateChange& change) {
460       return ComponentStateChange{change.timestamp, component,
461                                   std::move(change.changed_properties)};
462     };
463     std::transform(changes.begin(), changes.end(),
464                    std::back_inserter(snapshot.state_changes), conv);
465   }
466 
467   // Sort events by the timestamp.
468   auto pred = [](const ComponentStateChange& lhs,
469                  const ComponentStateChange& rhs) {
470     return lhs.timestamp < rhs.timestamp;
471   };
472   std::sort(snapshot.state_changes.begin(), snapshot.state_changes.end(), pred);
473   state_change_queues_.clear();
474   return snapshot;
475 }
476 
NotifyStateUpdatedOnServer(UpdateID id)477 void ComponentManagerImpl::NotifyStateUpdatedOnServer(UpdateID id) {
478   on_server_state_updated_.Notify(id);
479 }
480 
AddServerStateUpdatedCallback(const base::Callback<void (UpdateID)> & callback)481 ComponentManager::Token ComponentManagerImpl::AddServerStateUpdatedCallback(
482     const base::Callback<void(UpdateID)>& callback) {
483   if (state_change_queues_.empty())
484     callback.Run(GetLastStateChangeId());
485   return Token{on_server_state_updated_.Add(callback).release()};
486 }
487 
FindComponentWithTrait(const std::string & trait) const488 std::string ComponentManagerImpl::FindComponentWithTrait(
489     const std::string& trait) const {
490   for (base::DictionaryValue::Iterator it(components_); !it.IsAtEnd();
491        it.Advance()) {
492     const base::ListValue* supported_traits = nullptr;
493     const base::DictionaryValue* component = nullptr;
494     CHECK(it.value().GetAsDictionary(&component));
495     if (component->GetList("traits", &supported_traits)) {
496       for (const base::Value* value : *supported_traits) {
497         std::string supported_trait;
498         CHECK(value->GetAsString(&supported_trait));
499         if (trait == supported_trait)
500           return it.key();
501       }
502     }
503   }
504   return std::string{};
505 }
506 
AddLegacyCommandDefinitions(const base::DictionaryValue & dict,ErrorPtr * error)507 bool ComponentManagerImpl::AddLegacyCommandDefinitions(
508     const base::DictionaryValue& dict,
509     ErrorPtr* error) {
510   bool result = true;
511   bool modified = false;
512   for (base::DictionaryValue::Iterator it(dict); !it.IsAtEnd(); it.Advance()) {
513     const base::DictionaryValue* command_dict = nullptr;
514     if (!it.value().GetAsDictionary(&command_dict)) {
515       Error::AddToPrintf(error, FROM_HERE, errors::commands::kTypeMismatch,
516                          "Package '%s' must be an object", it.key().c_str());
517       result = false;
518       continue;
519     }
520     AddTraitToLegacyComponent(it.key());
521     for (base::DictionaryValue::Iterator it_def(*command_dict);
522          !it_def.IsAtEnd(); it_def.Advance()) {
523       std::string key = base::StringPrintf("%s.commands.%s", it.key().c_str(),
524                                            it_def.key().c_str());
525       if (traits_.GetDictionary(key, nullptr)) {
526         Error::AddToPrintf(error, FROM_HERE,
527                            errors::commands::kInvalidPropValue,
528                            "Redefining command '%s.%s'", it.key().c_str(),
529                            it_def.key().c_str());
530         result = false;
531         continue;
532       }
533       traits_.Set(key, it_def.value().DeepCopy());
534       modified = true;
535     }
536   }
537 
538   if (modified) {
539     for (const auto& cb : on_trait_changed_)
540       cb.Run();
541   }
542   return result;
543 }
544 
AddLegacyStateDefinitions(const base::DictionaryValue & dict,ErrorPtr * error)545 bool ComponentManagerImpl::AddLegacyStateDefinitions(
546     const base::DictionaryValue& dict,
547     ErrorPtr* error) {
548   bool result = true;
549   bool modified = false;
550   for (base::DictionaryValue::Iterator it(dict); !it.IsAtEnd(); it.Advance()) {
551     const base::DictionaryValue* state_dict = nullptr;
552     if (!it.value().GetAsDictionary(&state_dict)) {
553       Error::AddToPrintf(error, FROM_HERE, errors::commands::kTypeMismatch,
554                          "Package '%s' must be an object", it.key().c_str());
555       result = false;
556       continue;
557     }
558     AddTraitToLegacyComponent(it.key());
559     for (base::DictionaryValue::Iterator it_def(*state_dict); !it_def.IsAtEnd();
560          it_def.Advance()) {
561       std::string key = base::StringPrintf("%s.state.%s", it.key().c_str(),
562                                            it_def.key().c_str());
563       if (traits_.GetDictionary(key, nullptr)) {
564         Error::AddToPrintf(error, FROM_HERE,
565                            errors::commands::kInvalidPropValue,
566                            "Redefining state property '%s.%s'",
567                            it.key().c_str(), it_def.key().c_str());
568         result = false;
569         continue;
570       }
571       traits_.Set(key, it_def.value().DeepCopy());
572       modified = true;
573     }
574   }
575 
576   if (modified) {
577     for (const auto& cb : on_trait_changed_)
578       cb.Run();
579   }
580   return result;
581 }
582 
GetLegacyState() const583 const base::DictionaryValue& ComponentManagerImpl::GetLegacyState() const {
584   legacy_state_.Clear();
585   // Build state from components.
586   for (base::DictionaryValue::Iterator it(components_); !it.IsAtEnd();
587        it.Advance()) {
588     const base::DictionaryValue* component_dict = nullptr;
589     const base::DictionaryValue* component_state = nullptr;
590     if (it.value().GetAsDictionary(&component_dict) &&
591         component_dict->GetDictionary("state", &component_state)) {
592       legacy_state_.MergeDictionary(component_state);
593     }
594   }
595   return legacy_state_;
596 }
597 
GetLegacyCommandDefinitions() const598 const base::DictionaryValue& ComponentManagerImpl::GetLegacyCommandDefinitions()
599     const {
600   legacy_command_defs_.Clear();
601   // Build commandDefs from traits.
602   for (base::DictionaryValue::Iterator it(traits_); !it.IsAtEnd();
603        it.Advance()) {
604     const base::DictionaryValue* trait_dict = nullptr;
605     const base::DictionaryValue* trait_commands = nullptr;
606     if (it.value().GetAsDictionary(&trait_dict) &&
607         trait_dict->GetDictionary("commands", &trait_commands)) {
608       base::DictionaryValue dict;
609       dict.Set(it.key(), trait_commands->DeepCopy());
610       legacy_command_defs_.MergeDictionary(&dict);
611     }
612   }
613   return legacy_command_defs_;
614 }
615 
AddTraitToLegacyComponent(const std::string & trait)616 void ComponentManagerImpl::AddTraitToLegacyComponent(const std::string& trait) {
617   // First check if we already have a component supporting this trait.
618   if (!FindComponentWithTrait(trait).empty())
619     return;
620 
621   // If not, add this trait to the first component available.
622   base::DictionaryValue* component = nullptr;
623   base::DictionaryValue::Iterator it(components_);
624   if (it.IsAtEnd()) {
625     // No components at all. Create a new one with dummy name.
626     // This normally wouldn't happen since libweave creates its own component
627     // at startup.
628     component = new base::DictionaryValue;
629     components_.Set("__weave__", component);
630   } else {
631     CHECK(components_.GetDictionary(it.key(), &component));
632   }
633   base::ListValue* traits = nullptr;
634   if (!component->GetList("traits", &traits)) {
635     traits = new base::ListValue;
636     component->Set("traits", traits);
637   }
638   traits->AppendString(trait);
639 }
640 
FindComponentGraftNode(const std::string & path,ErrorPtr * error)641 base::DictionaryValue* ComponentManagerImpl::FindComponentGraftNode(
642     const std::string& path,
643     ErrorPtr* error) {
644   base::DictionaryValue* root = nullptr;
645   base::DictionaryValue* component = FindMutableComponent(path, error);
646   if (component && !component->GetDictionary("components", &root)) {
647     root = new base::DictionaryValue;
648     component->Set("components", root);
649   }
650   return root;
651 }
652 
FindMutableComponent(const std::string & path,ErrorPtr * error)653 base::DictionaryValue* ComponentManagerImpl::FindMutableComponent(
654     const std::string& path,
655     ErrorPtr* error) {
656   return const_cast<base::DictionaryValue*>(
657       FindComponentAt(&components_, path, error));
658 }
659 
FindComponentAt(const base::DictionaryValue * root,const std::string & path,ErrorPtr * error)660 const base::DictionaryValue* ComponentManagerImpl::FindComponentAt(
661     const base::DictionaryValue* root,
662     const std::string& path,
663     ErrorPtr* error) {
664   auto parts = Split(path, ".", true, false);
665   std::string root_path;
666   for (size_t i = 0; i < parts.size(); i++) {
667     auto element = SplitAtFirst(parts[i], "[", true);
668     int array_index = -1;
669     if (element.first.empty()) {
670       return Error::AddToPrintf(
671           error, FROM_HERE, errors::commands::kPropertyMissing,
672           "Empty path element at '%s'", root_path.c_str());
673     }
674     if (!element.second.empty()) {
675       if (element.second.back() != ']') {
676         return Error::AddToPrintf(
677             error, FROM_HERE, errors::commands::kPropertyMissing,
678             "Invalid array element syntax '%s'", parts[i].c_str());
679       }
680       element.second.pop_back();
681       std::string index_str;
682       base::TrimWhitespaceASCII(element.second, base::TrimPositions::TRIM_ALL,
683                                 &index_str);
684       if (!base::StringToInt(index_str, &array_index) || array_index < 0) {
685         return Error::AddToPrintf(
686             error, FROM_HERE, errors::commands::kInvalidPropValue,
687             "Invalid array index '%s'", element.second.c_str());
688       }
689     }
690 
691     if (!root_path.empty()) {
692       // We have processed at least one item in the path before, so now |root|
693       // points to the actual parent component. We need the root to point to
694       // the 'components' element containing child sub-components instead.
695       if (!root->GetDictionary("components", &root)) {
696         return Error::AddToPrintf(error, FROM_HERE,
697                                   errors::commands::kPropertyMissing,
698                                   "Component '%s' does not exist at '%s'",
699                                   element.first.c_str(), root_path.c_str());
700       }
701     }
702 
703     const base::Value* value = nullptr;
704     if (!root->GetWithoutPathExpansion(element.first, &value)) {
705       Error::AddToPrintf(error, FROM_HERE, errors::commands::kPropertyMissing,
706                          "Component '%s' does not exist at '%s'",
707                          element.first.c_str(), root_path.c_str());
708       return nullptr;
709     }
710 
711     if (value->GetType() == base::Value::TYPE_LIST && array_index < 0) {
712       return Error::AddToPrintf(error, FROM_HERE,
713                                 errors::commands::kTypeMismatch,
714                                 "Element '%s.%s' is an array",
715                                 root_path.c_str(), element.first.c_str());
716     }
717     if (value->GetType() == base::Value::TYPE_DICTIONARY && array_index >= 0) {
718       return Error::AddToPrintf(error, FROM_HERE,
719                                 errors::commands::kTypeMismatch,
720                                 "Element '%s.%s' is not an array",
721                                 root_path.c_str(), element.first.c_str());
722     }
723 
724     if (value->GetType() == base::Value::TYPE_DICTIONARY) {
725       CHECK(value->GetAsDictionary(&root));
726     } else {
727       const base::ListValue* component_array = nullptr;
728       CHECK(value->GetAsList(&component_array));
729       const base::Value* component_value = nullptr;
730       if (!component_array->Get(array_index, &component_value) ||
731           !component_value->GetAsDictionary(&root)) {
732         return Error::AddToPrintf(
733             error, FROM_HERE, errors::commands::kPropertyMissing,
734             "Element '%s.%s' does not contain item #%d", root_path.c_str(),
735             element.first.c_str(), array_index);
736       }
737     }
738     if (!root_path.empty())
739       root_path += '.';
740     root_path += parts[i];
741   }
742   return root;
743 }
744 
745 }  // namespace weave
746