GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/utils/xml/libxml.cpp Lines: 111 136 81.6 %
Date: 2018-09-20 Branches: 51 134 38.1 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2004-2009  The Mana World Development Team
4
 *  Copyright (C) 2009-2010  The Mana Developers
5
 *  Copyright (C) 2011-2018  The ManaPlus Developers
6
 *
7
 *  This file is part of The ManaPlus Client.
8
 *
9
 *  This program is free software; you can redistribute it and/or modify
10
 *  it under the terms of the GNU General Public License as published by
11
 *  the Free Software Foundation; either version 2 of the License, or
12
 *  any later version.
13
 *
14
 *  This program is distributed in the hope that it will be useful,
15
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 *  GNU General Public License for more details.
18
 *
19
 *  You should have received a copy of the GNU General Public License
20
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 */
22
23
#ifdef ENABLE_LIBXML
24
25
#include "utils/xml/libxml.h"
26
27
#include "fs/virtfs/fs.h"
28
29
#include "utils/cast.h"
30
#include "utils/checkutils.h"
31
#include "utils/fuzzer.h"
32
#include "utils/stringutils.h"
33
34
#include "utils/translation/podict.h"
35
36
#include <fstream>
37
38
#include "debug.h"
39
40
namespace
41
{
42
    bool valid = false;
43
}  // namespace
44
45
static void xmlErrorLogger(void *ctx A_UNUSED, const char *msg A_UNUSED, ...)
46
#ifdef __GNUC__
47
#ifdef __OpenBSD__
48
    __attribute__((__format__(printf, 2, 3)))
49
#else  // __OpenBSD__
50
    __attribute__((__format__(gnu_printf, 2, 3)))
51
#endif  // __OpenBSD__
52
#endif  // __GNUC__
53
;
54
55
6
static void xmlErrorLogger(void *ctx A_UNUSED, const char *msg, ...)
56
{
57
6
    if (msg == nullptr)
58
        return;
59
60
6
    size_t size = 1024;
61
6
    const size_t msgSize = strlen(msg);
62
6
    if (msgSize * 3 > size)
63
        size = msgSize * 3;
64
65
6
    char* buf = new char[size + 1];
66
    va_list ap;
67
68
    // Use a temporary buffer to fill in the variables
69
6
    va_start(ap, msg);
70
6
    vsnprintf(buf, size, msg, ap);
71
6
    buf[size] = 0;
72
6
    va_end(ap);
73
74
6
    if (logger != nullptr)
75
6
        logger->log_r("%s", buf);
76
    else
77
        puts(buf);
78
79
    // Delete temporary buffer
80
6
    delete [] buf;
81
6
    valid = false;
82
}
83
84
namespace XML
85
{
86
2908
    Document::Document(const std::string &filename,
87
                       const UseVirtFs useResman,
88
2908
                       const SkipError skipError) :
89
        Resource(),
90
        mDoc(nullptr),
91
5816
        mIsValid(false)
92
    {
93
#ifdef USE_FUZZER
94
        if (Fuzzer::conditionTerminate(filename.c_str()))
95
            return;
96
#endif  // USE_FUZZER
97
98
        BLOCK_START("XML::Document::Document")
99
2908
        int size = 0;
100
2908
        char *data = nullptr;
101
2908
        valid = true;
102
2908
        if (useResman == UseVirtFs_true)
103
        {
104
5048
            data = const_cast<char*>(VirtFs::loadFile(
105
                filename,
106
2524
                size));
107
        }
108
        else
109
        {
110
768
            std::ifstream file;
111
384
            file.open(filename.c_str(), std::ios::in);
112
113
384
            if (file.is_open())
114
            {
115
                // Get length of file
116
384
                file.seekg(0, std::ios::end);
117
384
                size = CAST_S32(file.tellg());
118
384
                if (size < 0)
119
                {
120
                    reportAlways("Error loading XML file %s",
121
                        filename.c_str());
122
                }
123
                else
124
                {
125
384
                    file.seekg(0, std::ios::beg);
126
384
                    data = new char[size];
127
384
                    file.read(data, size);
128
                }
129
384
                file.close();
130
            }
131
            else if (skipError == SkipError_false)
132
            {
133
                reportAlways("Error loading XML file %s",
134
                    filename.c_str());
135
            }
136
        }
137
138
2908
        if (data != nullptr)
139
        {
140
2827
            mDoc = xmlParseMemory(data, size);
141
2827
            delete [] data;
142
143
2827
            if (mDoc == nullptr)
144
            {
145
                reportAlways("Error parsing XML file %s", filename.c_str());
146
            }
147
        }
148
81
        else if (skipError == SkipError_false)
149
        {
150
            reportAlways("Error loading XML file %s", filename.c_str());
151
        }
152
2908
        mIsValid = valid;
153
        BLOCK_END("XML::Document::Document")
154
2908
    }
155
156
5
    Document::Document(const char *const data, const int size) :
157

5
        mDoc(data != nullptr ? xmlParseMemory(data, size) : nullptr),
158
15
        mIsValid(true)
159
    {
160
5
    }
161
162
8161
    Document::~Document()
163
    {
164
2913
        if (mDoc != nullptr)
165
2832
            xmlFreeDoc(mDoc);
166
5248
    }
167
168
3358
    XmlNodePtr Document::rootNode()
169
    {
170
3358
        return mDoc != nullptr ? xmlDocGetRootElement(mDoc) : nullptr;
171
    }
172
173
47212
    int getProperty(XmlNodeConstPtr node,
174
                    const char *const name,
175
                    int def)
176
    {
177
47212
        int &ret = def;
178
179
47212
        xmlChar *const prop = XmlGetProp(node, name);
180
47212
        if (prop != nullptr)
181
        {
182
33189
            ret = atoi(reinterpret_cast<const char*>(prop));
183
33189
            xmlFree(prop);
184
        }
185
186
47212
        return ret;
187
    }
188
189
9263
    int getIntProperty(XmlNodeConstPtr node,
190
                       const char *const name,
191
                       int def,
192
                       const int min,
193
                       const int max)
194
    {
195
9263
        int &ret = def;
196
197
9263
        xmlChar *const prop = XmlGetProp(node, name);
198
9263
        if (prop != nullptr)
199
        {
200
1322
            ret = atoi(reinterpret_cast<char*>(prop));
201
1322
            xmlFree(prop);
202
        }
203
9263
        if (ret < min)
204
            ret = min;
205
9263
        else if (ret > max)
206
            ret = max;
207
9263
        return ret;
208
    }
209
210
110
    float getFloatProperty(XmlNodeConstPtr node,
211
                           const char *const name,
212
                           float def)
213
    {
214
110
        float &ret = def;
215
216
110
        xmlChar *const prop = XmlGetProp(node, name);
217
110
        if (prop != nullptr)
218
        {
219
2
            ret = static_cast<float>(atof(reinterpret_cast<char*>(prop)));
220
2
            xmlFree(prop);
221
        }
222
223
110
        return ret;
224
    }
225
226
    double getDoubleProperty(XmlNodeConstPtr node,
227
                             const char *const name,
228
                             double def)
229
    {
230
        double &ret = def;
231
232
        xmlChar *const prop = XmlGetProp(node, name);
233
        if (prop != nullptr)
234
        {
235
            ret = atof(reinterpret_cast<char*>(prop));
236
            xmlFree(prop);
237
        }
238
239
        return ret;
240
    }
241
242
448420
    std::string getProperty(XmlNodeConstPtr node,
243
                            const char *const name,
244
                            const std::string &def)
245
    {
246
448420
        xmlChar *const prop = XmlGetProp(node, name);
247
448420
        if (prop != nullptr)
248
        {
249
1367340
            std::string val = reinterpret_cast<char*>(prop);
250
341835
            xmlFree(prop);
251
341835
            return val;
252
        }
253
254
        return def;
255
    }
256
257
189
    std::string langProperty(XmlNodeConstPtr node,
258
                             const char *const name,
259
                             const std::string &def)
260
    {
261
378
        std::string str = getProperty(node, name, def);
262
189
        if (translator == nullptr)
263
            return str;
264
265
186
        return translator->getStr(str);
266
    }
267
268
45
    bool getBoolProperty(XmlNodeConstPtr node,
269
                         const char *const name,
270
                         const bool def)
271
    {
272
45
        xmlChar *const prop = XmlGetProp(node, name);
273
274
45
        if (XmlStrEqual(prop, "true"))
275
        {
276
2
            XmlFree(prop);
277
2
            return true;
278
        }
279
43
        if (XmlStrEqual(prop, "false"))
280
        {
281
2
            XmlFree(prop);
282
2
            return false;
283
        }
284
41
        XmlFree(prop);
285
41
        return def;
286
    }
287
288
25
    XmlNodePtr findFirstChildByName(XmlNodeConstPtrConst parent,
289
                                    const char *const name)
290
    {
291
25
        if (parent == nullptr)
292
            return nullptr;
293
229
        for_each_xml_child_node(child, parent)
294
        {
295
229
            if (xmlNameEqual(child, name))
296
                return child;
297
        }
298
299
        return nullptr;
300
    }
301
302
    // Initialize libxml2 and check for potential ABI mismatches between
303
    // compiled version and the shared library actually used.
304
184
    void initXML()
305
    {
306
184
        xmlInitParser();
307
184
        LIBXML_TEST_VERSION;
308
309
        // Suppress libxml2 error messages
310
184
        xmlSetGenericErrorFunc(nullptr, &xmlErrorLogger);
311
184
    }
312
313
    // Shutdown libxml
314
215
    void cleanupXML()
315
    {
316
215
        xmlCleanupParser();
317
215
    }
318
319
2
    bool Document::validateXml(const std::string &fileName)
320
    {
321
2
        const xmlDocPtr doc = xmlReadFile(fileName.c_str(),
322
2
            nullptr, XML_PARSE_PEDANTIC);
323
2
        const bool valid1(doc != nullptr);
324
2
        xmlFreeDoc(doc);
325
2
        if (!valid1)
326
            return false;
327
328
        std::ifstream file;
329
        file.open(fileName.c_str(), std::ios::in);
330
        if (!file.is_open())
331
        {
332
            file.close();
333
            return false;
334
        }
335
        char line[101];
336
        if (!file.getline(line, 100))
337
            return false;
338
        file.close();
339
340
        const std::string str = line;
341
        if (!strStartWith(str, "<?xml "))
342
            return false;
343
344
        return true;
345
    }
346
}  // namespace XML
347
348
#endif  // ENABLE_LIBXML