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