Add generic XML parser + tests.
[platform/core/security/key-manager.git] / src / manager / initial-values / parser.cpp
1 /*
2  *  Copyright (c) 2000 - 2015 Samsung Electronics Co., Ltd All Rights Reserved
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  * @file        parser.cpp
18  * @author      Maciej Karpiuk (m.karpiuk2@samsung.com)
19  * @version     1.0
20  * @brief       XML parser class implementation.
21  */
22
23 #include <string>
24 #include <string.h>
25 #include <algorithm>
26 #include <exception>
27 #include <libxml/parser.h>
28 #include <libxml/valid.h>
29 #include <libxml/xmlschemas.h>
30 #include <parser.h>
31 #include <dpl/log/log.h>
32
33 using namespace XML;
34
35 namespace
36 {
37 const char * const WHITESPACE = " \n\r\t";
38 std::string trim_left(const std::string& s)
39 {
40     size_t startpos = s.find_first_not_of(WHITESPACE);
41     return (startpos == std::string::npos) ? "" : s.substr(startpos);
42 }
43
44 std::string trim_right(const std::string& s)
45 {
46     size_t endpos = s.find_last_not_of(WHITESPACE);
47     return (endpos == std::string::npos) ? "" : s.substr(0, endpos+1);
48 }
49 std::string trim(const std::string& s)
50 {
51     return trim_right(trim_left(s));
52 }
53 }
54
55 Parser::Parser(const char *XML_filename)
56     : m_errorCb(0)
57 {
58     if(XML_filename)
59         m_XMLfile = XML_filename;
60     memset(&m_saxHandler, 0, sizeof(m_saxHandler));
61     m_saxHandler.startElement = &Parser::StartElement;
62     m_saxHandler.endElement = &Parser::EndElement;
63     m_saxHandler.characters = &Parser::Characters;
64     m_saxHandler.error = &Parser::Error;
65     m_saxHandler.warning = &Parser::Warning;
66 }
67 Parser::~Parser()
68 {
69     xmlCleanupParser();
70 }
71
72 int Parser::Validate(const char *XSD_schema)
73 {
74     if(!XSD_schema) {
75         LogError("no XSD file path given");
76         return ERROR_INVALID_ARGUMENT;
77     }
78
79     int retCode;
80     std::unique_ptr<xmlSchemaParserCtxt, void(*)(xmlSchemaParserCtxtPtr)>
81             parserCtxt(xmlSchemaNewParserCtxt(XSD_schema),
82                        [](xmlSchemaParserCtxtPtr ctx){ xmlSchemaFreeParserCtxt(ctx); });
83     if(!parserCtxt) {
84         LogError("XSD file path is invalid");
85         return ERROR_INVALID_ARGUMENT;
86     }
87
88     std::unique_ptr<xmlSchema, void(*)(xmlSchemaPtr)>
89         schema(xmlSchemaParse(parserCtxt.get()),
90                        [](xmlSchemaPtr schemaPtr){ xmlSchemaFree(schemaPtr); });
91     if(!schema) {
92         LogError("Parsing XSD file failed");
93         return ERROR_XSD_PARSE_FAILED;
94     }
95
96
97     std::unique_ptr<xmlSchemaValidCtxt, void(*)(xmlSchemaValidCtxtPtr)>
98         validCtxt(xmlSchemaNewValidCtxt(schema.get()),
99                        [](xmlSchemaValidCtxtPtr validCtxPtr){ xmlSchemaFreeValidCtxt(validCtxPtr); });
100     if(!validCtxt) {
101         LogError("Internal parser error");
102         return ERROR_INTERNAL;
103     }
104
105     xmlSetStructuredErrorFunc(NULL, NULL);
106     xmlSetGenericErrorFunc(this, &Parser::ErrorValidate);
107     xmlThrDefSetStructuredErrorFunc(NULL, NULL);
108     xmlThrDefSetGenericErrorFunc(this, &Parser::ErrorValidate);
109
110     retCode = xmlSchemaValidateFile(validCtxt.get(), m_XMLfile.c_str(), 0);
111     if(0 != retCode) {
112         LogWarning("Validating XML file failed, ec: " << retCode);
113         retCode = ERROR_XML_VALIDATION_FAILED;
114     }
115     else
116         retCode = SUCCESS;
117
118     return retCode;
119 }
120
121 int Parser::Parse()
122 {
123     if(m_elementListenerMap.empty()) {
124         LogError("Can not parse XML file: no registered element callbacks.");
125         return ERROR_INVALID_ARGUMENT;
126     }
127     int retCode = xmlSAXUserParseFile(&m_saxHandler, this, m_XMLfile.c_str());
128     if(0 != retCode) {
129         LogWarning("Parsing XML file failed, ec: " << retCode);
130         return ERROR_XML_PARSE_FAILED;
131     }
132     // if error detected while parsing
133     if(m_elementListenerMap.empty()) {
134         LogError("Critical error detected while parsing.");
135         return ERROR_INTERNAL;
136     }
137     return SUCCESS;
138 }
139
140 int Parser::RegisterErrorCb(const ErrorCb newCb)
141 {
142     if(m_errorCb) {
143         LogError("Callback already registered!");
144         return ERROR_CALLBACK_PRESENT;
145     }
146     m_errorCb = newCb;
147     return SUCCESS;
148 }
149
150 int Parser::RegisterElementCb(const char * elementName,
151                               const StartCb startCb,
152                               const EndCb endCb)
153 {
154     if(!elementName)
155         return ERROR_INVALID_ARGUMENT;
156
157     std::string key(elementName);
158
159     if(m_elementListenerMap.find(elementName) != m_elementListenerMap.end()) {
160         LogError("Callback for element " << elementName << " already registered!");
161         return ERROR_CALLBACK_PRESENT;
162     }
163
164     m_elementListenerMap[key] = {startCb, endCb};
165     return SUCCESS;
166 }
167
168 void Parser::StartElement(const xmlChar *name,
169                           const xmlChar **attrs)
170 {
171     std::string key(reinterpret_cast<const char*>(name));
172     if(m_elementListenerMap.find(key) == m_elementListenerMap.end())
173         return;
174
175     ElementHandlerPtr newHandler;
176     const ElementListener & current = m_elementListenerMap[key];
177     if(current.startCb)
178     {
179         Attributes attribs;
180         {
181             size_t numAttrs = 0;
182             std::string key;
183             while(attrs && attrs[numAttrs])
184             {
185                 const char *attrChr = reinterpret_cast<const char*>(attrs[numAttrs]);
186                 if((numAttrs%2)==0)
187                     key = std::string(attrChr);
188                 else
189                     attribs[key] = std::string(attrChr);
190                 numAttrs ++;
191             }
192         }
193
194         newHandler = current.startCb();
195         if(newHandler)
196             newHandler->Start(attribs);
197     }
198     // always put a handler, even if it's empty. This will not break
199     // the sequence of queued elements when popping from the queue.
200     m_elementHandlerStack.push(newHandler);
201 }
202
203 void Parser::EndElement(const xmlChar *name)
204 {
205     std::string key(reinterpret_cast<const char*>(name));
206     if(m_elementListenerMap.find(key) == m_elementListenerMap.end())
207         return;
208
209     // this should never ever happen
210     if( m_elementHandlerStack.empty() )
211         throw std::runtime_error("internal error: element queue desynchronized!");
212
213     ElementHandlerPtr &currentHandler = m_elementHandlerStack.top();
214     if(currentHandler)
215         currentHandler.get()->End();
216
217     const ElementListener & current = m_elementListenerMap[key];
218     if(current.endCb)
219         current.endCb(currentHandler);
220
221     m_elementHandlerStack.pop();
222 }
223
224 void Parser::Characters(const xmlChar *ch, size_t chLen)
225 {
226     std::string chars = trim(std::string(reinterpret_cast<const char*>(ch), chLen));
227     if(chars.empty())
228         return;
229
230     if( !m_elementHandlerStack.empty() )
231     {
232         ElementHandlerPtr &currentHandler = m_elementHandlerStack.top();
233         if(currentHandler)
234             currentHandler.get()->Characters(chars);
235     }
236 }
237
238 void Parser::Error(const ErrorType errorType, const char *msg, va_list &args)
239 {
240     if(!m_errorCb)
241         return;
242
243     va_list args2;
244     try
245     {
246         va_copy(args2, args);
247         std::vector<char> buf(1 + std::vsnprintf(NULL, 0, msg, args));
248         std::vsnprintf(buf.data(), buf.size(), msg, args2);
249         m_errorCb(errorType, trim(std::string(buf.begin(), buf.end())));
250     }
251     catch(...) {
252         LogError("Error callback throwed an exception.");
253         // if an error handler throwed exception,
254         // do not call further callbacks
255         m_elementListenerMap.clear();
256     }
257     va_end(args2);
258 }
259
260 //
261 // -------------------------- start of static wrappers --------------------------
262 //
263 void Parser::CallbackHelper(std::function<void (void)> func)
264 {
265     try
266     {
267         func();
268         return;
269     }
270     catch(const std::exception &e) {
271         LogError("parser error: " << e.what());
272         if(m_errorCb)
273             m_errorCb(PARSE_ERROR, e.what());
274     }
275     catch(...) {
276         LogError("unknown parser error");
277         if(m_errorCb)
278             m_errorCb(PARSE_ERROR, "unknown parser error");
279     }
280     // raise error flag - unregister listeners
281     m_elementListenerMap.clear();
282 }
283 void Parser::StartElement(void *userData,
284                           const xmlChar *name,
285                           const xmlChar **attrs)
286 {
287     Parser *parser = static_cast<Parser *>(userData);
288     parser->CallbackHelper([&parser, &name, &attrs] { parser->StartElement(name, attrs); });
289 }
290 void Parser::EndElement(void *userData,
291                         const xmlChar *name)
292 {
293     Parser *parser = static_cast<Parser *>(userData);
294     parser->CallbackHelper([&parser, &name] { parser->EndElement(name); });
295 }
296 void Parser::Characters(void *userData,
297                         const xmlChar *ch,
298                         int len)
299 {
300     Parser *parser = static_cast<Parser *>(userData);
301     parser->CallbackHelper([&parser, &ch, &len] { parser->Characters(ch, static_cast<size_t>(len)); });
302 }
303
304 void Parser::ErrorValidate(void *userData,
305                            const char *msg,
306                            ...)
307 {
308     va_list args;
309     va_start(args, msg);
310     Parser *parser = static_cast<Parser *>(userData);
311     parser->Error(VALIDATION_ERROR, msg, args);
312     va_end(args);
313 }
314
315 void Parser::Error(void *userData,
316                    const char *msg,
317                    ...)
318 {
319     va_list args;
320     va_start(args, msg);
321     Parser *parser = static_cast<Parser *>(userData);
322     parser->Error(PARSE_ERROR, msg, args);
323     va_end(args);
324 }
325
326 void Parser::Warning(void *userData,
327                      const char *msg,
328                      ...)
329 {
330     va_list args;
331     va_start(args, msg);
332     Parser &parser = *(static_cast<Parser *>(userData));
333     parser.Error(PARSE_WARNING, msg, args);
334     va_end(args);
335 }
336 //
337 // -------------------------- end of static wrappers --------------------------
338 //