1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "idmap2/XmlParser.h"
18 
19 #include <memory>
20 #include <string>
21 #include <utility>
22 
23 namespace android::idmap2 {
24 
25 template <typename T>
get_tree_position(const T & tree)26 ResXMLParser::ResXMLPosition get_tree_position(const T& tree) {
27   ResXMLParser::ResXMLPosition pos{};
28   tree.getPosition(&pos);
29   return pos;
30 }
31 
Node(const ResXMLTree & tree)32 XmlParser::Node::Node(const ResXMLTree& tree) : Node(tree, get_tree_position(tree)) {
33 }
Node(const ResXMLTree & tree,const ResXMLParser::ResXMLPosition & pos)34 XmlParser::Node::Node(const ResXMLTree& tree, const ResXMLParser::ResXMLPosition& pos)
35     : parser_(tree) {
36   set_position(pos);
37 }
38 
operator ==(const XmlParser::Node & rhs) const39 bool XmlParser::Node::operator==(const XmlParser::Node& rhs) const {
40   ResXMLParser::ResXMLPosition pos = get_position();
41   ResXMLParser::ResXMLPosition rhs_pos = rhs.get_position();
42   return pos.curExt == rhs_pos.curExt && pos.curNode == rhs_pos.curNode &&
43          pos.eventCode == rhs_pos.eventCode;
44 }
45 
operator !=(const XmlParser::Node & rhs) const46 bool XmlParser::Node::operator!=(const XmlParser::Node& rhs) const {
47   return !(*this == rhs);
48 }
49 
get_position() const50 ResXMLParser::ResXMLPosition XmlParser::Node::get_position() const {
51   return get_tree_position(parser_);
52 }
53 
set_position(const ResXMLParser::ResXMLPosition & pos)54 void XmlParser::Node::set_position(const ResXMLParser::ResXMLPosition& pos) {
55   parser_.setPosition(pos);
56 }
57 
Seek(bool inner_child)58 bool XmlParser::Node::Seek(bool inner_child) {
59   if (parser_.getEventType() == XmlParser::Event::END_TAG) {
60     return false;
61   }
62 
63   ssize_t depth = 0;
64   XmlParser::Event code;
65   while ((code = parser_.next()) != XmlParser::Event::BAD_DOCUMENT &&
66          code != XmlParser::Event::END_DOCUMENT) {
67     if (code == XmlParser::Event::START_TAG) {
68       if (++depth == (inner_child ? 1 : 0)) {
69         return true;
70       }
71     } else if (code == XmlParser::Event::END_TAG) {
72       if (--depth == (inner_child ? -1 : -2)) {
73         return false;
74       }
75     }
76   }
77 
78   return false;
79 }
80 
event() const81 XmlParser::Event XmlParser::Node::event() const {
82   return parser_.getEventType();
83 }
84 
name() const85 std::string XmlParser::Node::name() const {
86   size_t len;
87   const String16 key16(parser_.getElementName(&len));
88   return String8(key16).c_str();
89 }
90 
91 template <typename Func>
FindAttribute(const ResXMLParser & parser,const std::string & label,Func && predicate)92 Result<Res_value> FindAttribute(const ResXMLParser& parser, const std::string& label,
93                                 Func&& predicate) {
94   for (size_t i = 0; i < parser.getAttributeCount(); i++) {
95     if (!predicate(i)) {
96       continue;
97     }
98     Res_value res_value{};
99     if (parser.getAttributeValue(i, &res_value) == BAD_TYPE) {
100       return Error(R"(Bad value for attribute "%s")", label.c_str());
101     }
102     return res_value;
103   }
104   return Error(R"(Failed to find attribute "%s")", label.c_str());
105 }
106 
GetStringValue(const ResXMLParser & parser,const Res_value & value,const std::string & label)107 Result<std::string> GetStringValue(const ResXMLParser& parser, const Res_value& value,
108                                    const std::string& label) {
109   switch (value.dataType) {
110     case Res_value::TYPE_STRING: {
111       if (auto str = parser.getStrings().string8ObjectAt(value.data); str.ok()) {
112         return std::string(str->c_str());
113       }
114       break;
115     }
116     case Res_value::TYPE_INT_DEC:
117     case Res_value::TYPE_INT_HEX:
118     case Res_value::TYPE_INT_BOOLEAN: {
119       return std::to_string(value.data);
120     }
121   }
122   return Error(R"(Failed to convert attribute "%s" value to a string)", label.c_str());
123 }
124 
GetAttributeValue(ResourceId attr,const std::string & label) const125 Result<Res_value> XmlParser::Node::GetAttributeValue(ResourceId attr,
126                                                      const std::string& label) const {
127   return FindAttribute(parser_, label, [&](size_t index) -> bool {
128     return parser_.getAttributeNameResID(index) == attr;
129   });
130 }
131 
GetAttributeValue(const std::string & name) const132 Result<Res_value> XmlParser::Node::GetAttributeValue(const std::string& name) const {
133   String16 name16;
134   return FindAttribute(parser_, name, [&](size_t index) -> bool {
135     if (name16.empty()) {
136         name16 = String16(name.c_str(), name.size());
137     }
138     size_t key_len;
139     const auto key16 = parser_.getAttributeName(index, &key_len);
140     return key16 && name16.size() == key_len && name16 == key16;
141   });
142 }
143 
GetAttributeStringValue(ResourceId attr,const std::string & label) const144 Result<std::string> XmlParser::Node::GetAttributeStringValue(ResourceId attr,
145                                                              const std::string& label) const {
146   auto value = GetAttributeValue(attr, label);
147   return value ? GetStringValue(parser_, *value, label) : value.GetError();
148 }
149 
GetAttributeStringValue(const std::string & name) const150 Result<std::string> XmlParser::Node::GetAttributeStringValue(const std::string& name) const {
151   auto value = GetAttributeValue(name);
152   return value ? GetStringValue(parser_, *value, name) : value.GetError();
153 }
154 
XmlParser(std::unique_ptr<ResXMLTree> tree)155 XmlParser::XmlParser(std::unique_ptr<ResXMLTree> tree) : tree_(std::move(tree)) {
156 }
157 
Create(const void * data,size_t size,bool copy_data)158 Result<XmlParser> XmlParser::Create(const void* data, size_t size, bool copy_data) {
159   auto tree = std::make_unique<ResXMLTree>();
160   if (tree->setTo(data, size, copy_data) != NO_ERROR) {
161     return Error("Malformed xml block");
162   }
163 
164   // Find the beginning of the first tag.
165   XmlParser::Event event;
166   while ((event = tree->next()) != XmlParser::Event::BAD_DOCUMENT &&
167          event != XmlParser::Event::END_DOCUMENT && event != XmlParser::Event::START_TAG) {
168   }
169 
170   if (event == XmlParser::Event::END_DOCUMENT) {
171     return Error("Root tag was not be found");
172   }
173 
174   if (event == XmlParser::Event::BAD_DOCUMENT) {
175     return Error("Bad xml document");
176   }
177 
178   return XmlParser{std::move(tree)};
179 }
180 
181 }  // namespace android::idmap2
182