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