new upstream release (3.3.0); modify package compatibility for Stretch
[ossec-hids.git] / src / os_xml / os_xml.c
1 /* Copyright (C) 2009 Trend Micro Inc.
2  * All rights reserved.
3  *
4  * This program is a free software; you can redistribute it
5  * and/or modify it under the terms of the GNU General Public
6  * License (version 2) as published by the FSF - Free Software
7  * Foundation
8  */
9
10 /* os_xml Library */
11
12 #include <stdio.h>
13 #include <stdarg.h>
14 #include <string.h>
15 #include <stdlib.h>
16 #include <ctype.h>
17
18 #include "os_xml.h"
19 #include "os_xml_internal.h"
20
21 /* Prototypes */
22 static int _oscomment(FILE *fp) __attribute__((nonnull));
23 static int _writecontent(const char *str, size_t size, unsigned int parent, OS_XML *_lxml) __attribute__((nonnull));
24 static int _writememory(const char *str, XML_TYPE type, size_t size,
25                         unsigned int parent, OS_XML *_lxml) __attribute__((nonnull));
26 static int _xml_fgetc(FILE *fp) __attribute__((nonnull));
27 static int _ReadElem(FILE *fp, unsigned int parent, OS_XML *_lxml) __attribute__((nonnull));
28 static int _getattributes(FILE *fp, unsigned int parent, OS_XML *_lxml) __attribute__((nonnull));
29 static void xml_error(OS_XML *_lxml, const char *msg, ...) __attribute__((format(printf, 2, 3), nonnull));
30
31 /* Current line */
32 static unsigned int _line;
33
34
35 /* Local fgetc */
36 static int _xml_fgetc(FILE *fp)
37 {
38     int c;
39     c = fgetc(fp);
40
41     if (c == '\n') { /* add newline */
42         _line++;
43     }
44
45     return (c);
46 }
47
48 static void xml_error(OS_XML *_lxml, const char *msg, ...)
49 {
50     va_list args;
51     va_start(args, msg);
52
53     memset(_lxml->err, '\0', XML_ERR_LENGTH);
54     vsnprintf(_lxml->err, XML_ERR_LENGTH - 1, msg, args);
55     va_end(args);
56     _lxml->err_line = _line;
57 }
58
59 /* Clear memory */
60 void OS_ClearXML(OS_XML *_lxml)
61 {
62     unsigned int i;
63     for (i = 0; i < _lxml->cur; i++) {
64         free(_lxml->el[i]);
65         free(_lxml->ct[i]);
66     }
67     _lxml->cur = 0;
68     _lxml->fol = 0;
69     _lxml->err_line = 0;
70
71     free(_lxml->el);
72     _lxml->el = NULL;
73
74     free(_lxml->ct);
75     _lxml->ct = NULL;
76
77     free(_lxml->rl);
78     _lxml->rl = NULL;
79
80     free(_lxml->tp);
81     _lxml->tp = NULL;
82
83     free(_lxml->ck);
84     _lxml->ck = NULL;
85
86     free(_lxml->ln);
87     _lxml->ln = NULL;
88
89     memset(_lxml->err, '\0', XML_ERR_LENGTH);
90 }
91
92 /* Read an XML file and generate the necessary structs */
93 int OS_ReadXML(const char *file, OS_XML *_lxml)
94 {
95     int r;
96     unsigned int i;
97     FILE *fp;
98
99     /* Initialize xml structure */
100     _lxml->cur = 0;
101     _lxml->fol = 0;
102     _lxml->el = NULL;
103     _lxml->ct = NULL;
104     _lxml->tp = NULL;
105     _lxml->rl = NULL;
106     _lxml->ck = NULL;
107     _lxml->ln = NULL;
108
109     _lxml->err_line = 0;
110     memset(_lxml->err, '\0', XML_ERR_LENGTH);
111
112     fp = fopen(file, "r");
113     if (!fp) {
114         xml_error(_lxml, "XMLERR: File '%s' not found.", file);
115         return (-2);
116     }
117
118     /* Zero the line */
119     _line = 1;
120
121     if ((r = _ReadElem(fp, 0, _lxml)) < 0) { /* First position */
122         if (r != LEOF) {
123             fclose(fp);
124             return (-1);
125         }
126     }
127
128     for (i = 0; i < _lxml->cur; i++) {
129         if (_lxml->ck[i] == 0) {
130             xml_error(_lxml, "XMLERR: Element '%s' not closed.", _lxml->el[i]);
131             fclose(fp);
132             return (-1);
133         }
134     }
135
136     fclose(fp);
137     return (0);
138 }
139
140 static int _oscomment(FILE *fp)
141 {
142     int c;
143     if ((c = fgetc(fp)) == _R_COM) {
144         while ((c = _xml_fgetc(fp)) != EOF) {
145             if (c == _R_COM) {
146                 if ((c = fgetc(fp)) == _R_CONFE) {
147                     return (1);
148                 }
149                 ungetc(c, fp);
150             } else if (c == '-') {  /* W3C way of finishing comments */
151                 if ((c = _xml_fgetc(fp)) == '-') {
152                     if ((c = fgetc(fp)) == _R_CONFE) {
153                         return (1);
154                     }
155                     ungetc(c, fp);
156                 }
157                 ungetc(c, fp);
158             } else {
159                 continue;
160             }
161         }
162         return (-1);
163     } else {
164         ungetc(c, fp);
165     }
166     return (0);
167 }
168
169 static int _ReadElem(FILE *fp, unsigned int parent, OS_XML *_lxml)
170 {
171     int c;
172     unsigned int count = 0;
173     unsigned int _currentlycont = 0;
174     short int location = -1;
175
176     int prevv = 0;
177     char elem[XML_MAXSIZE + 1];
178     char cont[XML_MAXSIZE + 1];
179     char closedelim[XML_MAXSIZE + 1];
180
181     memset(elem, '\0', XML_MAXSIZE + 1);
182     memset(cont, '\0', XML_MAXSIZE + 1);
183     memset(closedelim, '\0', XML_MAXSIZE + 1);
184
185     while ((c = _xml_fgetc(fp)) != EOF) {
186         if (c == '\\') {
187             prevv = c;
188         } else if (prevv == '\\') {
189             if (c != _R_CONFS) {
190                 prevv = 0;
191             }
192         }
193
194         /* Max size */
195         if (count >= XML_MAXSIZE) {
196             xml_error(_lxml, "XMLERR: String overflow.");
197             return (-1);
198         }
199
200         /* Check for comments */
201         if (c == _R_CONFS) {
202             int r = 0;
203             if ((r = _oscomment(fp)) < 0) {
204                 xml_error(_lxml, "XMLERR: Comment not closed.");
205                 return (-1);
206             } else if (r == 1) {
207                 continue;
208             }
209         }
210
211         /* Real checking */
212         if ((location == -1) && (prevv == 0)) {
213             if (c == _R_CONFS) {
214                 if ((c = fgetc(fp)) == '/') {
215                     xml_error(_lxml, "XMLERR: Element not opened.");
216                     return (-1);
217                 } else {
218                     ungetc(c, fp);
219                 }
220                 location = 0;
221             } else {
222                 continue;
223             }
224         }
225
226         else if ((location == 0) && ((c == _R_CONFE) || isspace(c))) {
227             int _ge = 0;
228             int _ga = 0;
229             elem[count] = '\0';
230
231             /* Remove the / at the end of the element name */
232             if (count > 0 && elem[count - 1] == '/') {
233                 _ge = '/';
234                 elem[count - 1] = '\0';
235             }
236
237             if (_writememory(elem, XML_ELEM, count + 1, parent, _lxml) < 0) {
238                 return (-1);
239             }
240             _currentlycont = _lxml->cur - 1;
241             if (isspace(c)) {
242                 if ((_ga = _getattributes(fp, parent, _lxml)) < 0) {
243                     return (-1);
244                 }
245             }
246
247             /* If the element is closed already (finished in />) */
248             if ((_ge == '/') || (_ga == '/')) {
249                 if (_writecontent("\0", 2, _currentlycont, _lxml) < 0) {
250                     return (-1);
251                 }
252                 _lxml->ck[_currentlycont] = 1;
253                 _currentlycont = 0;
254                 count = 0;
255                 location = -1;
256
257                 memset(elem, '\0', XML_MAXSIZE);
258                 memset(closedelim, '\0', XML_MAXSIZE);
259                 memset(cont, '\0', XML_MAXSIZE);
260
261                 if (parent > 0) {
262                     return (0);
263                 }
264             } else {
265                 count = 0;
266                 location = 1;
267             }
268         }
269
270         else if ((location == 2) && (c == _R_CONFE)) {
271             closedelim[count] = '\0';
272             if (strcmp(closedelim, elem) != 0) {
273                 xml_error(_lxml, "XMLERR: Element '%s' not closed.", elem);
274                 return (-1);
275             }
276             if (_writecontent(cont, strlen(cont) + 1, _currentlycont, _lxml) < 0) {
277                 return (-1);
278             }
279             _lxml->ck[_currentlycont] = 1;
280             memset(elem, '\0', XML_MAXSIZE);
281             memset(closedelim, '\0', XML_MAXSIZE);
282             memset(cont, '\0', XML_MAXSIZE);
283             _currentlycont = 0;
284             count = 0;
285             location = -1;
286             if (parent > 0) {
287                 return (0);
288             }
289         } else if ((location == 1) && (c == _R_CONFS) && (prevv == 0)) {
290             if ((c = fgetc(fp)) == '/') {
291                 cont[count] = '\0';
292                 count = 0;
293                 location = 2;
294             } else {
295                 ungetc(c, fp);
296                 ungetc(_R_CONFS, fp);
297
298                 if (_ReadElem(fp, parent + 1, _lxml) < 0) {
299                     return (-1);
300                 }
301                 count = 0;
302             }
303         } else {
304             if (location == 0) {
305                 elem[count++] = (char) c;
306             } else if (location == 1) {
307                 cont[count++] = (char) c;
308             } else if (location == 2) {
309                 closedelim[count++] = (char) c;
310             }
311
312             if ((_R_CONFS == c) && (prevv != 0)) {
313                 prevv = 0;
314             }
315         }
316     }
317     if (location == -1) {
318         return (LEOF);
319     }
320
321     xml_error(_lxml, "XMLERR: End of file and some elements were not closed.");
322     return (-1);
323 }
324
325 static int _writememory(const char *str, XML_TYPE type, size_t size,
326                         unsigned int parent, OS_XML *_lxml)
327 {
328     char **tmp;
329     int *tmp2;
330     unsigned int *tmp3;
331     XML_TYPE *tmp4;
332
333     /* Allocate for the element */
334     tmp = (char **)realloc(_lxml->el, (_lxml->cur + 1) * sizeof(char *));
335     if (tmp == NULL) {
336         goto fail;
337     }
338     _lxml->el = tmp;
339     _lxml->el[_lxml->cur] = (char *)calloc(size, sizeof(char));
340     if (_lxml->el[_lxml->cur] == NULL) {
341         goto fail;
342     }
343     strncpy(_lxml->el[_lxml->cur], str, size - 1);
344
345     /* Allocate for the content */
346     tmp = (char **)realloc(_lxml->ct, (_lxml->cur + 1) * sizeof(char *));
347     if (tmp == NULL) {
348         goto fail;
349     }
350     _lxml->ct = tmp;
351     _lxml->ct[_lxml->cur] = NULL;
352
353     /* Allocate for the type */
354     tmp4 = (XML_TYPE *) realloc(_lxml->tp, (_lxml->cur + 1) * sizeof(XML_TYPE));
355     if (tmp4 == NULL) {
356         goto fail;
357     }
358     _lxml->tp = tmp4;
359     _lxml->tp[_lxml->cur] = type;
360
361     /* Allocate for the relation */
362     tmp3 = (unsigned int *) realloc(_lxml->rl, (_lxml->cur + 1) * sizeof(unsigned int));
363     if (tmp3 == NULL) {
364         goto fail;
365     }
366     _lxml->rl = tmp3;
367     _lxml->rl[_lxml->cur] = parent;
368
369     /* Allocate for the "check" */
370     tmp2 = (int *) realloc(_lxml->ck, (_lxml->cur + 1) * sizeof(int));
371     if (tmp2 == NULL) {
372         goto fail;
373     }
374     _lxml->ck = tmp2;
375     _lxml->ck[_lxml->cur] = 0;
376
377     /* Allocate for the line */
378     tmp3 = (unsigned int *) realloc(_lxml->ln, (_lxml->cur + 1) * sizeof(unsigned int));
379     if (tmp3 == NULL) {
380         goto fail;
381     }
382     _lxml->ln = tmp3;
383     _lxml->ln[_lxml->cur] = _line;
384
385     /* Attributes does not need to be closed */
386     if (type == XML_ATTR) {
387         _lxml->ck[_lxml->cur] = 1;
388     }
389
390     /* Check if it is a variable */
391     if (strcasecmp(XML_VAR, str) == 0) {
392         _lxml->tp[_lxml->cur] = XML_VARIABLE_BEGIN;
393     }
394
395     _lxml->cur++;
396     return (0);
397
398 fail:
399     snprintf(_lxml->err, XML_ERR_LENGTH, "XMLERR: Memory error.");
400     return (-1);
401 }
402
403 static int _writecontent(const char *str, size_t size, unsigned int parent, OS_XML *_lxml)
404 {
405     _lxml->ct[parent] = (char *)calloc(size, sizeof(char));
406     if ( _lxml->ct[parent] == NULL) {
407         snprintf(_lxml->err, XML_ERR_LENGTH, "XMLERR: Memory error.");
408         return (-1);
409     }
410     strncpy(_lxml->ct[parent], str, size - 1);
411
412     return (0);
413 }
414
415 /* Read the attributes of an element */
416 static int _getattributes(FILE *fp, unsigned int parent, OS_XML *_lxml)
417 {
418     int location = 0;
419     unsigned int count = 0;
420     int c;
421     int c_to_match = 0;
422
423     char attr[XML_MAXSIZE + 1];
424     char value[XML_MAXSIZE + 1];
425
426     memset(attr, '\0', XML_MAXSIZE + 1);
427     memset(value, '\0', XML_MAXSIZE + 1);
428
429     while ((c = _xml_fgetc(fp)) != EOF) {
430         if (count >= XML_MAXSIZE) {
431             attr[count - 1] = '\0';
432             xml_error(_lxml,
433                       "XMLERR: Overflow attempt at attribute '%.20s'.", attr);
434             return (-1);
435         }
436
437         else if ((c == _R_CONFE) || ((location == 0) && (c == '/'))) {
438             if (location == 1) {
439                 xml_error(_lxml, "XMLERR: Attribute '%s' not closed.",
440                           attr);
441                 return (-1);
442             } else if ((location == 0) && (count > 0)) {
443                 xml_error(_lxml, "XMLERR: Attribute '%s' has no value.",
444                           attr);
445                 return (-1);
446             } else if (c == '/') {
447                 return (c);
448             } else {
449                 return (0);
450             }
451         } else if ((location == 0) && (c == '=')) {
452             attr[count] = '\0';
453
454             /* Check for existing attribute with same name */
455             unsigned int i = _lxml->cur - 1;
456             /* Search attributes backwards in same parent */
457             while (_lxml->rl[i] == parent && _lxml->tp[i] == XML_ATTR) {
458                 if (strcmp(_lxml->el[i], attr) == 0) {
459                     xml_error(_lxml, "XMLERR: Attribute '%s' already defined.", attr);
460                     return (-1);
461                 }
462
463                 /* Continue with previous element */
464                 if (i == 0) {
465                     break;
466                 }
467                 i--;
468             }
469
470             c = _xml_fgetc(fp);
471             if ((c != '"') && (c != '\'')) {
472                 unsigned short int _err = 1;
473                 if (isspace(c)) {
474                     while ((c = _xml_fgetc(fp)) != EOF) {
475                         if (isspace(c)) {
476                             continue;
477                         } else if ((c == '"') || (c == '\'')) {
478                             _err = 0;
479                             break;
480                         } else {
481                             break;
482                         }
483                     }
484                 }
485                 if (_err != 0) {
486                     xml_error(_lxml,
487                               "XMLERR: Attribute '%s' not followed by a \" or \'."
488                               , attr);
489                     return (-1);
490                 }
491             }
492
493             c_to_match = c;
494             location = 1;
495             count = 0;
496         } else if ((location == 0) && (isspace(c))) {
497             if (count == 0) {
498                 continue;
499             } else {
500                 xml_error(_lxml, "XMLERR: Attribute '%s' has no value.", attr);
501                 return (-1);
502             }
503         } else if ((location == 1) && (c == c_to_match)) {
504             value[count] = '\0';
505
506             if (_writememory(attr, XML_ATTR, strlen(attr) + 1,
507                              parent, _lxml) < 0) {
508                 return (-1);
509             }
510             if (_writecontent(value, count + 1, _lxml->cur - 1, _lxml) < 0) {
511                 return (-1);
512             }
513             c = _xml_fgetc(fp);
514             if (isspace(c)) {
515                 return (_getattributes(fp, parent, _lxml));
516             } else if (c == _R_CONFE) {
517                 return (0);
518             } else if (c == '/') {
519                 return (c);
520             }
521
522             xml_error(_lxml,
523                       "XMLERR: Bad attribute closing for '%s'='%s'.",
524                       attr, value);
525             return (-1);
526         } else if (location == 0) {
527             attr[count++] = (char) c;
528         } else if (location == 1) {
529             value[count++] = (char) c;
530         }
531     }
532
533     xml_error(_lxml, "XMLERR: End of file while reading an attribute.");
534     return (-1);
535 }
536