/src/opensips/db/pi_xml.c
Line | Count | Source |
1 | | /* |
2 | | * Small XML DOM adapter for the PI framework. |
3 | | * |
4 | | * The parser itself is yxml, a small MIT-licensed streaming XML parser. |
5 | | */ |
6 | | #include <stdio.h> |
7 | | #include <stdlib.h> |
8 | | #include <string.h> |
9 | | #include <strings.h> |
10 | | |
11 | | #include "../mem/mem.h" |
12 | | #include "pi_xml.h" |
13 | | #include "../lib/yxml.h" |
14 | | |
15 | 0 | #define PI_XML_DOC_MAGIC 0x5049584d |
16 | | |
17 | | static char *pi_xml_strdup(const char *s) |
18 | 0 | { |
19 | 0 | char *copy = malloc(strlen(s) + 1); |
20 | 0 | if (copy) strcpy(copy, s); |
21 | 0 | return copy; |
22 | 0 | } |
23 | | |
24 | | static void pi_xml_free_node(xmlNodePtr node) |
25 | 0 | { |
26 | 0 | xmlNodePtr child, next; |
27 | 0 | xmlAttrPtr attr, attr_next; |
28 | |
|
29 | 0 | if (!node) return; |
30 | 0 | for (child = node->children; child; child = next) { |
31 | 0 | next = child->next; |
32 | 0 | pi_xml_free_node(child); |
33 | 0 | } |
34 | 0 | for (attr = node->properties; attr; attr = attr_next) { |
35 | 0 | attr_next = attr->next; |
36 | 0 | pi_xml_free_node(attr->children); |
37 | 0 | free(attr->name); |
38 | 0 | free(attr); |
39 | 0 | } |
40 | 0 | free(node->name); |
41 | 0 | free(node->content); |
42 | 0 | free(node); |
43 | 0 | } |
44 | | |
45 | | static void pi_xml_append_child(xmlNodePtr parent, xmlNodePtr child) |
46 | 0 | { |
47 | 0 | xmlNodePtr *tail; |
48 | 0 | child->parent = parent; |
49 | 0 | if (!parent->children) { |
50 | 0 | parent->children = child; |
51 | 0 | return; |
52 | 0 | } |
53 | 0 | for (tail = &parent->children; *tail; tail = &(*tail)->next); |
54 | 0 | *tail = child; |
55 | 0 | } |
56 | | |
57 | | static int pi_xml_append_content(xmlNodePtr node, const char *data) |
58 | 0 | { |
59 | 0 | size_t old_len = node->content ? strlen(node->content) : 0; |
60 | 0 | size_t add_len = strlen(data); |
61 | 0 | char *content = realloc(node->content, old_len + add_len + 1); |
62 | 0 | if (!content) return -1; |
63 | 0 | memcpy(content + old_len, data, add_len + 1); |
64 | 0 | node->content = content; |
65 | 0 | return 0; |
66 | 0 | } |
67 | | |
68 | | static int pi_xml_add_attr(xmlNodePtr node, const char *name, |
69 | | const char *value) |
70 | 0 | { |
71 | 0 | xmlAttrPtr attr, *tail; |
72 | 0 | attr = calloc(1, sizeof(*attr)); |
73 | 0 | if (!attr) return -1; |
74 | 0 | attr->name = pi_xml_strdup(name); |
75 | 0 | attr->children = calloc(1, sizeof(*attr->children)); |
76 | 0 | if (!attr->name || !attr->children) { |
77 | 0 | free(attr->children); |
78 | 0 | free(attr->name); |
79 | 0 | free(attr); |
80 | 0 | return -1; |
81 | 0 | } |
82 | 0 | attr->children->content = pi_xml_strdup(value); |
83 | 0 | if (!attr->children->content) { |
84 | 0 | pi_xml_free_node(attr->children); |
85 | 0 | free(attr->name); |
86 | 0 | free(attr); |
87 | 0 | return -1; |
88 | 0 | } |
89 | 0 | for (tail = &node->properties; *tail; tail = &(*tail)->next); |
90 | 0 | *tail = attr; |
91 | 0 | return 0; |
92 | 0 | } |
93 | | |
94 | | xmlDocPtr pi_xml_parse_file(const char *filename) |
95 | 0 | { |
96 | 0 | FILE *file; |
97 | 0 | long size; |
98 | 0 | char *input = NULL, *attrval = NULL; |
99 | 0 | size_t pos, attr_len = 0; |
100 | 0 | char stack[65536]; |
101 | 0 | yxml_t parser; |
102 | 0 | xmlDocPtr doc = NULL; |
103 | 0 | xmlNodePtr current = NULL, node; |
104 | 0 | yxml_ret_t ret; |
105 | |
|
106 | 0 | file = fopen(filename, "rb"); |
107 | 0 | if (!file) return NULL; |
108 | 0 | if (fseek(file, 0, SEEK_END) || (size = ftell(file)) < 0 || |
109 | 0 | fseek(file, 0, SEEK_SET)) goto error; |
110 | 0 | input = malloc((size_t)size); |
111 | 0 | if (!input || fread(input, 1, (size_t)size, file) != (size_t)size) |
112 | 0 | goto error; |
113 | 0 | fclose(file); |
114 | 0 | file = NULL; |
115 | |
|
116 | 0 | doc = calloc(1, sizeof(*doc)); |
117 | 0 | if (!doc) goto error; |
118 | 0 | doc->magic = PI_XML_DOC_MAGIC; |
119 | 0 | yxml_init(&parser, stack, sizeof(stack)); |
120 | 0 | for (pos = 0; pos < (size_t)size; pos++) { |
121 | 0 | ret = yxml_parse(&parser, (unsigned char)input[pos]); |
122 | 0 | if (ret < 0) goto error; |
123 | 0 | switch (ret) { |
124 | 0 | case YXML_ELEMSTART: |
125 | 0 | node = calloc(1, sizeof(*node)); |
126 | 0 | if (!node) goto error; |
127 | 0 | node->name = strndup(parser.elem, |
128 | 0 | yxml_symlen(&parser, parser.elem)); |
129 | 0 | if (!node->name) { |
130 | 0 | pi_xml_free_node(node); |
131 | 0 | goto error; |
132 | 0 | } |
133 | 0 | if (current) pi_xml_append_child(current, node); |
134 | 0 | else if (!doc->children) doc->children = node; |
135 | 0 | else { |
136 | 0 | pi_xml_free_node(node); |
137 | 0 | goto error; |
138 | 0 | } |
139 | 0 | current = node; |
140 | 0 | break; |
141 | 0 | case YXML_ATTRSTART: |
142 | 0 | free(attrval); |
143 | 0 | attrval = calloc(1, 1); |
144 | 0 | attr_len = 0; |
145 | 0 | if (!attrval) goto error; |
146 | 0 | break; |
147 | 0 | case YXML_ATTRVAL: { |
148 | 0 | size_t len = strlen(parser.data); |
149 | 0 | char *value = realloc(attrval, attr_len + len + 1); |
150 | 0 | if (!value) goto error; |
151 | 0 | memcpy(value + attr_len, parser.data, len + 1); |
152 | 0 | attrval = value; |
153 | 0 | attr_len += len; |
154 | 0 | } |
155 | 0 | break; |
156 | 0 | case YXML_ATTREND: |
157 | 0 | if (!current || pi_xml_add_attr(current, parser.attr, attrval)) |
158 | 0 | goto error; |
159 | 0 | break; |
160 | 0 | case YXML_CONTENT: |
161 | 0 | if (!current || pi_xml_append_content(current, parser.data)) |
162 | 0 | goto error; |
163 | 0 | break; |
164 | 0 | case YXML_ELEMEND: |
165 | 0 | if (!current) goto error; |
166 | 0 | current = current->parent; |
167 | 0 | break; |
168 | 0 | default: |
169 | 0 | break; |
170 | 0 | } |
171 | 0 | } |
172 | 0 | if (yxml_eof(&parser) < 0 || current) goto error; |
173 | 0 | free(attrval); |
174 | 0 | free(input); |
175 | 0 | return doc; |
176 | | |
177 | 0 | error: |
178 | 0 | if (file) fclose(file); |
179 | 0 | free(attrval); |
180 | 0 | free(input); |
181 | 0 | if (doc) { |
182 | 0 | pi_xml_free_node(doc->children); |
183 | 0 | free(doc); |
184 | 0 | } |
185 | 0 | return NULL; |
186 | 0 | } |
187 | | |
188 | | char *pi_xml_node_get_content(xmlNodePtr node) |
189 | 0 | { |
190 | 0 | xmlNodePtr child; |
191 | 0 | size_t len = 0; |
192 | 0 | char *content; |
193 | |
|
194 | 0 | if (!node) return NULL; |
195 | 0 | if (node->content) len = strlen(node->content); |
196 | 0 | for (child = node->children; child; child = child->next) |
197 | 0 | if (child->content) len += strlen(child->content); |
198 | 0 | content = malloc(len + 1); |
199 | 0 | if (!content) return NULL; |
200 | 0 | content[0] = 0; |
201 | 0 | if (node->content) strcat(content, node->content); |
202 | 0 | for (child = node->children; child; child = child->next) |
203 | 0 | if (child->content) strcat(content, child->content); |
204 | 0 | return content; |
205 | 0 | } |
206 | | |
207 | | void pi_xml_free(void *ptr) |
208 | 0 | { |
209 | 0 | xmlDocPtr doc = ptr; |
210 | 0 | if (!ptr) return; |
211 | 0 | if (doc->magic == PI_XML_DOC_MAGIC) { |
212 | 0 | pi_xml_free_node(doc->children); |
213 | 0 | free(doc); |
214 | 0 | } else free(ptr); |
215 | 0 | } |
216 | | |
217 | | int pi_xml_strcasecmp(const xmlChar *a, const xmlChar *b) |
218 | 0 | { |
219 | 0 | return strcasecmp(a, b); |
220 | 0 | } |