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