]> git.ipfire.org Git - thirdparty/freeswitch.git/blame - src/switch_xml.c
update more fields on re-subscribe
[thirdparty/freeswitch.git] / src / switch_xml.c
CommitLineData
a2fcb419 1/*
59241895 2 * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
c5554eb9 3 * Copyright (C) 2005-2012, Anthony Minessale II <anthm@freeswitch.org>
59241895
MJ
4 *
5 * Version: MPL 1.1
6 *
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
11 *
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
16 *
17 * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
18 *
19 * The Initial Developer of the Original Code is
ae220d33 20 * Anthony Minessale II <anthm@freeswitch.org>
59241895
MJ
21 * Portions created by the Initial Developer are Copyright (C)
22 * the Initial Developer. All Rights Reserved.
23 *
24 * Contributor(s):
a2fcb419 25 *
ae220d33 26 * Anthony Minessale II <anthm@freeswitch.org>
59241895 27 * Simon Capper <skyjunky@sbcglobal.net>
4137b360 28 * Marc Olivier Chouinard <mochouinard@moctel.com>
a2fcb419 29 * Raymond Chandler <intralanman@freeswitch.org>
59241895
MJ
30 *
31 * switch_xml.c -- XML PARSER
32 *
33 * Derived from ezxml http://ezxml.sourceforge.net
34 * Original Copyright
35 *
36 * Copyright 2004, 2006 Aaron Voisine <aaron@voisine.org>
37 *
38 * Permission is hereby granted, free of charge, to any person obtaining
39 * a copy of this software and associated documentation files (the
40 * "Software"), to deal in the Software without restriction, including
41 * without limitation the rights to use, copy, modify, merge, publish,
42 * distribute, sublicense, and/or sell copies of the Software, and to
43 * permit persons to whom the Software is furnished to do so, subject to
44 * the following conditions:
45 *
46 * The above copyright notice and this permission notice shall be included
47 * in all copies or substantial portions of the Software.
48 *
49 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
50 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
51 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
52 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
53 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
54 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
55 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
56 */
57
58#include <switch.h>
59#ifndef WIN32
dae2cb4a 60#include <sys/wait.h>
59241895
MJ
61#include <switch_private.h>
62#include <glob.h>
63#else /* we're on windoze :( */
64/* glob functions at end of this file */
65#include <apr_file_io.h>
66
67typedef struct {
886e1ddb
AM
68 size_t gl_pathc; /* Count of total paths so far. */
69 size_t gl_matchc; /* Count of paths matching pattern. */
70 size_t gl_offs; /* Reserved at beginning of gl_pathv. */
71 int gl_flags; /* Copy of flags parameter to glob. */
72 char **gl_pathv; /* List of paths matching pattern. */
59241895 73 /* Copy of errfunc parameter to glob. */
886e1ddb 74 int (*gl_errfunc) (const char *, int);
59241895
MJ
75} glob_t;
76
77/* Believed to have been introduced in 1003.2-1992 */
de13f431
BW
78#define GLOB_APPEND 0x0001 /* Append to output from previous call. */
79#define GLOB_DOOFFS 0x0002 /* Use gl_offs. */
80#define GLOB_ERR 0x0004 /* Return on error. */
81#define GLOB_MARK 0x0008 /* Append / to matching directories. */
59241895 82#define GLOB_NOCHECK 0x0010 /* Return pattern itself if nothing matches. */
de13f431 83#define GLOB_NOSORT 0x0020 /* Don't sort. */
59241895
MJ
84
85/* Error values returned by glob(3) */
86#define GLOB_NOSPACE (-1) /* Malloc call failed. */
87#define GLOB_ABORTED (-2) /* Unignored error. */
88#define GLOB_NOMATCH (-3) /* No match and GLOB_NOCHECK was not set. */
de13f431 89#define GLOB_NOSYS (-4) /* Obsolete: source comptability only. */
59241895
MJ
90
91#define GLOB_ALTDIRFUNC 0x0040 /* Use alternately specified directory funcs. */
92#define GLOB_MAGCHAR 0x0100 /* Pattern had globbing characters. */
93#define GLOB_NOMAGIC 0x0200 /* GLOB_NOCHECK without magic chars (csh). */
de13f431
BW
94#define GLOB_QUOTE 0x0400 /* Quote special chars with \. */
95#define GLOB_LIMIT 0x1000 /* limit number of returned paths */
59241895 96
886e1ddb 97int glob(const char *, int, int (*)(const char *, int), glob_t *);
de13f431 98void globfree(glob_t *);
59241895 99
59241895
MJ
100#endif
101
de13f431 102#define SWITCH_XML_WS "\t\r\n " /* whitespace */
886e1ddb 103#define SWITCH_XML_ERRL 128 /* maximum error string length */
59241895 104
4cd616cc 105/* Use UTF-8 as the general encoding */
442f4d25 106static switch_bool_t USE_UTF_8_ENCODING = SWITCH_TRUE;
4cd616cc 107
f30e40a8
KR
108static void preprocess_exec_set(char *keyval)
109{
110 char *key = keyval;
111 char *val = strchr(key, '=');
112
113 if (val) {
114 char *ve = val++;
115 while (*val && *val == ' ') {
116 val++;
117 }
118 *ve-- = '\0';
119 while (*ve && *ve == ' ') {
120 *ve-- = '\0';
121 }
122 }
123
124 if (key && val) {
125 switch_stream_handle_t exec_result = { 0 };
126 SWITCH_STANDARD_STREAM(exec_result);
127 if (switch_stream_system_fork(val, &exec_result) == 0) {
128 if (!zstr(exec_result.data)) {
129 char *tmp = (char *) exec_result.data;
130 tmp = &tmp[strlen(tmp)-1];
131 while (tmp >= (char *) exec_result.data && ( tmp[0] == ' ' || tmp[0] == '\n') ) {
132 tmp[0] = '\0'; /* remove trailing spaces and newlines */
133 tmp--;
134 }
135 switch_core_set_variable(key, exec_result.data);
136 }
137 } else {
138 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error while executing command: %s\n", val);
139 }
140 switch_safe_free(exec_result.data);
141 }
142}
143
c5204c21 144static int preprocess(const char *cwd, const char *file, FILE *write_fd, int rlevel);
59241895
MJ
145
146typedef struct switch_xml_root *switch_xml_root_t;
de13f431
BW
147struct switch_xml_root { /* additional data for the root tag */
148 struct switch_xml xml; /* is a super-struct built on top of switch_xml struct */
149 switch_xml_t cur; /* current xml tree insertion point */
150 char *m; /* original xml string */
34bd0e5e 151 switch_size_t len; /* length of allocated memory */
bc46c9a0 152 uint8_t dynamic; /* Free the original string when calling switch_xml_free */
de13f431
BW
153 char *u; /* UTF-8 conversion of string if original was UTF-16 */
154 char *s; /* start of work area */
155 char *e; /* end of work area */
156 char **ent; /* general entities (ampersand sequences) */
157 char ***attr; /* default attributes */
158 char ***pi; /* processing instructions */
159 short standalone; /* non-zero if <?xml standalone="yes"?> */
160 char err[SWITCH_XML_ERRL]; /* error string */
59241895
MJ
161};
162
de13f431 163char *SWITCH_XML_NIL[] = { NULL }; /* empty, null terminated array of strings */
59241895
MJ
164
165struct switch_xml_binding {
166 switch_xml_search_function_t function;
167 switch_xml_section_t sections;
168 void *user_data;
169 struct switch_xml_binding *next;
170};
171
172
173static switch_xml_binding_t *BINDINGS = NULL;
174static switch_xml_t MAIN_XML_ROOT = NULL;
c5831538 175static switch_memory_pool_t *XML_MEMORY_POOL = NULL;
4714ed43 176
c5831538 177static switch_thread_rwlock_t *B_RWLOCK = NULL;
4714ed43 178static switch_mutex_t *XML_LOCK = NULL;
2582bbcb 179static switch_mutex_t *CACHE_MUTEX = NULL;
4714ed43 180static switch_mutex_t *REFLOCK = NULL;
3ed59f1d 181static switch_mutex_t *FILE_LOCK = NULL;
3a85348c 182static switch_mutex_t *XML_GEN_LOCK = NULL;
4714ed43 183
3aaa6209 184SWITCH_DECLARE_NONSTD(switch_xml_t) __switch_xml_open_root(uint8_t reload, const char **err, void *user_data);
07a71592
AM
185
186static switch_xml_open_root_function_t XML_OPEN_ROOT_FUNCTION = (switch_xml_open_root_function_t)__switch_xml_open_root;
187static void *XML_OPEN_ROOT_FUNCTION_USER_DATA = NULL;
188
2582bbcb 189static switch_hash_t *CACHE_HASH = NULL;
a2fcb419 190static switch_hash_t *CACHE_EXPIRES_HASH = NULL;
59241895
MJ
191
192struct xml_section_t {
193 const char *name;
de13f431 194 /* switch_xml_section_t section; */
59241895
MJ
195 uint32_t section;
196};
197
198static struct xml_section_t SECTIONS[] = {
199 {"result", SWITCH_XML_SECTION_RESULT},
200 {"config", SWITCH_XML_SECTION_CONFIG},
201 {"directory", SWITCH_XML_SECTION_DIRECTORY},
202 {"dialplan", SWITCH_XML_SECTION_DIALPLAN},
5b8a1337 203 {"languages", SWITCH_XML_SECTION_LANGUAGES},
7333d46d 204 {"chatplan", SWITCH_XML_SECTION_CHATPLAN},
59241895
MJ
205 {NULL, 0}
206};
207
208SWITCH_DECLARE(switch_xml_section_t) switch_xml_parse_section_string(const char *str)
209{
210 size_t x;
211 char buf[1024] = "";
de13f431 212 /*switch_xml_section_t sections = SWITCH_XML_SECTION_RESULT; */
59241895 213 uint32_t sections = SWITCH_XML_SECTION_RESULT;
886e1ddb 214
59241895
MJ
215 if (str) {
216 for (x = 0; x < strlen(str); x++) {
217 buf[x] = (char) tolower((int) str[x]);
218 }
219 for (x = 0;; x++) {
220 if (!SECTIONS[x].name) {
221 break;
222 }
223 if (strstr(buf, SECTIONS[x].name)) {
224 sections |= SECTIONS[x].section;
225 }
226 }
227 }
228 return (switch_xml_section_t) sections;
229}
230
231SWITCH_DECLARE(switch_status_t) switch_xml_unbind_search_function(switch_xml_binding_t **binding)
232{
233 switch_xml_binding_t *ptr, *last = NULL;
234 switch_status_t status = SWITCH_STATUS_FALSE;
235
8ec9b8f7
AM
236
237 switch_thread_rwlock_wrlock(B_RWLOCK);
59241895
MJ
238 for (ptr = BINDINGS; ptr; ptr = ptr->next) {
239 if (ptr == *binding) {
240 if (last) {
241 last->next = (*binding)->next;
242 } else {
243 BINDINGS = (*binding)->next;
244 }
245 status = SWITCH_STATUS_SUCCESS;
246 break;
247 }
248 last = ptr;
249 }
8ec9b8f7 250 switch_thread_rwlock_unlock(B_RWLOCK);
59241895
MJ
251
252 return status;
253}
254
255SWITCH_DECLARE(switch_status_t) switch_xml_unbind_search_function_ptr(switch_xml_search_function_t function)
256{
257 switch_xml_binding_t *ptr, *last = NULL;
258 switch_status_t status = SWITCH_STATUS_FALSE;
259
8ec9b8f7 260 switch_thread_rwlock_wrlock(B_RWLOCK);
59241895
MJ
261 for (ptr = BINDINGS; ptr; ptr = ptr->next) {
262 if (ptr->function == function) {
30a13ca0
AM
263 status = SWITCH_STATUS_SUCCESS;
264
59241895
MJ
265 if (last) {
266 last->next = ptr->next;
267 } else {
268 BINDINGS = ptr->next;
30a13ca0
AM
269 last = NULL;
270 continue;
59241895 271 }
59241895
MJ
272 }
273 last = ptr;
274 }
8ec9b8f7 275 switch_thread_rwlock_unlock(B_RWLOCK);
59241895
MJ
276
277 return status;
278}
279
fc6a7ced
AM
280SWITCH_DECLARE(void) switch_xml_set_binding_sections(switch_xml_binding_t *binding, switch_xml_section_t sections)
281{
282 switch_assert(binding);
283 binding->sections = sections;
284}
285
286SWITCH_DECLARE(void) switch_xml_set_binding_user_data(switch_xml_binding_t *binding, void *user_data)
287{
288 switch_assert(binding);
289 binding->user_data = user_data;
290}
291
34931616
AM
292SWITCH_DECLARE(switch_xml_section_t) switch_xml_get_binding_sections(switch_xml_binding_t *binding)
293{
294 return binding->sections;
295}
296
297SWITCH_DECLARE(void *) switch_xml_get_binding_user_data(switch_xml_binding_t *binding)
298{
299 return binding->user_data;
300}
301
de13f431 302SWITCH_DECLARE(switch_status_t) switch_xml_bind_search_function_ret(switch_xml_search_function_t function,
886e1ddb 303 switch_xml_section_t sections, void *user_data, switch_xml_binding_t **ret_binding)
59241895
MJ
304{
305 switch_xml_binding_t *binding = NULL, *ptr = NULL;
306 assert(function != NULL);
307
308 if (!(binding = (switch_xml_binding_t *) switch_core_alloc(XML_MEMORY_POOL, sizeof(*binding)))) {
309 return SWITCH_STATUS_MEMERR;
310 }
311
312 binding->function = function;
313 binding->sections = sections;
314 binding->user_data = user_data;
315
8ec9b8f7 316 switch_thread_rwlock_wrlock(B_RWLOCK);
59241895
MJ
317 for (ptr = BINDINGS; ptr && ptr->next; ptr = ptr->next);
318
319 if (ptr) {
320 ptr->next = binding;
321 } else {
322 BINDINGS = binding;
323 }
446d7285 324
ff0c6deb
AM
325 if (ret_binding) {
326 *ret_binding = binding;
446d7285
AM
327 }
328
8ec9b8f7 329 switch_thread_rwlock_unlock(B_RWLOCK);
59241895
MJ
330
331 return SWITCH_STATUS_SUCCESS;
332}
333
334SWITCH_DECLARE(switch_xml_t) switch_xml_find_child(switch_xml_t node, const char *childname, const char *attrname, const char *value)
335{
336 switch_xml_t p = NULL;
337
338 if (!(childname && attrname && value)) {
339 return node;
340 }
341
342 for (p = switch_xml_child(node, childname); p; p = p->next) {
343 const char *aname = switch_xml_attr(p, attrname);
344 if (aname && value && !strcasecmp(aname, value)) {
345 break;
346 }
347 }
348
349 return p;
350}
351
886e1ddb 352SWITCH_DECLARE(switch_xml_t) switch_xml_find_child_multi(switch_xml_t node, const char *childname,...)
ed100f44
AM
353{
354 switch_xml_t p = NULL;
886e1ddb
AM
355 const char *names[256] = { 0 };
356 const char *vals[256] = { 0 };
ed100f44
AM
357 int x, i = 0;
358 va_list ap;
376b42c3 359 const char *attrname, *value = NULL;
ed100f44
AM
360
361 va_start(ap, childname);
886e1ddb
AM
362
363 while (i < 255) {
ed100f44
AM
364 if ((attrname = va_arg(ap, const char *))) {
365 value = va_arg(ap, const char *);
366 }
367 if (attrname && value) {
368 names[i] = attrname;
369 vals[i] = value;
370 } else {
371 break;
372 }
373 i++;
374 }
375
376 va_end(ap);
377
378 if (!(childname && i)) {
379 return node;
380 }
886e1ddb 381
ed100f44
AM
382 for (p = switch_xml_child(node, childname); p; p = p->next) {
383 for (x = 0; x < i; x++) {
384 if (names[x] && vals[x]) {
385 const char *aname = switch_xml_attr(p, names[x]);
386
387 if (aname) {
388 if (*vals[x] == '!') {
389 const char *sval = vals[x] + 1;
390 if (sval && strcasecmp(aname, sval)) {
391 goto done;
392 }
393 } else {
394 if (!strcasecmp(aname, vals[x])) {
395 goto done;
396 }
397 }
398 }
399 }
400 }
401 }
402
886e1ddb 403 done:
ed100f44
AM
404
405 return p;
406}
407
de13f431 408/* returns the first child tag with the given name or NULL if not found */
59241895
MJ
409SWITCH_DECLARE(switch_xml_t) switch_xml_child(switch_xml_t xml, const char *name)
410{
411 xml = (xml) ? xml->child : NULL;
412 while (xml && strcmp(name, xml->name))
413 xml = xml->sibling;
414 return xml;
415}
416
de13f431 417/* returns the Nth tag with the same name in the same subsection or NULL if not found */
59241895
MJ
418switch_xml_t switch_xml_idx(switch_xml_t xml, int idx)
419{
420 for (; xml && idx; idx--)
421 xml = xml->next;
422 return xml;
423}
424
de13f431 425/* returns the value of the requested tag attribute or "" if not found */
59241895
MJ
426SWITCH_DECLARE(const char *) switch_xml_attr_soft(switch_xml_t xml, const char *attr)
427{
428 const char *ret = switch_xml_attr(xml, attr);
429
430 return ret ? ret : "";
431}
432
de13f431 433/* returns the value of the requested tag attribute or NULL if not found */
59241895
MJ
434SWITCH_DECLARE(const char *) switch_xml_attr(switch_xml_t xml, const char *attr)
435{
436 int i = 0, j = 1;
437 switch_xml_root_t root = (switch_xml_root_t) xml;
438
439 if (!xml || !xml->attr)
440 return NULL;
383ff711 441 while (xml->attr[i] && attr && strcmp(attr, xml->attr[i]))
59241895
MJ
442 i += 2;
443 if (xml->attr[i])
de13f431 444 return xml->attr[i + 1]; /* found attribute */
59241895
MJ
445
446 while (root->xml.parent)
de13f431 447 root = (switch_xml_root_t) root->xml.parent; /* root tag */
62ce8538
AM
448
449 /* Make sure root is really a switch_xml_root_t (Issues with switch_xml_toxml) */
450 if (!root->xml.is_switch_xml_root_t) {
451 return NULL;
452 }
453
41557506
MJ
454 if (!root->attr) {
455 return NULL;
456 }
a2fcb419 457
383ff711 458 for (i = 0; root->attr[i] && xml->name && strcmp(xml->name, root->attr[i][0]); i++);
59241895 459 if (!root->attr[i])
de13f431 460 return NULL; /* no matching default attributes */
383ff711 461 while (root->attr[i][j] && attr && strcmp(attr, root->attr[i][j]))
59241895 462 j += 3;
de13f431 463 return (root->attr[i][j]) ? root->attr[i][j + 1] : NULL; /* found default */
59241895
MJ
464}
465
de13f431 466/* same as switch_xml_get but takes an already initialized va_list */
59241895
MJ
467static switch_xml_t switch_xml_vget(switch_xml_t xml, va_list ap)
468{
469 char *name = va_arg(ap, char *);
470 int idx = -1;
471
472 if (name && *name) {
473 idx = va_arg(ap, int);
474 xml = switch_xml_child(xml, name);
475 }
476 return (idx < 0) ? xml : switch_xml_vget(switch_xml_idx(xml, idx), ap);
477}
478
de13f431
BW
479/* Traverses the xml tree to retrieve a specific subtag. Takes a variable
480 length list of tag names and indexes. The argument list must be terminated
a2fcb419 481 by either an index of -1 or an empty string tag name. Example:
de13f431
BW
482 title = switch_xml_get(library, "shelf", 0, "book", 2, "title", -1);
483 This retrieves the title of the 3rd book on the 1st shelf of library.
484 Returns NULL if not found. */
59241895
MJ
485SWITCH_DECLARE(switch_xml_t) switch_xml_get(switch_xml_t xml,...)
486{
487 va_list ap;
488 switch_xml_t r;
489
490 va_start(ap, xml);
491 r = switch_xml_vget(xml, ap);
492 va_end(ap);
493 return r;
494}
495
de13f431 496/* returns a null terminated array of processing instructions for the given target */
59241895
MJ
497SWITCH_DECLARE(const char **) switch_xml_pi(switch_xml_t xml, const char *target)
498{
499 switch_xml_root_t root = (switch_xml_root_t) xml;
500 int i = 0;
501
502 if (!root)
503 return (const char **) SWITCH_XML_NIL;
504 while (root->xml.parent)
de13f431 505 root = (switch_xml_root_t) root->xml.parent; /* root tag */
41557506
MJ
506 if (!root || !root->pi) {
507 return (const char **) SWITCH_XML_NIL;
508 }
59241895 509 while (root->pi[i] && strcmp(target, root->pi[i][0]))
de13f431 510 i++; /* find target */
59241895
MJ
511 return (const char **) ((root->pi[i]) ? root->pi[i] + 1 : SWITCH_XML_NIL);
512}
513
de13f431 514/* set an error string and return root */
59241895
MJ
515static switch_xml_t switch_xml_err(switch_xml_root_t root, char *s, const char *err, ...)
516{
517 va_list ap;
518 int line = 1;
519 char *t, fmt[SWITCH_XML_ERRL];
520
41557506
MJ
521 if (!root || !root->s) {
522 return NULL;
523 }
524
59241895
MJ
525 for (t = root->s; t && t < s; t++)
526 if (*t == '\n')
527 line++;
528 switch_snprintf(fmt, SWITCH_XML_ERRL, "[error near line %d]: %s", line, err);
529
530 va_start(ap, err);
531 vsnprintf(root->err, SWITCH_XML_ERRL, fmt, ap);
532 va_end(ap);
533
534 return &root->xml;
535}
536
de13f431
BW
537/* Recursively decodes entity and character references and normalizes new lines
538 ent is a null terminated array of alternating entity names and values. set t
539 to '&' for general entity decoding, '%' for parameter entity decoding, 'c'
540 for cdata sections, ' ' for attribute normalization, or '*' for non-cdata
541 attribute normalization. Returns s, or if the decoded string is longer than
542 s, returns a malloced string that must be freed. */
59241895
MJ
543static char *switch_xml_decode(char *s, char **ent, char t)
544{
545 char *e, *r = s, *m = s;
546 long b, c, d, l;
547
886e1ddb 548 for (; *s; s++) { /* normalize line endings */
59241895
MJ
549 while (*s == '\r') {
550 *(s++) = '\n';
551 if (*s == '\n')
552 memmove(s, (s + 1), strlen(s));
553 }
554 }
555
556 for (s = r;;) {
e92fd635 557 while (*s && *s != '&' && (*s != '%' || t != '%') && !isspace((unsigned char) (*s)))
59241895
MJ
558 s++;
559
560 if (!*s)
561 break;
de13f431 562 else if (t != 'c' && !strncmp(s, "&#", 2)) { /* character reference */
59241895 563 if (s[2] == 'x')
de13f431 564 c = strtol(s + 3, &e, 16); /* base 16 */
59241895 565 else
de13f431 566 c = strtol(s + 2, &e, 10); /* base 10 */
59241895
MJ
567 if (!c || *e != ';') {
568 s++;
569 continue;
886e1ddb
AM
570 }
571 /* not a character ref */
59241895 572 if (c < 0x80)
de13f431 573 *(s++) = (char) c; /* US-ASCII subset */
886e1ddb 574 else { /* multi-byte UTF-8 sequence */
59241895 575 for (b = 0, d = c; d; d /= 2)
886e1ddb 576 b++; /* number of bits in c */
de13f431
BW
577 b = (b - 2) / 5; /* number of bytes in payload */
578 *(s++) = (char) ((0xFF << (7 - b)) | (c >> (6 * b))); /* head */
59241895 579 while (b)
de13f431 580 *(s++) = (char) (0x80 | ((c >> (6 * --b)) & 0x3F)); /* payload */
59241895
MJ
581 }
582
583 memmove(s, strchr(s, ';') + 1, strlen(strchr(s, ';')));
de13f431
BW
584 } else if ((*s == '&' && (t == '&' || t == ' ' || t == '*')) || (*s == '%' && t == '%')) { /* entity reference */
585 for (b = 0; ent[b] && strncmp(s + 1, ent[b], strlen(ent[b])); b += 2); /* find entity in entity list */
59241895 586
de13f431 587 if (ent[b++]) { /* found a match */
59241895 588 if ((c = (long) strlen(ent[b])) - 1 > (e = strchr(s, ';')) - s) {
de13f431 589 l = (d = (long) (s - r)) + c + (long) strlen(e); /* new length */
59241895
MJ
590 if (l) {
591 if (r == m) {
886e1ddb 592 char *tmp = (char *) malloc(l);
59241895
MJ
593 if (tmp) {
594 r = strcpy(tmp, r);
595 } else {
886e1ddb
AM
596 if (r)
597 free(r);
59241895
MJ
598 return NULL;
599 }
600 } else {
886e1ddb 601 char *tmp = (char *) realloc(r, l);
59241895
MJ
602 if (tmp) {
603 r = tmp;
604 } else {
886e1ddb
AM
605 if (r)
606 free(r);
59241895
MJ
607 return NULL;
608 }
609 }
610 }
de13f431 611 e = strchr((s = r + d), ';'); /* fix up pointers */
59241895
MJ
612 }
613
de13f431
BW
614 memmove(s + c, e + 1, strlen(e)); /* shift rest of string */
615 strncpy(s, ent[b], c); /* copy in replacement text */
59241895 616 } else
de13f431 617 s++; /* not a known entity */
59241895
MJ
618 } else if ((t == ' ' || t == '*') && isspace((int) (*s)))
619 *(s++) = ' ';
620 else
de13f431 621 s++; /* no decoding needed */
59241895
MJ
622 }
623
de13f431 624 if (t == '*') { /* normalize spaces for non-cdata attributes */
59241895
MJ
625 for (s = r; *s; s++) {
626 if ((l = (long) strspn(s, " ")))
627 memmove(s, s + l, strlen(s + l) + 1);
628 while (*s && *s != ' ')
629 s++;
630 }
631 if (--s >= r && *s == ' ')
de13f431 632 *s = '\0'; /* trim any trailing space */
59241895
MJ
633 }
634 return r;
635}
636
de13f431 637/* called when parser finds start of new tag */
59241895
MJ
638static void switch_xml_open_tag(switch_xml_root_t root, char *name, char **attr)
639{
41557506
MJ
640 switch_xml_t xml;
641
642 if (!root || !root->cur) {
643 return;
644 }
645
646 xml = root->cur;
59241895
MJ
647
648 if (xml->name)
649 xml = switch_xml_add_child(xml, name, strlen(xml->txt));
650 else
de13f431 651 xml->name = name; /* first open tag */
59241895
MJ
652
653 xml->attr = attr;
de13f431 654 root->cur = xml; /* update tag insertion point */
59241895
MJ
655}
656
de13f431 657/* called when parser finds character content between open and closing tag */
59241895
MJ
658static void switch_xml_char_content(switch_xml_root_t root, char *s, switch_size_t len, char t)
659{
41557506 660 switch_xml_t xml;
59241895
MJ
661 char *m = s;
662 switch_size_t l;
663
41557506
MJ
664 if (!root || !root->cur) {
665 return;
666 }
667
668 xml = root->cur;
669
59241895 670 if (!xml || !xml->name || !len)
de13f431 671 return; /* sanity check */
59241895 672
de13f431 673 s[len] = '\0'; /* null terminate text (calling functions anticipate this) */
59241895
MJ
674 len = strlen(s = switch_xml_decode(s, root->ent, t)) + 1;
675
676 if (!*(xml->txt))
de13f431
BW
677 xml->txt = s; /* initial character content */
678 else { /* allocate our own memory and make a copy */
886e1ddb
AM
679 if ((xml->flags & SWITCH_XML_TXTM)) { /* allocate some space */
680 char *tmp = (char *) realloc(xml->txt, (l = strlen(xml->txt)) + len);
59241895
MJ
681 if (tmp) {
682 xml->txt = tmp;
683 } else {
684 return;
685 }
686 } else {
886e1ddb 687 char *tmp = (char *) malloc((l = strlen(xml->txt)) + len);
59241895
MJ
688 if (tmp) {
689 xml->txt = strcpy(tmp, xml->txt);
690 } else {
691 return;
692 }
693 }
de13f431 694 strcpy(xml->txt + l, s); /* add new char content */
59241895 695 if (s != m)
de13f431 696 free(s); /* free s if it was malloced by switch_xml_decode() */
59241895
MJ
697 }
698
699 if (xml->txt != m)
700 switch_xml_set_flag(xml, SWITCH_XML_TXTM);
701}
702
de13f431 703/* called when parser finds closing tag */
59241895
MJ
704static switch_xml_t switch_xml_close_tag(switch_xml_root_t root, char *name, char *s)
705{
41557506 706 if (!root || !root->cur || !root->cur->name || strcmp(name, root->cur->name))
59241895
MJ
707 return switch_xml_err(root, s, "unexpected closing tag </%s>", name);
708
709 root->cur = root->cur->parent;
710 return NULL;
711}
712
de13f431
BW
713/* checks for circular entity references, returns non-zero if no circular
714 references are found, zero otherwise */
59241895
MJ
715static int switch_xml_ent_ok(char *name, char *s, char **ent)
716{
717 int i;
718
719 for (;; s++) {
720 while (*s && *s != '&')
de13f431 721 s++; /* find next entity reference */
59241895
MJ
722 if (!*s)
723 return 1;
724 if (!strncmp(s + 1, name, strlen(name)))
de13f431 725 return 0; /* circular ref. */
59241895
MJ
726 for (i = 0; ent[i] && strncmp(ent[i], s + 1, strlen(ent[i])); i += 2);
727 if (ent[i] && !switch_xml_ent_ok(name, ent[i + 1], ent))
728 return 0;
729 }
730}
731
de13f431 732/* called when the parser finds a processing instruction */
59241895
MJ
733static void switch_xml_proc_inst(switch_xml_root_t root, char *s, switch_size_t len)
734{
735 int i = 0, j = 1;
736 char *target = s;
737 char **sstmp;
738 char *stmp;
739
de13f431 740 s[len] = '\0'; /* null terminate instruction */
59241895 741 if (*(s += strcspn(s, SWITCH_XML_WS))) {
de13f431
BW
742 *s = '\0'; /* null terminate target */
743 s += strspn(s + 1, SWITCH_XML_WS) + 1; /* skip whitespace after target */
59241895
MJ
744 }
745
886e1ddb
AM
746 if (!root)
747 return;
59241895 748
de13f431 749 if (!strcmp(target, "xml")) { /* <?xml ... ?> */
59241895
MJ
750 if ((s = strstr(s, "standalone")) && !strncmp(s + strspn(s + 10, SWITCH_XML_WS "='\"") + 10, "yes", 3))
751 root->standalone = 1;
752 return;
753 }
754
41557506 755 if (!root->pi || !root->pi[0]) {
886e1ddb
AM
756 root->pi = (char ***) malloc(sizeof(char **));
757 if (!root->pi)
758 return;
759 *(root->pi) = NULL; /* first pi */
59241895
MJ
760 }
761
762 while (root->pi[i] && strcmp(target, root->pi[i][0]))
de13f431
BW
763 i++; /* find target */
764 if (!root->pi[i]) { /* new target */
886e1ddb
AM
765 char ***ssstmp = (char ***) realloc(root->pi, sizeof(char **) * (i + 2));
766 if (!ssstmp)
767 return;
768 root->pi = ssstmp;
769 if (!root->pi)
770 return;
771 root->pi[i] = (char **) malloc(sizeof(char *) * 3);
772 if (!root->pi[i])
773 return;
59241895 774 root->pi[i][0] = target;
de13f431
BW
775 root->pi[i][1] = (char *) (root->pi[i + 1] = NULL); /* terminate pi list */
776 root->pi[i][2] = strdup(""); /* empty document position list */
59241895
MJ
777 }
778
779 while (root->pi[i][j])
de13f431 780 j++; /* find end of instruction list for this target */
886e1ddb
AM
781 sstmp = (char **) realloc(root->pi[i], sizeof(char *) * (j + 3));
782 if (!sstmp)
783 return;
59241895 784 root->pi[i] = sstmp;
886e1ddb
AM
785 stmp = (char *) realloc(root->pi[i][j + 1], j + 1);
786 if (!stmp)
787 return;
788 root->pi[i][j + 2] = stmp;
59241895 789 strcpy(root->pi[i][j + 2] + j - 1, (root->xml.name) ? ">" : "<");
de13f431
BW
790 root->pi[i][j + 1] = NULL; /* null terminate pi list for this target */
791 root->pi[i][j] = s; /* set instruction */
59241895
MJ
792}
793
de13f431 794/* called when the parser finds an internal doctype subset */
59241895
MJ
795static short switch_xml_internal_dtd(switch_xml_root_t root, char *s, switch_size_t len)
796{
797 char q, *c, *t, *n = NULL, *v, **ent, **pe;
798 int i, j;
799 char **sstmp;
800
886e1ddb
AM
801 pe = (char **) memcpy(malloc(sizeof(SWITCH_XML_NIL)), SWITCH_XML_NIL, sizeof(SWITCH_XML_NIL));
802
59241895
MJ
803 for (s[len] = '\0'; s;) {
804 while (*s && *s != '<' && *s != '%')
de13f431 805 s++; /* find next declaration */
59241895
MJ
806
807 if (!*s)
808 break;
886e1ddb 809 else if (!strncmp(s, "<!ENTITY", 8)) { /* parse entity definitions */
de13f431 810 c = s += strspn(s + 8, SWITCH_XML_WS) + 8; /* skip white space separator */
886e1ddb 811 n = s + strspn(s, SWITCH_XML_WS "%"); /* find name */
de13f431 812 *(s = n + strcspn(n, SWITCH_XML_WS)) = ';'; /* append ; to name */
59241895 813
de13f431 814 v = s + strspn(s + 1, SWITCH_XML_WS) + 1; /* find value */
886e1ddb 815 if ((q = *(v++)) != '"' && q != '\'') { /* skip externals */
59241895
MJ
816 s = strchr(s, '>');
817 continue;
818 }
819
820 for (i = 0, ent = (*c == '%') ? pe : root->ent; ent[i]; i++);
886e1ddb 821 sstmp = (char **) realloc(ent, (i + 3) * sizeof(char *)); /* space for next ent */
59241895
MJ
822 if (!sstmp) {
823 switch_xml_err(root, v, "Allocation Error!");
824 break;
825 }
826 ent = sstmp;
827 if (*c == '%')
828 pe = ent;
829 else
830 root->ent = ent;
831
de13f431 832 *(++s) = '\0'; /* null terminate name */
59241895 833 if ((s = strchr(v, q)))
de13f431
BW
834 *(s++) = '\0'; /* null terminate value */
835 ent[i + 1] = switch_xml_decode(v, pe, '%'); /* set value */
836 ent[i + 2] = NULL; /* null terminate entity list */
837 if (!switch_xml_ent_ok(n, ent[i + 1], ent)) { /* circular reference */
59241895
MJ
838 if (ent[i + 1] != v)
839 free(ent[i + 1]);
840 switch_xml_err(root, v, "circular entity declaration &%s", n);
841 break;
842 } else
de13f431
BW
843 ent[i] = n; /* set entity name */
844 } else if (!strncmp(s, "<!ATTLIST", 9)) { /* parse default attributes */
845 t = s + strspn(s + 9, SWITCH_XML_WS) + 9; /* skip whitespace separator */
59241895
MJ
846 if (!*t) {
847 switch_xml_err(root, t, "unclosed <!ATTLIST");
848 break;
849 }
850 if (*(s = t + strcspn(t, SWITCH_XML_WS ">")) == '>')
851 continue;
852 else
de13f431 853 *s = '\0'; /* null terminate tag name */
59241895
MJ
854 for (i = 0; root->attr[i] && strcmp(n, root->attr[i][0]); i++);
855
9300e0ad
AM
856 //while (*(n = ++s + strspn(s, SWITCH_XML_WS)) && *n != '>') {
857 // gcc 4.4 you are a creep
886e1ddb 858 for (;;) {
9300e0ad
AM
859 s++;
860 if (!(*(n = s + strspn(s, SWITCH_XML_WS)) && *n != '>')) {
861 break;
862 }
59241895 863 if (*(s = n + strcspn(n, SWITCH_XML_WS)))
de13f431 864 *s = '\0'; /* attr name */
59241895
MJ
865 else {
866 switch_xml_err(root, t, "malformed <!ATTLIST");
867 break;
868 }
869
de13f431 870 s += strspn(s + 1, SWITCH_XML_WS) + 1; /* find next token */
886e1ddb 871 c = (strncmp(s, "CDATA", 5)) ? (char *) "*" : (char *) " "; /* is it cdata? */
59241895
MJ
872 if (!strncmp(s, "NOTATION", 8))
873 s += strspn(s + 8, SWITCH_XML_WS) + 8;
874 s = (*s == '(') ? strchr(s, ')') : s + strcspn(s, SWITCH_XML_WS);
875 if (!s) {
876 switch_xml_err(root, t, "malformed <!ATTLIST");
877 break;
878 }
879
de13f431 880 s += strspn(s, SWITCH_XML_WS ")"); /* skip white space separator */
59241895
MJ
881 if (!strncmp(s, "#FIXED", 6))
882 s += strspn(s + 6, SWITCH_XML_WS) + 6;
de13f431 883 if (*s == '#') { /* no default value */
59241895
MJ
884 s += strcspn(s, SWITCH_XML_WS ">") - 1;
885 if (*c == ' ')
de13f431 886 continue; /* cdata is default, nothing to do */
59241895 887 v = NULL;
de13f431 888 } else if ((*s == '"' || *s == '\'') && /* default value */
59241895
MJ
889 (s = strchr(v = s + 1, *s)))
890 *s = '\0';
891 else {
892 switch_xml_err(root, t, "malformed <!ATTLIST");
893 break;
894 }
895
de13f431 896 if (!root->attr[i]) { /* new tag name */
886e1ddb
AM
897 root->attr = (!i) ? (char ***) malloc(2 * sizeof(char **))
898 : (char ***) realloc(root->attr, (i + 2) * sizeof(char **));
899 root->attr[i] = (char **) malloc(2 * sizeof(char *));
de13f431 900 root->attr[i][0] = t; /* set tag name */
59241895
MJ
901 root->attr[i][1] = (char *) (root->attr[i + 1] = NULL);
902 }
903
de13f431 904 for (j = 1; root->attr[i][j]; j += 3); /* find end of list */
886e1ddb 905 sstmp = (char **) realloc(root->attr[i], (j + 4) * sizeof(char *));
59241895
MJ
906
907 if (!sstmp) {
908 switch_xml_err(root, t, "Allocation Error!");
909 break;
910 }
911
912 root->attr[i] = sstmp;
de13f431 913 root->attr[i][j + 3] = NULL; /* null terminate list */
886e1ddb 914 root->attr[i][j + 2] = c; /* is it cdata? */
de13f431 915 root->attr[i][j + 1] = (v) ? switch_xml_decode(v, root->ent, *c) : NULL;
886e1ddb 916 root->attr[i][j] = n; /* attribute name */
59241895
MJ
917 }
918 } else if (!strncmp(s, "<!--", 4))
886e1ddb
AM
919 s = strstr(s + 4, "-->"); /* comments */
920 else if (!strncmp(s, "<?", 2)) { /* processing instructions */
59241895
MJ
921 if ((s = strstr(c = s + 2, "?>")))
922 switch_xml_proc_inst(root, c, s++ - c);
923 } else if (*s == '<')
886e1ddb 924 s = strchr(s, '>'); /* skip other declarations */
59241895
MJ
925 else if (*(s++) == '%' && !root->standalone)
926 break;
927 }
928
929 free(pe);
930 return !*root->err;
931}
932
de13f431
BW
933/* Converts a UTF-16 string to UTF-8. Returns a new string that must be freed
934 or NULL if no conversion was needed. */
59241895
MJ
935static char *switch_xml_str2utf8(char **s, switch_size_t *len)
936{
937 char *u;
938 switch_size_t l = 0, sl, max = *len;
939 long c, d;
940 int b, be = (**s == '\xFE') ? 1 : (**s == '\xFF') ? 0 : -1;
941
942 if (be == -1)
de13f431 943 return NULL; /* not UTF-16 */
59241895 944
886e1ddb 945 u = (char *) malloc(max);
59241895 946 for (sl = 2; sl < *len - 1; sl += 2) {
de13f431 947 c = (be) ? (((*s)[sl] & 0xFF) << 8) | ((*s)[sl + 1] & 0xFF) /* UTF-16BE */
886e1ddb 948 : (((*s)[sl + 1] & 0xFF) << 8) | ((*s)[sl] & 0xFF); /* UTF-16LE */
de13f431 949 if (c >= 0xD800 && c <= 0xDFFF && (sl += 2) < *len - 1) { /* high-half */
59241895
MJ
950 d = (be) ? (((*s)[sl] & 0xFF) << 8) | ((*s)[sl + 1] & 0xFF)
951 : (((*s)[sl + 1] & 0xFF) << 8) | ((*s)[sl] & 0xFF);
952 c = (((c & 0x3FF) << 10) | (d & 0x3FF)) + 0x10000;
953 }
954
955 while (l + 6 > max) {
956 char *tmp;
886e1ddb
AM
957 tmp = (char *) realloc(u, max += SWITCH_XML_BUFSIZE);
958 if (!tmp)
959 return NULL;
59241895
MJ
960 u = tmp;
961 }
962 if (c < 0x80)
de13f431
BW
963 u[l++] = (char) c; /* US-ASCII subset */
964 else { /* multi-byte UTF-8 sequence */
59241895 965 for (b = 0, d = c; d; d /= 2)
de13f431
BW
966 b++; /* bits in c */
967 b = (b - 2) / 5; /* bytes in payload */
968 u[l++] = (char) ((0xFF << (7 - b)) | (c >> (6 * b))); /* head */
59241895 969 while (b)
de13f431 970 u[l++] = (char) (0x80 | ((c >> (6 * --b)) & 0x3F)); /* payload */
59241895
MJ
971 }
972 }
886e1ddb 973 return *s = (char *) realloc(u, *len = l);
59241895
MJ
974}
975
de13f431 976/* frees a tag attribute list */
59241895
MJ
977static void switch_xml_free_attr(char **attr)
978{
979 int i = 0;
980 char *m;
981
982 if (!attr || attr == SWITCH_XML_NIL)
de13f431 983 return; /* nothing to free */
59241895 984 while (attr[i])
de13f431
BW
985 i += 2; /* find end of attribute list */
986 m = attr[i + 1]; /* list of which names and values are malloced */
59241895
MJ
987 for (i = 0; m[i]; i++) {
988 if (m[i] & SWITCH_XML_NAMEM)
989 free(attr[i * 2]);
990 if (m[i] & SWITCH_XML_TXTM)
991 free(attr[(i * 2) + 1]);
992 }
993 free(m);
994 free(attr);
995}
996
886e1ddb 997SWITCH_DECLARE(switch_xml_t) switch_xml_parse_str_dynamic(char *s, switch_bool_t dup)
3b168bcb
MR
998{
999 switch_xml_root_t root;
5802cb94 1000 char *data;
886e1ddb 1001
5802cb94
MR
1002 switch_assert(s);
1003 data = dup ? strdup(s) : s;
886e1ddb 1004
3b168bcb 1005 if ((root = (switch_xml_root_t) switch_xml_parse_str(data, strlen(data)))) {
886e1ddb 1006 root->dynamic = 1; /* Make sure we free the memory is switch_xml_free() */
3b168bcb
MR
1007 return &root->xml;
1008 } else {
5802cb94
MR
1009 if (dup) {
1010 free(data);
1011 }
3b168bcb
MR
1012 return NULL;
1013 }
1014}
886e1ddb 1015
6b6c83a7 1016/* parse the given xml string and return a switch_xml structure */
59241895
MJ
1017SWITCH_DECLARE(switch_xml_t) switch_xml_parse_str(char *s, switch_size_t len)
1018{
1019 switch_xml_root_t root = (switch_xml_root_t) switch_xml_new(NULL);
de13f431 1020 char q, e, *d, **attr, **a = NULL; /* initialize a to avoid compile warning */
59241895
MJ
1021 int l, i, j;
1022
1023 root->m = s;
1024 if (!len)
1025 return switch_xml_err(root, s, "root tag missing");
de13f431
BW
1026 root->u = switch_xml_str2utf8(&s, &len); /* convert utf-16 to utf-8 */
1027 root->e = (root->s = s) + len; /* record start and end of work area */
59241895 1028
de13f431
BW
1029 e = s[len - 1]; /* save end char */
1030 s[len - 1] = '\0'; /* turn end char into null terminator */
59241895
MJ
1031
1032 while (*s && *s != '<')
de13f431 1033 s++; /* find first tag */
59241895
MJ
1034 if (!*s)
1035 return switch_xml_err(root, s, "root tag missing");
1036
1037 for (;;) {
1038 attr = (char **) SWITCH_XML_NIL;
1039 d = ++s;
1040
886e1ddb 1041 if (isalpha((int) (*s)) || *s == '_' || *s == ':' || (int8_t) * s < '\0') { /* new tag */
59241895
MJ
1042 if (!root->cur)
1043 return switch_xml_err(root, d, "markup outside of root element");
1044
1045 s += strcspn(s, SWITCH_XML_WS "/>");
1046 while (isspace((int) (*s)))
886e1ddb 1047 *(s++) = '\0'; /* null terminate tag name */
59241895 1048
de13f431 1049 if (*s && *s != '/' && *s != '>') /* find tag in default attr list */
59241895
MJ
1050 for (i = 0; (a = root->attr[i]) && strcmp(a[0], d); i++);
1051
de13f431 1052 for (l = 0; *s && *s != '/' && *s != '>'; l += 2) { /* new attrib */
886e1ddb
AM
1053 attr = (l) ? (char **) realloc(attr, (l + 4) * sizeof(char *))
1054 : (char **) malloc(4 * sizeof(char *)); /* allocate space */
1055 attr[l + 3] = (l) ? (char *) realloc(attr[l + 1], (l / 2) + 2)
1056 : (char *) malloc(2); /* mem for list of maloced vals */
1057 strcpy(attr[l + 3] + (l / 2), " "); /* value is not malloced */
1058 attr[l + 2] = NULL; /* null terminate list */
1059 attr[l + 1] = (char *) ""; /* temporary attribute value */
1060 attr[l] = s; /* set attribute name */
59241895
MJ
1061
1062 s += strcspn(s, SWITCH_XML_WS "=/>");
1063 if (*s == '=' || isspace((int) (*s))) {
de13f431 1064 *(s++) = '\0'; /* null terminate tag attribute name */
59241895 1065 q = *(s += strspn(s, SWITCH_XML_WS "="));
de13f431 1066 if (q == '"' || q == '\'') { /* attribute value */
59241895
MJ
1067 attr[l + 1] = ++s;
1068 while (*s && *s != q)
1069 s++;
1070 if (*s)
de13f431 1071 *(s++) = '\0'; /* null terminate attribute val */
59241895
MJ
1072 else {
1073 switch_xml_free_attr(attr);
1074 return switch_xml_err(root, d, "missing %c", q);
1075 }
1076
1077 for (j = 1; a && a[j] && strcmp(a[j], attr[l]); j += 3);
1078 attr[l + 1] = switch_xml_decode(attr[l + 1], root->ent, (a && a[j]) ? *a[j + 2] : ' ');
1079 if (attr[l + 1] < d || attr[l + 1] > s)
de13f431 1080 attr[l + 3][l / 2] = SWITCH_XML_TXTM; /* value malloced */
59241895
MJ
1081 }
1082 }
1083 while (isspace((int) (*s)))
1084 s++;
1085 }
1086
de13f431 1087 if (*s == '/') { /* self closing tag */
59241895
MJ
1088 *(s++) = '\0';
1089 if ((*s && *s != '>') || (!*s && e != '>')) {
1090 if (l)
1091 switch_xml_free_attr(attr);
1092 return switch_xml_err(root, d, "missing >");
1093 }
1094 switch_xml_open_tag(root, d, attr);
1095 switch_xml_close_tag(root, d, s);
de13f431
BW
1096 } else if ((q = *s) == '>' || (!*s && e == '>')) { /* open tag */
1097 *s = '\0'; /* temporarily null terminate tag name */
59241895
MJ
1098 switch_xml_open_tag(root, d, attr);
1099 *s = q;
1100 } else {
1101 if (l)
1102 switch_xml_free_attr(attr);
1103 return switch_xml_err(root, d, "missing >");
1104 }
de13f431 1105 } else if (*s == '/') { /* close tag */
59241895
MJ
1106 s += strcspn(d = s + 1, SWITCH_XML_WS ">") + 1;
1107 if (!(q = *s) && e != '>')
1108 return switch_xml_err(root, d, "missing >");
de13f431 1109 *s = '\0'; /* temporarily null terminate tag name */
59241895
MJ
1110 if (switch_xml_close_tag(root, d, s))
1111 return &root->xml;
1112 if (isspace((int) (*s = q)))
1113 s += strspn(s, SWITCH_XML_WS);
de13f431 1114 } else if (!strncmp(s, "!--", 3)) { /* xml comment */
59241895
MJ
1115 if (!(s = strstr(s + 3, "--")) || (*(s += 2) != '>' && *s) || (!*s && e != '>'))
1116 return switch_xml_err(root, d, "unclosed <!--");
de13f431 1117 } else if (!strncmp(s, "![CDATA[", 8)) { /* cdata */
59241895
MJ
1118 if ((s = strstr(s, "]]>")))
1119 switch_xml_char_content(root, d + 8, (s += 2) - d - 10, 'c');
1120 else
1121 return switch_xml_err(root, d, "unclosed <![CDATA[");
de13f431 1122 } else if (!strncmp(s, "!DOCTYPE", 8)) { /* dtd */
59241895
MJ
1123 for (l = 0; *s && ((!l && *s != '>') || (l && (*s != ']' || *(s + strspn(s + 1, SWITCH_XML_WS) + 1) != '>'))); l = (*s == '[') ? 1 : l)
1124 s += strcspn(s + 1, "[]>") + 1;
1125 if (!*s && e != '>')
1126 return switch_xml_err(root, d, "unclosed <!DOCTYPE");
1127 d = (l) ? strchr(d, '[') + 1 : d;
1128 if (l && !switch_xml_internal_dtd(root, d, s++ - d))
1129 return &root->xml;
de13f431 1130 } else if (*s == '?') { /* <?...?> processing instructions */
59241895
MJ
1131 do {
1132 s = strchr(s, '?');
1133 } while (s && *(++s) && *s != '>');
1134 if (!s || (!*s && e != '>'))
1135 return switch_xml_err(root, d, "unclosed <?");
1136 else
1137 switch_xml_proc_inst(root, d + 1, s - d - 2);
1138 } else
1139 return switch_xml_err(root, d, "unexpected <");
1140
1141 if (!s || !*s)
1142 break;
1143 *s = '\0';
1144 d = ++s;
de13f431 1145 if (*s && *s != '<') { /* tag character content */
59241895
MJ
1146 while (*s && *s != '<')
1147 s++;
1148 if (*s)
1149 switch_xml_char_content(root, d, s - d, '&');
1150 else
1151 break;
1152 } else if (!*s)
1153 break;
1154 }
1155
1156 if (!root->cur)
1157 return &root->xml;
1158 else if (!root->cur->name)
1159 return switch_xml_err(root, d, "root tag missing");
1160 else
1161 return switch_xml_err(root, d, "unclosed tag <%s>", root->cur->name);
1162}
1163
de13f431
BW
1164/* Wrapper for switch_xml_parse_str() that accepts a file stream. Reads the entire
1165 stream into memory and then parses it. For xml files, use switch_xml_parse_file()
1166 or switch_xml_parse_fd() */
59241895
MJ
1167SWITCH_DECLARE(switch_xml_t) switch_xml_parse_fp(FILE * fp)
1168{
1169 switch_xml_root_t root;
1170 switch_size_t l, len = 0;
1171 char *s;
1172
886e1ddb 1173 if (!(s = (char *) malloc(SWITCH_XML_BUFSIZE)))
59241895
MJ
1174 return NULL;
1175 do {
1176 len += (l = fread((s + len), 1, SWITCH_XML_BUFSIZE, fp));
1177 if (l == SWITCH_XML_BUFSIZE) {
5923f71a
JL
1178 char *tmp = s;
1179 s = (char *) realloc(s, len + SWITCH_XML_BUFSIZE);
1180 if (!s) {
1181 free(tmp);
59241895
MJ
1182 return NULL;
1183 }
59241895
MJ
1184 }
1185 } while (s && l == SWITCH_XML_BUFSIZE);
1186
1187 if (!s)
1188 return NULL;
1189 root = (switch_xml_root_t) switch_xml_parse_str(s, len);
de13f431 1190 root->dynamic = 1; /* so we know to free s in switch_xml_free() */
59241895
MJ
1191 return &root->xml;
1192}
1193
de13f431
BW
1194/* A wrapper for switch_xml_parse_str() that accepts a file descriptor. First
1195 attempts to mem map the file. Failing that, reads the file into memory.
1196 Returns NULL on failure. */
59241895
MJ
1197SWITCH_DECLARE(switch_xml_t) switch_xml_parse_fd(int fd)
1198{
1199 switch_xml_root_t root;
1200 struct stat st;
1201 switch_size_t l;
1202 void *m;
1203
1204 if (fd < 0)
1205 return NULL;
1206 fstat(fd, &st);
1207
8f4e6392
AM
1208 if (!st.st_size) {
1209 return NULL;
1210 }
1211
34bd0e5e
MOC
1212 m = malloc(st.st_size);
1213 if (!m)
1214 return NULL;
1215 l = read(fd, m, st.st_size);
1216 if (!l || !(root = (switch_xml_root_t) switch_xml_parse_str((char *) m, l))) {
1217 free(m);
1218 return NULL;
59241895 1219 }
34bd0e5e
MOC
1220 root->dynamic = 1; /* so we know to free s in switch_xml_free() */
1221
59241895
MJ
1222 return &root->xml;
1223}
1224
531e0f2d 1225static char *expand_vars(char *buf, char *ebuf, switch_size_t elen, switch_size_t *newlen, const char **err)
59241895
MJ
1226{
1227 char *var, *val;
1228 char *rp = buf;
1229 char *wp = ebuf;
1230 char *ep = ebuf + elen - 1;
1231
1232 if (!(var = strstr(rp, "$${"))) {
1233 *newlen = strlen(buf);
1234 return buf;
1235 }
1236
1237 while (*rp && wp < ep) {
1238
1239 if (*rp == '$' && *(rp + 1) == '$' && *(rp + 2) == '{') {
3718cbc9 1240 char *e = switch_find_end_paren(rp + 2, '{', '}');
59241895
MJ
1241
1242 if (e) {
1243 rp += 3;
1244 var = rp;
1245 *e++ = '\0';
1246 rp = e;
4ae8282e 1247 if ((val = switch_core_get_variable_dup(var))) {
59241895
MJ
1248 char *p;
1249 for (p = val; p && *p && wp <= ep; p++) {
1250 *wp++ = *p;
1251 }
4ae8282e 1252 free(val);
59241895 1253 }
3718cbc9 1254 continue;
531e0f2d
AM
1255 } else if (err) {
1256 *err = "unterminated ${var}";
59241895
MJ
1257 }
1258 }
1259
1260 *wp++ = *rp++;
1261 }
1262 *wp++ = '\0';
1263 *newlen = strlen(ebuf);
1264
1265 return ebuf;
1266}
1267
c5204c21 1268static FILE *preprocess_exec(const char *cwd, const char *command, FILE *write_fd, int rlevel)
64a0bfc5
AM
1269{
1270#ifdef WIN32
16289ce9
JL
1271 FILE *fp = NULL;
1272 char buffer[1024];
64a0bfc5 1273
16289ce9
JL
1274 if (!command || !strlen(command)) goto end;
1275
1276 if ((fp = _popen(command, "r"))) {
1277 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
1278 if (fwrite(buffer, 1, strlen(buffer), write_fd) <= 0) {
1279 break;
1280 }
1281 }
a2fcb419 1282
16289ce9
JL
1283 if(feof(fp)) {
1284 _pclose(fp);
1285 } else {
1286 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Exec failed to read the pipe of [%s] to the end\n", command);
1287 }
1288 } else {
1289 switch_snprintf(buffer, sizeof(buffer), "<!-- exec can not execute [%s] -->", command);
1290 fwrite( buffer, 1, strlen(buffer), write_fd);
1291 }
64a0bfc5
AM
1292#else
1293 int fds[2], pid = 0;
1294
1295 if (pipe(fds)) {
1296 goto end;
886e1ddb 1297 } else { /* good to go */
52eff945 1298 pid = switch_fork();
64a0bfc5 1299
886e1ddb 1300 if (pid < 0) { /* ok maybe not */
64a0bfc5
AM
1301 close(fds[0]);
1302 close(fds[1]);
1303 goto end;
886e1ddb 1304 } else if (pid) { /* parent */
64a0bfc5
AM
1305 char buf[1024] = "";
1306 int bytes;
1307 close(fds[1]);
1308 while ((bytes = read(fds[0], buf, sizeof(buf))) > 0) {
c5204c21 1309 if (fwrite(buf, 1, bytes, write_fd) <= 0) {
64a0bfc5
AM
1310 break;
1311 }
1312 }
1313 close(fds[0]);
dae2cb4a 1314 waitpid(pid, NULL, 0);
886e1ddb 1315 } else { /* child */
0f6a2bf8 1316 switch_close_extra_files(fds, 2);
64a0bfc5
AM
1317 close(fds[0]);
1318 dup2(fds[1], STDOUT_FILENO);
70ecd353 1319 switch_system(command, SWITCH_TRUE);
64a0bfc5
AM
1320 close(fds[1]);
1321 exit(0);
1322 }
1323 }
1324#endif
886e1ddb 1325 end:
64a0bfc5
AM
1326
1327 return write_fd;
886e1ddb 1328
64a0bfc5
AM
1329}
1330
2f48c631 1331static FILE *preprocess_glob(const char *cwd, const char *pattern, FILE *write_fd, int rlevel)
59241895
MJ
1332{
1333 char *full_path = NULL;
1334 char *dir_path = NULL, *e = NULL;
886e1ddb 1335 glob_t glob_data;
59241895
MJ
1336 size_t n;
1337
1338 if (!switch_is_file_path(pattern)) {
1339 full_path = switch_mprintf("%s%s%s", cwd, SWITCH_PATH_SEPARATOR, pattern);
1340 pattern = full_path;
1341 }
886e1ddb 1342
2f48c631
KR
1343 if (glob(pattern, GLOB_NOCHECK, NULL, &glob_data) != 0) {
1344 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error including %s\n", pattern);
59241895
MJ
1345 goto end;
1346 }
1347
1348 for (n = 0; n < glob_data.gl_pathc; ++n) {
1349 dir_path = strdup(glob_data.gl_pathv[n]);
1350 switch_assert(dir_path);
1351 if ((e = strrchr(dir_path, *SWITCH_PATH_SEPARATOR))) {
1352 *e = '\0';
1353 }
1354 if (preprocess(dir_path, glob_data.gl_pathv[n], write_fd, rlevel) < 0) {
59241895 1355 if (rlevel > 100) {
2d6161e8 1356 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error including %s (Maximum recursion limit reached)\n", pattern);
59241895 1357 }
59241895 1358 }
7ffcb0e6 1359 free(dir_path);
59241895 1360 }
886e1ddb 1361 globfree(&glob_data);
59241895 1362
886e1ddb 1363 end:
59241895
MJ
1364
1365 switch_safe_free(full_path);
1366
1367 return write_fd;
1368}
1369
c5204c21 1370static int preprocess(const char *cwd, const char *file, FILE *write_fd, int rlevel)
59241895 1371{
c5204c21 1372 FILE *read_fd = NULL;
59241895 1373 switch_size_t cur = 0, ml = 0;
c5204c21 1374 char *q, *cmd, *buf = NULL, *ebuf = NULL;
59241895 1375 char *tcmd, *targ;
531e0f2d 1376 int line = 0;
c5204c21 1377 switch_size_t len = 0, eblen = 0;
886e1ddb 1378
c5204c21
AM
1379 if (rlevel > 100) {
1380 return -1;
59241895
MJ
1381 }
1382
c5204c21
AM
1383 if (!(read_fd = fopen(file, "r"))) {
1384 const char *reason = strerror(errno);
1385 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldnt open %s (%s)\n", file, reason);
59241895
MJ
1386 return -1;
1387 }
1388
c5204c21 1389 setvbuf(read_fd, (char *) NULL, _IOFBF, 65536);
a2fcb419 1390
c5204c21 1391 for(;;) {
59241895 1392 char *arg, *e;
531e0f2d 1393 const char *err = NULL;
c5204c21
AM
1394 char *bp;
1395
1396 switch_safe_free(ebuf);
1397
1398 if ((cur = switch_fp_read_dline(read_fd, &buf, &len)) <= 0) {
1399 break;
1400 }
1401
1402 eblen = len *2;
1403 ebuf = malloc(eblen);
1404 memset(ebuf, 0, eblen);
a2fcb419 1405
c5204c21 1406 bp = expand_vars(buf, ebuf, eblen, &cur, &err);
531e0f2d 1407 line++;
886e1ddb 1408
4577ec87
AM
1409 if (err) {
1410 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error [%s] in file %s line %d\n", err, file, line);
531e0f2d 1411 }
59241895
MJ
1412
1413 /* we ignore <include> or </include> for the sake of validators as well as <?xml version="1.0"?> type stuff */
1414 if (strstr(buf, "<include>") || strstr(buf, "</include>") || strstr(buf, "<?")) {
1415 continue;
1416 }
1417
1418 if (ml) {
1419 if ((e = strstr(buf, "-->"))) {
1420 ml = 0;
1421 bp = e + 3;
1422 cur = strlen(bp);
1423 } else {
1424 continue;
1425 }
1426 }
1427
886e1ddb
AM
1428 if ((tcmd = (char *) switch_stristr("X-pre-process", bp))) {
1429 if (*(tcmd - 1) != '<') {
59241895
MJ
1430 continue;
1431 }
1432 if ((e = strstr(tcmd, "/>"))) {
1433 *e += 2;
1434 *e = '\0';
c5204c21 1435 if (fwrite(e, 1, (unsigned) strlen(e), write_fd) != (int) strlen(e)) {
59241895
MJ
1436 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Short write!\n");
1437 }
1438 }
886e1ddb
AM
1439
1440 if (!(tcmd = (char *) switch_stristr("cmd", tcmd))) {
59241895
MJ
1441 continue;
1442 }
1443
886e1ddb 1444 if (!(tcmd = (char *) switch_stristr("=", tcmd))) {
59241895
MJ
1445 continue;
1446 }
1447
886e1ddb 1448 if (!(tcmd = (char *) switch_stristr("\"", tcmd))) {
59241895
MJ
1449 continue;
1450 }
886e1ddb 1451
59241895
MJ
1452 tcmd++;
1453
886e1ddb 1454
59241895
MJ
1455 if ((e = strchr(tcmd, '"'))) {
1456 *e++ = '\0';
1457 }
1458
886e1ddb 1459 if (!(targ = (char *) switch_stristr("data", e))) {
59241895
MJ
1460 continue;
1461 }
1462
886e1ddb 1463 if (!(targ = (char *) switch_stristr("=", targ))) {
59241895
MJ
1464 continue;
1465 }
1466
886e1ddb 1467 if (!(targ = (char *) switch_stristr("\"", targ))) {
59241895
MJ
1468 continue;
1469 }
1470
1471 targ++;
1472
1473 if ((e = strchr(targ, '"'))) {
1474 *e++ = '\0';
1475 }
886e1ddb 1476
59241895 1477 if (!strcasecmp(tcmd, "set")) {
886e1ddb 1478 char *name = (char *) targ;
59241895 1479 char *val = strchr(name, '=');
886e1ddb 1480
59241895
MJ
1481 if (val) {
1482 char *ve = val++;
1483 while (*val && *val == ' ') {
886e1ddb 1484 val++;
59241895
MJ
1485 }
1486 *ve-- = '\0';
1487 while (*ve && *ve == ' ') {
1488 *ve-- = '\0';
1489 }
1490 }
886e1ddb 1491
59241895
MJ
1492 if (name && val) {
1493 switch_core_set_variable(name, val);
1494 }
886e1ddb 1495
f30e40a8
KR
1496 } else if (!strcasecmp(tcmd, "exec-set")) {
1497 preprocess_exec_set(targ);
59241895 1498 } else if (!strcasecmp(tcmd, "include")) {
2f48c631 1499 preprocess_glob(cwd, targ, write_fd, rlevel + 1);
64a0bfc5
AM
1500 } else if (!strcasecmp(tcmd, "exec")) {
1501 preprocess_exec(cwd, targ, write_fd, rlevel + 1);
59241895
MJ
1502 }
1503
1504 continue;
1505 }
886e1ddb 1506
59241895 1507 if ((cmd = strstr(bp, "<!--#"))) {
b39232c7 1508 if (fwrite(bp, 1, (unsigned) (cmd - bp), write_fd) != (unsigned) (cmd - bp)) {
59241895
MJ
1509 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Short write!\n");
1510 }
1511 if ((e = strstr(cmd, "-->"))) {
1512 *e = '\0';
1513 e += 3;
c5204c21 1514 if (fwrite(e, 1, (unsigned) strlen(e), write_fd) != (int) strlen(e)) {
59241895
MJ
1515 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Short write!\n");
1516 }
1517 } else {
1518 ml++;
1519 }
1520
1521 cmd += 5;
1522 if ((e = strchr(cmd, '\r')) || (e = strchr(cmd, '\n'))) {
1523 *e = '\0';
1524 }
1525
1526 if ((arg = strchr(cmd, ' '))) {
1527 *arg++ = '\0';
1528 if ((q = strchr(arg, '"'))) {
1529 char *qq = q + 1;
1530
1531 if ((qq = strchr(qq, '"'))) {
1532 *qq = '\0';
1533 arg = q + 1;
1534 }
1535 }
1536
1537 if (!strcasecmp(cmd, "set")) {
1538 char *name = arg;
1539 char *val = strchr(name, '=');
1540
1541 if (val) {
1542 char *ve = val++;
1543 while (*val && *val == ' ') {
1544 val++;
1545 }
1546 *ve-- = '\0';
1547 while (*ve && *ve == ' ') {
1548 *ve-- = '\0';
1549 }
1550 }
1551
1552 if (name && val) {
1553 switch_core_set_variable(name, val);
1554 }
1555
f30e40a8
KR
1556 } else if (!strcasecmp(cmd, "exec-set")) {
1557 preprocess_exec_set(arg);
59241895 1558 } else if (!strcasecmp(cmd, "include")) {
2f48c631 1559 preprocess_glob(cwd, arg, write_fd, rlevel + 1);
64a0bfc5
AM
1560 } else if (!strcasecmp(cmd, "exec")) {
1561 preprocess_exec(cwd, arg, write_fd, rlevel + 1);
59241895
MJ
1562 }
1563 }
1564
1565 continue;
1566 }
1567
c5204c21 1568 if (fwrite(bp, 1, (unsigned) cur, write_fd) != (int) cur) {
59241895
MJ
1569 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Short write!\n");
1570 }
c5204c21 1571
59241895
MJ
1572 }
1573
c5204c21
AM
1574 switch_safe_free(buf);
1575 switch_safe_free(ebuf);
1576
1577 fclose(read_fd);
1578
1579 return 0;
59241895
MJ
1580}
1581
1582SWITCH_DECLARE(switch_xml_t) switch_xml_parse_file_simple(const char *file)
1583{
1584 int fd = -1;
1585 struct stat st;
1586 switch_size_t l;
1587 void *m;
1588 switch_xml_root_t root;
1589
1590 if ((fd = open(file, O_RDONLY, 0)) > -1) {
1591 fstat(fd, &st);
9da34921 1592 if (!st.st_size) goto error;
59241895
MJ
1593 m = malloc(st.st_size);
1594 switch_assert(m);
9da34921
AM
1595 if (!(l = read(fd, m, st.st_size))) goto error;
1596 if (!(root = (switch_xml_root_t) switch_xml_parse_str((char *) m, l))) goto error;
59241895
MJ
1597 root->dynamic = 1;
1598 close(fd);
1599 return &root->xml;
1600 }
9da34921
AM
1601
1602 error:
1603
1604 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Parsing File [%s]\n", file);
886e1ddb 1605
59241895
MJ
1606 return NULL;
1607}
1608
1609SWITCH_DECLARE(switch_xml_t) switch_xml_parse_file(const char *file)
1610{
c5204c21
AM
1611 int fd = -1;
1612 FILE *write_fd = NULL;
59241895
MJ
1613 switch_xml_t xml = NULL;
1614 char *new_file = NULL;
da7f9715 1615 const char *abs, *absw;
59241895 1616
886e1ddb
AM
1617 abs = strrchr(file, '/');
1618 absw = strrchr(file, '\\');
da7f9715
JL
1619 if (abs || absw) {
1620 abs > absw ? abs++ : (abs = ++absw);
59241895
MJ
1621 } else {
1622 abs = file;
1623 }
a2fcb419 1624
3ed59f1d 1625 switch_mutex_lock(FILE_LOCK);
886e1ddb 1626
59241895 1627 if (!(new_file = switch_mprintf("%s%s%s.fsxml", SWITCH_GLOBAL_dirs.log_dir, SWITCH_PATH_SEPARATOR, abs))) {
3ed59f1d 1628 goto done;
59241895
MJ
1629 }
1630
c5204c21 1631 if ((write_fd = fopen(new_file, "w+")) == NULL) {
59241895
MJ
1632 goto done;
1633 }
1634
c5204c21
AM
1635 setvbuf(write_fd, (char *) NULL, _IOFBF, 65536);
1636
59241895 1637 if (preprocess(SWITCH_GLOBAL_dirs.conf_dir, file, write_fd, 0) > -1) {
c5204c21
AM
1638 fclose(write_fd);
1639 write_fd = NULL;
59241895
MJ
1640 if ((fd = open(new_file, O_RDONLY, 0)) > -1) {
1641 if ((xml = switch_xml_parse_fd(fd))) {
1780e228 1642 if (strcmp(abs, SWITCH_GLOBAL_filenames.conf_name)) {
bd41ecc3
AM
1643 xml->free_path = new_file;
1644 new_file = NULL;
1645 }
59241895
MJ
1646 }
1647 close(fd);
1648 fd = -1;
1649 }
1650 }
1651
1652 done:
3ed59f1d
AM
1653
1654 switch_mutex_unlock(FILE_LOCK);
1655
c5204c21
AM
1656 if (write_fd) {
1657 fclose(write_fd);
1658 write_fd = NULL;
59241895 1659 }
3ed59f1d 1660
59241895
MJ
1661 if (fd > -1) {
1662 close(fd);
1663 }
3ed59f1d 1664
59241895 1665 switch_safe_free(new_file);
3ed59f1d 1666
59241895
MJ
1667 return xml;
1668}
1669
1670SWITCH_DECLARE(switch_status_t) switch_xml_locate(const char *section,
1671 const char *tag_name,
886e1ddb
AM
1672 const char *key_name,
1673 const char *key_value,
1674 switch_xml_t *root, switch_xml_t *node, switch_event_t *params, switch_bool_t clone)
59241895
MJ
1675{
1676 switch_xml_t conf = NULL;
1677 switch_xml_t tag = NULL;
1678 switch_xml_t xml = NULL;
1679 switch_xml_binding_t *binding;
1680 uint8_t loops = 0;
9918f705 1681 switch_xml_section_t sections = BINDINGS ? switch_xml_parse_section_string(section) : 0;
59241895 1682
8ec9b8f7 1683 switch_thread_rwlock_rdlock(B_RWLOCK);
59241895
MJ
1684
1685 for (binding = BINDINGS; binding; binding = binding->next) {
59241895
MJ
1686 if (binding->sections && !(sections & binding->sections)) {
1687 continue;
1688 }
1689
1690 if ((xml = binding->function(section, tag_name, key_name, key_value, params, binding->user_data))) {
1691 const char *err = NULL;
1692
1693 err = switch_xml_error(xml);
df7637f6 1694 if (zstr(err)) {
59241895
MJ
1695 if ((conf = switch_xml_find_child(xml, "section", "name", "result"))) {
1696 switch_xml_t p;
1697 const char *aname;
1698
1699 if ((p = switch_xml_child(conf, "result"))) {
1700 aname = switch_xml_attr(p, "status");
1701 if (aname && !strcasecmp(aname, "not found")) {
1702 switch_xml_free(xml);
1703 xml = NULL;
1704 continue;
1705 }
1706 }
1707 }
1708 break;
1709 } else {
1710 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error[%s]\n", err);
1711 switch_xml_free(xml);
1712 xml = NULL;
1713 }
1714 }
1715 }
8ec9b8f7 1716 switch_thread_rwlock_unlock(B_RWLOCK);
59241895
MJ
1717
1718 for (;;) {
1719 if (!xml) {
1720 if (!(xml = switch_xml_root())) {
1721 *node = NULL;
1722 *root = NULL;
1723 return SWITCH_STATUS_FALSE;
1724 }
1725 }
1726
1727 if ((conf = switch_xml_find_child(xml, "section", "name", section)) && (tag = switch_xml_find_child(conf, tag_name, key_name, key_value))) {
93ec3d68 1728 if (clone) {
3b168bcb 1729 char *x = switch_xml_toxml(tag, SWITCH_FALSE);
93ec3d68 1730 switch_assert(x);
886e1ddb 1731 *node = *root = switch_xml_parse_str_dynamic(x, SWITCH_FALSE); /* x will be free()'d in switch_xml_free() */
93ec3d68
MJ
1732 switch_xml_free(xml);
1733 } else {
1734 *node = tag;
1735 *root = xml;
1736 }
59241895
MJ
1737 return SWITCH_STATUS_SUCCESS;
1738 } else {
1739 switch_xml_free(xml);
1740 xml = NULL;
1741 *node = NULL;
1742 *root = NULL;
1743 if (loops++ > 1) {
1744 break;
1745 }
1746 }
1747 }
1748
1749 return SWITCH_STATUS_FALSE;
1750}
1751
886e1ddb 1752SWITCH_DECLARE(switch_status_t) switch_xml_locate_domain(const char *domain_name, switch_event_t *params, switch_xml_t *root, switch_xml_t *domain)
59241895
MJ
1753{
1754 switch_event_t *my_params = NULL;
1755 switch_status_t status;
1756 *domain = NULL;
1757
1758 if (!params) {
003847dd 1759 switch_event_create(&my_params, SWITCH_EVENT_REQUEST_PARAMS);
59241895
MJ
1760 switch_assert(my_params);
1761 switch_event_add_header_string(my_params, SWITCH_STACK_BOTTOM, "domain", domain_name);
1762 params = my_params;
1763 }
1764
93ec3d68 1765 status = switch_xml_locate("directory", "domain", "name", domain_name, root, domain, params, SWITCH_FALSE);
59241895
MJ
1766 if (my_params) {
1767 switch_event_destroy(&my_params);
1768 }
1769 return status;
1770}
1771
ed100f44 1772SWITCH_DECLARE(switch_status_t) switch_xml_locate_group(const char *group_name,
886e1ddb
AM
1773 const char *domain_name,
1774 switch_xml_t *root, switch_xml_t *domain, switch_xml_t *group, switch_event_t *params)
ed100f44
AM
1775{
1776 switch_status_t status = SWITCH_STATUS_FALSE;
1777 switch_event_t *my_params = NULL;
1778 switch_xml_t groups = NULL;
1779
1780 *root = NULL;
1781 *group = NULL;
1782 *domain = NULL;
1783
1784 if (!params) {
1785 switch_event_create(&my_params, SWITCH_EVENT_REQUEST_PARAMS);
1786 switch_assert(my_params);
1787 params = my_params;
1788 }
1789
1790 if (group_name) {
1791 switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "group_name", group_name);
1792 }
1793
1794 if (domain_name) {
1795 switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "domain", domain_name);
1796 }
1797
1798 if ((status = switch_xml_locate_domain(domain_name, params, root, domain)) != SWITCH_STATUS_SUCCESS) {
1799 goto end;
1800 }
886e1ddb 1801
ed100f44
AM
1802 status = SWITCH_STATUS_FALSE;
1803
1804 if ((groups = switch_xml_child(*domain, "groups"))) {
1805 if ((*group = switch_xml_find_child(groups, "group", "name", group_name))) {
1806 status = SWITCH_STATUS_SUCCESS;
1807 }
1808 }
1809
886e1ddb 1810 end:
ed100f44
AM
1811
1812 if (my_params) {
1813 switch_event_destroy(&my_params);
1814 }
1815
1816 return status;
1817}
1818
de13f431
BW
1819static switch_status_t find_user_in_tag(switch_xml_t tag, const char *ip, const char *user_name,
1820 const char *key, switch_event_t *params, switch_xml_t *user)
ed100f44
AM
1821{
1822 const char *type = "!pointer";
1823 const char *val;
1824
1825 if (params && (val = switch_event_get_header(params, "user_type"))) {
1826 if (!strcasecmp(val, "any")) {
1827 type = NULL;
1828 } else {
1829 type = val;
1830 }
1831 }
1832
1833 if (ip) {
1834 if ((*user = switch_xml_find_child_multi(tag, "user", "ip", ip, "type", type, NULL))) {
1835 return SWITCH_STATUS_SUCCESS;
1836 }
886e1ddb 1837 }
ed100f44
AM
1838
1839 if (user_name) {
d18fa24a
MJ
1840 if (!strcasecmp(key, "id")) {
1841 if ((*user = switch_xml_find_child_multi(tag, "user", key, user_name, "number-alias", user_name, "type", type, NULL))) {
1842 return SWITCH_STATUS_SUCCESS;
1843 }
1844 } else {
1845 if ((*user = switch_xml_find_child_multi(tag, "user", key, user_name, "type", type, NULL))) {
ed100f44
AM
1846 return SWITCH_STATUS_SUCCESS;
1847 }
ed100f44
AM
1848 }
1849 }
886e1ddb 1850
ed100f44 1851 return SWITCH_STATUS_FALSE;
886e1ddb 1852
ed100f44
AM
1853}
1854
886e1ddb 1855SWITCH_DECLARE(switch_status_t) switch_xml_locate_user_in_domain(const char *user_name, switch_xml_t domain, switch_xml_t *user, switch_xml_t *ingroup)
bb974720
AM
1856{
1857 switch_xml_t group = NULL, groups = NULL, users = NULL;
1858 switch_status_t status = SWITCH_STATUS_FALSE;
886e1ddb 1859
bb974720
AM
1860 if ((groups = switch_xml_child(domain, "groups"))) {
1861 for (group = switch_xml_child(groups, "group"); group; group = group->next) {
1862 if ((users = switch_xml_child(group, "users"))) {
1863 if ((status = find_user_in_tag(users, NULL, user_name, "id", NULL, user)) == SWITCH_STATUS_SUCCESS) {
1864 if (ingroup) {
1865 *ingroup = group;
1866 }
1867 break;
1868 }
1869 }
1870 }
1871 }
1872
1873 return status;
1874}
ed100f44 1875
c65da59d
AM
1876
1877SWITCH_DECLARE(switch_xml_t) switch_xml_dup(switch_xml_t xml)
1878{
1879 char *x = switch_xml_toxml(xml, SWITCH_FALSE);
a2fcb419 1880 return switch_xml_parse_str_dynamic(x, SWITCH_FALSE);
c65da59d
AM
1881}
1882
1883
a2fcb419 1884static void do_merge(switch_xml_t in, switch_xml_t src, const char *container, const char *tag_name)
c65da59d
AM
1885{
1886 switch_xml_t itag, tag, param, iparam, iitag;
1887
1888 if (!(itag = switch_xml_child(in, container))) {
1889 itag = switch_xml_add_child_d(in, container, 0);
1890 }
1891
1892 if ((tag = switch_xml_child(src, container))) {
1893 for (param = switch_xml_child(tag, tag_name); param; param = param->next) {
1894 const char *var = switch_xml_attr(param, "name");
1895 const char *val = switch_xml_attr(param, "value");
a2fcb419 1896
c65da59d
AM
1897 int go = 1;
1898
1899 for (iparam = switch_xml_child(itag, tag_name); iparam; iparam = iparam->next) {
1900 const char *ivar = switch_xml_attr(iparam, "name");
a2fcb419 1901
c65da59d
AM
1902 if (var && ivar && !strcasecmp(var, ivar)) {
1903 go = 0;
1904 break;
1905 }
1906 }
a2fcb419 1907
c65da59d
AM
1908 if (go) {
1909 iitag = switch_xml_add_child_d(itag, tag_name, 0);
ea5db72a
BW
1910 switch_xml_set_attr_d(iitag, "name", var);
1911 switch_xml_set_attr_d(iitag, "value", val);
c65da59d
AM
1912 }
1913 }
1914 }
a2fcb419 1915
c65da59d
AM
1916}
1917
1918
1919SWITCH_DECLARE(void) switch_xml_merge_user(switch_xml_t user, switch_xml_t domain, switch_xml_t group)
1920{
f08c3309 1921 const char *domain_name = switch_xml_attr(domain, "name");
c65da59d
AM
1922
1923 do_merge(user, group, "params", "param");
1924 do_merge(user, group, "variables", "variable");
a6bffd38 1925 do_merge(user, group, "profile-variables", "variable");
c65da59d
AM
1926 do_merge(user, domain, "params", "param");
1927 do_merge(user, domain, "variables", "variable");
a6bffd38 1928 do_merge(user, domain, "profile-variables", "variable");
f08c3309
AM
1929
1930 if (!zstr(domain_name)) {
1931 switch_xml_set_attr_d(user, "domain-name", domain_name);
1932 }
c65da59d
AM
1933}
1934
2582bbcb
AM
1935SWITCH_DECLARE(uint32_t) switch_xml_clear_user_cache(const char *key, const char *user_name, const char *domain_name)
1936{
1937 switch_hash_index_t *hi;
1938 void *val;
1939 const void *var;
1940 char mega_key[1024];
1941 int r = 0;
1942 switch_xml_t lookup;
1943
1944 switch_mutex_lock(CACHE_MUTEX);
1945
1946 if (key && user_name && domain_name) {
1947 switch_snprintf(mega_key, sizeof(mega_key), "%s%s%s", key, user_name, domain_name);
1948
1949 if ((lookup = switch_core_hash_find(CACHE_HASH, mega_key))) {
1950 switch_core_hash_delete(CACHE_HASH, mega_key);
a2fcb419
RC
1951 if ((lookup = switch_core_hash_find(CACHE_EXPIRES_HASH, mega_key))) {
1952 switch_core_hash_delete(CACHE_EXPIRES_HASH, mega_key);
1953 }
2582bbcb
AM
1954 switch_xml_free(lookup);
1955 r++;
1956 }
a2fcb419 1957
2582bbcb 1958 } else {
a2fcb419 1959
1086cba2 1960 while ((hi = switch_hash_first(NULL, CACHE_HASH))) {
2582bbcb
AM
1961 switch_hash_this(hi, &var, NULL, &val);
1962 switch_xml_free(val);
1963 switch_core_hash_delete(CACHE_HASH, var);
1964 r++;
2582bbcb 1965 }
a2fcb419
RC
1966
1967 while ((hi = switch_hash_first(NULL, CACHE_EXPIRES_HASH))) {
1968 switch_hash_this(hi, &var, NULL, &val);
1969 switch_safe_free(val);
1970 switch_core_hash_delete(CACHE_EXPIRES_HASH, var);
1971 }
2582bbcb
AM
1972 }
1973
1974 switch_mutex_unlock(CACHE_MUTEX);
a2fcb419 1975
2582bbcb 1976 return r;
a2fcb419 1977
2582bbcb
AM
1978}
1979
1980static switch_status_t switch_xml_locate_user_cache(const char *key, const char *user_name, const char *domain_name, switch_xml_t *user)
1981{
1982 char mega_key[1024];
2582bbcb 1983 switch_status_t status = SWITCH_STATUS_FALSE;
a2fcb419 1984 switch_xml_t lookup;
2582bbcb
AM
1985
1986 switch_snprintf(mega_key, sizeof(mega_key), "%s%s%s", key, user_name, domain_name);
1987
1988 switch_mutex_lock(CACHE_MUTEX);
1989 if ((lookup = switch_core_hash_find(CACHE_HASH, mega_key))) {
a2fcb419
RC
1990 char *expires_lookup = NULL;
1991
1992 if ((expires_lookup = switch_core_hash_find(CACHE_EXPIRES_HASH, mega_key))) {
1993 switch_time_t time_expires = 0;
1994 switch_time_t time_now = 0;
1995
1996 time_now = switch_micro_time_now();
1997 time_expires = atol(expires_lookup);
ab7ae9f7 1998 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Cache Info\nTime Now:\t%ld\nExpires:\t%ld\n", (long)time_now, (long)time_expires);
a2fcb419
RC
1999 if (time_expires < time_now) {
2000 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Cache expired for %s@%s, doing fresh lookup\n", user_name, domain_name);
2001 } else {
2002 *user = switch_xml_dup(lookup);
2003 status = SWITCH_STATUS_SUCCESS;
2004 }
2005 } else {
2006 *user = switch_xml_dup(lookup);
2007 status = SWITCH_STATUS_SUCCESS;
2008 }
2582bbcb
AM
2009 }
2010 switch_mutex_unlock(CACHE_MUTEX);
2011
2012 return status;
2013}
2014
a2fcb419 2015static void switch_xml_user_cache(const char *key, const char *user_name, const char *domain_name, switch_xml_t user, switch_time_t expires)
2582bbcb
AM
2016{
2017 char mega_key[1024];
2018 switch_xml_t lookup;
a2fcb419 2019 char *expires_lookup;
2582bbcb
AM
2020
2021 switch_snprintf(mega_key, sizeof(mega_key), "%s%s%s", key, user_name, domain_name);
a2fcb419 2022
2582bbcb
AM
2023 switch_mutex_lock(CACHE_MUTEX);
2024 if ((lookup = switch_core_hash_find(CACHE_HASH, mega_key))) {
2025 switch_core_hash_delete(CACHE_HASH, mega_key);
2026 switch_xml_free(lookup);
2027 }
a2fcb419
RC
2028 if ((expires_lookup = switch_core_hash_find(CACHE_EXPIRES_HASH, mega_key))) {
2029 switch_core_hash_delete(CACHE_EXPIRES_HASH, mega_key);
2030 switch_safe_free(expires_lookup);
2031 }
2032 if (expires) {
2033 char *expires_val = malloc(1024);
0bf912e8 2034 if (sprintf(expires_val, "%ld", (long)expires)) {
a2fcb419
RC
2035 switch_core_hash_insert(CACHE_EXPIRES_HASH, mega_key, expires_val);
2036 }
2037 }
2582bbcb
AM
2038 switch_core_hash_insert(CACHE_HASH, mega_key, switch_xml_dup(user));
2039 switch_mutex_unlock(CACHE_MUTEX);
2040}
2041
c65da59d
AM
2042SWITCH_DECLARE(switch_status_t) switch_xml_locate_user_merged(const char *key, const char *user_name, const char *domain_name,
2043 const char *ip, switch_xml_t *user, switch_event_t *params)
2044{
2045 switch_xml_t xml, domain, group, x_user, x_user_dup;
2046 switch_status_t status = SWITCH_STATUS_FALSE;
2047
2582bbcb
AM
2048 if ((status = switch_xml_locate_user_cache(key, user_name, domain_name, &x_user)) == SWITCH_STATUS_SUCCESS) {
2049 *user = x_user;
2050 } else if ((status = switch_xml_locate_user(key, user_name, domain_name, ip, &xml, &domain, &x_user, &group, params)) == SWITCH_STATUS_SUCCESS) {
a2fcb419
RC
2051 const char *cacheable = NULL;
2052
c65da59d
AM
2053 x_user_dup = switch_xml_dup(x_user);
2054 switch_xml_merge_user(x_user_dup, domain, group);
a2fcb419
RC
2055
2056 cacheable = switch_xml_attr(x_user_dup, "cacheable");
2057 if (switch_true(cacheable)) {
2058 switch_time_t expires = 0;
2059 switch_time_t time_now = 0;
2060
a2fcb419
RC
2061 if (switch_is_number(cacheable)) {
2062 int cache_ms = atol(cacheable);
610718fd 2063 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "caching lookup for user %s@%s for %d milliseconds\n", user_name, domain_name, cache_ms);
a2fcb419
RC
2064 time_now = switch_micro_time_now();
2065 expires = time_now + (cache_ms * 1000);
610718fd
RC
2066 } else {
2067 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "caching lookup for user %s@%s indefinitely\n", user_name, domain_name);
a2fcb419
RC
2068 }
2069 switch_xml_user_cache(key, user_name, domain_name, x_user_dup, expires);
2582bbcb 2070 }
c65da59d 2071 *user = x_user_dup;
ea5db72a 2072 switch_xml_free(xml);
c65da59d
AM
2073 }
2074
2075 return status;
2076
2077}
2078
59241895
MJ
2079SWITCH_DECLARE(switch_status_t) switch_xml_locate_user(const char *key,
2080 const char *user_name,
886e1ddb
AM
2081 const char *domain_name,
2082 const char *ip,
59241895 2083 switch_xml_t *root,
886e1ddb 2084 switch_xml_t *domain, switch_xml_t *user, switch_xml_t *ingroup, switch_event_t *params)
59241895 2085{
5e5847f3 2086 switch_status_t status = SWITCH_STATUS_FALSE;
73279f01 2087 switch_event_t *my_params = NULL;
ed100f44 2088 switch_xml_t group = NULL, groups = NULL, users = NULL;
886e1ddb 2089
59241895
MJ
2090 *root = NULL;
2091 *user = NULL;
2092 *domain = NULL;
2093
772f9570
AM
2094 if (ingroup) {
2095 *ingroup = NULL;
2096 }
2097
5e5847f3 2098 if (!params) {
003847dd 2099 switch_event_create(&my_params, SWITCH_EVENT_REQUEST_PARAMS);
5e5847f3
AM
2100 switch_assert(my_params);
2101 params = my_params;
2102 }
59241895 2103
5e5847f3 2104 switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "key", key);
59241895 2105
5e5847f3
AM
2106 if (user_name) {
2107 switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "user", user_name);
2108 }
59241895 2109
5e5847f3
AM
2110 if (domain_name) {
2111 switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "domain", domain_name);
59241895 2112 }
59241895 2113
5e5847f3
AM
2114 if (ip) {
2115 switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "ip", ip);
2116 }
886e1ddb 2117
59241895 2118 if ((status = switch_xml_locate_domain(domain_name, params, root, domain)) != SWITCH_STATUS_SUCCESS) {
5e5847f3 2119 goto end;
59241895 2120 }
886e1ddb 2121
a2a748dd
AM
2122 status = SWITCH_STATUS_FALSE;
2123
ed100f44
AM
2124 if ((groups = switch_xml_child(*domain, "groups"))) {
2125 for (group = switch_xml_child(groups, "group"); group; group = group->next) {
2126 if ((users = switch_xml_child(group, "users"))) {
2127 if ((status = find_user_in_tag(users, ip, user_name, key, params, user)) == SWITCH_STATUS_SUCCESS) {
2128 if (ingroup) {
2129 *ingroup = group;
2130 }
2131 break;
2132 }
59241895
MJ
2133 }
2134 }
ed100f44 2135 }
59241895 2136
ed100f44
AM
2137 if (status != SWITCH_STATUS_SUCCESS) {
2138 status = find_user_in_tag(*domain, ip, user_name, key, params, user);
59241895 2139 }
886e1ddb
AM
2140
2141 end:
5e5847f3
AM
2142
2143 if (my_params) {
2144 switch_event_destroy(&my_params);
2145 }
2146
e5f6e42e
AM
2147 if (status != SWITCH_STATUS_SUCCESS && root && *root) {
2148 switch_xml_free(*root);
2149 *root = NULL;
2150 *domain = NULL;
2151 }
2152
5e5847f3 2153 return status;
59241895
MJ
2154}
2155
2156SWITCH_DECLARE(switch_xml_t) switch_xml_root(void)
2157{
4714ed43 2158 switch_xml_t xml;
471bd6df 2159
4714ed43
AM
2160 switch_mutex_lock(REFLOCK);
2161 xml = MAIN_XML_ROOT;
2162 xml->refs++;
2163 switch_mutex_unlock(REFLOCK);
a2fcb419 2164
4714ed43 2165 return xml;
59241895
MJ
2166}
2167
7e9f64ee
AM
2168struct destroy_xml {
2169 switch_xml_t xml;
2170 switch_memory_pool_t *pool;
2171};
2172
2173static void *SWITCH_THREAD_FUNC destroy_thread(switch_thread_t *thread, void *obj)
2174{
2175 struct destroy_xml *dx = (struct destroy_xml *) obj;
2176 switch_memory_pool_t *pool = dx->pool;
2177 switch_xml_free(dx->xml);
2178 switch_core_destroy_memory_pool(&pool);
2179 return NULL;
2180}
2181
2182SWITCH_DECLARE(void) switch_xml_free_in_thread(switch_xml_t xml, int stacksize)
2183{
2184 switch_thread_t *thread;
886e1ddb 2185 switch_threadattr_t *thd_attr;
7e9f64ee
AM
2186 switch_memory_pool_t *pool = NULL;
2187 struct destroy_xml *dx;
2188
2189 switch_core_new_memory_pool(&pool);
2190
2191 switch_threadattr_create(&thd_attr, pool);
886e1ddb 2192 switch_threadattr_detach_set(thd_attr, 1);
de13f431 2193 /* TBD figure out how much space we need by looking at the xml_t when stacksize == 0 */
886e1ddb 2194 switch_threadattr_stacksize_set(thd_attr, stacksize);
7e9f64ee
AM
2195
2196 dx = switch_core_alloc(pool, sizeof(*dx));
2197 dx->pool = pool;
2198 dx->xml = xml;
2199
2200 switch_thread_create(&thread, thd_attr, destroy_thread, dx, pool);
7e9f64ee
AM
2201}
2202
59241895
MJ
2203static char not_so_threadsafe_error_buffer[256] = "";
2204
07a71592
AM
2205SWITCH_DECLARE(switch_status_t) switch_xml_set_root(switch_xml_t new_main)
2206{
2207 switch_xml_t old_root = NULL;
a2fcb419 2208
07a71592
AM
2209 switch_mutex_lock(REFLOCK);
2210
2211 old_root = MAIN_XML_ROOT;
2212 MAIN_XML_ROOT = new_main;
2213 switch_set_flag(MAIN_XML_ROOT, SWITCH_XML_ROOT);
2214 MAIN_XML_ROOT->refs++;
a2fcb419 2215
07a71592
AM
2216 if (old_root) {
2217 if (old_root->refs) {
2218 old_root->refs--;
2219 }
2220
2221 if (!old_root->refs) {
2222 switch_xml_free(old_root);
2223 }
2224 }
2225
2226 switch_mutex_unlock(REFLOCK);
2227
2228 return SWITCH_STATUS_SUCCESS;
2229}
2230
2231SWITCH_DECLARE(switch_status_t) switch_xml_set_open_root_function(switch_xml_open_root_function_t func, void *user_data)
2232{
2233 if (XML_LOCK) {
2234 switch_mutex_lock(XML_LOCK);
2235 }
a2fcb419 2236
07a71592
AM
2237 XML_OPEN_ROOT_FUNCTION = func;
2238 XML_OPEN_ROOT_FUNCTION_USER_DATA = user_data;
2239
2240 if (XML_LOCK) {
2241 switch_mutex_unlock(XML_LOCK);
2242 }
2243 return SWITCH_STATUS_SUCCESS;
2244}
2245
a2fcb419 2246SWITCH_DECLARE(switch_xml_t) switch_xml_open_root(uint8_t reload, const char **err)
07a71592
AM
2247{
2248 switch_xml_t root = NULL;
32437d04 2249 switch_event_t *event;
07a71592
AM
2250
2251 switch_mutex_lock(XML_LOCK);
2252
2253 if (XML_OPEN_ROOT_FUNCTION) {
2254 root = XML_OPEN_ROOT_FUNCTION(reload, err, XML_OPEN_ROOT_FUNCTION_USER_DATA);
2255 }
2256 switch_mutex_unlock(XML_LOCK);
2257
32437d04
AM
2258
2259 if (root) {
2260 if (switch_event_create(&event, SWITCH_EVENT_RELOADXML) == SWITCH_STATUS_SUCCESS) {
2261 if (switch_event_fire(&event) != SWITCH_STATUS_SUCCESS) {
2262 switch_event_destroy(&event);
2263 }
2264 }
2265 }
2266
07a71592
AM
2267 return root;
2268}
2269
3aaa6209 2270SWITCH_DECLARE_NONSTD(switch_xml_t) __switch_xml_open_root(uint8_t reload, const char **err, void *user_data)
59241895
MJ
2271{
2272 char path_buf[1024];
1ba98b02 2273 uint8_t errcnt = 0;
4714ed43 2274 switch_xml_t new_main, r = NULL;
59241895 2275
4714ed43
AM
2276 if (MAIN_XML_ROOT) {
2277 if (!reload) {
2278 r = switch_xml_root();
2279 goto done;
2280 }
59241895
MJ
2281 }
2282
bf607b0d 2283 switch_snprintf(path_buf, sizeof(path_buf), "%s%s%s", SWITCH_GLOBAL_dirs.conf_dir, SWITCH_PATH_SEPARATOR, SWITCH_GLOBAL_filenames.conf_name);
59241895
MJ
2284 if ((new_main = switch_xml_parse_file(path_buf))) {
2285 *err = switch_xml_error(new_main);
2286 switch_copy_string(not_so_threadsafe_error_buffer, *err, sizeof(not_so_threadsafe_error_buffer));
2287 *err = not_so_threadsafe_error_buffer;
df7637f6 2288 if (!zstr(*err)) {
59241895
MJ
2289 switch_xml_free(new_main);
2290 new_main = NULL;
2291 errcnt++;
2292 } else {
59241895 2293 *err = "Success";
07a71592 2294 switch_xml_set_root(new_main);
471bd6df 2295
59241895
MJ
2296 }
2297 } else {
2298 *err = "Cannot Open log directory or XML Root!";
2299 errcnt++;
2300 }
2301
59241895 2302 if (errcnt == 0) {
333019eb 2303 r = switch_xml_root();
59241895
MJ
2304 }
2305
07a71592 2306 done:
4714ed43 2307
333019eb 2308 return r;
59241895
MJ
2309}
2310
42c9df72
AM
2311SWITCH_DECLARE(switch_status_t) switch_xml_reload(const char **err)
2312{
2313 switch_xml_t xml_root;
a2fcb419 2314
42c9df72
AM
2315 if ((xml_root = switch_xml_open_root(1, err))) {
2316 switch_xml_free(xml_root);
2317 return SWITCH_STATUS_SUCCESS;
2318 }
2319
2320 return SWITCH_STATUS_GENERR;
2321}
2322
59241895
MJ
2323SWITCH_DECLARE(switch_status_t) switch_xml_init(switch_memory_pool_t *pool, const char **err)
2324{
2325 switch_xml_t xml;
2326 XML_MEMORY_POOL = pool;
2327 *err = "Success";
2328
2582bbcb 2329 switch_mutex_init(&CACHE_MUTEX, SWITCH_MUTEX_NESTED, XML_MEMORY_POOL);
4714ed43
AM
2330 switch_mutex_init(&XML_LOCK, SWITCH_MUTEX_NESTED, XML_MEMORY_POOL);
2331 switch_mutex_init(&REFLOCK, SWITCH_MUTEX_NESTED, XML_MEMORY_POOL);
3ed59f1d 2332 switch_mutex_init(&FILE_LOCK, SWITCH_MUTEX_NESTED, XML_MEMORY_POOL);
3a85348c 2333 switch_mutex_init(&XML_GEN_LOCK, SWITCH_MUTEX_NESTED, XML_MEMORY_POOL);
2582bbcb 2334 switch_core_hash_init(&CACHE_HASH, XML_MEMORY_POOL);
a2fcb419 2335 switch_core_hash_init(&CACHE_EXPIRES_HASH, XML_MEMORY_POOL);
4714ed43 2336
8ec9b8f7 2337 switch_thread_rwlock_create(&B_RWLOCK, XML_MEMORY_POOL);
59241895
MJ
2338
2339 assert(pool != NULL);
2340
2341 if ((xml = switch_xml_open_root(FALSE, err))) {
2342 switch_xml_free(xml);
2343 return SWITCH_STATUS_SUCCESS;
2344 } else {
2345 return SWITCH_STATUS_FALSE;
2346 }
2347}
2348
2349SWITCH_DECLARE(switch_status_t) switch_xml_destroy(void)
2350{
333019eb 2351 switch_status_t status = SWITCH_STATUS_FALSE;
3ed59f1d 2352
2582bbcb 2353
4714ed43 2354 switch_mutex_lock(XML_LOCK);
3ed59f1d 2355 switch_mutex_lock(REFLOCK);
886e1ddb 2356
59241895
MJ
2357 if (MAIN_XML_ROOT) {
2358 switch_xml_t xml = MAIN_XML_ROOT;
2359 MAIN_XML_ROOT = NULL;
2360 switch_xml_free(xml);
333019eb 2361 status = SWITCH_STATUS_SUCCESS;
59241895
MJ
2362 }
2363
4714ed43 2364 switch_mutex_unlock(XML_LOCK);
3ed59f1d 2365 switch_mutex_unlock(REFLOCK);
333019eb 2366
2582bbcb
AM
2367 switch_xml_clear_user_cache(NULL, NULL, NULL);
2368
2369 switch_core_hash_destroy(&CACHE_HASH);
2370
333019eb 2371 return status;
59241895
MJ
2372}
2373
2374SWITCH_DECLARE(switch_xml_t) switch_xml_open_cfg(const char *file_path, switch_xml_t *node, switch_event_t *params)
2375{
2376 switch_xml_t xml = NULL, cfg = NULL;
2377
2378 *node = NULL;
2379
2380 assert(MAIN_XML_ROOT != NULL);
2381
93ec3d68 2382 if (switch_xml_locate("configuration", "configuration", "name", file_path, &xml, &cfg, params, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) {
59241895
MJ
2383 *node = cfg;
2384 }
2385
2386 return xml;
2387
2388}
2389
de13f431
BW
2390/* Encodes ampersand sequences appending the results to *dst, reallocating *dst
2391 if length exceeds max. a is non-zero for attribute encoding. Returns *dst */
59241895
MJ
2392static char *switch_xml_ampencode(const char *s, switch_size_t len, char **dst, switch_size_t *dlen, switch_size_t *max, short a)
2393{
2394 const char *e = NULL;
2395 int immune = 0;
4cd616cc
MOC
2396 int expecting_x_utf_8_char = 0;
2397 int unicode_char = 0x000000;
59241895 2398
886e1ddb
AM
2399 if (!(s && *s))
2400 return *dst;
59241895
MJ
2401
2402 if (len) {
2403 e = s + len;
2404 }
2405
2406 while (s != e) {
2407 while (*dlen + 10 > *max) {
886e1ddb
AM
2408 char *tmp = (char *) realloc(*dst, *max += SWITCH_XML_BUFSIZE);
2409 if (!tmp)
2410 return *dst;
59241895
MJ
2411 *dst = tmp;
2412 }
2413
2414 if (immune) {
2415 if (*s == '\0') {
2416 return *dst;
2417 }
2418 (*dst)[(*dlen)++] = *s;
886e1ddb 2419 } else
59241895 2420 switch (*s) {
886e1ddb
AM
2421 case '\0':
2422 return *dst;
2423 case '&':
2424 *dlen += sprintf(*dst + *dlen, "&amp;");
2425 break;
2426 case '<':
2427 if (*(s + 1) == '!') {
2428 (*dst)[(*dlen)++] = *s;
2429 immune++;
2430 break;
2431 }
2432 *dlen += sprintf(*dst + *dlen, "&lt;");
2433 break;
2434 case '>':
2435 *dlen += sprintf(*dst + *dlen, "&gt;");
59241895 2436 break;
886e1ddb
AM
2437 case '"':
2438 *dlen += sprintf(*dst + *dlen, (a) ? "&quot;" : "\"");
2439 break;
2440 case '\n':
2441 *dlen += sprintf(*dst + *dlen, (a) ? "&#xA;" : "\n");
2442 break;
2443 case '\t':
2444 *dlen += sprintf(*dst + *dlen, (a) ? "&#x9;" : "\t");
2445 break;
2446 case '\r':
2447 *dlen += sprintf(*dst + *dlen, "&#xD;");
2448 break;
2449 default:
4cd616cc
MOC
2450 if (USE_UTF_8_ENCODING && expecting_x_utf_8_char == 0 && ((*s >> 8) & 0x01)) {
2451 int num = 1;
2452 for (;num<4;num++) {
2453 if (! ((*s >> (7-num)) & 0x01)) {
2454 break;
2455 }
2456 }
2457 switch (num) {
2458 case 2:
2459 unicode_char = *s & 0x1f;
2460 break;
2461 case 3:
2462 unicode_char = *s & 0x0f;
2463 break;
2464 case 4:
2465 unicode_char = *s & 0x07;
2466 break;
2467 default:
2468 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid UTF-8 Initial charactere, skip it\n");
2469 /* ERROR HERE */
2470 break;
2471 }
2472 expecting_x_utf_8_char = num - 1;
2473
2474 } else if (USE_UTF_8_ENCODING && expecting_x_utf_8_char > 0) {
2475 if (((*s >> 6) & 0x03) == 0x2) {
2476
2477 unicode_char = unicode_char << 6;
2478 unicode_char = unicode_char | (*s & 0x3f);
2479 } else {
2480 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid UTF-8 character to ampersand, skip it\n");
2481 expecting_x_utf_8_char = 0;
2482 break;
2483 }
2484 expecting_x_utf_8_char--;
2485 if (expecting_x_utf_8_char == 0) {
2486 *dlen += sprintf(*dst + *dlen, "&#x%X;", unicode_char);
2487 }
2488 } else {
2489 (*dst)[(*dlen)++] = *s;
2490 }
59241895 2491 }
59241895
MJ
2492 s++;
2493 }
2494 return *dst;
2495}
2496
2497#define XML_INDENT " "
de13f431
BW
2498/* Recursively converts each tag to xml appending it to *s. Reallocates *s if
2499 its length exceeds max. start is the location of the previous tag in the
2500 parent tag's character content. Returns *s. */
886e1ddb 2501static char *switch_xml_toxml_r(switch_xml_t xml, char **s, switch_size_t *len, switch_size_t *max, switch_size_t start, char ***attr, uint32_t *count)
59241895
MJ
2502{
2503 int i, j;
9fa14d52
BW
2504 char *txt;
2505 switch_size_t off;
2506 uint32_t lcount;
2507
886e1ddb 2508 tailrecurse:
9fa14d52
BW
2509 off = 0;
2510 lcount = 0;
886e1ddb 2511 txt = (char *) (xml->parent) ? xml->parent->txt : (char *) "";
59241895 2512
de13f431 2513 /* parent character content up to this tag */
59241895
MJ
2514 *s = switch_xml_ampencode(txt + start, xml->off - start, s, len, max, 0);
2515
de13f431 2516 while (*len + strlen(xml->name) + 5 + (strlen(XML_INDENT) * (*count)) + 1 > *max) { /* reallocate s */
5923f71a
JL
2517 char *tmp = *s;
2518 *s = (char *) realloc(*s, *max += SWITCH_XML_BUFSIZE);
2519 if (!*s)
2520 return tmp;
59241895
MJ
2521 }
2522
53ab0e2a 2523 if (*len && *(*s + (*len) - 1) == '>') {
de13f431 2524 *len += sprintf(*s + *len, "\n"); /* indent */
59241895
MJ
2525 }
2526 for (lcount = 0; lcount < *count; lcount++) {
de13f431 2527 *len += sprintf(*s + *len, "%s", XML_INDENT); /* indent */
59241895
MJ
2528 }
2529
de13f431
BW
2530 *len += sprintf(*s + *len, "<%s", xml->name); /* open tag */
2531 for (i = 0; xml->attr[i]; i += 2) { /* tag attributes */
59241895
MJ
2532 if (switch_xml_attr(xml, xml->attr[i]) != xml->attr[i + 1])
2533 continue;
de13f431 2534 while (*len + strlen(xml->attr[i]) + 7 + (strlen(XML_INDENT) * (*count)) > *max) { /* reallocate s */
886e1ddb
AM
2535 char *tmp = (char *) realloc(*s, *max += SWITCH_XML_BUFSIZE);
2536 if (!tmp)
2537 return *s;
59241895
MJ
2538 *s = tmp;
2539 }
2540
2541 *len += sprintf(*s + *len, " %s=\"", xml->attr[i]);
2542 switch_xml_ampencode(xml->attr[i + 1], 0, s, len, max, 1);
2543 *len += sprintf(*s + *len, "\"");
2544 }
2545
2546 for (i = 0; attr[i] && strcmp(attr[i][0], xml->name); i++);
de13f431 2547 for (j = 1; attr[i] && attr[i][j]; j += 3) { /* default attributes */
59241895 2548 if (!attr[i][j + 1] || switch_xml_attr(xml, attr[i][j]) != attr[i][j + 1])
de13f431
BW
2549 continue; /* skip duplicates and non-values */
2550 while (*len + strlen(attr[i][j]) + 8 + (strlen(XML_INDENT) * (*count)) > *max) { /* reallocate s */
886e1ddb
AM
2551 char *tmp = (char *) realloc(*s, *max += SWITCH_XML_BUFSIZE);
2552 if (!tmp)
2553 return *s;
59241895
MJ
2554 *s = tmp;
2555 }
2556
2557 *len += sprintf(*s + *len, " %s=\"", attr[i][j]);
2558 switch_xml_ampencode(attr[i][j + 1], 0, s, len, max, 1);
2559 *len += sprintf(*s + *len, "\"");
2560 }
2561
2562 *len += sprintf(*s + *len, (xml->child || xml->txt) ? ">" : "/>\n");
2563
2564 if (xml->child) {
2565 (*count)++;
2566 *s = switch_xml_toxml_r(xml->child, s, len, max, 0, attr, count);
2567
2568 } else {
de13f431 2569 *s = switch_xml_ampencode(xml->txt, 0, s, len, max, 0); /* data */
59241895
MJ
2570 }
2571
de13f431 2572 while (*len + strlen(xml->name) + 5 + (strlen(XML_INDENT) * (*count)) > *max) { /* reallocate s */
5923f71a
JL
2573 char *tmp = *s;
2574 *s = (char *) realloc(*s, *max += SWITCH_XML_BUFSIZE);
2575 if (!*s)
2576 return tmp;
886e1ddb 2577 }
59241895
MJ
2578
2579 if (xml->child || xml->txt) {
2580 if (*(*s + (*len) - 1) == '\n') {
2581 for (lcount = 0; lcount < *count; lcount++) {
de13f431 2582 *len += sprintf(*s + *len, "%s", XML_INDENT); /* indent */
59241895
MJ
2583 }
2584 }
f4e55fb1 2585 *len += sprintf(*s + (*len), "</%s>\n", xml->name); /* close tag */
59241895
MJ
2586 }
2587
2588 while (txt[off] && off < xml->off)
de13f431 2589 off++; /* make sure off is within bounds */
59241895
MJ
2590
2591 if (xml->ordered) {
9fa14d52
BW
2592 xml = xml->ordered;
2593 start = off;
2594 goto tailrecurse;
2595/*
59241895 2596 return switch_xml_toxml_r(xml->ordered, s, len, max, off, attr, count);
9fa14d52 2597*/
59241895
MJ
2598 } else {
2599 if (*count > 0)
2600 (*count)--;
2601 return switch_xml_ampencode(txt + off, 0, s, len, max, 0);
2602 }
2603}
2604
277c1141 2605SWITCH_DECLARE(char *) switch_xml_toxml_nolock(switch_xml_t xml, switch_bool_t prn_header)
2606{
2607 char *s = (char *) malloc(SWITCH_XML_BUFSIZE);
2608 switch_assert(s);
2609 return switch_xml_toxml_buf(xml, s, SWITCH_XML_BUFSIZE, 0, prn_header);
2610}
2611
2612
59241895
MJ
2613SWITCH_DECLARE(char *) switch_xml_toxml(switch_xml_t xml, switch_bool_t prn_header)
2614{
4a520847 2615 char *r, *s;
277c1141 2616
886e1ddb 2617 s = (char *) malloc(SWITCH_XML_BUFSIZE);
383ff711 2618 switch_assert(s);
277c1141 2619
2620 switch_mutex_lock(XML_GEN_LOCK);
4a520847
AM
2621 r = switch_xml_toxml_buf(xml, s, SWITCH_XML_BUFSIZE, 0, prn_header);
2622 switch_mutex_unlock(XML_GEN_LOCK);
2623 return r;
59241895
MJ
2624}
2625
6b6c83a7
JL
2626SWITCH_DECLARE(char *) switch_xml_tohtml(switch_xml_t xml, switch_bool_t prn_header)
2627{
2628 char *r, *s, *h;
2629 switch_size_t rlen = 0;
2630 switch_size_t len = SWITCH_XML_BUFSIZE;
2631 switch_mutex_lock(XML_GEN_LOCK);
2632 s = (char *) malloc(SWITCH_XML_BUFSIZE);
2633 switch_assert(s);
2634 h = (char *) malloc(SWITCH_XML_BUFSIZE);
2635 switch_assert(h);
2636 r = switch_xml_toxml_buf(xml, s, SWITCH_XML_BUFSIZE, 0, prn_header);
2637 h = switch_xml_ampencode(r, 0, &h, &rlen, &len, 1);
2638 switch_safe_free(r);
2639 switch_mutex_unlock(XML_GEN_LOCK);
2640 return h;
2641}
2642
2643/* converts a switch_xml structure back to xml, returning a string of xml data that
de13f431 2644 must be freed */
59241895
MJ
2645SWITCH_DECLARE(char *) switch_xml_toxml_buf(switch_xml_t xml, char *buf, switch_size_t buflen, switch_size_t offset, switch_bool_t prn_header)
2646{
2647 switch_xml_t p = (xml) ? xml->parent : NULL, o = (xml) ? xml->ordered : NULL;
2648 switch_xml_root_t root = (switch_xml_root_t) xml;
2649 switch_size_t len = 0, max = buflen;
2650 char *s, *t, *n, *r;
2651 int i, j, k;
2652 uint32_t count = 0;
886e1ddb 2653
59241895
MJ
2654 s = buf;
2655 assert(s != NULL);
2656 memset(s, 0, max);
2657 len += offset;
2658 if (prn_header) {
2659 len += sprintf(s + len, "<?xml version=\"1.0\"?>\n");
2660 }
886e1ddb 2661
59241895 2662 if (!xml || !xml->name) {
886e1ddb 2663 if (!(r = (char *) realloc(s, len + 1))) {
59241895
MJ
2664 abort();
2665 }
2666 return r;
2667 }
2668
2669 while (root->xml.parent) {
de13f431 2670 root = (switch_xml_root_t) root->xml.parent; /* root tag */
59241895
MJ
2671 }
2672
de13f431 2673 for (i = 0; !p && root->pi[i]; i++) { /* pre-root processing instructions */
59241895
MJ
2674 for (k = 2; root->pi[i][k - 1]; k++);
2675 for (j = 1; (n = root->pi[i][j]); j++) {
2676 if (root->pi[i][k][j - 1] == '>') {
de13f431 2677 continue; /* not pre-root */
59241895
MJ
2678 }
2679 while (len + strlen(t = root->pi[i][0]) + strlen(n) + 7 > max) {
886e1ddb 2680 if (!(r = (char *) realloc(s, max += SWITCH_XML_BUFSIZE))) {
59241895
MJ
2681 abort();
2682 }
2683 s = r;
2684 }
2685 len += sprintf(s + len, "<?%s%s%s?>", t, *n ? " " : "", n);
2686 }
2687 }
2688
2689 xml->parent = xml->ordered = NULL;
2690 s = switch_xml_toxml_r(xml, &s, &len, &max, 0, root->attr, &count);
2691 xml->parent = p;
2692 xml->ordered = o;
2693
de13f431 2694 for (i = 0; !p && root->pi[i]; i++) { /* post-root processing instructions */
59241895
MJ
2695 for (k = 2; root->pi[i][k - 1]; k++);
2696 for (j = 1; (n = root->pi[i][j]); j++) {
2697 if (root->pi[i][k][j - 1] == '<') {
de13f431 2698 continue; /* not post-root */
59241895
MJ
2699 }
2700 while (len + strlen(t = root->pi[i][0]) + strlen(n) + 7 > max) {
886e1ddb 2701 if (!(r = (char *) realloc(s, max += SWITCH_XML_BUFSIZE))) {
59241895
MJ
2702 abort();
2703 }
2704 s = r;
2705 }
2706 len += sprintf(s + len, "\n<?%s%s%s?>", t, *n ? " " : "", n);
2707 }
2708 }
2709
886e1ddb 2710 if (!(r = (char *) realloc(s, len + 1))) {
59241895
MJ
2711 abort();
2712 }
2713
2714 return r;
2715}
2716
de13f431 2717/* free the memory allocated for the switch_xml structure */
59241895
MJ
2718SWITCH_DECLARE(void) switch_xml_free(switch_xml_t xml)
2719{
9fa14d52 2720 switch_xml_root_t root;
59241895
MJ
2721 int i, j;
2722 char **a, *s;
9fa14d52 2723 switch_xml_t orig_xml;
4714ed43 2724 int refs = 0;
59241895 2725
4714ed43 2726 tailrecurse:
886e1ddb 2727 root = (switch_xml_root_t) xml;
f6f7f31c 2728 if (!xml) {
59241895 2729 return;
59241895
MJ
2730 }
2731
471bd6df 2732 if (switch_test_flag(xml, SWITCH_XML_ROOT)) {
4714ed43 2733 switch_mutex_lock(REFLOCK);
471bd6df 2734
4714ed43
AM
2735 if (xml->refs) {
2736 xml->refs--;
2737 refs = xml->refs;
471bd6df 2738 }
4714ed43 2739 switch_mutex_unlock(REFLOCK);
471bd6df 2740 }
471bd6df 2741
4714ed43 2742 if (refs) {
59241895
MJ
2743 return;
2744 }
2745
2746 if (xml->free_path) {
bd41ecc3
AM
2747 if (unlink(xml->free_path) != 0) {
2748 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Failed to delete file [%s]\n", xml->free_path);
59241895
MJ
2749 }
2750 switch_safe_free(xml->free_path);
2751 }
2752
2753 switch_xml_free(xml->child);
886e1ddb 2754 /*switch_xml_free(xml->ordered); */
59241895 2755
de13f431 2756 if (!xml->parent) { /* free root tag allocations */
383ff711 2757#if (_MSC_VER >= 1400) // VC8+
886e1ddb 2758 __analysis_assume(sizeof(root->ent) > 44); /* tail recursion confuses code analysis */
383ff711 2759#endif
de13f431 2760 for (i = 10; root->ent[i]; i += 2) /* 0 - 9 are default entities (<>&"') */
59241895
MJ
2761 if ((s = root->ent[i + 1]) < root->s || s > root->e)
2762 free(s);
de13f431 2763 free(root->ent); /* free list of general entities */
59241895
MJ
2764
2765 for (i = 0; (a = root->attr[i]); i++) {
de13f431 2766 for (j = 1; a[j++]; j += 2) /* free malloced attribute values */
59241895
MJ
2767 if (a[j] && (a[j] < root->s || a[j] > root->e))
2768 free(a[j]);
2769 free(a);
2770 }
2771 if (root->attr[0])
de13f431 2772 free(root->attr); /* free default attribute list */
59241895
MJ
2773
2774 for (i = 0; root->pi[i]; i++) {
2775 for (j = 1; root->pi[i][j]; j++);
2776 free(root->pi[i][j + 1]);
2777 free(root->pi[i]);
2778 }
2779 if (root->pi[0])
de13f431 2780 free(root->pi); /* free processing instructions */
59241895
MJ
2781
2782 if (root->dynamic == 1)
de13f431 2783 free(root->m); /* malloced xml data */
59241895 2784 if (root->u)
de13f431 2785 free(root->u); /* utf8 conversion */
59241895
MJ
2786 }
2787
de13f431 2788 switch_xml_free_attr(xml->attr); /* tag attributes */
59241895 2789 if ((xml->flags & SWITCH_XML_TXTM))
de13f431 2790 free(xml->txt); /* character content */
59241895 2791 if ((xml->flags & SWITCH_XML_NAMEM))
de13f431 2792 free(xml->name); /* tag name */
9fa14d52
BW
2793 if (xml->ordered) {
2794 orig_xml = xml;
2795 xml = xml->ordered;
2796 free(orig_xml);
2797 goto tailrecurse;
2798 }
59241895
MJ
2799 free(xml);
2800}
2801
de13f431 2802/* return parser error message or empty string if none */
59241895
MJ
2803SWITCH_DECLARE(const char *) switch_xml_error(switch_xml_t xml)
2804{
2805 while (xml && xml->parent)
de13f431 2806 xml = xml->parent; /* find root tag */
59241895
MJ
2807 return (xml) ? ((switch_xml_root_t) xml)->err : "";
2808}
2809
de13f431 2810/* returns a new empty switch_xml structure with the given root tag name */
59241895
MJ
2811SWITCH_DECLARE(switch_xml_t) switch_xml_new(const char *name)
2812{
2813 static const char *ent[] = { "lt;", "&#60;", "gt;", "&#62;", "quot;", "&#34;",
2814 "apos;", "&#39;", "amp;", "&#38;", NULL
2815 };
bbb8adf2 2816 switch_xml_root_t root = (switch_xml_root_t) malloc(sizeof(struct switch_xml_root));
886e1ddb
AM
2817 if (!root)
2818 return NULL;
bbb8adf2 2819 memset(root, '\0', sizeof(struct switch_xml_root));
59241895
MJ
2820 root->xml.name = (char *) name;
2821 root->cur = &root->xml;
886e1ddb
AM
2822 strcpy(root->err, root->xml.txt = (char *) "");
2823 root->ent = (char **) memcpy(malloc(sizeof(ent)), ent, sizeof(ent));
59241895 2824 root->attr = root->pi = (char ***) (root->xml.attr = SWITCH_XML_NIL);
62ce8538 2825 root->xml.is_switch_xml_root_t = SWITCH_TRUE;
59241895
MJ
2826 return &root->xml;
2827}
2828
6b6c83a7 2829/* inserts an existing tag into a switch_xml structure */
59241895
MJ
2830SWITCH_DECLARE(switch_xml_t) switch_xml_insert(switch_xml_t xml, switch_xml_t dest, switch_size_t off)
2831{
2832 switch_xml_t cur, prev, head;
2833
2834 xml->next = xml->sibling = xml->ordered = NULL;
2835 xml->off = off;
2836 xml->parent = dest;
2837
de13f431
BW
2838 if ((head = dest->child)) { /* already have sub tags */
2839 if (head->off <= off) { /* not first subtag */
59241895
MJ
2840 for (cur = head; cur->ordered && cur->ordered->off <= off; cur = cur->ordered);
2841 xml->ordered = cur->ordered;
2842 cur->ordered = xml;
de13f431 2843 } else { /* first subtag */
59241895
MJ
2844 xml->ordered = head;
2845 dest->child = xml;
2846 }
2847
de13f431
BW
2848 for (cur = head, prev = NULL; cur && strcmp(cur->name, xml->name); prev = cur, cur = cur->sibling); /* find tag type */
2849 if (cur && cur->off <= off) { /* not first of type */
59241895
MJ
2850 while (cur->next && cur->next->off <= off)
2851 cur = cur->next;
2852 xml->next = cur->next;
2853 cur->next = xml;
de13f431 2854 } else { /* first tag of this type */
59241895 2855 if (prev && cur)
de13f431
BW
2856 prev->sibling = cur->sibling; /* remove old first */
2857 xml->next = cur; /* old first tag is now next */
2858 for (cur = head, prev = NULL; cur && cur->off <= off; prev = cur, cur = cur->sibling); /* new sibling insert point */
59241895
MJ
2859 xml->sibling = cur;
2860 if (prev)
2861 prev->sibling = xml;
2862 }
2863 } else
de13f431 2864 dest->child = xml; /* only sub tag */
59241895
MJ
2865
2866 return xml;
2867}
2868
de13f431
BW
2869/* Adds a child tag. off is the offset of the child tag relative to the start
2870 of the parent tag's character content. Returns the child tag */
59241895
MJ
2871SWITCH_DECLARE(switch_xml_t) switch_xml_add_child(switch_xml_t xml, const char *name, switch_size_t off)
2872{
2873 switch_xml_t child;
2874
886e1ddb
AM
2875 if (!xml)
2876 return NULL;
2877 if (!(child = (switch_xml_t) malloc(sizeof(struct switch_xml))))
2878 return NULL;
bbb8adf2 2879 memset(child, '\0', sizeof(struct switch_xml));
59241895
MJ
2880 child->name = (char *) name;
2881 child->attr = SWITCH_XML_NIL;
2882 child->off = off;
2883 child->parent = xml;
886e1ddb 2884 child->txt = (char *) "";
59241895
MJ
2885
2886 return switch_xml_insert(child, xml, off);
2887}
2888
de13f431 2889/* sets the character content for the given tag and returns the tag */
59241895
MJ
2890SWITCH_DECLARE(switch_xml_t) switch_xml_set_txt(switch_xml_t xml, const char *txt)
2891{
2892 if (!xml)
2893 return NULL;
2894 if (xml->flags & SWITCH_XML_TXTM)
de13f431 2895 free(xml->txt); /* existing txt was malloced */
59241895
MJ
2896 xml->flags &= ~SWITCH_XML_TXTM;
2897 xml->txt = (char *) txt;
2898 return xml;
2899}
2900
de13f431
BW
2901/* Sets the given tag attribute or adds a new attribute if not found. A value
2902 of NULL will remove the specified attribute. Returns the tag given */
59241895
MJ
2903SWITCH_DECLARE(switch_xml_t) switch_xml_set_attr(switch_xml_t xml, const char *name, const char *value)
2904{
2905 int l = 0, c;
2906
2907 if (!xml)
2908 return NULL;
2909 while (xml->attr[l] && strcmp(xml->attr[l], name))
2910 l += 2;
de13f431 2911 if (!xml->attr[l]) { /* not found, add as new attribute */
59241895 2912 if (!value)
de13f431
BW
2913 return xml; /* nothing to do */
2914 if (xml->attr == SWITCH_XML_NIL) { /* first attribute */
886e1ddb
AM
2915 xml->attr = (char **) malloc(4 * sizeof(char *));
2916 if (!xml->attr)
2917 return NULL;
de13f431 2918 xml->attr[1] = strdup(""); /* empty list of malloced names/vals */
59241895 2919 } else {
886e1ddb
AM
2920 char **tmp = (char **) realloc(xml->attr, (l + 4) * sizeof(char *));
2921 if (!tmp)
2922 return xml;
59241895
MJ
2923 xml->attr = tmp;
2924 }
2925
de13f431
BW
2926 xml->attr[l] = (char *) name; /* set attribute name */
2927 xml->attr[l + 2] = NULL; /* null terminate attribute list */
886e1ddb 2928 xml->attr[l + 3] = (char *) realloc(xml->attr[l + 1], (c = (int) strlen(xml->attr[l + 1])) + 2);
de13f431 2929 strcpy(xml->attr[l + 3] + c, " "); /* set name/value as not malloced */
59241895
MJ
2930 if (xml->flags & SWITCH_XML_DUP)
2931 xml->attr[l + 3][c] = SWITCH_XML_NAMEM;
2932 } else if (xml->flags & SWITCH_XML_DUP)
de13f431 2933 free((char *) name); /* name was strduped */
59241895 2934
de13f431 2935 for (c = l; xml->attr[c]; c += 2); /* find end of attribute list */
59241895 2936 if (xml->attr[c + 1][l / 2] & SWITCH_XML_TXTM)
de13f431 2937 free(xml->attr[l + 1]); /* old val */
59241895
MJ
2938 if (xml->flags & SWITCH_XML_DUP)
2939 xml->attr[c + 1][l / 2] |= SWITCH_XML_TXTM;
2940 else
2941 xml->attr[c + 1][l / 2] &= ~SWITCH_XML_TXTM;
2942
2943 if (value)
de13f431
BW
2944 xml->attr[l + 1] = (char *) value; /* set attribute value */
2945 else { /* remove attribute */
59241895
MJ
2946 char **tmp;
2947 if (xml->attr[c + 1][l / 2] & SWITCH_XML_NAMEM)
2948 free(xml->attr[l]);
2949 memmove(xml->attr + l, xml->attr + l + 2, (c - l + 2) * sizeof(char *));
886e1ddb
AM
2950 tmp = (char **) realloc(xml->attr, (c + 2) * sizeof(char *));
2951 if (!tmp)
2952 return xml;
59241895 2953 xml->attr = tmp;
de13f431 2954 memmove(xml->attr[c + 1] + (l / 2), xml->attr[c + 1] + (l / 2) + 1, (c / 2) - (l / 2)); /* fix list of which name/vals are malloced */
59241895 2955 }
de13f431 2956 xml->flags &= ~SWITCH_XML_DUP; /* clear strdup() flag */
59241895
MJ
2957
2958 return xml;
2959}
2960
de13f431 2961/* sets a flag for the given tag and returns the tag */
59241895
MJ
2962SWITCH_DECLARE(switch_xml_t) switch_xml_set_flag(switch_xml_t xml, switch_xml_flag_t flag)
2963{
2964 if (xml)
2965 xml->flags |= flag;
2966 return xml;
2967}
2968
de13f431 2969/* removes a tag along with its subtags without freeing its memory */
59241895
MJ
2970SWITCH_DECLARE(switch_xml_t) switch_xml_cut(switch_xml_t xml)
2971{
2972 switch_xml_t cur;
2973
2974 if (!xml)
de13f431 2975 return NULL; /* nothing to do */
59241895 2976 if (xml->next)
de13f431 2977 xml->next->sibling = xml->sibling; /* patch sibling list */
59241895 2978
de13f431
BW
2979 if (xml->parent) { /* not root tag */
2980 cur = xml->parent->child; /* find head of subtag list */
59241895 2981 if (cur == xml)
de13f431
BW
2982 xml->parent->child = xml->ordered; /* first subtag */
2983 else { /* not first subtag */
59241895
MJ
2984 while (cur->ordered != xml)
2985 cur = cur->ordered;
de13f431 2986 cur->ordered = cur->ordered->ordered; /* patch ordered list */
59241895 2987
de13f431
BW
2988 cur = xml->parent->child; /* go back to head of subtag list */
2989 if (strcmp(cur->name, xml->name)) { /* not in first sibling list */
59241895
MJ
2990 while (strcmp(cur->sibling->name, xml->name))
2991 cur = cur->sibling;
de13f431 2992 if (cur->sibling == xml) { /* first of a sibling list */
59241895
MJ
2993 cur->sibling = (xml->next) ? xml->next : cur->sibling->sibling;
2994 } else
de13f431 2995 cur = cur->sibling; /* not first of a sibling list */
59241895
MJ
2996 }
2997
2998 while (cur->next && cur->next != xml)
2999 cur = cur->next;
3000 if (cur->next)
de13f431 3001 cur->next = cur->next->next; /* patch next list */
59241895
MJ
3002 }
3003 }
de13f431 3004 xml->ordered = xml->sibling = xml->next = NULL; /* prevent switch_xml_free() from clobbering ordered list */
59241895
MJ
3005 return xml;
3006}
3007
a2fcb419 3008SWITCH_DECLARE(int) switch_xml_std_datetime_check(switch_xml_t xcond, int *offset, const char *tzname)
65a75664 3009{
09a734bb 3010
c9fcce08 3011 const char *xdt = switch_xml_attr(xcond, "date-time");
09a734bb
MJ
3012 const char *xyear = switch_xml_attr(xcond, "year");
3013 const char *xyday = switch_xml_attr(xcond, "yday");
3014 const char *xmon = switch_xml_attr(xcond, "mon");
3015 const char *xmday = switch_xml_attr(xcond, "mday");
3016 const char *xweek = switch_xml_attr(xcond, "week");
3017 const char *xmweek = switch_xml_attr(xcond, "mweek");
3018 const char *xwday = switch_xml_attr(xcond, "wday");
3019 const char *xhour = switch_xml_attr(xcond, "hour");
3020 const char *xminute = switch_xml_attr(xcond, "minute");
3021 const char *xminday = switch_xml_attr(xcond, "minute-of-day");
4ab8fa13 3022 const char *xtod = switch_xml_attr(xcond, "time-of-day");
65a75664 3023 const char *tzoff = switch_xml_attr(xcond, "tz-offset");
01dde19c 3024 const char *isdst = switch_xml_attr(xcond, "dst");
09a734bb 3025
01dde19c
AM
3026 int loffset = -1000;
3027 int eoffset = -1000;
3028 int dst = -1000;
09a734bb
MJ
3029 switch_time_t ts = switch_micro_time_now();
3030 int time_match = -1;
01dde19c
AM
3031 switch_time_exp_t tm, tm2;
3032
3033 if (!zstr(isdst)) {
3034 dst = switch_true(isdst);
3035 }
09a734bb 3036
65a75664
AM
3037 if (!zstr(tzoff) && switch_is_number(tzoff)) {
3038 loffset = atoi(tzoff);
65a75664
AM
3039 }
3040
01dde19c 3041 switch_time_exp_lt(&tm2, ts);
65a75664
AM
3042
3043 if (offset) {
01dde19c 3044 eoffset = *offset;
6d7e2799 3045 switch_time_exp_tz(&tm, ts, *offset * 3600);
a4a44fb1
AM
3046 } else if (!zstr(tzname)) {
3047 switch_time_exp_tz_name(tzname, &tm, ts);
65a75664 3048 } else {
01dde19c
AM
3049 tm = tm2;
3050 }
3051
3052 if (eoffset == -1000) {
3053 eoffset = tm.tm_gmtoff / 3600;
3054 }
3055
3056 if (loffset == -1000) {
3057 loffset = eoffset;
3058 }
3059
3060
3061 if (time_match && tzoff) {
3062 time_match = loffset == eoffset;
3063 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG9,
3064 "XML DateTime Check: TZOFFSET[%d] == %d (%s)\n", eoffset, loffset, time_match ? "PASS" : "FAIL");
3065
3066 }
3067
3068 if (time_match && dst > -1) {
3069 time_match = (tm2.tm_isdst > 0 && dst > 0);
3070 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG9,
a2fcb419 3071 "XML DateTime Check: DST[%s] == %s (%s)\n",
01dde19c
AM
3072 tm2.tm_isdst > 0 ? "true" : "false", dst > 0 ? "true" : "false", time_match ? "PASS" : "FAIL");
3073
65a75664 3074 }
09a734bb 3075
c9fcce08
MOC
3076 if (time_match && xdt) {
3077 char tmpdate[80];
3078 switch_size_t retsize;
3079 switch_strftime(tmpdate, &retsize, sizeof(tmpdate), "%Y-%m-%d %H:%M:%S", &tm);
3080 time_match = switch_fulldate_cmp(xdt, &ts);
3081 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,
3082 "XML DateTime Check: date time[%s] =~ %s (%s)\n", tmpdate, xdt, time_match ? "PASS" : "FAIL");
3083 }
3084
09a734bb
MJ
3085 if (time_match && xyear) {
3086 int test = tm.tm_year + 1900;
3087 time_match = switch_number_cmp(xyear, test);
3088 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG9,
3089 "XML DateTime Check: year[%d] =~ %s (%s)\n", test, xyear, time_match ? "PASS" : "FAIL");
3090 }
3091
3092 if (time_match && xyday) {
3093 int test = tm.tm_yday + 1;
3094 time_match = switch_number_cmp(xyday, test);
3095 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG9,
3096 "XML DateTime Check: day of year[%d] =~ %s (%s)\n", test, xyday, time_match ? "PASS" : "FAIL");
3097 }
3098
3099 if (time_match && xmon) {
3100 int test = tm.tm_mon + 1;
3101 time_match = switch_number_cmp(xmon, test);
3102 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG9,
3103 "XML DateTime Check: month[%d] =~ %s (%s)\n", test, xmon, time_match ? "PASS" : "FAIL");
3104 }
3105
3106 if (time_match && xmday) {
3107 int test = tm.tm_mday;
3108 time_match = switch_number_cmp(xmday, test);
3109 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG9,
3110 "XML DateTime Check: day of month[%d] =~ %s (%s)\n", test, xmday, time_match ? "PASS" : "FAIL");
3111 }
3112
3113 if (time_match && xweek) {
3114 int test = (int) (tm.tm_yday / 7 + 1);
3115 time_match = switch_number_cmp(xweek, test);
3116 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG9,
3117 "XML DateTime Check: week of year[%d] =~ %s (%s)\n", test, xweek, time_match ? "PASS" : "FAIL");
3118 }
3119 if (time_match && xweek) {
3120 int test = (int) (tm.tm_yday / 7 + 1);
3121 time_match = switch_number_cmp(xweek, test);
3122 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG9,
3123 "XML DateTime Check: week of year[%d] =~ %s (%s)\n", test, xweek, time_match ? "PASS" : "FAIL");
3124 }
3125
3126 if (time_match && xmweek) {
3127 /* calculate the day of the week of the first of the month (0-6) */
3128 int firstdow = (int) (7 - (tm.tm_mday - (tm.tm_wday + 1)) % 7) % 7;
3129 /* calculate the week of the month (1-6)*/
3130 int test = (int) ceil((tm.tm_mday + firstdow) / 7.0);
3131 time_match = switch_number_cmp(xmweek, test);
3132 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG9,
3133 "XML DateTime: week of month[%d] =~ %s (%s)\n", test, xmweek, time_match ? "PASS" : "FAIL");
3134 }
3135
3136 if (time_match && xwday) {
3137 int test = tm.tm_wday + 1;
59ec8ced 3138 time_match = switch_dow_cmp(xwday, test);
09a734bb 3139 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG9,
59ec8ced 3140 "XML DateTime Check: day of week[%s] =~ %s (%s)\n", switch_dow_int2str(test), xwday, time_match ? "PASS" : "FAIL");
09a734bb
MJ
3141 }
3142 if (time_match && xhour) {
3143 int test = tm.tm_hour;
3144 time_match = switch_number_cmp(xhour, test);
3145 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG9,
3146 "XML DateTime Check: hour[%d] =~ %s (%s)\n", test, xhour, time_match ? "PASS" : "FAIL");
3147 }
3148
3149 if (time_match && xminute) {
3150 int test = tm.tm_min;
3151 time_match = switch_number_cmp(xminute, test);
3152 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG9,
3153 "XML DateTime Check: minute[%d] =~ %s (%s)\n", test, xminute, time_match ? "PASS" : "FAIL");
3154 }
3155
3156 if (time_match && xminday) {
3157 int test = (tm.tm_hour * 60) + (tm.tm_min + 1);
3158 time_match = switch_number_cmp(xminday, test);
3159 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG9,
3160 "XML DateTime Check: minute of day[%d] =~ %s (%s)\n", test, xminday, time_match ? "PASS" : "FAIL");
3161 }
3162
4ab8fa13
MOC
3163 if (time_match && xtod) {
3164 int test = (tm.tm_hour * 60 * 60) + (tm.tm_min * 60) + tm.tm_sec;
3165 char tmpdate[10];
d09e6c60 3166 switch_snprintf(tmpdate, 10, "%d:%d:%d", tm.tm_hour, tm.tm_min, tm.tm_sec);
4ab8fa13
MOC
3167 time_match = switch_tod_cmp(xtod, test);
3168 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG9,
3169 "XML DateTime Check: time of day[%s] =~ %s (%s)\n", tmpdate, xtod, time_match ? "PASS" : "FAIL");
3170 }
3171
09a734bb
MJ
3172 return time_match;
3173}
3174
4137b360
MOC
3175SWITCH_DECLARE(switch_status_t) switch_xml_locate_language(switch_xml_t *root, switch_xml_t *node, switch_event_t *params, switch_xml_t *language, switch_xml_t *phrases, switch_xml_t *macros, const char *str_language) {
3176 switch_status_t status = SWITCH_STATUS_FALSE;
3177
3178 if (switch_xml_locate("languages", NULL, NULL, NULL, root, node, params, SWITCH_TRUE) != SWITCH_STATUS_SUCCESS) {
3179 switch_xml_t sub_macros;
3180
3181 if (switch_xml_locate("phrases", NULL, NULL, NULL, root, node, params, SWITCH_TRUE) != SWITCH_STATUS_SUCCESS) {
3182 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of languages and phrases failed.\n");
3183 goto done;
3184 }
3185 if (!(sub_macros = switch_xml_child(*node, "macros"))) {
3186 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't find macros tag.\n");
3187 switch_xml_free(*root);
3188 *root = NULL;
3189 *node = NULL;
3190 goto done;
3191 }
3192 if (!(*language = switch_xml_find_child(sub_macros, "language", "name", str_language))) {
3193 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't find language %s.\n", str_language);
3194 switch_xml_free(*root);
3195 *root = NULL;
3196 *node = NULL;
3197 goto done;
3198 }
3199 *macros = *language;
3200 } else {
3201 if (!(*language = switch_xml_find_child(*node, "language", "name", str_language))) {
3202 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't find language %s.\n", str_language);
3203 switch_xml_free(*root);
3204 *root = NULL;
3205 goto done;
3206 }
3207 if (!(*phrases = switch_xml_child(*language, "phrases"))) {
3208 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't find phrases tag.\n");
3209 switch_xml_free(*root);
3210 *root = NULL;
3211 *node = NULL;
3212 *language = NULL;
3213 goto done;
3214 }
3215
3216 if (!(*macros = switch_xml_child(*phrases, "macros"))) {
3217 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't find macros tag.\n");
3218 switch_xml_free(*root);
3219 *root = NULL;
3220 *node = NULL;
3221 *language = NULL;
3222 *phrases = NULL;
3223 goto done;
3224 }
3225 }
3226 status = SWITCH_STATUS_SUCCESS;
3227
3228done:
3229 return status;
3230}
09a734bb 3231
59241895 3232#ifdef WIN32
a2fcb419
RC
3233/*
3234 * globbing functions for windows, part of libc on unix, this code was cut and paste from
59241895
MJ
3235 * freebsd lib and distilled a bit to work with windows
3236 */
3237
3238/*
3239 * Copyright (c) 1989, 1993
3240 * The Regents of the University of California. All rights reserved.
3241 *
3242 * This code is derived from software contributed to Berkeley by
3243 * Guido van Rossum.
3244 *
3245 * Redistribution and use in source and binary forms, with or without
3246 * modification, are permitted provided that the following conditions
3247 * are met:
3248 * 1. Redistributions of source code must retain the above copyright
3249 * notice, this list of conditions and the following disclaimer.
3250 * 2. Redistributions in binary form must reproduce the above copyright
3251 * notice, this list of conditions and the following disclaimer in the
3252 * documentation and/or other materials provided with the distribution.
3253 * 4. Neither the name of the University nor the names of its contributors
3254 * may be used to endorse or promote products derived from this software
3255 * without specific prior written permission.
3256 *
3257 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
3258 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
3259 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
3260 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
3261 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3262 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3263 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3264 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3265 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3266 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3267 * SUCH DAMAGE.
3268 */
3269
3270#define DOLLAR '$'
3271#define DOT '.'
3272#define EOS '\0'
3273#define LBRACKET '['
3274#define NOT '!'
3275#define QUESTION '?'
3276#define RANGE '-'
3277#define RBRACKET ']'
3278#define SEP '/'
e22f4233 3279#define WIN_SEP '/'
59241895
MJ
3280#define STAR '*'
3281#define TILDE '~'
3282#define UNDERSCORE '_'
3283#define LBRACE '{'
3284#define RBRACE '}'
3285#define SLASH '/'
3286#define COMMA ','
3287
3288#define M_QUOTE (char)0x80
3289#define M_PROTECT (char)0x40
3290#define M_MASK (char)0xff
3291#define M_ASCII (char)0x7f
3292
3293#define CHAR(c) ((char)((c)&M_ASCII))
3294#define META(c) ((char)((c)|M_QUOTE))
3295#define M_ALL META('*')
3296#define M_END META(']')
3297#define M_NOT META('!')
3298#define M_ONE META('?')
3299#define M_RNG META('-')
3300#define M_SET META('[')
3301#define ismeta(c) (((c)&M_QUOTE) != 0)
3302
3303#ifndef MAXPATHLEN
3304#define MAXPATHLEN 256
3305#endif
3306
886e1ddb
AM
3307static int compare(const void *, const void *);
3308static int glob0(const char *, glob_t *, size_t *);
3309static int glob1(char *, glob_t *, size_t *);
3310static int glob2(char *, char *, char *, char *, glob_t *, size_t *);
3311static int glob3(char *, char *, char *, char *, char *, glob_t *, size_t *);
3312static int globextend(const char *, glob_t *, size_t *);
3313static int match(char *, char *, char *);
59241895
MJ
3314
3315#pragma warning(push)
3316#pragma warning(disable:4310)
3317
886e1ddb 3318int glob(const char *pattern, int flags, int (*errfunc) (const char *, int), glob_t *pglob)
59241895
MJ
3319{
3320 const unsigned char *patnext;
5d4b5c3d 3321 size_t limit;
59241895
MJ
3322 char c;
3323 char *bufnext, *bufend, patbuf[MAXPATHLEN];
886e1ddb 3324
59241895
MJ
3325 patnext = (unsigned char *) pattern;
3326 if (!(flags & GLOB_APPEND)) {
3327 pglob->gl_pathc = 0;
3328 pglob->gl_pathv = NULL;
3329 if (!(flags & GLOB_DOOFFS))
3330 pglob->gl_offs = 0;
3331 }
3332 if (flags & GLOB_LIMIT) {
3333 limit = pglob->gl_matchc;
3334 if (limit == 0)
3335 limit = 9999999;
3336 } else
3337 limit = 0;
3338 pglob->gl_flags = flags & ~GLOB_MAGCHAR;
3339 pglob->gl_errfunc = errfunc;
3340 pglob->gl_matchc = 0;
886e1ddb 3341
59241895
MJ
3342 bufnext = patbuf;
3343 bufend = bufnext + MAXPATHLEN - 1;
3344 while (bufnext < bufend && (c = *patnext++) != EOS)
3345 *bufnext++ = c;
3346 *bufnext = EOS;
886e1ddb 3347
59241895
MJ
3348 return glob0(patbuf, pglob, &limit);
3349}
3350
3351/*
3352 * The main glob() routine: compiles the pattern (optionally processing
3353 * quotes), calls glob1() to do the real pattern matching, and finally
3354 * sorts the list (unless unsorted operation is requested). Returns 0
3355 * if things went well, nonzero if errors occurred.
3356 */
5d4b5c3d 3357static int glob0(const char *pattern, glob_t *pglob, size_t *limit)
59241895
MJ
3358{
3359 const char *qpatnext;
3360 int c, err;
5d4b5c3d 3361 size_t oldpathc;
59241895 3362 char *bufnext, patbuf[MAXPATHLEN];
886e1ddb 3363
59241895
MJ
3364 qpatnext = pattern;
3365 oldpathc = pglob->gl_pathc;
3366 bufnext = patbuf;
886e1ddb 3367
59241895
MJ
3368 /* We don't need to check for buffer overflow any more. */
3369 while ((c = *qpatnext++) != EOS) {
3370 switch (c) {
886e1ddb
AM
3371 case SEP:
3372 *bufnext++ = WIN_SEP;
3373 break;
3374 case LBRACKET:
3375 c = *qpatnext;
3376 if (c == NOT)
3377 ++qpatnext;
3378 if (*qpatnext == EOS || strchr((char *) qpatnext + 1, RBRACKET) == NULL) {
3379 *bufnext++ = LBRACKET;
59241895 3380 if (c == NOT)
886e1ddb 3381 --qpatnext;
59241895 3382 break;
886e1ddb
AM
3383 }
3384 *bufnext++ = M_SET;
3385 if (c == NOT)
3386 *bufnext++ = M_NOT;
3387 c = *qpatnext++;
3388 do {
59241895 3389 *bufnext++ = CHAR(c);
886e1ddb
AM
3390 if (*qpatnext == RANGE && (c = qpatnext[1]) != RBRACKET) {
3391 *bufnext++ = M_RNG;
3392 *bufnext++ = CHAR(c);
3393 qpatnext += 2;
3394 }
3395 } while ((c = *qpatnext++) != RBRACKET);
3396 pglob->gl_flags |= GLOB_MAGCHAR;
3397 *bufnext++ = M_END;
3398 break;
3399 case QUESTION:
3400 pglob->gl_flags |= GLOB_MAGCHAR;
3401 *bufnext++ = M_ONE;
3402 break;
3403 case STAR:
3404 pglob->gl_flags |= GLOB_MAGCHAR;
3405 /* collapse adjacent stars to one,
3406 * to avoid exponential behavior
3407 */
3408 if (bufnext == patbuf || bufnext[-1] != M_ALL)
3409 *bufnext++ = M_ALL;
3410 break;
3411 default:
3412 *bufnext++ = CHAR(c);
3413 break;
59241895
MJ
3414 }
3415 }
3416 *bufnext = EOS;
886e1ddb 3417
59241895 3418 if ((err = glob1(patbuf, pglob, limit)) != 0)
886e1ddb
AM
3419 return (err);
3420
59241895
MJ
3421 /*
3422 * If there was no match we are going to append the pattern
3423 * if GLOB_NOCHECK was specified or if GLOB_NOMAGIC was specified
3424 * and the pattern did not contain any magic characters
3425 * GLOB_NOMAGIC is there just for compatibility with csh.
3426 */
3427 if (pglob->gl_pathc == oldpathc) {
886e1ddb
AM
3428 if (((pglob->gl_flags & GLOB_NOCHECK) || ((pglob->gl_flags & GLOB_NOMAGIC) && !(pglob->gl_flags & GLOB_MAGCHAR))))
3429 return (globextend(pattern, pglob, limit));
59241895 3430 else
886e1ddb 3431 return (GLOB_NOMATCH);
59241895
MJ
3432 }
3433 if (!(pglob->gl_flags & GLOB_NOSORT))
886e1ddb
AM
3434 qsort(pglob->gl_pathv + pglob->gl_offs + oldpathc, pglob->gl_pathc - oldpathc, sizeof(char *), compare);
3435 return (0);
59241895
MJ
3436}
3437
3438static int compare(const void *p, const void *q)
3439{
886e1ddb 3440 return (strcmp(*(char **) p, *(char **) q));
59241895
MJ
3441}
3442
5d4b5c3d 3443static int glob1(char *pattern, glob_t *pglob, size_t *limit)
59241895
MJ
3444{
3445 char pathbuf[MAXPATHLEN];
886e1ddb 3446
59241895
MJ
3447 /* A null pathname is invalid -- POSIX 1003.1 sect. 2.4. */
3448 if (*pattern == EOS)
886e1ddb
AM
3449 return (0);
3450 return (glob2(pathbuf, pathbuf, pathbuf + MAXPATHLEN - 1, pattern, pglob, limit));
59241895
MJ
3451}
3452
3453/*
3454 * The functions glob2 and glob3 are mutually recursive; there is one level
3455 * of recursion for each segment in the pattern that contains one or more
3456 * meta characters.
3457 */
5d4b5c3d 3458static int glob2(char *pathbuf, char *pathend, char *pathend_last, char *pattern, glob_t *pglob, size_t *limit)
59241895
MJ
3459{
3460 struct stat sb;
3461 char *p, *q;
3462 int anymeta;
886e1ddb 3463
59241895
MJ
3464 /*
3465 * Loop over pattern segments until end of pattern or until
3466 * segment with meta character found.
3467 */
3468 for (anymeta = 0;;) {
886e1ddb 3469 if (*pattern == EOS) { /* End of pattern? */
59241895
MJ
3470 *pathend = EOS;
3471 if (stat(pathbuf, &sb))
886e1ddb
AM
3472 return (0);
3473
3474 if (((pglob->gl_flags & GLOB_MARK) && pathend[-1] != SEP && pathend[-1] != WIN_SEP) && (_S_IFDIR & sb.st_mode)) {
59241895
MJ
3475 if (pathend + 1 > pathend_last)
3476 return (GLOB_ABORTED);
3477 *pathend++ = WIN_SEP;
3478 *pathend = EOS;
3479 }
3480 ++pglob->gl_matchc;
886e1ddb 3481 return (globextend(pathbuf, pglob, limit));
59241895 3482 }
886e1ddb 3483
59241895
MJ
3484 /* Find end of next segment, copy tentatively to pathend. */
3485 q = pathend;
3486 p = pattern;
3487 while (*p != EOS && *p != SEP && *p != WIN_SEP) {
3488 if (ismeta(*p))
3489 anymeta = 1;
3490 if (q + 1 > pathend_last)
3491 return (GLOB_ABORTED);
3492 *q++ = *p++;
3493 }
886e1ddb
AM
3494
3495 if (!anymeta) { /* No expansion, do next segment. */
59241895
MJ
3496 pathend = q;
3497 pattern = p;
3498 while (*pattern == SEP || *pattern == WIN_SEP) {
3499 if (pathend + 1 > pathend_last)
3500 return (GLOB_ABORTED);
3501 *pathend++ = *pattern++;
3502 }
886e1ddb
AM
3503 } else /* Need expansion, recurse. */
3504 return (glob3(pathbuf, pathend, pathend_last, pattern, p, pglob, limit));
59241895
MJ
3505 }
3506 /* NOTREACHED */
3507}
3508
5d4b5c3d 3509static int glob3(char *pathbuf, char *pathend, char *pathend_last, char *pattern, char *restpattern, glob_t *pglob, size_t *limit)
59241895
MJ
3510{
3511 int err;
886e1ddb
AM
3512 apr_dir_t *dirp;
3513 apr_pool_t *pool;
3514
59241895 3515 apr_pool_create(&pool, NULL);
886e1ddb 3516
59241895
MJ
3517 if (pathend > pathend_last)
3518 return (GLOB_ABORTED);
3519 *pathend = EOS;
3520 errno = 0;
886e1ddb
AM
3521
3522 if (apr_dir_open(&dirp, pathbuf, pool) != APR_SUCCESS) {
59241895
MJ
3523 /* TODO: don't call for ENOENT or ENOTDIR? */
3524 apr_pool_destroy(pool);
3525 if (pglob->gl_errfunc) {
886e1ddb 3526 if (pglob->gl_errfunc(pathbuf, errno) || pglob->gl_flags & GLOB_ERR)
59241895
MJ
3527 return (GLOB_ABORTED);
3528 }
886e1ddb 3529 return (0);
59241895 3530 }
886e1ddb 3531
59241895 3532 err = 0;
886e1ddb 3533
59241895 3534 /* Search directory for matching names. */
886e1ddb 3535 while (dirp) {
59241895
MJ
3536 apr_finfo_t dp;
3537 unsigned char *sc;
3538 char *dc;
886e1ddb 3539
d0688264 3540 if (apr_dir_read(&dp, APR_FINFO_NAME, dirp) != APR_SUCCESS)
59241895 3541 break;
d0688264 3542 if (!(dp.valid & APR_FINFO_NAME) || !(dp.name) || !strlen(dp.name))
59241895 3543 break;
886e1ddb 3544
59241895
MJ
3545 /* Initial DOT must be matched literally. */
3546 if (dp.name[0] == DOT && *pattern != DOT)
3547 continue;
3548 dc = pathend;
3549 sc = (unsigned char *) dp.name;
886e1ddb 3550
59241895 3551 while (dc < pathend_last && (*dc++ = *sc++) != EOS);
886e1ddb 3552
59241895
MJ
3553 if (!match(pathend, pattern, restpattern)) {
3554 *pathend = EOS;
3555 continue;
3556 }
886e1ddb 3557 err = glob2(pathbuf, --dc, pathend_last, restpattern, pglob, limit);
59241895
MJ
3558 if (err)
3559 break;
3560 }
886e1ddb
AM
3561
3562 if (dirp)
3563 apr_dir_close(dirp);
59241895 3564 apr_pool_destroy(pool);
886e1ddb 3565 return (err);
59241895
MJ
3566}
3567
3568
3569/*
ff3f04f1 3570 * Extend the gl_pathv member of a glob_t structure to accommodate a new item,
59241895
MJ
3571 * add the new item, and update gl_pathc.
3572 *
3573 * This assumes the BSD realloc, which only copies the block when its size
3574 * crosses a power-of-two boundary; for v7 realloc, this would cause quadratic
3575 * behavior.
3576 *
3577 * Return 0 if new item added, error code if memory couldn't be allocated.
3578 *
3579 * Invariant of the glob_t structure:
3580 * Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and
3581 * gl_pathv points to (gl_offs + gl_pathc + 1) items.
3582 */
5d4b5c3d 3583static int globextend(const char *path, glob_t *pglob, size_t *limit)
59241895
MJ
3584{
3585 char **pathv;
886e1ddb 3586 char *copy;
5d4b5c3d
RJ
3587 size_t i;
3588 size_t newsize, len;
59241895 3589 const char *p;
886e1ddb 3590
1bbf3a82 3591 if (*limit && pglob->gl_pathc > *limit) {
59241895
MJ
3592 errno = 0;
3593 return (GLOB_NOSPACE);
3594 }
886e1ddb 3595
59241895 3596 newsize = sizeof(*pathv) * (2 + pglob->gl_pathc + pglob->gl_offs);
886e1ddb 3597 pathv = pglob->gl_pathv ? realloc((char *) pglob->gl_pathv, newsize) : malloc(newsize);
59241895
MJ
3598 if (pathv == NULL) {
3599 if (pglob->gl_pathv) {
3600 free(pglob->gl_pathv);
3601 pglob->gl_pathv = NULL;
3602 }
886e1ddb 3603 return (GLOB_NOSPACE);
59241895 3604 }
886e1ddb 3605
59241895
MJ
3606 if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) {
3607 /* first time around -- clear initial gl_offs items */
3608 pathv += pglob->gl_offs;
886e1ddb 3609 for (i = pglob->gl_offs; i-- > 0;)
59241895
MJ
3610 *--pathv = NULL;
3611 }
3612 pglob->gl_pathv = pathv;
886e1ddb 3613
59241895
MJ
3614 for (p = path; *p++;)
3615 continue;
886e1ddb 3616 len = (size_t) (p - path);
59241895
MJ
3617 if ((copy = malloc(len)) != NULL) {
3618 memcpy(copy, path, len);
3619 pathv[pglob->gl_offs + pglob->gl_pathc++] = copy;
3620 }
3621 pathv[pglob->gl_offs + pglob->gl_pathc] = NULL;
886e1ddb 3622 return (copy == NULL ? GLOB_NOSPACE : 0);
59241895
MJ
3623}
3624
3625/*
3626 * pattern matching function for filenames. Each occurrence of the *
3627 * pattern causes a recursion level.
3628 */
3629static int match(char *name, char *pat, char *patend)
3630{
3631 int ok, negate_range;
3632 char c, k;
3633 char s1[6];
886e1ddb 3634
59241895
MJ
3635 while (pat < patend) {
3636 c = *pat++;
3637 switch (c & M_MASK) {
886e1ddb
AM
3638 case M_ALL:
3639 if (pat == patend)
3640 return (1);
3641 do
3642 if (match(name, pat, patend))
3643 return (1);
3644 while (*name++ != EOS);
3645 return (0);
3646 case M_ONE:
3647 if (*name++ == EOS)
3648 return (0);
3649 break;
3650 case M_SET:
3651 ok = 0;
3652 if ((k = *name++) == EOS)
3653 return (0);
3654 if ((negate_range = ((*pat & M_MASK) == M_NOT)) != EOS)
3655 ++pat;
3656 while (((c = *pat++) & M_MASK) != M_END)
3657 if ((*pat & M_MASK) == M_RNG) {
3658 memset(s1, 0, sizeof(s1));
3659 s1[0] = c;
3660 s1[2] = k;
3661 s1[4] = pat[1];
3662 if (strcoll(&s1[0], &s1[2]) <= 0 && strcoll(&s1[2], &s1[4]) <= 0)
59241895 3663 ok = 1;
886e1ddb
AM
3664 pat += 2;
3665 } else if (c == k)
3666 ok = 1;
3667 if (ok == negate_range)
3668 return (0);
3669 break;
3670 default:
3671 if (*name++ != c)
3672 return (0);
3673 break;
59241895
MJ
3674 }
3675 }
886e1ddb 3676 return (*name == EOS);
59241895
MJ
3677}
3678
3679/* Free allocated data belonging to a glob_t structure. */
3680void globfree(glob_t *pglob)
3681{
5d4b5c3d 3682 size_t i;
59241895 3683 char **pp;
886e1ddb 3684
59241895
MJ
3685 if (pglob->gl_pathv != NULL) {
3686 pp = pglob->gl_pathv + pglob->gl_offs;
3687 for (i = pglob->gl_pathc; i--; ++pp)
3688 if (*pp)
3689 free(*pp);
3690 free(pglob->gl_pathv);
3691 pglob->gl_pathv = NULL;
3692 }
3693}
886e1ddb 3694
59241895
MJ
3695#pragma warning(pop)
3696#endif
3697
3698/* For Emacs:
3699 * Local Variables:
3700 * mode:c
3701 * indent-tabs-mode:t
3702 * tab-width:4
3703 * c-basic-offset:4
3704 * End:
3705 * For VIM:
32adc789 3706 * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
59241895 3707 */