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