2 * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
3 * Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
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/
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
17 * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
19 * The Initial Developer of the Original Code is
20 * Anthony Minessale II <anthm@freeswitch.org>
21 * Portions created by the Initial Developer are Copyright (C)
22 * the Initial Developer. All Rights Reserved.
26 * Anthony Minessale II <anthm@freeswitch.org>
27 * Simon Capper <skyjunky@sbcglobal.net>
28 * Marc Olivier Chouinard <mochouinard@moctel.com>
29 * Raymond Chandler <intralanman@freeswitch.org>
31 * switch_xml.c -- XML PARSER
33 * Derived from ezxml http://ezxml.sourceforge.net
36 * Copyright 2004, 2006 Aaron Voisine <aaron@voisine.org>
38 * Permission is hereby granted, free of charge, to any person obtaining
39 * a copy of this software and associated documentation files (the
40 * "Software"), to deal in the Software without restriction, including
41 * without limitation the rights to use, copy, modify, merge, publish,
42 * distribute, sublicense, and/or sell copies of the Software, and to
43 * permit persons to whom the Software is furnished to do so, subject to
44 * the following conditions:
46 * The above copyright notice and this permission notice shall be included
47 * in all copies or substantial portions of the Software.
49 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
50 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
51 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
52 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
53 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
54 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
55 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
59 #include <switch_stun.h>
62 #include <switch_private.h>
64 #else /* we're on windoze :( */
65 /* glob functions at end of this file */
66 #include <fspr_file_io.h>
69 size_t gl_pathc
; /* Count of total paths so far. */
70 size_t gl_matchc
; /* Count of paths matching pattern. */
71 size_t gl_offs
; /* Reserved at beginning of gl_pathv. */
72 int gl_flags
; /* Copy of flags parameter to glob. */
73 char **gl_pathv
; /* List of paths matching pattern. */
74 /* Copy of errfunc parameter to glob. */
75 int (*gl_errfunc
) (const char *, int);
78 /* Believed to have been introduced in 1003.2-1992 */
79 #define GLOB_APPEND 0x0001 /* Append to output from previous call. */
80 #define GLOB_DOOFFS 0x0002 /* Use gl_offs. */
81 #define GLOB_ERR 0x0004 /* Return on error. */
82 #define GLOB_MARK 0x0008 /* Append / to matching directories. */
83 #define GLOB_NOCHECK 0x0010 /* Return pattern itself if nothing matches. */
84 #define GLOB_NOSORT 0x0020 /* Don't sort. */
86 /* Error values returned by glob(3) */
87 #define GLOB_NOSPACE (-1) /* Malloc call failed. */
88 #define GLOB_ABORTED (-2) /* Unignored error. */
89 #define GLOB_NOMATCH (-3) /* No match and GLOB_NOCHECK was not set. */
90 #define GLOB_NOSYS (-4) /* Obsolete: source comptability only. */
92 #define GLOB_ALTDIRFUNC 0x0040 /* Use alternately specified directory funcs. */
93 #define GLOB_MAGCHAR 0x0100 /* Pattern had globbing characters. */
94 #define GLOB_NOMAGIC 0x0200 /* GLOB_NOCHECK without magic chars (csh). */
95 #define GLOB_QUOTE 0x0400 /* Quote special chars with \. */
96 #define GLOB_LIMIT 0x1000 /* limit number of returned paths */
98 int glob(const char *, int, int (*)(const char *, int), glob_t
*);
99 void globfree(glob_t
*);
103 #define SWITCH_XML_WS "\t\r\n " /* whitespace */
104 #define SWITCH_XML_ERRL 128 /* maximum error string length */
106 static void preprocess_exec_set(char *keyval
)
109 char *val
= strchr(keyval
, '=');
113 while (*val
&& *val
== ' ') {
117 while (*ve
&& *ve
== ' ') {
123 switch_stream_handle_t exec_result
= { 0 };
124 SWITCH_STANDARD_STREAM(exec_result
);
125 if (switch_stream_system(val
, &exec_result
) == 0) {
126 if (!zstr(exec_result
.data
)) {
127 char *tmp
= (char *) exec_result
.data
;
128 tmp
= &tmp
[strlen(tmp
)-1];
129 while (tmp
>= (char *) exec_result
.data
&& ( tmp
[0] == ' ' || tmp
[0] == '\n') ) {
130 tmp
[0] = '\0'; /* remove trailing spaces and newlines */
133 switch_core_set_variable(key
, exec_result
.data
);
136 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_ERROR
, "Error while executing command: %s\n", val
);
138 switch_safe_free(exec_result
.data
);
142 static void preprocess_stun_set(char *keyval
)
145 char *val
= strchr(keyval
, '=');
149 while (*val
&& *val
== ' ') {
153 while (*ve
&& *ve
== ' ') {
159 char *external_ip
= NULL
;
160 switch_memory_pool_t
*pool
;
162 switch_core_new_memory_pool(&pool
);
164 if (switch_stun_ip_lookup(&external_ip
, val
, pool
) == SWITCH_STATUS_SUCCESS
) {
165 if (!zstr(external_ip
)) {
166 char *tmp
= external_ip
;
167 tmp
= &tmp
[strlen(tmp
) - 1];
168 while (tmp
>= external_ip
&& (tmp
[0] == ' ' || tmp
[0] == '\n')) {
169 tmp
[0] = '\0'; /* remove trailing spaces and newlines */
172 switch_core_set_variable(key
, external_ip
);
175 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_ERROR
, "stun-set failed.\n");
178 switch_core_destroy_memory_pool(&pool
);
182 static void preprocess_env_set(char *keyval
)
185 char *val
= strchr(keyval
, '=');
191 char *data
= getenv(val
);
194 switch_core_set_variable(key
, data
);
200 static int preprocess(const char *cwd
, const char *file
, FILE *write_fd
, int rlevel
);
202 typedef struct switch_xml_root
*switch_xml_root_t
;
203 struct switch_xml_root
{ /* additional data for the root tag */
204 struct switch_xml xml
; /* is a super-struct built on top of switch_xml struct */
205 switch_xml_t cur
; /* current xml tree insertion point */
206 char *m
; /* original xml string */
207 switch_size_t len
; /* length of allocated memory */
208 uint8_t dynamic
; /* Free the original string when calling switch_xml_free */
209 char *u
; /* UTF-8 conversion of string if original was UTF-16 */
210 char *s
; /* start of work area */
211 char *e
; /* end of work area */
212 char **ent
; /* general entities (ampersand sequences) */
213 char ***attr
; /* default attributes */
214 char ***pi
; /* processing instructions */
215 short standalone
; /* non-zero if <?xml standalone="yes"?> */
216 char err
[SWITCH_XML_ERRL
]; /* error string */
219 char *SWITCH_XML_NIL
[] = { NULL
}; /* empty, null terminated array of strings */
221 struct switch_xml_binding
{
222 switch_xml_search_function_t function
;
223 switch_xml_section_t sections
;
225 struct switch_xml_binding
*next
;
229 static switch_xml_binding_t
*BINDINGS
= NULL
;
230 static switch_xml_t MAIN_XML_ROOT
= NULL
;
231 static switch_memory_pool_t
*XML_MEMORY_POOL
= NULL
;
233 static switch_thread_rwlock_t
*B_RWLOCK
= NULL
;
234 static switch_mutex_t
*XML_LOCK
= NULL
;
235 static switch_mutex_t
*CACHE_MUTEX
= NULL
;
236 static switch_mutex_t
*REFLOCK
= NULL
;
237 static switch_mutex_t
*FILE_LOCK
= NULL
;
239 SWITCH_DECLARE_NONSTD(switch_xml_t
) __switch_xml_open_root(uint8_t reload
, const char **err
, void *user_data
);
241 static switch_xml_open_root_function_t XML_OPEN_ROOT_FUNCTION
= (switch_xml_open_root_function_t
)__switch_xml_open_root
;
242 static void *XML_OPEN_ROOT_FUNCTION_USER_DATA
= NULL
;
244 static switch_hash_t
*CACHE_HASH
= NULL
;
245 static switch_hash_t
*CACHE_EXPIRES_HASH
= NULL
;
247 struct xml_section_t
{
249 /* switch_xml_section_t section; */
253 static struct xml_section_t SECTIONS
[] = {
254 {"result", SWITCH_XML_SECTION_RESULT
},
255 {"config", SWITCH_XML_SECTION_CONFIG
},
256 {"directory", SWITCH_XML_SECTION_DIRECTORY
},
257 {"dialplan", SWITCH_XML_SECTION_DIALPLAN
},
258 {"languages", SWITCH_XML_SECTION_LANGUAGES
},
259 {"chatplan", SWITCH_XML_SECTION_CHATPLAN
},
260 {"channels", SWITCH_XML_SECTION_CHANNELS
},
264 SWITCH_DECLARE(switch_xml_section_t
) switch_xml_parse_section_string(const char *str
)
268 /*switch_xml_section_t sections = SWITCH_XML_SECTION_RESULT; */
269 uint32_t sections
= SWITCH_XML_SECTION_RESULT
;
272 for (x
= 0; x
< strlen(str
); x
++) {
273 buf
[x
] = (char) tolower((int) str
[x
]);
276 if (!SECTIONS
[x
].name
) {
279 if (strstr(buf
, SECTIONS
[x
].name
)) {
280 sections
|= SECTIONS
[x
].section
;
284 return (switch_xml_section_t
) sections
;
287 SWITCH_DECLARE(switch_status_t
) switch_xml_unbind_search_function(switch_xml_binding_t
**binding
)
289 switch_xml_binding_t
*ptr
, *last
= NULL
;
290 switch_status_t status
= SWITCH_STATUS_FALSE
;
293 switch_thread_rwlock_wrlock(B_RWLOCK
);
294 for (ptr
= BINDINGS
; ptr
; ptr
= ptr
->next
) {
295 if (ptr
== *binding
) {
297 last
->next
= (*binding
)->next
;
299 BINDINGS
= (*binding
)->next
;
301 status
= SWITCH_STATUS_SUCCESS
;
306 switch_thread_rwlock_unlock(B_RWLOCK
);
311 SWITCH_DECLARE(switch_status_t
) switch_xml_unbind_search_function_ptr(switch_xml_search_function_t function
)
313 switch_xml_binding_t
*ptr
, *last
= NULL
;
314 switch_status_t status
= SWITCH_STATUS_FALSE
;
316 switch_thread_rwlock_wrlock(B_RWLOCK
);
317 for (ptr
= BINDINGS
; ptr
; ptr
= ptr
->next
) {
318 if (ptr
->function
== function
) {
319 status
= SWITCH_STATUS_SUCCESS
;
322 last
->next
= ptr
->next
;
324 BINDINGS
= ptr
->next
;
331 switch_thread_rwlock_unlock(B_RWLOCK
);
336 SWITCH_DECLARE(void) switch_xml_set_binding_sections(switch_xml_binding_t
*binding
, switch_xml_section_t sections
)
338 switch_assert(binding
);
339 binding
->sections
= sections
;
342 SWITCH_DECLARE(void) switch_xml_set_binding_user_data(switch_xml_binding_t
*binding
, void *user_data
)
344 switch_assert(binding
);
345 binding
->user_data
= user_data
;
348 SWITCH_DECLARE(switch_xml_section_t
) switch_xml_get_binding_sections(switch_xml_binding_t
*binding
)
350 return binding
->sections
;
353 SWITCH_DECLARE(void *) switch_xml_get_binding_user_data(switch_xml_binding_t
*binding
)
355 return binding
->user_data
;
358 SWITCH_DECLARE(switch_status_t
) switch_xml_bind_search_function_ret(switch_xml_search_function_t function
,
359 switch_xml_section_t sections
, void *user_data
, switch_xml_binding_t
**ret_binding
)
361 switch_xml_binding_t
*binding
= NULL
, *ptr
= NULL
;
362 assert(function
!= NULL
);
364 if (!(binding
= (switch_xml_binding_t
*) switch_core_alloc(XML_MEMORY_POOL
, sizeof(*binding
)))) {
365 return SWITCH_STATUS_MEMERR
;
368 binding
->function
= function
;
369 binding
->sections
= sections
;
370 binding
->user_data
= user_data
;
372 switch_thread_rwlock_wrlock(B_RWLOCK
);
373 for (ptr
= BINDINGS
; ptr
&& ptr
->next
; ptr
= ptr
->next
);
382 *ret_binding
= binding
;
385 switch_thread_rwlock_unlock(B_RWLOCK
);
387 return SWITCH_STATUS_SUCCESS
;
390 SWITCH_DECLARE(switch_xml_t
) switch_xml_find_child(switch_xml_t node
, const char *childname
, const char *attrname
, const char *value
)
392 switch_xml_t p
= NULL
;
394 if (!(childname
&& attrname
&& value
)) {
398 for (p
= switch_xml_child(node
, childname
); p
; p
= p
->next
) {
399 const char *aname
= switch_xml_attr(p
, attrname
);
400 if (aname
&& !strcasecmp(aname
, value
)) {
408 SWITCH_DECLARE(switch_xml_t
) switch_xml_find_child_multi(switch_xml_t node
, const char *childname
,...)
410 switch_xml_t p
= NULL
;
411 const char *names
[256] = { 0 };
412 const char *vals
[256] = { 0 };
415 const char *attrname
, *value
= NULL
;
417 va_start(ap
, childname
);
420 if ((attrname
= va_arg(ap
, const char *))) {
421 value
= va_arg(ap
, const char *);
423 if (attrname
&& value
) {
434 if (!(childname
&& i
)) {
438 for (p
= switch_xml_child(node
, childname
); p
; p
= p
->next
) {
439 for (x
= 0; x
< i
; x
++) {
440 if (names
[x
] && vals
[x
]) {
441 const char *aname
= switch_xml_attr(p
, names
[x
]);
444 if (*vals
[x
] == '!') {
445 const char *sval
= vals
[x
] + 1;
446 if (strcasecmp(aname
, sval
)) {
450 if (!strcasecmp(aname
, vals
[x
])) {
464 /* returns the first child tag with the given name or NULL if not found */
465 SWITCH_DECLARE(switch_xml_t
) switch_xml_child(switch_xml_t xml
, const char *name
)
467 xml
= (xml
) ? xml
->child
: NULL
;
468 while (xml
&& strcmp(name
, xml
->name
))
473 /* returns the Nth tag with the same name in the same subsection or NULL if not found */
474 switch_xml_t
switch_xml_idx(switch_xml_t xml
, int idx
)
476 for (; xml
&& idx
; idx
--)
481 /* returns the value of the requested tag attribute or "" if not found */
482 SWITCH_DECLARE(const char *) switch_xml_attr_soft(switch_xml_t xml
, const char *attr
)
484 const char *ret
= switch_xml_attr(xml
, attr
);
486 return ret
? ret
: "";
489 /* returns the value of the requested tag attribute or NULL if not found */
490 SWITCH_DECLARE(const char *) switch_xml_attr(switch_xml_t xml
, const char *attr
)
493 switch_xml_root_t root
= (switch_xml_root_t
) xml
;
495 if (!xml
|| !xml
->attr
)
497 while (xml
->attr
[i
] && attr
&& strcmp(attr
, xml
->attr
[i
]))
500 return xml
->attr
[i
+ 1]; /* found attribute */
502 while (root
->xml
.parent
)
503 root
= (switch_xml_root_t
) root
->xml
.parent
; /* root tag */
509 for (i
= 0; root
->attr
[i
] && xml
->name
&& strcmp(xml
->name
, root
->attr
[i
][0]); i
++);
511 return NULL
; /* no matching default attributes */
512 while (root
->attr
[i
][j
] && attr
&& strcmp(attr
, root
->attr
[i
][j
]))
514 return (root
->attr
[i
][j
]) ? root
->attr
[i
][j
+ 1] : NULL
; /* found default */
517 /* same as switch_xml_get but takes an already initialized va_list */
518 static switch_xml_t
switch_xml_vget(switch_xml_t xml
, va_list ap
)
520 char *name
= va_arg(ap
, char *);
524 idx
= va_arg(ap
, int);
525 xml
= switch_xml_child(xml
, name
);
527 return (idx
< 0) ? xml
: switch_xml_vget(switch_xml_idx(xml
, idx
), ap
);
530 /* Traverses the xml tree to retrieve a specific subtag. Takes a variable
531 length list of tag names and indexes. The argument list must be terminated
532 by either an index of -1 or an empty string tag name. Example:
533 title = switch_xml_get(library, "shelf", 0, "book", 2, "title", -1);
534 This retrieves the title of the 3rd book on the 1st shelf of library.
535 Returns NULL if not found. */
536 SWITCH_DECLARE(switch_xml_t
) switch_xml_get(switch_xml_t xml
,...)
542 r
= switch_xml_vget(xml
, ap
);
547 /* returns a null terminated array of processing instructions for the given target */
548 SWITCH_DECLARE(const char **) switch_xml_pi(switch_xml_t xml
, const char *target
)
550 switch_xml_root_t root
= (switch_xml_root_t
) xml
;
554 return (const char **) SWITCH_XML_NIL
;
557 while (root
&& root
->xml
.parent
) {
558 root
= (switch_xml_root_t
) root
->xml
.parent
; /* root tag */
561 if (!root
|| !root
->pi
) {
562 return (const char **) SWITCH_XML_NIL
;
565 while (root
->pi
[i
] && strcmp(target
, root
->pi
[i
][0])) {
566 i
++; /* find target */
569 return (const char **) ((root
->pi
[i
]) ? root
->pi
[i
] + 1 : SWITCH_XML_NIL
);
572 /* set an error string and return root */
573 static switch_xml_t
switch_xml_err(switch_xml_root_t root
, char *s
, const char *err
, ...)
577 char *t
, fmt
[SWITCH_XML_ERRL
];
579 if (!root
|| !root
->s
) {
583 for (t
= root
->s
; t
&& t
< s
; t
++)
586 switch_snprintf(fmt
, SWITCH_XML_ERRL
, "[error near line %d]: %s", line
, err
);
589 vsnprintf(root
->err
, SWITCH_XML_ERRL
, fmt
, ap
);
595 /* Recursively decodes entity and character references and normalizes new lines
596 ent is a null terminated array of alternating entity names and values. set t
597 to '&' for general entity decoding, '%' for parameter entity decoding, 'c'
598 for cdata sections, ' ' for attribute normalization, or '*' for non-cdata
599 attribute normalization. Returns s, or if the decoded string is longer than
600 s, returns a malloced string that must be freed. */
601 static char *switch_xml_decode(char *s
, char **ent
, char t
)
603 char *e
, *r
= s
, *m
= s
;
604 unsigned long b
, c
, d
, l
;
606 for (; *s
; s
++) { /* normalize line endings */
610 memmove(s
, (s
+ 1), strlen(s
));
615 while (*s
&& *s
!= '&' && (*s
!= '%' || t
!= '%') && !isspace((unsigned char) (*s
)))
620 else if (t
!= 'c' && !strncmp(s
, "&#", 2)) { /* character reference */
627 if (!isxdigit((int)*code
)) { /* "&# 1;" and "&#-1;" are invalid */
631 c
= strtoul(code
, &e
, base
);
632 if (!c
|| *e
!= ';') {
636 /* not a character ref */
638 *(s
++) = (char) c
; /* US-ASCII subset */
639 else if (c
> 0x7FFFFFFF) { /* out of UTF-8 range */
642 } else { /* multi-byte UTF-8 sequence */
643 for (b
= 0, d
= c
; d
; d
/= 2)
644 b
++; /* number of bits in c */
645 b
= (b
- 2) / 5; /* number of bytes in payload */
646 assert(b
< 7); /* because c <= 0x7FFFFFFF */
647 *(s
++) = (char) ((0xFF << (7 - b
)) | (c
>> (6 * b
))); /* head */
649 *(s
++) = (char) (0x80 | ((c
>> (6 * --b
)) & 0x3F)); /* payload */
652 memmove(s
, strchr(s
, ';') + 1, strlen(strchr(s
, ';')));
653 } else if ((*s
== '&' && (t
== '&' || t
== ' ' || t
== '*')) || (*s
== '%' && t
== '%')) { /* entity reference */
654 for (b
= 0; ent
[b
] && strncmp(s
+ 1, ent
[b
], strlen(ent
[b
])); b
+= 2); /* find entity in entity list */
656 if (ent
[b
++]) { /* found a match */
657 if ((c
= (unsigned long) strlen(ent
[b
])) - 1 > (e
= strchr(s
, ';')) - s
) {
658 l
= (d
= (unsigned long) (s
- r
)) + c
+ (unsigned long) strlen(e
); /* new length */
661 char *tmp
= (char *) switch_must_malloc(l
);
664 r
= (char *) switch_must_realloc(r
, l
);
667 e
= strchr((s
= r
+ d
), ';'); /* fix up pointers */
670 memmove(s
+ c
, e
+ 1, strlen(e
)); /* shift rest of string */
671 strncpy(s
, ent
[b
], c
); /* copy in replacement text */
673 s
++; /* not a known entity */
674 } else if ((t
== ' ' || t
== '*') && isspace((int) (*s
)))
677 s
++; /* no decoding needed */
680 if (t
== '*') { /* normalize spaces for non-cdata attributes */
681 for (s
= r
; *s
; s
++) {
682 if ((l
= (unsigned long) strspn(s
, " ")))
683 memmove(s
, s
+ l
, strlen(s
+ l
) + 1);
684 while (*s
&& *s
!= ' ')
687 if (--s
>= r
&& *s
== ' ')
688 *s
= '\0'; /* trim any trailing space */
693 /* called when parser finds start of new tag */
694 static void switch_xml_open_tag(switch_xml_root_t root
, char *name
, char *open_pos
, char **attr
)
698 if (!root
|| !root
->cur
) {
705 xml
= switch_xml_add_child(xml
, name
, strlen(xml
->txt
));
707 xml
->name
= name
; /* first open tag */
710 root
->cur
= xml
; /* update tag insertion point */
711 root
->cur
->open
= open_pos
;
714 /* called when parser finds character content between open and closing tag */
715 static void switch_xml_char_content(switch_xml_root_t root
, char *s
, switch_size_t len
, char t
)
721 if (!root
|| !root
->cur
) {
727 if (!xml
|| !xml
->name
|| !len
)
728 return; /* sanity check */
730 s
[len
] = '\0'; /* null terminate text (calling functions anticipate this) */
731 len
= strlen(s
= switch_xml_decode(s
, root
->ent
, t
)) + 1;
734 xml
->txt
= s
; /* initial character content */
735 else { /* allocate our own memory and make a copy */
736 if ((xml
->flags
& SWITCH_XML_TXTM
)) { /* allocate some space */
737 xml
->txt
= (char *) switch_must_realloc(xml
->txt
, (l
= strlen(xml
->txt
)) + len
);
739 char *tmp
= (char *) switch_must_malloc((l
= strlen(xml
->txt
)) + len
);
741 xml
->txt
= strcpy(tmp
, xml
->txt
);
743 strcpy(xml
->txt
+ l
, s
); /* add new char content */
745 free(s
); /* free s if it was malloced by switch_xml_decode() */
749 switch_xml_set_flag(xml
, SWITCH_XML_TXTM
);
752 /* called when parser finds closing tag */
753 static switch_xml_t
switch_xml_close_tag(switch_xml_root_t root
, char *name
, char *s
, char *close_pos
)
755 if (!root
|| !root
->cur
|| !root
->cur
->name
|| strcmp(name
, root
->cur
->name
))
756 return switch_xml_err(root
, s
, "unexpected closing tag </%s>", name
);
758 root
->cur
->close
= close_pos
;
759 root
->cur
= root
->cur
->parent
;
763 /* checks for circular entity references, returns non-zero if no circular
764 references are found, zero otherwise */
765 static int switch_xml_ent_ok(char *name
, char *s
, char **ent
)
770 while (*s
&& *s
!= '&')
771 s
++; /* find next entity reference */
774 if (!strncmp(s
+ 1, name
, strlen(name
)))
775 return 0; /* circular ref. */
776 for (i
= 0; ent
[i
] && strncmp(ent
[i
], s
+ 1, strlen(ent
[i
])); i
+= 2);
777 if (ent
[i
] && !switch_xml_ent_ok(name
, ent
[i
+ 1], ent
))
782 /* called when the parser finds a processing instruction */
783 static void switch_xml_proc_inst(switch_xml_root_t root
, char *s
, switch_size_t len
)
790 s
[len
] = '\0'; /* null terminate instruction */
791 if (*(s
+= strcspn(s
, SWITCH_XML_WS
))) {
792 *s
= '\0'; /* null terminate target */
793 s
+= strspn(s
+ 1, SWITCH_XML_WS
) + 1; /* skip whitespace after target */
799 if (!strcmp(target
, "xml")) { /* <?xml ... ?> */
800 if ((s
= strstr(s
, "standalone")) && !strncmp(s
+ strspn(s
+ 10, SWITCH_XML_WS
"='\"") + 10, "yes", 3))
801 root
->standalone
= 1;
805 if (root
->pi
== (char ***)(SWITCH_XML_NIL
) || !root
->pi
|| !root
->pi
[0]) {
806 root
->pi
= (char ***) switch_must_malloc(sizeof(char **));
807 *(root
->pi
) = NULL
; /* first pi */
810 while (root
->pi
[i
] && strcmp(target
, root
->pi
[i
][0]))
811 i
++; /* find target */
812 if (!root
->pi
[i
]) { /* new target */
813 char ***ssstmp
= (char ***) switch_must_realloc(root
->pi
, sizeof(char **) * (i
+ 2));
818 root
->pi
[i
] = (char **) switch_must_malloc(sizeof(char *) * 3);
819 root
->pi
[i
][0] = target
;
820 root
->pi
[i
][1] = (char *) (root
->pi
[i
+ 1] = NULL
); /* terminate pi list */
821 root
->pi
[i
][2] = switch_must_strdup(""); /* empty document position list */
824 while (root
->pi
[i
][j
])
825 j
++; /* find end of instruction list for this target */
826 sstmp
= (char **) switch_must_realloc(root
->pi
[i
], sizeof(char *) * (j
+ 3));
828 stmp
= (char *) switch_must_realloc(root
->pi
[i
][j
+ 1], j
+ 1);
829 root
->pi
[i
][j
+ 2] = stmp
;
830 strcpy(root
->pi
[i
][j
+ 2] + j
- 1, (root
->xml
.name
) ? ">" : "<");
831 root
->pi
[i
][j
+ 1] = NULL
; /* null terminate pi list for this target */
832 root
->pi
[i
][j
] = s
; /* set instruction */
835 /* called when the parser finds an internal doctype subset */
836 static short switch_xml_internal_dtd(switch_xml_root_t root
, char *s
, switch_size_t len
)
838 char q
, *c
, *t
, *n
= NULL
, *v
, **ent
, **pe
;
842 pe
= (char **) memcpy(switch_must_malloc(sizeof(SWITCH_XML_NIL
)), SWITCH_XML_NIL
, sizeof(SWITCH_XML_NIL
));
844 for (s
[len
] = '\0'; s
;) {
845 while (*s
&& *s
!= '<' && *s
!= '%')
846 s
++; /* find next declaration */
850 else if (!strncmp(s
, "<!ENTITY", 8)) { /* parse entity definitions */
853 c
= s
+= strspn(s
+ 8, SWITCH_XML_WS
) + 8; /* skip white space separator */
854 n
= s
+ strspn(s
, SWITCH_XML_WS
"%"); /* find name */
855 *(s
= n
+ strcspn(n
, SWITCH_XML_WS
)) = ';'; /* append ; to name */
857 v
= s
+ strspn(s
+ 1, SWITCH_XML_WS
) + 1; /* find value */
858 if ((q
= *(v
++)) != '"' && q
!= '\'') { /* skip externals */
863 use_pe
= (*c
== '%');
864 for (i
= 0, ent
= (use_pe
) ? pe
: root
->ent
; ent
[i
]; i
++);
865 sstmp
= (char **) switch_must_realloc(ent
, (i
+ 3) * sizeof(char *)); /* space for next ent */
872 *(++s
) = '\0'; /* null terminate name */
873 if ((s
= strchr(v
, q
)))
874 *(s
++) = '\0'; /* null terminate value */
875 ent
[i
+ 1] = switch_xml_decode(v
, pe
, '%'); /* set value */
876 ent
[i
+ 2] = NULL
; /* null terminate entity list */
877 if (!switch_xml_ent_ok(n
, ent
[i
+ 1], ent
)) { /* circular reference */
880 switch_xml_err(root
, v
, "circular entity declaration &%s", n
);
883 ent
[i
] = n
; /* set entity name */
884 } else if (!strncmp(s
, "<!ATTLIST", 9)) { /* parse default attributes */
885 t
= s
+ strspn(s
+ 9, SWITCH_XML_WS
) + 9; /* skip whitespace separator */
887 switch_xml_err(root
, t
, "unclosed <!ATTLIST");
890 if (*(s
= t
+ strcspn(t
, SWITCH_XML_WS
">")) == '>')
893 *s
= '\0'; /* null terminate tag name */
894 for (i
= 0; root
->attr
[i
] && n
&& strcmp(n
, root
->attr
[i
][0]); i
++);
896 //while (*(n = ++s + strspn(s, SWITCH_XML_WS)) && *n != '>') {
897 // gcc 4.4 you are a creep
900 if (!(*(n
= s
+ strspn(s
, SWITCH_XML_WS
)) && *n
!= '>')) {
903 if (*(s
= n
+ strcspn(n
, SWITCH_XML_WS
)))
904 *s
= '\0'; /* attr name */
906 switch_xml_err(root
, t
, "malformed <!ATTLIST");
910 s
+= strspn(s
+ 1, SWITCH_XML_WS
) + 1; /* find next token */
911 c
= (strncmp(s
, "CDATA", 5)) ? (char *) "*" : (char *) " "; /* is it cdata? */
912 if (!strncmp(s
, "NOTATION", 8))
913 s
+= strspn(s
+ 8, SWITCH_XML_WS
) + 8;
914 s
= (*s
== '(') ? strchr(s
, ')') : s
+ strcspn(s
, SWITCH_XML_WS
);
916 switch_xml_err(root
, t
, "malformed <!ATTLIST");
920 s
+= strspn(s
, SWITCH_XML_WS
")"); /* skip white space separator */
921 if (!strncmp(s
, "#FIXED", 6))
922 s
+= strspn(s
+ 6, SWITCH_XML_WS
) + 6;
923 if (*s
== '#') { /* no default value */
924 s
+= strcspn(s
, SWITCH_XML_WS
">") - 1;
926 continue; /* cdata is default, nothing to do */
928 } else if ((*s
== '"' || *s
== '\'') && /* default value */
929 (s
= strchr(v
= s
+ 1, *s
)))
932 switch_xml_err(root
, t
, "malformed <!ATTLIST");
936 if (!root
->attr
[i
]) { /* new tag name */
937 root
->attr
= (!i
) ? (char ***) switch_must_malloc(2 * sizeof(char **))
938 : (char ***) switch_must_realloc(root
->attr
, (i
+ 2) * sizeof(char **));
939 root
->attr
[i
] = (char **) switch_must_malloc(2 * sizeof(char *));
940 root
->attr
[i
][0] = t
; /* set tag name */
941 root
->attr
[i
][1] = (char *) (root
->attr
[i
+ 1] = NULL
);
944 for (j
= 1; root
->attr
[i
][j
]; j
+= 3); /* find end of list */
945 sstmp
= (char **) switch_must_realloc(root
->attr
[i
], (j
+ 4) * sizeof(char *));
947 root
->attr
[i
] = sstmp
;
948 root
->attr
[i
][j
+ 3] = NULL
; /* null terminate list */
949 root
->attr
[i
][j
+ 2] = c
; /* is it cdata? */
950 root
->attr
[i
][j
+ 1] = (v
) ? switch_xml_decode(v
, root
->ent
, *c
) : NULL
;
951 root
->attr
[i
][j
] = n
; /* attribute name */
953 } else if (!strncmp(s
, "<!--", 4))
954 s
= strstr(s
+ 4, "-->"); /* comments */
955 else if (!strncmp(s
, "<?", 2)) { /* processing instructions */
956 if ((s
= strstr(c
= s
+ 2, "?>")))
957 switch_xml_proc_inst(root
, c
, s
++ - c
);
958 } else if (*s
== '<')
959 s
= strchr(s
, '>'); /* skip other declarations */
960 else if (*(s
++) == '%' && !root
->standalone
)
968 /* Converts a UTF-16 string to UTF-8. Returns a new string that must be freed
969 or NULL if no conversion was needed. */
970 static char *switch_xml_str2utf8(char **s
, switch_size_t
*len
)
973 switch_size_t l
= 0, sl
, max
= *len
;
975 int b
, be
= (**s
== '\xFE') ? 1 : (**s
== '\xFF') ? 0 : -1;
978 return NULL
; /* not UTF-16 */
983 u
= (char *) switch_must_malloc(max
);
984 for (sl
= 2; sl
< *len
- 1; sl
+= 2) {
985 c
= (be
) ? (((*s
)[sl
] & 0xFF) << 8) | ((*s
)[sl
+ 1] & 0xFF) /* UTF-16BE */
986 : (((*s
)[sl
+ 1] & 0xFF) << 8) | ((*s
)[sl
] & 0xFF); /* UTF-16LE */
987 if (c
>= 0xD800 && c
<= 0xDFFF && (sl
+= 2) < *len
- 1) { /* high-half */
988 d
= (be
) ? (((*s
)[sl
] & 0xFF) << 8) | ((*s
)[sl
+ 1] & 0xFF)
989 : (((*s
)[sl
+ 1] & 0xFF) << 8) | ((*s
)[sl
] & 0xFF);
990 c
= (((c
& 0x3FF) << 10) | (d
& 0x3FF)) + 0x10000;
993 while (l
+ 6 > max
) {
995 tmp
= (char *) switch_must_realloc(u
, max
+= SWITCH_XML_BUFSIZE
);
999 u
[l
++] = (char) c
; /* US-ASCII subset */
1000 else { /* multi-byte UTF-8 sequence */
1001 for (b
= 0, d
= c
; d
; d
/= 2)
1002 b
++; /* bits in c */
1003 b
= (b
- 2) / 5; /* bytes in payload */
1004 u
[l
++] = (char) ((0xFF << (7 - b
)) | (c
>> (6 * b
))); /* head */
1006 u
[l
++] = (char) (0x80 | ((c
>> (6 * --b
)) & 0x3F)); /* payload */
1009 return *s
= (char *) switch_must_realloc(u
, *len
= l
);
1012 /* frees a tag attribute list */
1013 static void switch_xml_free_attr(char **attr
)
1018 if (!attr
|| attr
== SWITCH_XML_NIL
)
1019 return; /* nothing to free */
1021 c
+= 2; /* find end of attribute list */
1022 m
= attr
[c
+ 1]; /* list of which names and values are malloced */
1023 for (i
= c
/ 2 - 1; i
>= 0 ; i
--) {
1024 if (m
[i
] & SWITCH_XML_NAMEM
)
1026 if (m
[i
] & SWITCH_XML_TXTM
)
1027 free(attr
[(i
* 2) + 1]);
1033 SWITCH_DECLARE(switch_xml_t
) switch_xml_parse_str_dynamic(char *s
, switch_bool_t dup
)
1035 switch_xml_root_t root
;
1039 data
= dup
? switch_must_strdup(s
) : s
;
1041 if ((root
= (switch_xml_root_t
) switch_xml_parse_str(data
, strlen(data
)))) {
1042 root
->dynamic
= 1; /* Make sure we free the memory is switch_xml_free() */
1052 /* parse the given xml string and return a switch_xml structure */
1053 SWITCH_DECLARE(switch_xml_t
) switch_xml_parse_str(char *s
, switch_size_t len
)
1055 switch_xml_root_t root
= (switch_xml_root_t
) switch_xml_new(NULL
);
1056 char q
, e
, *d
, **attr
, **a
= NULL
; /* initialize a to avoid compile warning */
1061 return switch_xml_err(root
, s
, "root tag missing");
1062 root
->u
= switch_xml_str2utf8(&s
, &len
); /* convert utf-16 to utf-8 */
1063 root
->e
= (root
->s
= s
) + len
; /* record start and end of work area */
1065 e
= s
[len
- 1]; /* save end char */
1066 s
[len
- 1] = '\0'; /* turn end char into null terminator */
1068 while (*s
&& *s
!= '<')
1069 s
++; /* find first tag */
1071 return switch_xml_err(root
, s
, "root tag missing");
1074 attr
= (char **) SWITCH_XML_NIL
;
1077 if (isalpha((int) (*s
)) || *s
== '_' || *s
== ':' || (int8_t) * s
< '\0') { /* new tag */
1079 return switch_xml_err(root
, d
, "markup outside of root element");
1081 s
+= strcspn(s
, SWITCH_XML_WS
"/>");
1082 while (isspace((int) (*s
)))
1083 *(s
++) = '\0'; /* null terminate tag name */
1085 if (*s
&& *s
!= '/' && *s
!= '>') /* find tag in default attr list */
1086 for (i
= 0; (a
= root
->attr
[i
]) && strcmp(a
[0], d
); i
++);
1088 for (l
= 0; *s
&& *s
!= '/' && *s
!= '>'; l
+= 2) { /* new attrib */
1089 attr
= (l
) ? (char **) switch_must_realloc(attr
, (l
+ 4) * sizeof(char *))
1090 : (char **) switch_must_malloc(4 * sizeof(char *)); /* allocate space */
1091 attr
[l
+ 3] = (l
) ? (char *) switch_must_realloc(attr
[l
+ 1], (l
/ 2) + 2)
1092 : (char *) switch_must_malloc(2); /* mem for list of maloced vals */
1093 strcpy(attr
[l
+ 3] + (l
/ 2), " "); /* value is not malloced */
1094 attr
[l
+ 2] = NULL
; /* null terminate list */
1095 attr
[l
+ 1] = (char *) ""; /* temporary attribute value */
1096 attr
[l
] = s
; /* set attribute name */
1098 s
+= strcspn(s
, SWITCH_XML_WS
"=/>");
1099 if (*s
== '=' || isspace((int) (*s
))) {
1100 *(s
++) = '\0'; /* null terminate tag attribute name */
1101 q
= *(s
+= strspn(s
, SWITCH_XML_WS
"="));
1102 if (q
== '"' || q
== '\'') { /* attribute value */
1104 while (*s
&& *s
!= q
)
1107 *(s
++) = '\0'; /* null terminate attribute val */
1109 switch_xml_free_attr(attr
);
1110 return switch_xml_err(root
, d
, "missing %c", q
);
1113 for (j
= 1; a
&& a
[j
] && strcmp(a
[j
], attr
[l
]); j
+= 3);
1114 attr
[l
+ 1] = switch_xml_decode(attr
[l
+ 1], root
->ent
, (a
&& a
[j
]) ? *a
[j
+ 2] : ' ');
1115 if (attr
[l
+ 1] < d
|| attr
[l
+ 1] > s
)
1116 attr
[l
+ 3][l
/ 2] = SWITCH_XML_TXTM
; /* value malloced */
1119 while (isspace((int) (*s
)))
1123 if (*s
== '/') { /* self closing tag */
1125 if ((*s
&& *s
!= '>') || (!*s
&& e
!= '>')) {
1127 switch_xml_free_attr(attr
);
1128 return switch_xml_err(root
, d
, "missing >");
1130 switch_xml_open_tag(root
, d
, s
+ 1, attr
);
1131 switch_xml_close_tag(root
, d
, s
, NULL
);
1132 } else if ((q
= *s
) == '>' || (!*s
&& e
== '>')) { /* open tag */
1133 *s
= '\0'; /* temporarily null terminate tag name */
1134 switch_xml_open_tag(root
, d
, s
, attr
);
1138 switch_xml_free_attr(attr
);
1139 return switch_xml_err(root
, d
, "missing >");
1141 } else if (*s
== '/') { /* close tag */
1142 char *close_pos
= d
- 1;
1143 s
+= strcspn(d
= s
+ 1, SWITCH_XML_WS
">") + 1;
1144 if (!(q
= *s
) && e
!= '>')
1145 return switch_xml_err(root
, d
, "missing >");
1146 *s
= '\0'; /* temporarily null terminate tag name */
1147 if (switch_xml_close_tag(root
, d
, s
, close_pos
))
1149 if (isspace((int) (*s
= q
)))
1150 s
+= strspn(s
, SWITCH_XML_WS
);
1151 } else if (!strncmp(s
, "!--", 3)) { /* xml comment */
1152 if (!(s
= strstr(s
+ 3, "--")) || (*(s
+= 2) != '>' && *s
) || (!*s
&& e
!= '>'))
1153 return switch_xml_err(root
, d
, "unclosed <!--");
1154 } else if (!strncmp(s
, "![CDATA[", 8)) { /* cdata */
1155 if ((s
= strstr(s
, "]]>"))) {
1157 root
->cur
->flags
|= SWITCH_XML_CDATA
;
1159 switch_xml_char_content(root
, d
+ 8, (s
+= 2) - d
- 10, 'c');
1161 return switch_xml_err(root
, d
, "unclosed <![CDATA[");
1163 } else if (!strncmp(s
, "!DOCTYPE", 8)) { /* dtd */
1164 for (l
= 0; *s
&& ((!l
&& *s
!= '>') || (l
&& (*s
!= ']' || *(s
+ strspn(s
+ 1, SWITCH_XML_WS
) + 1) != '>'))); l
= (*s
== '[') ? 1 : l
)
1165 s
+= strcspn(s
+ 1, "[]>") + 1;
1166 if (!*s
&& e
!= '>')
1167 return switch_xml_err(root
, d
, "unclosed <!DOCTYPE");
1168 d
= (l
) ? strchr(d
, '[') + 1 : d
;
1169 if (l
&& !switch_xml_internal_dtd(root
, d
, s
++ - d
))
1171 } else if (*s
== '?') { /* <?...?> processing instructions */
1174 } while (s
&& *(++s
) && *s
!= '>');
1175 if (!s
|| (!*s
&& e
!= '>'))
1176 return switch_xml_err(root
, d
, "unclosed <?");
1178 switch_xml_proc_inst(root
, d
+ 1, s
- d
- 2);
1180 return switch_xml_err(root
, d
, "unexpected <");
1186 if (*s
&& *s
!= '<') { /* tag character content */
1187 while (*s
&& *s
!= '<')
1190 switch_xml_char_content(root
, d
, s
- d
, '&');
1199 else if (!root
->cur
->name
)
1200 return switch_xml_err(root
, d
, "root tag missing");
1202 return switch_xml_err(root
, d
, "unclosed tag <%s>", root
->cur
->name
);
1205 /* Wrapper for switch_xml_parse_str() that accepts a file stream. Reads the entire
1206 stream into memory and then parses it. For xml files, use switch_xml_parse_file()
1207 or switch_xml_parse_fd() */
1208 SWITCH_DECLARE(switch_xml_t
) switch_xml_parse_fp(FILE * fp
)
1210 switch_xml_root_t root
;
1211 switch_size_t l
, len
= 0;
1214 s
= (char *) switch_must_malloc(SWITCH_XML_BUFSIZE
);
1217 len
+= (l
= fread((s
+ len
), 1, SWITCH_XML_BUFSIZE
, fp
));
1218 if (l
== SWITCH_XML_BUFSIZE
) {
1219 s
= (char *) switch_must_realloc(s
, len
+ SWITCH_XML_BUFSIZE
);
1221 } while (s
&& l
== SWITCH_XML_BUFSIZE
);
1227 if (!(root
= (switch_xml_root_t
) switch_xml_parse_str(s
, len
))) {
1233 root
->dynamic
= 1; /* so we know to free s in switch_xml_free() */
1238 /* A wrapper for switch_xml_parse_str() that accepts a file descriptor. First
1239 attempts to mem map the file. Failing that, reads the file into memory.
1240 Returns NULL on failure. */
1241 SWITCH_DECLARE(switch_xml_t
) switch_xml_parse_fd(int fd
)
1243 switch_xml_root_t root
;
1251 if (fstat(fd
, &st
) == -1 || !st
.st_size
) {
1255 m
= switch_must_malloc(st
.st_size
);
1257 if (!(0<(l
= read(fd
, m
, st
.st_size
)))
1258 || !(root
= (switch_xml_root_t
) switch_xml_parse_str((char *) m
, l
))) {
1264 root
->dynamic
= 1; /* so we know to free s in switch_xml_free() */
1269 static char *expand_vars(char *buf
, char *ebuf
, switch_size_t elen
, switch_size_t
*newlen
, const char **err
)
1274 char *ep
= ebuf
+ elen
- 1;
1276 if (!strstr(rp
, "$${")) {
1277 *newlen
= strlen(buf
);
1281 while (*rp
&& wp
< ep
) {
1283 if (*rp
== '$' && *(rp
+ 1) == '$' && *(rp
+ 2) == '{') {
1284 char *e
= switch_find_end_paren(rp
+ 2, '{', '}');
1291 if ((val
= switch_core_get_variable_dup(var
))) {
1293 for (p
= val
; p
&& *p
&& wp
<= ep
; p
++) {
1300 *err
= "unterminated ${var}";
1312 *newlen
= strlen(ebuf
);
1317 static FILE *preprocess_exec(const char *cwd
, const char *command
, FILE *write_fd
, int rlevel
)
1323 if (!command
|| !strlen(command
)) goto end
;
1325 if ((fp
= _popen(command
, "r"))) {
1326 while (fgets(buffer
, sizeof(buffer
), fp
) != NULL
) {
1327 if (fwrite(buffer
, 1, strlen(buffer
), write_fd
) <= 0) {
1335 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_ERROR
, "Exec failed to read the pipe of [%s] to the end\n", command
);
1338 switch_snprintf(buffer
, sizeof(buffer
), "<!-- exec can not execute [%s] -->", command
);
1339 fwrite( buffer
, 1, strlen(buffer
), write_fd
);
1342 int fds
[2], pid
= 0;
1346 } else { /* good to go */
1347 pid
= switch_fork();
1349 if (pid
< 0) { /* ok maybe not */
1353 } else if (pid
) { /* parent */
1354 char buf
[1024] = "";
1357 while ((bytes
= read(fds
[0], buf
, sizeof(buf
))) > 0) {
1358 if (fwrite(buf
, 1, bytes
, write_fd
) <= 0) {
1363 waitpid(pid
, NULL
, 0);
1364 } else { /* child */
1365 switch_close_extra_files(fds
, 2);
1367 dup2(fds
[1], STDOUT_FILENO
);
1368 switch_system(command
, SWITCH_TRUE
);
1380 static FILE *preprocess_glob(const char *cwd
, const char *pattern
, FILE *write_fd
, int rlevel
)
1382 char *full_path
= NULL
;
1383 char *dir_path
= NULL
, *e
= NULL
;
1388 if (!switch_is_file_path(pattern
)) {
1389 full_path
= switch_mprintf("%s%s%s", cwd
, SWITCH_PATH_SEPARATOR
, pattern
);
1390 pattern
= full_path
;
1393 glob_return
= glob(pattern
, GLOB_ERR
, NULL
, &glob_data
);
1394 if (glob_return
== GLOB_NOSPACE
|| glob_return
== GLOB_ABORTED
) {
1395 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_ERROR
, "Error including %s\n", pattern
);
1397 } else if (glob_return
== GLOB_NOMATCH
) {
1398 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_INFO
, "No files to include at %s\n", pattern
);
1402 for (n
= 0; n
< glob_data
.gl_pathc
; ++n
) {
1403 dir_path
= switch_must_strdup(glob_data
.gl_pathv
[n
]);
1405 if ((e
= strrchr(dir_path
, *SWITCH_PATH_SEPARATOR
))) {
1408 if (preprocess(dir_path
, glob_data
.gl_pathv
[n
], write_fd
, rlevel
) < 0) {
1410 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_ERROR
, "Error including %s (Maximum recursion limit reached)\n", pattern
);
1415 globfree(&glob_data
);
1419 switch_safe_free(full_path
);
1424 static int preprocess(const char *cwd
, const char *file
, FILE *write_fd
, int rlevel
)
1426 FILE *read_fd
= NULL
;
1427 switch_size_t cur
= 0, ml
= 0;
1428 char *q
, *cmd
, *buf
= NULL
, *ebuf
= NULL
;
1431 switch_size_t len
= 0, eblen
= 0;
1437 if (!(read_fd
= fopen(file
, "r"))) {
1438 const char *reason
= strerror(errno
);
1439 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_ERROR
, "Couldn't open %s (%s)\n", file
, reason
);
1443 setvbuf(read_fd
, (char *) NULL
, _IOFBF
, 65536);
1447 const char *err
= NULL
;
1450 switch_safe_free(ebuf
);
1452 if ((cur
= switch_fp_read_dline(read_fd
, &buf
, &len
)) <= 0) {
1457 ebuf
= switch_must_malloc(eblen
);
1458 memset(ebuf
, 0, eblen
);
1460 while (!(bp
= expand_vars(buf
, ebuf
, eblen
, &cur
, &err
))) {
1462 ebuf
= switch_must_realloc(ebuf
, eblen
);
1463 memset(ebuf
, 0, eblen
);
1469 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_ERROR
, "Error [%s] in file %s line %d\n", err
, file
, line
);
1472 /* we ignore <include> or </include> for the sake of validators as well as <?xml version="1.0"?> type stuff */
1473 if (strstr(buf
, "<include>") || strstr(buf
, "</include>") || strstr(buf
, "<?")) {
1478 if ((e
= strstr(buf
, "-->"))) {
1487 if ((tcmd
= (char *) switch_stristr("X-pre-process", bp
))) {
1488 if (*(tcmd
- 1) != '<') {
1491 if ((e
= strstr(tcmd
, "/>"))) {
1494 if (fwrite(e
, 1, (unsigned) strlen(e
), write_fd
) != (int) strlen(e
)) {
1495 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_ERROR
, "Short write!\n");
1499 if (!(tcmd
= (char *) switch_stristr("cmd", tcmd
))) {
1503 if (!(tcmd
= (char *) switch_stristr("=", tcmd
))) {
1507 if (!(tcmd
= (char *) switch_stristr("\"", tcmd
))) {
1514 if ((e
= strchr(tcmd
, '"'))) {
1518 if (!(targ
= (char *) switch_stristr("data", e
))) {
1522 if (!(targ
= (char *) switch_stristr("=", targ
))) {
1526 if (!(targ
= (char *) switch_stristr("\"", targ
))) {
1532 if ((e
= strchr(targ
, '"'))) {
1536 if (!strcasecmp(tcmd
, "set")) {
1537 char *name
= (char *) targ
;
1538 char *val
= strchr(name
, '=');
1542 while (*val
&& *val
== ' ') {
1546 while (*ve
&& *ve
== ' ') {
1552 switch_core_set_variable(name
, val
);
1555 } else if (!strcasecmp(tcmd
, "exec-set")) {
1556 preprocess_exec_set(targ
);
1557 } else if (!strcasecmp(tcmd
, "stun-set")) {
1558 preprocess_stun_set(targ
);
1559 } else if (!strcasecmp(tcmd
, "env-set")) {
1560 preprocess_env_set(targ
);
1561 } else if (!strcasecmp(tcmd
, "include")) {
1562 preprocess_glob(cwd
, targ
, write_fd
, rlevel
+ 1);
1563 } else if (!strcasecmp(tcmd
, "exec")) {
1564 preprocess_exec(cwd
, targ
, write_fd
, rlevel
+ 1);
1570 if ((cmd
= strstr(bp
, "<!--#"))) {
1571 if (fwrite(bp
, 1, (unsigned) (cmd
- bp
), write_fd
) != (unsigned) (cmd
- bp
)) {
1572 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_ERROR
, "Short write!\n");
1574 if ((e
= strstr(cmd
, "-->"))) {
1577 if (fwrite(e
, 1, (unsigned) strlen(e
), write_fd
) != (int) strlen(e
)) {
1578 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_ERROR
, "Short write!\n");
1585 if ((e
= strchr(cmd
, '\r')) || (e
= strchr(cmd
, '\n'))) {
1589 if ((arg
= strchr(cmd
, ' '))) {
1591 if ((q
= strchr(arg
, '"'))) {
1594 if ((qq
= strchr(qq
, '"'))) {
1600 if (!strcasecmp(cmd
, "set")) {
1602 char *val
= strchr(name
, '=');
1606 while (*val
&& *val
== ' ') {
1610 while (*ve
&& *ve
== ' ') {
1616 switch_core_set_variable(name
, val
);
1619 } else if (!strcasecmp(cmd
, "exec-set")) {
1620 preprocess_exec_set(arg
);
1621 } else if (!strcasecmp(cmd
, "stun-set")) {
1622 preprocess_stun_set(arg
);
1623 } else if (!strcasecmp(cmd
, "include")) {
1624 preprocess_glob(cwd
, arg
, write_fd
, rlevel
+ 1);
1625 } else if (!strcasecmp(cmd
, "exec")) {
1626 preprocess_exec(cwd
, arg
, write_fd
, rlevel
+ 1);
1633 if (fwrite(bp
, 1, (unsigned) cur
, write_fd
) != (int) cur
) {
1634 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_ERROR
, "Short write!\n");
1639 switch_safe_free(buf
);
1640 switch_safe_free(ebuf
);
1647 SWITCH_DECLARE(switch_xml_t
) switch_xml_parse_file_simple(const char *file
)
1653 switch_xml_root_t root
;
1655 if ((fd
= open(file
, O_RDONLY
, 0)) > -1) {
1656 if (fstat(fd
, &st
) == -1 || !st
.st_size
) {
1661 m
= switch_must_malloc(st
.st_size
);
1663 if (!(0 < (l
= read(fd
, m
, st
.st_size
)))) {
1669 if (!(root
= (switch_xml_root_t
)switch_xml_parse_str((char*)m
, l
))) {
1683 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_ERROR
, "Error Parsing File [%s]\n", file
);
1688 SWITCH_DECLARE(switch_xml_t
) switch_xml_parse_file(const char *file
)
1691 FILE *write_fd
= NULL
;
1692 switch_xml_t xml
= NULL
;
1693 char *new_file
= NULL
;
1694 char *new_file_tmp
= NULL
;
1695 const char *abs
, *absw
;
1697 abs
= strrchr(file
, '/');
1698 absw
= strrchr(file
, '\\');
1700 abs
> absw
? abs
++ : (abs
= ++absw
);
1705 switch_mutex_lock(FILE_LOCK
);
1707 if (!(new_file
= switch_mprintf("%s%s%s.fsxml", SWITCH_GLOBAL_dirs
.log_dir
, SWITCH_PATH_SEPARATOR
, abs
))) {
1711 if (!(new_file_tmp
= switch_mprintf("%s%s%s.fsxml.tmp", SWITCH_GLOBAL_dirs
.log_dir
, SWITCH_PATH_SEPARATOR
, abs
))) {
1715 if ((write_fd
= fopen(new_file_tmp
, "w+")) == NULL
) {
1719 setvbuf(write_fd
, (char *) NULL
, _IOFBF
, 65536);
1721 if (preprocess(SWITCH_GLOBAL_dirs
.conf_dir
, file
, write_fd
, 0) > -1) {
1726 if ( rename(new_file_tmp
,new_file
) ) {
1730 if ((fd
= open(new_file
, O_RDONLY
, 0)) > -1) {
1731 if ((xml
= switch_xml_parse_fd(fd
))) {
1732 if (strcmp(abs
, SWITCH_GLOBAL_filenames
.conf_name
)) {
1733 xml
->free_path
= new_file
;
1744 switch_mutex_unlock(FILE_LOCK
);
1751 switch_safe_free(new_file_tmp
);
1752 switch_safe_free(new_file
);
1757 SWITCH_DECLARE(switch_status_t
) switch_xml_locate(const char *section
,
1758 const char *tag_name
,
1759 const char *key_name
,
1760 const char *key_value
,
1761 switch_xml_t
*root
, switch_xml_t
*node
, switch_event_t
*params
, switch_bool_t clone
)
1763 switch_xml_t conf
= NULL
;
1764 switch_xml_t tag
= NULL
;
1765 switch_xml_t xml
= NULL
;
1766 switch_xml_binding_t
*binding
;
1768 switch_xml_section_t sections
= BINDINGS
? switch_xml_parse_section_string(section
) : 0;
1770 switch_thread_rwlock_rdlock(B_RWLOCK
);
1772 for (binding
= BINDINGS
; binding
; binding
= binding
->next
) {
1773 if (binding
->sections
&& !(sections
& binding
->sections
)) {
1777 if ((xml
= binding
->function(section
, tag_name
, key_name
, key_value
, params
, binding
->user_data
))) {
1778 const char *err
= NULL
;
1780 err
= switch_xml_error(xml
);
1782 if ((conf
= switch_xml_find_child(xml
, "section", "name", "result"))) {
1786 if ((p
= switch_xml_child(conf
, "result"))) {
1787 aname
= switch_xml_attr(p
, "status");
1788 if (aname
&& !strcasecmp(aname
, "not found")) {
1789 switch_xml_free(xml
);
1797 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_ERROR
, "Error[%s]\n", err
);
1798 switch_xml_free(xml
);
1803 switch_thread_rwlock_unlock(B_RWLOCK
);
1807 if (!(xml
= switch_xml_root())) {
1810 return SWITCH_STATUS_FALSE
;
1814 if ((conf
= switch_xml_find_child(xml
, "section", "name", section
)) && (tag
= switch_xml_find_child(conf
, tag_name
, key_name
, key_value
))) {
1816 char *x
= switch_xml_toxml(tag
, SWITCH_FALSE
);
1818 *node
= *root
= switch_xml_parse_str_dynamic(x
, SWITCH_FALSE
); /* x will be free()'d in switch_xml_free() */
1819 switch_xml_free(xml
);
1824 return SWITCH_STATUS_SUCCESS
;
1826 switch_xml_free(xml
);
1836 return SWITCH_STATUS_FALSE
;
1839 SWITCH_DECLARE(switch_status_t
) switch_xml_locate_domain(const char *domain_name
, switch_event_t
*params
, switch_xml_t
*root
, switch_xml_t
*domain
)
1841 switch_event_t
*my_params
= NULL
;
1842 switch_status_t status
;
1846 switch_event_create(&my_params
, SWITCH_EVENT_REQUEST_PARAMS
);
1847 switch_assert(my_params
);
1848 switch_event_add_header_string(my_params
, SWITCH_STACK_BOTTOM
, "domain", domain_name
);
1852 status
= switch_xml_locate("directory", "domain", "name", domain_name
, root
, domain
, params
, SWITCH_FALSE
);
1854 switch_event_destroy(&my_params
);
1859 SWITCH_DECLARE(switch_status_t
) switch_xml_locate_group(const char *group_name
,
1860 const char *domain_name
,
1861 switch_xml_t
*root
, switch_xml_t
*domain
, switch_xml_t
*group
, switch_event_t
*params
)
1863 switch_status_t status
= SWITCH_STATUS_FALSE
;
1864 switch_event_t
*my_params
= NULL
;
1865 switch_xml_t groups
= NULL
;
1872 switch_event_create(&my_params
, SWITCH_EVENT_REQUEST_PARAMS
);
1873 switch_assert(my_params
);
1878 switch_event_add_header_string(params
, SWITCH_STACK_BOTTOM
, "group_name", group_name
);
1882 switch_event_add_header_string(params
, SWITCH_STACK_BOTTOM
, "domain", domain_name
);
1885 if ((status
= switch_xml_locate_domain(domain_name
, params
, root
, domain
)) != SWITCH_STATUS_SUCCESS
) {
1889 status
= SWITCH_STATUS_FALSE
;
1891 if ((groups
= switch_xml_child(*domain
, "groups"))) {
1892 if ((*group
= switch_xml_find_child(groups
, "group", "name", group_name
))) {
1893 status
= SWITCH_STATUS_SUCCESS
;
1900 switch_event_destroy(&my_params
);
1906 static switch_status_t
find_user_in_tag(switch_xml_t tag
, const char *ip
, const char *user_name
,
1907 const char *key
, switch_event_t
*params
, switch_xml_t
*user
)
1909 const char *type
= "!pointer";
1912 if (params
&& (val
= switch_event_get_header(params
, "user_type"))) {
1913 if (!strcasecmp(val
, "any")) {
1921 if ((*user
= switch_xml_find_child_multi(tag
, "user", "ip", ip
, "type", type
, NULL
))) {
1922 return SWITCH_STATUS_SUCCESS
;
1927 if (!strcasecmp(key
, "id")) {
1928 if ((*user
= switch_xml_find_child_multi(tag
, "user", key
, user_name
, "number-alias", user_name
, "type", type
, NULL
))) {
1929 return SWITCH_STATUS_SUCCESS
;
1932 if ((*user
= switch_xml_find_child_multi(tag
, "user", key
, user_name
, "type", type
, NULL
))) {
1933 return SWITCH_STATUS_SUCCESS
;
1938 return SWITCH_STATUS_FALSE
;
1942 SWITCH_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
)
1944 switch_xml_t group
= NULL
, groups
= NULL
, users
= NULL
;
1945 switch_status_t status
= SWITCH_STATUS_FALSE
;
1947 if ((groups
= switch_xml_child(domain
, "groups"))) {
1948 for (group
= switch_xml_child(groups
, "group"); group
; group
= group
->next
) {
1949 if ((users
= switch_xml_child(group
, "users"))) {
1950 if ((status
= find_user_in_tag(users
, NULL
, user_name
, "id", NULL
, user
)) == SWITCH_STATUS_SUCCESS
) {
1959 if ((users
= switch_xml_child(domain
, "users"))) {
1960 status
= find_user_in_tag(users
, NULL
, user_name
, "id", NULL
, user
);
1962 status
= find_user_in_tag(domain
, NULL
, user_name
, "id", NULL
, user
);
1970 SWITCH_DECLARE(switch_xml_t
) switch_xml_dup(switch_xml_t xml
)
1972 char *x
= switch_xml_toxml(xml
, SWITCH_FALSE
);
1973 return switch_xml_parse_str_dynamic(x
, SWITCH_FALSE
);
1977 static void do_merge(switch_xml_t in
, switch_xml_t src
, const char *container
, const char *tag_name
)
1979 switch_xml_t itag
, tag
, param
, iparam
, iitag
;
1981 if (!(itag
= switch_xml_child(in
, container
))) {
1982 itag
= switch_xml_add_child_d(in
, container
, 0);
1985 if ((tag
= switch_xml_child(src
, container
))) {
1986 for (param
= switch_xml_child(tag
, tag_name
); param
; param
= param
->next
) {
1987 const char *var
= switch_xml_attr(param
, "name");
1988 const char *val
= switch_xml_attr(param
, "value");
1990 switch_bool_t add_child
= SWITCH_TRUE
;
1992 for (iparam
= switch_xml_child(itag
, tag_name
); iparam
; iparam
= iparam
->next
) {
1993 const char *ivar
= switch_xml_attr(iparam
, "name");
1995 if (var
&& ivar
&& !strcasecmp(var
, ivar
)) {
1996 add_child
= SWITCH_FALSE
;
2002 iitag
= switch_xml_add_child_d(itag
, tag_name
, 0);
2003 switch_xml_set_attr_d(iitag
, "name", var
);
2004 switch_xml_set_attr_d(iitag
, "value", val
);
2012 SWITCH_DECLARE(void) switch_xml_merge_user(switch_xml_t user
, switch_xml_t domain
, switch_xml_t group
)
2014 const char *domain_name
= switch_xml_attr(domain
, "name");
2016 do_merge(user
, group
, "params", "param");
2017 do_merge(user
, group
, "variables", "variable");
2018 do_merge(user
, group
, "profile-variables", "variable");
2019 do_merge(user
, domain
, "params", "param");
2020 do_merge(user
, domain
, "variables", "variable");
2021 do_merge(user
, domain
, "profile-variables", "variable");
2023 if (!zstr(domain_name
)) {
2024 switch_xml_set_attr_d(user
, "domain-name", domain_name
);
2028 SWITCH_DECLARE(uint32_t) switch_xml_clear_user_cache(const char *key
, const char *user_name
, const char *domain_name
)
2030 switch_hash_index_t
*hi
= NULL
;
2033 char mega_key
[1024];
2035 switch_xml_t lookup
;
2036 char *expires_val
= NULL
;
2038 switch_mutex_lock(CACHE_MUTEX
);
2040 if (key
&& user_name
&& !domain_name
) {
2041 domain_name
= switch_core_get_variable("domain");
2044 if (key
&& user_name
&& domain_name
) {
2045 switch_snprintf(mega_key
, sizeof(mega_key
), "%s%s%s", key
, user_name
, domain_name
);
2047 if ((lookup
= switch_core_hash_find(CACHE_HASH
, mega_key
))) {
2048 switch_core_hash_delete(CACHE_HASH
, mega_key
);
2049 if ((expires_val
= switch_core_hash_find(CACHE_EXPIRES_HASH
, mega_key
))) {
2050 switch_core_hash_delete(CACHE_EXPIRES_HASH
, mega_key
);
2054 switch_xml_free(lookup
);
2060 while ((hi
= switch_core_hash_first_iter( CACHE_HASH
, hi
))) {
2061 switch_core_hash_this(hi
, &var
, NULL
, &val
);
2062 switch_xml_free(val
);
2063 switch_core_hash_delete(CACHE_HASH
, var
);
2067 while ((hi
= switch_core_hash_first_iter( CACHE_EXPIRES_HASH
, hi
))) {
2068 switch_core_hash_this(hi
, &var
, NULL
, &val
);
2069 switch_safe_free(val
);
2070 switch_core_hash_delete(CACHE_EXPIRES_HASH
, var
);
2073 switch_safe_free(hi
);
2076 switch_mutex_unlock(CACHE_MUTEX
);
2082 static switch_status_t
switch_xml_locate_user_cache(const char *key
, const char *user_name
, const char *domain_name
, switch_xml_t
*user
)
2084 char mega_key
[1024];
2085 switch_status_t status
= SWITCH_STATUS_FALSE
;
2086 switch_xml_t lookup
;
2088 switch_snprintf(mega_key
, sizeof(mega_key
), "%s%s%s", key
, user_name
, domain_name
);
2090 switch_mutex_lock(CACHE_MUTEX
);
2091 if ((lookup
= switch_core_hash_find(CACHE_HASH
, mega_key
))) {
2092 char *expires_lookup
= NULL
;
2094 if ((expires_lookup
= switch_core_hash_find(CACHE_EXPIRES_HASH
, mega_key
))) {
2095 switch_time_t time_expires
= 0;
2096 switch_time_t time_now
= 0;
2098 time_now
= switch_micro_time_now();
2099 time_expires
= atol(expires_lookup
);
2100 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_DEBUG
, "Cache Info\nTime Now:\t%ld\nExpires:\t%ld\n", (long)time_now
, (long)time_expires
);
2101 if (time_expires
< time_now
) {
2102 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_DEBUG
, "Cache expired for %s@%s, doing fresh lookup\n", user_name
, domain_name
);
2104 *user
= switch_xml_dup(lookup
);
2105 status
= SWITCH_STATUS_SUCCESS
;
2108 *user
= switch_xml_dup(lookup
);
2109 status
= SWITCH_STATUS_SUCCESS
;
2112 switch_mutex_unlock(CACHE_MUTEX
);
2117 static void switch_xml_user_cache(const char *key
, const char *user_name
, const char *domain_name
, switch_xml_t user
, switch_time_t expires
)
2119 char mega_key
[1024];
2120 switch_xml_t lookup
;
2121 char *expires_lookup
;
2123 switch_snprintf(mega_key
, sizeof(mega_key
), "%s%s%s", key
, user_name
, domain_name
);
2125 switch_mutex_lock(CACHE_MUTEX
);
2126 if ((lookup
= switch_core_hash_find(CACHE_HASH
, mega_key
))) {
2127 switch_core_hash_delete(CACHE_HASH
, mega_key
);
2128 switch_xml_free(lookup
);
2130 if ((expires_lookup
= switch_core_hash_find(CACHE_EXPIRES_HASH
, mega_key
))) {
2131 switch_core_hash_delete(CACHE_EXPIRES_HASH
, mega_key
);
2132 switch_safe_free(expires_lookup
);
2135 char *expires_val
= (char *)switch_core_hash_insert_alloc(CACHE_EXPIRES_HASH
, mega_key
, 22);
2136 if (!snprintf(expires_val
, 22, "%ld", (long)expires
)) {
2137 switch_core_hash_delete(CACHE_EXPIRES_HASH
, mega_key
);
2138 switch_safe_free(expires_val
);
2141 switch_core_hash_insert(CACHE_HASH
, mega_key
, switch_xml_dup(user
));
2142 switch_mutex_unlock(CACHE_MUTEX
);
2145 SWITCH_DECLARE(switch_status_t
) switch_xml_locate_user_merged(const char *key
, const char *user_name
, const char *domain_name
,
2146 const char *ip
, switch_xml_t
*user
, switch_event_t
*params
)
2148 switch_xml_t xml
, domain
, group
, x_user
, x_user_dup
;
2149 switch_status_t status
= SWITCH_STATUS_FALSE
;
2151 char *keys
[10] = {0};
2154 if (strchr(key
, ':')) {
2155 kdup
= switch_must_strdup(key
);
2156 nkeys
= switch_split(kdup
, ':', keys
);
2158 keys
[0] = (char *)key
;
2162 for(i
= 0; i
< nkeys
; i
++) {
2163 if ((status
= switch_xml_locate_user_cache(keys
[i
], user_name
, domain_name
, &x_user
)) == SWITCH_STATUS_SUCCESS
) {
2166 } else if ((status
= switch_xml_locate_user(keys
[i
], user_name
, domain_name
, ip
, &xml
, &domain
, &x_user
, &group
, params
)) == SWITCH_STATUS_SUCCESS
) {
2167 const char *cacheable
= NULL
;
2169 x_user_dup
= switch_xml_dup(x_user
);
2170 switch_xml_merge_user(x_user_dup
, domain
, group
);
2172 cacheable
= switch_xml_attr(x_user_dup
, "cacheable");
2173 if (!zstr(cacheable
)) {
2174 switch_time_t expires
= 0;
2175 switch_time_t time_now
= 0;
2177 if (switch_is_number(cacheable
)) {
2178 int cache_ms
= atol(cacheable
);
2179 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_DEBUG
, "caching lookup for user %s@%s for %d milliseconds\n",
2180 user_name
, domain_name
, cache_ms
);
2181 time_now
= switch_micro_time_now();
2182 expires
= time_now
+ (cache_ms
* 1000);
2184 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_DEBUG
, "caching lookup for user %s@%s indefinitely\n", user_name
, domain_name
);
2186 switch_xml_user_cache(keys
[i
], user_name
, domain_name
, x_user_dup
, expires
);
2189 switch_xml_free(xml
);
2194 switch_safe_free(kdup
);
2200 SWITCH_DECLARE(switch_status_t
) switch_xml_locate_user(const char *key
,
2201 const char *user_name
,
2202 const char *domain_name
,
2205 switch_xml_t
*domain
, switch_xml_t
*user
, switch_xml_t
*ingroup
, switch_event_t
*params
)
2207 switch_status_t status
= SWITCH_STATUS_FALSE
;
2208 switch_event_t
*my_params
= NULL
;
2209 switch_xml_t group
= NULL
, groups
= NULL
, users
= NULL
;
2220 switch_event_create(&my_params
, SWITCH_EVENT_REQUEST_PARAMS
);
2221 switch_assert(my_params
);
2225 switch_event_add_header_string(params
, SWITCH_STACK_BOTTOM
, "key", key
);
2228 switch_event_add_header_string(params
, SWITCH_STACK_BOTTOM
, "user", user_name
);
2232 switch_event_add_header_string(params
, SWITCH_STACK_BOTTOM
, "domain", domain_name
);
2236 switch_event_add_header_string(params
, SWITCH_STACK_BOTTOM
, "ip", ip
);
2239 if ((status
= switch_xml_locate_domain(domain_name
, params
, root
, domain
)) != SWITCH_STATUS_SUCCESS
) {
2243 status
= SWITCH_STATUS_FALSE
;
2245 if ((groups
= switch_xml_child(*domain
, "groups"))) {
2246 for (group
= switch_xml_child(groups
, "group"); group
; group
= group
->next
) {
2247 if ((users
= switch_xml_child(group
, "users"))) {
2248 if ((status
= find_user_in_tag(users
, ip
, user_name
, key
, params
, user
)) == SWITCH_STATUS_SUCCESS
) {
2258 if (status
!= SWITCH_STATUS_SUCCESS
) {
2259 if ((users
= switch_xml_child(*domain
, "users"))) {
2260 status
= find_user_in_tag(users
, ip
, user_name
, key
, params
, user
);
2262 status
= find_user_in_tag(*domain
, ip
, user_name
, key
, params
, user
);
2269 switch_event_destroy(&my_params
);
2272 if (status
!= SWITCH_STATUS_SUCCESS
&& *root
) {
2273 switch_xml_free(*root
);
2281 SWITCH_DECLARE(switch_xml_t
) switch_xml_root(void)
2285 switch_mutex_lock(REFLOCK
);
2286 xml
= MAIN_XML_ROOT
;
2288 switch_mutex_unlock(REFLOCK
);
2293 struct destroy_xml
{
2295 switch_memory_pool_t
*pool
;
2298 static void *SWITCH_THREAD_FUNC
destroy_thread(switch_thread_t
*thread
, void *obj
)
2300 struct destroy_xml
*dx
= (struct destroy_xml
*) obj
;
2301 switch_memory_pool_t
*pool
= dx
->pool
;
2302 switch_xml_free(dx
->xml
);
2303 switch_core_destroy_memory_pool(&pool
);
2307 SWITCH_DECLARE(void) switch_xml_free_in_thread(switch_xml_t xml
, int stacksize
)
2309 switch_thread_t
*thread
;
2310 switch_threadattr_t
*thd_attr
;
2311 switch_memory_pool_t
*pool
= NULL
;
2312 struct destroy_xml
*dx
;
2314 switch_core_new_memory_pool(&pool
);
2316 switch_threadattr_create(&thd_attr
, pool
);
2317 switch_threadattr_detach_set(thd_attr
, 1);
2318 /* TBD figure out how much space we need by looking at the xml_t when stacksize == 0 */
2319 switch_threadattr_stacksize_set(thd_attr
, stacksize
);
2321 dx
= switch_core_alloc(pool
, sizeof(*dx
));
2325 switch_thread_create(&thread
, thd_attr
, destroy_thread
, dx
, pool
);
2328 static char not_so_threadsafe_error_buffer
[256] = "";
2330 SWITCH_DECLARE(switch_status_t
) switch_xml_set_root(switch_xml_t new_main
)
2332 switch_xml_t old_root
= NULL
;
2334 switch_mutex_lock(REFLOCK
);
2336 old_root
= MAIN_XML_ROOT
;
2337 MAIN_XML_ROOT
= new_main
;
2338 switch_set_flag(MAIN_XML_ROOT
, SWITCH_XML_ROOT
);
2339 MAIN_XML_ROOT
->refs
++;
2342 if (old_root
->refs
) {
2346 if (!old_root
->refs
) {
2347 switch_xml_free(old_root
);
2351 switch_mutex_unlock(REFLOCK
);
2353 return SWITCH_STATUS_SUCCESS
;
2356 SWITCH_DECLARE(switch_status_t
) switch_xml_set_open_root_function(switch_xml_open_root_function_t func
, void *user_data
)
2359 switch_mutex_lock(XML_LOCK
);
2362 XML_OPEN_ROOT_FUNCTION
= func
;
2363 XML_OPEN_ROOT_FUNCTION_USER_DATA
= user_data
;
2366 switch_mutex_unlock(XML_LOCK
);
2368 return SWITCH_STATUS_SUCCESS
;
2371 SWITCH_DECLARE(switch_xml_t
) switch_xml_open_root(uint8_t reload
, const char **err
)
2373 switch_xml_t root
= NULL
;
2374 switch_event_t
*event
;
2376 switch_mutex_lock(XML_LOCK
);
2378 if (XML_OPEN_ROOT_FUNCTION
) {
2379 root
= XML_OPEN_ROOT_FUNCTION(reload
, err
, XML_OPEN_ROOT_FUNCTION_USER_DATA
);
2381 switch_mutex_unlock(XML_LOCK
);
2385 if (switch_event_create(&event
, SWITCH_EVENT_RELOADXML
) == SWITCH_STATUS_SUCCESS
) {
2386 if (switch_event_fire(&event
) != SWITCH_STATUS_SUCCESS
) {
2387 switch_event_destroy(&event
);
2395 SWITCH_DECLARE_NONSTD(switch_xml_t
) __switch_xml_open_root(uint8_t reload
, const char **err
, void *user_data
)
2397 char path_buf
[1024];
2399 switch_xml_t new_main
, r
= NULL
;
2401 if (MAIN_XML_ROOT
) {
2403 r
= switch_xml_root();
2408 switch_snprintf(path_buf
, sizeof(path_buf
), "%s%s%s", SWITCH_GLOBAL_dirs
.conf_dir
, SWITCH_PATH_SEPARATOR
, SWITCH_GLOBAL_filenames
.conf_name
);
2409 if ((new_main
= switch_xml_parse_file(path_buf
))) {
2410 *err
= switch_xml_error(new_main
);
2411 switch_copy_string(not_so_threadsafe_error_buffer
, *err
, sizeof(not_so_threadsafe_error_buffer
));
2412 *err
= not_so_threadsafe_error_buffer
;
2414 switch_xml_free(new_main
);
2419 switch_xml_set_root(new_main
);
2423 *err
= "Cannot Open log directory or XML Root!";
2428 r
= switch_xml_root();
2436 SWITCH_DECLARE(switch_status_t
) switch_xml_reload(const char **err
)
2438 switch_xml_t xml_root
;
2440 if ((xml_root
= switch_xml_open_root(1, err
))) {
2441 switch_xml_free(xml_root
);
2442 return SWITCH_STATUS_SUCCESS
;
2445 return SWITCH_STATUS_GENERR
;
2448 SWITCH_DECLARE(switch_status_t
) switch_xml_init(switch_memory_pool_t
*pool
, const char **err
)
2451 XML_MEMORY_POOL
= pool
;
2454 switch_mutex_init(&CACHE_MUTEX
, SWITCH_MUTEX_NESTED
, XML_MEMORY_POOL
);
2455 switch_mutex_init(&XML_LOCK
, SWITCH_MUTEX_NESTED
, XML_MEMORY_POOL
);
2456 switch_mutex_init(&REFLOCK
, SWITCH_MUTEX_NESTED
, XML_MEMORY_POOL
);
2457 switch_mutex_init(&FILE_LOCK
, SWITCH_MUTEX_NESTED
, XML_MEMORY_POOL
);
2458 switch_core_hash_init(&CACHE_HASH
);
2459 switch_core_hash_init(&CACHE_EXPIRES_HASH
);
2461 switch_thread_rwlock_create(&B_RWLOCK
, XML_MEMORY_POOL
);
2463 assert(pool
!= NULL
);
2465 if ((xml
= switch_xml_open_root(FALSE
, err
))) {
2466 switch_xml_free(xml
);
2467 return SWITCH_STATUS_SUCCESS
;
2469 return SWITCH_STATUS_FALSE
;
2473 SWITCH_DECLARE(switch_status_t
) switch_xml_destroy(void)
2475 switch_status_t status
= SWITCH_STATUS_FALSE
;
2478 switch_mutex_lock(XML_LOCK
);
2479 switch_mutex_lock(REFLOCK
);
2481 if (MAIN_XML_ROOT
) {
2482 switch_xml_t xml
= MAIN_XML_ROOT
;
2483 MAIN_XML_ROOT
= NULL
;
2484 switch_xml_free(xml
);
2485 status
= SWITCH_STATUS_SUCCESS
;
2488 switch_mutex_unlock(XML_LOCK
);
2489 switch_mutex_unlock(REFLOCK
);
2491 switch_xml_clear_user_cache(NULL
, NULL
, NULL
);
2493 switch_core_hash_destroy(&CACHE_HASH
);
2494 switch_core_hash_destroy(&CACHE_EXPIRES_HASH
);
2499 SWITCH_DECLARE(switch_xml_t
) switch_xml_open_cfg(const char *file_path
, switch_xml_t
*node
, switch_event_t
*params
)
2501 switch_xml_t xml
= NULL
, cfg
= NULL
;
2505 assert(MAIN_XML_ROOT
!= NULL
);
2507 if (switch_xml_locate("configuration", "configuration", "name", file_path
, &xml
, &cfg
, params
, SWITCH_FALSE
) == SWITCH_STATUS_SUCCESS
) {
2515 /* Encodes ampersand sequences appending the results to *dst, reallocating *dst
2516 if length exceeds max. a is non-zero for attribute encoding. Returns *dst */
2517 static char *switch_xml_ampencode(const char *s
, switch_size_t len
, char **dst
, switch_size_t
*dlen
, switch_size_t
*max
, short a
, switch_bool_t use_utf8_encoding
)
2519 const char *e
= NULL
;
2521 int expecting_x_utf_8_char
= 0;
2522 int unicode_char
= 0x000000;
2532 while (*dlen
+ 10 > *max
) {
2533 *dst
= (char *) switch_must_realloc(*dst
, *max
+= SWITCH_XML_BUFSIZE
);
2540 (*dst
)[(*dlen
)++] = *s
;
2546 *dlen
+= sprintf(*dst
+ *dlen
, "&");
2549 if (*(s
+ 1) == '!') {
2550 (*dst
)[(*dlen
)++] = *s
;
2554 *dlen
+= sprintf(*dst
+ *dlen
, "<");
2557 *dlen
+= sprintf(*dst
+ *dlen
, ">");
2560 *dlen
+= sprintf(*dst
+ *dlen
, (a
) ? """ : "\"");
2563 *dlen
+= sprintf(*dst
+ *dlen
, (a
) ? "
" : "\n");
2566 *dlen
+= sprintf(*dst
+ *dlen
, (a
) ? "	" : "\t");
2569 *dlen
+= sprintf(*dst
+ *dlen
, "
");
2572 if (use_utf8_encoding
&& expecting_x_utf_8_char
== 0 && ((*s
>> 8) & 0x01)) {
2574 for (;num
<4;num
++) {
2575 if (! ((*s
>> (7-num
)) & 0x01)) {
2581 unicode_char
= *s
& 0x1f;
2584 unicode_char
= *s
& 0x0f;
2587 unicode_char
= *s
& 0x07;
2590 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_WARNING
, "Invalid UTF-8 Initial charactere, skip it\n");
2594 expecting_x_utf_8_char
= num
- 1;
2596 } else if (use_utf8_encoding
&& expecting_x_utf_8_char
> 0) {
2597 if (((*s
>> 6) & 0x03) == 0x2) {
2599 unicode_char
= unicode_char
<< 6;
2600 unicode_char
= unicode_char
| (*s
& 0x3f);
2602 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_WARNING
, "Invalid UTF-8 character to ampersand, skip it\n");
2603 expecting_x_utf_8_char
= 0;
2606 expecting_x_utf_8_char
--;
2607 if (expecting_x_utf_8_char
== 0) {
2608 *dlen
+= sprintf(*dst
+ *dlen
, "&#x%X;", unicode_char
);
2611 (*dst
)[(*dlen
)++] = *s
;
2619 #define XML_INDENT " "
2620 /* Recursively converts each tag to xml appending it to *s. Reallocates *s if
2621 its length exceeds max. start is the location of the previous tag in the
2622 parent tag's character content. Returns *s. */
2623 static char *switch_xml_toxml_r(switch_xml_t xml
, char **s
, switch_size_t
*len
, switch_size_t
*max
, switch_size_t start
, char ***attr
, uint32_t *count
, int isroot
, switch_bool_t use_utf8_encoding
)
2639 if (!isroot
&& xml
->parent
) {
2640 txt
= (char *) xml
->parent
->txt
;
2643 /* parent character content up to this tag */
2644 *s
= switch_xml_ampencode(txt
+ start
, xml
->off
- start
, s
, len
, max
, 0, use_utf8_encoding
);
2646 while (*len
+ strlen(xml
->name
) + 5 + (strlen(XML_INDENT
) * (*count
)) + 1 > *max
) { /* reallocate s */
2647 *s
= (char *) switch_must_realloc(*s
, *max
+= SWITCH_XML_BUFSIZE
);
2650 if (*len
&& *(*s
+ (*len
) - 1) == '>') {
2651 *len
+= sprintf(*s
+ *len
, "\n"); /* indent */
2653 for (lcount
= 0; lcount
< *count
; lcount
++) {
2654 *len
+= sprintf(*s
+ *len
, "%s", XML_INDENT
); /* indent */
2657 *len
+= sprintf(*s
+ *len
, "<%s", xml
->name
); /* open tag */
2658 for (i
= 0; xml
->attr
[i
]; i
+= 2) { /* tag attributes */
2659 if (switch_xml_attr(xml
, xml
->attr
[i
]) != xml
->attr
[i
+ 1])
2661 while (*len
+ strlen(xml
->attr
[i
]) + 7 + (strlen(XML_INDENT
) * (*count
)) > *max
) { /* reallocate s */
2662 *s
= (char *) switch_must_realloc(*s
, *max
+= SWITCH_XML_BUFSIZE
);
2665 *len
+= sprintf(*s
+ *len
, " %s=\"", xml
->attr
[i
]);
2666 switch_xml_ampencode(xml
->attr
[i
+ 1], 0, s
, len
, max
, 1, use_utf8_encoding
);
2667 *len
+= sprintf(*s
+ *len
, "\"");
2670 for (i
= 0; attr
[i
] && strcmp(attr
[i
][0], xml
->name
); i
++);
2671 for (j
= 1; attr
[i
] && attr
[i
][j
]; j
+= 3) { /* default attributes */
2672 if (!attr
[i
][j
+ 1] || switch_xml_attr(xml
, attr
[i
][j
]) != attr
[i
][j
+ 1])
2673 continue; /* skip duplicates and non-values */
2674 while (*len
+ strlen(attr
[i
][j
]) + 8 + (strlen(XML_INDENT
) * (*count
)) > *max
) { /* reallocate s */
2675 *s
= (char *) switch_must_realloc(*s
, *max
+= SWITCH_XML_BUFSIZE
);
2678 *len
+= sprintf(*s
+ *len
, " %s=\"", attr
[i
][j
]);
2679 switch_xml_ampencode(attr
[i
][j
+ 1], 0, s
, len
, max
, 1, use_utf8_encoding
);
2680 *len
+= sprintf(*s
+ *len
, "\"");
2683 *len
+= sprintf(*s
+ *len
, (xml
->child
|| xml
->txt
) ? ">" : "/>\n");
2687 *s
= switch_xml_toxml_r(xml
->child
, s
, len
, max
, 0, attr
, count
, 0, use_utf8_encoding
);
2690 *s
= switch_xml_ampencode(xml
->txt
, 0, s
, len
, max
, 0, use_utf8_encoding
); /* data */
2693 while (*len
+ strlen(xml
->name
) + 5 + (strlen(XML_INDENT
) * (*count
)) > *max
) { /* reallocate s */
2694 *s
= (char *) switch_must_realloc(*s
, *max
+= SWITCH_XML_BUFSIZE
);
2697 if (xml
->child
|| xml
->txt
) {
2698 if (*(*s
+ (*len
) - 1) == '\n') {
2699 for (lcount
= 0; lcount
< *count
; lcount
++) {
2700 *len
+= sprintf(*s
+ *len
, "%s", XML_INDENT
); /* indent */
2703 *len
+= sprintf(*s
+ (*len
), "</%s>\n", xml
->name
); /* close tag */
2706 while (txt
[off
] && off
< xml
->off
)
2707 off
++; /* make sure off is within bounds */
2709 if (!isroot
&& xml
->ordered
) {
2714 return switch_xml_toxml_r(xml->ordered, s, len, max, off, attr, count, use_utf8_encoding);
2719 return switch_xml_ampencode(txt
+ off
, 0, s
, len
, max
, 0, use_utf8_encoding
);
2723 SWITCH_DECLARE(char *) switch_xml_toxml_nolock_ex(switch_xml_t xml
, switch_bool_t prn_header
, switch_bool_t use_utf8_encoding
)
2725 char *s
= (char *) switch_must_malloc(SWITCH_XML_BUFSIZE
);
2727 return switch_xml_toxml_buf_ex(xml
, s
, SWITCH_XML_BUFSIZE
, 0, prn_header
, use_utf8_encoding
);
2730 SWITCH_DECLARE(char *) switch_xml_toxml_ex(switch_xml_t xml
, switch_bool_t prn_header
, switch_bool_t use_utf8_encoding
)
2734 s
= (char *) switch_must_malloc(SWITCH_XML_BUFSIZE
);
2736 r
= switch_xml_toxml_buf_ex(xml
, s
, SWITCH_XML_BUFSIZE
, 0, prn_header
, use_utf8_encoding
);
2741 SWITCH_DECLARE(char *) switch_xml_tohtml_ex(switch_xml_t xml
, switch_bool_t prn_header
, switch_bool_t use_utf8_encoding
)
2744 switch_size_t rlen
= 0;
2745 switch_size_t len
= SWITCH_XML_BUFSIZE
;
2747 s
= (char *) switch_must_malloc(SWITCH_XML_BUFSIZE
);
2748 h
= (char *) switch_must_malloc(SWITCH_XML_BUFSIZE
);
2750 r
= switch_xml_toxml_buf_ex(xml
, s
, SWITCH_XML_BUFSIZE
, 0, prn_header
, use_utf8_encoding
);
2751 h
= switch_xml_ampencode(r
, 0, &h
, &rlen
, &len
, 1, use_utf8_encoding
);
2752 switch_safe_free(r
);
2756 /* converts a switch_xml structure back to xml, returning a string of xml data that
2758 SWITCH_DECLARE(char *) switch_xml_toxml_buf_ex(switch_xml_t xml
, char *buf
, switch_size_t buflen
, switch_size_t offset
, switch_bool_t prn_header
, switch_bool_t use_utf8_encoding
)
2760 switch_xml_t p
= (xml
) ? xml
->parent
: NULL
;
2761 switch_xml_root_t root
= (switch_xml_root_t
) xml
;
2762 switch_size_t len
= 0, max
= buflen
;
2772 len
+= sprintf(s
+ len
, "<?xml version=\"1.0\"?>\n");
2775 if (!xml
|| !xml
->name
) {
2776 return (char *) switch_must_realloc(s
, len
+ 1);
2779 while (root
->xml
.parent
) {
2780 root
= (switch_xml_root_t
) root
->xml
.parent
; /* root tag */
2783 for (i
= 0; !p
&& root
->pi
[i
]; i
++) { /* pre-root processing instructions */
2784 for (k
= 2; root
->pi
[i
][k
- 1]; k
++);
2785 for (j
= 1; (n
= root
->pi
[i
][j
]); j
++) {
2786 if (root
->pi
[i
][k
][j
- 1] == '>') {
2787 continue; /* not pre-root */
2789 while (len
+ strlen(t
= root
->pi
[i
][0]) + strlen(n
) + 7 > max
) {
2790 s
= (char *) switch_must_realloc(s
, max
+= SWITCH_XML_BUFSIZE
);
2792 len
+= sprintf(s
+ len
, "<?%s%s%s?>", t
, *n
? " " : "", n
);
2796 s
= switch_xml_toxml_r(xml
, &s
, &len
, &max
, 0, root
->attr
, &count
, 1, use_utf8_encoding
);
2798 for (i
= 0; !p
&& root
->pi
[i
]; i
++) { /* post-root processing instructions */
2799 for (k
= 2; root
->pi
[i
][k
- 1]; k
++);
2800 for (j
= 1; (n
= root
->pi
[i
][j
]); j
++) {
2801 if (root
->pi
[i
][k
][j
- 1] == '<') {
2802 continue; /* not post-root */
2804 while (len
+ strlen(t
= root
->pi
[i
][0]) + strlen(n
) + 7 > max
) {
2805 s
= (char *) switch_must_realloc(s
, max
+= SWITCH_XML_BUFSIZE
);
2807 len
+= sprintf(s
+ len
, "\n<?%s%s%s?>", t
, *n
? " " : "", n
);
2811 return (char *) switch_must_realloc(s
, len
+ 1);
2814 /* free the memory allocated for the switch_xml structure */
2815 SWITCH_DECLARE(void) switch_xml_free(switch_xml_t xml
)
2817 switch_xml_root_t root
;
2820 switch_xml_t orig_xml
;
2824 root
= (switch_xml_root_t
) xml
;
2829 if (switch_test_flag(xml
, SWITCH_XML_ROOT
)) {
2830 switch_mutex_lock(REFLOCK
);
2836 switch_mutex_unlock(REFLOCK
);
2843 if (xml
->free_path
) {
2844 if (unlink(xml
->free_path
) != 0) {
2845 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_WARNING
, "Failed to delete file [%s]\n", xml
->free_path
);
2847 switch_safe_free(xml
->free_path
);
2850 switch_xml_free(xml
->child
);
2851 /*switch_xml_free(xml->ordered); */
2853 if (!xml
->parent
) { /* free root tag allocations */
2854 #if (_MSC_VER >= 1400) // VC8+
2855 __analysis_assume(sizeof(root
->ent
) > 44); /* tail recursion confuses code analysis */
2857 for (i
= 10; root
->ent
[i
]; i
+= 2) /* 0 - 9 are default entities (<>&"') */
2858 if ((s
= root
->ent
[i
+ 1]) < root
->s
|| s
> root
->e
)
2860 free(root
->ent
); /* free list of general entities */
2862 for (i
= 0; (a
= root
->attr
[i
]); i
++) {
2863 for (j
= 1; a
[j
++]; j
+= 2) /* free malloced attribute values */
2864 if (a
[j
] && (a
[j
] < root
->s
|| a
[j
] > root
->e
))
2869 free(root
->attr
); /* free default attribute list */
2871 for (i
= 0; root
->pi
[i
]; i
++) {
2872 for (j
= 1; root
->pi
[i
][j
]; j
++);
2873 free(root
->pi
[i
][j
+ 1]);
2877 free(root
->pi
); /* free processing instructions */
2879 if (root
->dynamic
== 1)
2880 free(root
->m
); /* malloced xml data */
2882 free(root
->u
); /* utf8 conversion */
2885 switch_xml_free_attr(xml
->attr
); /* tag attributes */
2886 if ((xml
->flags
& SWITCH_XML_TXTM
))
2887 free(xml
->txt
); /* character content */
2888 if ((xml
->flags
& SWITCH_XML_NAMEM
))
2889 free(xml
->name
); /* tag name */
2899 /* return parser error message or empty string if none */
2900 SWITCH_DECLARE(const char *) switch_xml_error(switch_xml_t xml
)
2902 while (xml
&& xml
->parent
)
2903 xml
= xml
->parent
; /* find root tag */
2904 return (xml
) ? ((switch_xml_root_t
) xml
)->err
: "";
2907 /* returns a new empty switch_xml structure with the given root tag name */
2908 SWITCH_DECLARE(switch_xml_t
) switch_xml_new(const char *name
)
2910 static const char *ent
[] = { "lt;", "<", "gt;", ">", "quot;", """,
2911 "apos;", "'", "amp;", "&", NULL
2913 switch_xml_root_t root
= (switch_xml_root_t
) switch_must_malloc(sizeof(struct switch_xml_root
));
2915 memset(root
, '\0', sizeof(struct switch_xml_root
));
2916 root
->xml
.name
= (char *) name
;
2917 root
->cur
= &root
->xml
;
2918 strcpy(root
->err
, root
->xml
.txt
= (char *) "");
2919 root
->ent
= (char **) memcpy(switch_must_malloc(sizeof(ent
)), ent
, sizeof(ent
));
2920 root
->attr
= root
->pi
= (char ***) (root
->xml
.attr
= SWITCH_XML_NIL
);
2924 /* inserts an existing tag into a switch_xml structure */
2925 SWITCH_DECLARE(switch_xml_t
) switch_xml_insert(switch_xml_t xml
, switch_xml_t dest
, switch_size_t off
)
2927 switch_xml_t cur
, prev
, head
;
2929 xml
->next
= xml
->sibling
= xml
->ordered
= NULL
;
2933 if ((head
= dest
->child
)) { /* already have sub tags */
2934 if (head
->off
<= off
) { /* not first subtag */
2935 for (cur
= head
; cur
->ordered
&& cur
->ordered
->off
<= off
; cur
= cur
->ordered
);
2936 xml
->ordered
= cur
->ordered
;
2938 } else { /* first subtag */
2939 xml
->ordered
= head
;
2943 for (cur
= head
, prev
= NULL
; cur
&& strcmp(cur
->name
, xml
->name
); prev
= cur
, cur
= cur
->sibling
); /* find tag type */
2944 if (cur
&& cur
->off
<= off
) { /* not first of type */
2945 while (cur
->next
&& cur
->next
->off
<= off
)
2947 xml
->next
= cur
->next
;
2949 } else { /* first tag of this type */
2951 prev
->sibling
= cur
->sibling
; /* remove old first */
2952 xml
->next
= cur
; /* old first tag is now next */
2953 for (cur
= head
, prev
= NULL
; cur
&& cur
->off
<= off
; prev
= cur
, cur
= cur
->sibling
); /* new sibling insert point */
2956 prev
->sibling
= xml
;
2959 dest
->child
= xml
; /* only sub tag */
2964 /* Adds a child tag. off is the offset of the child tag relative to the start
2965 of the parent tag's character content. Returns the child tag */
2966 SWITCH_DECLARE(switch_xml_t
) switch_xml_add_child(switch_xml_t xml
, const char *name
, switch_size_t off
)
2972 child
= (switch_xml_t
) switch_must_malloc(sizeof(struct switch_xml
));
2974 memset(child
, '\0', sizeof(struct switch_xml
));
2975 child
->name
= (char *) name
;
2976 child
->attr
= SWITCH_XML_NIL
;
2978 child
->parent
= xml
;
2979 child
->txt
= (char *) "";
2981 return switch_xml_insert(child
, xml
, off
);
2984 /* Adds a child tag. off is the offset of the child tag relative to the start
2985 of the parent tag's character content. Returns the child tag */
2986 SWITCH_DECLARE(switch_xml_t
) switch_xml_add_child_d(switch_xml_t xml
, const char *name
, switch_size_t off
)
2988 if (!xml
) return NULL
;
2989 return switch_xml_set_flag(switch_xml_add_child(xml
, strdup(name
), off
), SWITCH_XML_NAMEM
);
2992 /* sets the character content for the given tag and returns the tag */
2993 SWITCH_DECLARE(switch_xml_t
) switch_xml_set_txt(switch_xml_t xml
, const char *txt
)
2997 if (xml
->flags
& SWITCH_XML_TXTM
)
2998 free(xml
->txt
); /* existing txt was malloced */
2999 xml
->flags
&= ~SWITCH_XML_TXTM
;
3000 xml
->txt
= (char *) txt
;
3004 /* sets the character content for the given tag and returns the tag */
3005 SWITCH_DECLARE(switch_xml_t
) switch_xml_set_txt_d(switch_xml_t xml
, const char *txt
)
3007 if (!xml
) return NULL
;
3008 return switch_xml_set_flag(switch_xml_set_txt(xml
, strdup(txt
)), SWITCH_XML_TXTM
);
3011 /* Sets the given tag attribute or adds a new attribute if not found. A value
3012 of NULL will remove the specified attribute. Returns the tag given */
3013 SWITCH_DECLARE(switch_xml_t
) switch_xml_set_attr(switch_xml_t xml
, const char *name
, const char *value
)
3019 while (xml
->attr
[l
] && strcmp(xml
->attr
[l
], name
))
3021 if (!xml
->attr
[l
]) { /* not found, add as new attribute */
3023 return xml
; /* nothing to do */
3024 if (xml
->attr
== SWITCH_XML_NIL
) { /* first attribute */
3025 xml
->attr
= (char **) switch_must_malloc(4 * sizeof(char *));
3026 xml
->attr
[l
+ 1] = switch_must_strdup(""); /* empty list of malloced names/vals */
3028 xml
->attr
= (char **) switch_must_realloc(xml
->attr
, (l
+ 4) * sizeof(char *));
3031 xml
->attr
[l
] = (char *) name
; /* set attribute name */
3032 xml
->attr
[l
+ 2] = NULL
; /* null terminate attribute list */
3033 xml
->attr
[l
+ 3] = (char *) switch_must_realloc(xml
->attr
[l
+ 1], (c
= (int) strlen(xml
->attr
[l
+ 1])) + 2);
3034 strcpy(xml
->attr
[l
+ 3] + c
, " "); /* set name/value as not malloced */
3035 if (xml
->flags
& SWITCH_XML_DUP
)
3036 xml
->attr
[l
+ 3][c
] = SWITCH_XML_NAMEM
;
3037 c
= l
+ 2; /* end of attribute list */
3039 for (c
= l
; xml
->attr
[c
]; c
+= 2); /* find end of attribute list */
3041 if (xml
->flags
& SWITCH_XML_DUP
)
3042 free((char*)name
); /* name was strduped */
3043 if (xml
->attr
[c
+ 1][l
/ 2] & SWITCH_XML_TXTM
)
3044 free(xml
->attr
[l
+ 1]); /* old val */
3047 if (xml
->flags
& SWITCH_XML_DUP
)
3048 xml
->attr
[c
+ 1][l
/ 2] |= SWITCH_XML_TXTM
;
3050 xml
->attr
[c
+ 1][l
/ 2] &= ~SWITCH_XML_TXTM
;
3053 xml
->attr
[l
+ 1] = (char *) value
; /* set attribute value */
3054 else { /* remove attribute */
3055 if (xml
->attr
[c
+ 1][l
/ 2] & SWITCH_XML_NAMEM
)
3059 memmove(xml
->attr
+ l
, xml
->attr
+ l
+ 2, (c
- l
+ 2) * sizeof(char*));
3060 xml
->attr
= (char**)switch_must_realloc(xml
->attr
, (c
+ 2) * sizeof(char*));
3061 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 */
3062 xml
->attr
[c
+ 1][c
/ 2] = '\0';
3064 /* last attribute removed, reset attribute list */
3067 xml
->attr
= SWITCH_XML_NIL
;
3070 xml
->flags
&= ~SWITCH_XML_DUP
; /* clear strdup() flag */
3075 /* Sets the given tag attribute or adds a new attribute if not found. A value
3076 of NULL will remove the specified attribute. Returns the tag given */
3077 SWITCH_DECLARE(switch_xml_t
) switch_xml_set_attr_d(switch_xml_t xml
, const char *name
, const char *value
)
3079 if (!xml
) return NULL
;
3080 return switch_xml_set_attr(switch_xml_set_flag(xml
, SWITCH_XML_DUP
), switch_must_strdup(name
), switch_must_strdup(switch_str_nil(value
)));
3083 /* Sets the given tag attribute or adds a new attribute if not found. A value
3084 of NULL will remove the specified attribute. Returns the tag given */
3085 SWITCH_DECLARE(switch_xml_t
) switch_xml_set_attr_d_buf(switch_xml_t xml
, const char *name
, const char *value
)
3087 if (!xml
) return NULL
;
3088 return switch_xml_set_attr(switch_xml_set_flag(xml
, SWITCH_XML_DUP
), switch_must_strdup(name
), switch_must_strdup(value
));
3091 /* sets a flag for the given tag and returns the tag */
3092 SWITCH_DECLARE(switch_xml_t
) switch_xml_set_flag(switch_xml_t xml
, switch_xml_flag_t flag
)
3099 /* removes a tag along with its subtags without freeing its memory */
3100 SWITCH_DECLARE(switch_xml_t
) switch_xml_cut(switch_xml_t xml
)
3105 return NULL
; /* nothing to do */
3107 xml
->next
->sibling
= xml
->sibling
; /* patch sibling list */
3109 if (xml
->parent
) { /* not root tag */
3110 cur
= xml
->parent
->child
; /* find head of subtag list */
3112 xml
->parent
->child
= xml
->ordered
; /* first subtag */
3113 else { /* not first subtag */
3114 while (cur
->ordered
!= xml
)
3116 cur
->ordered
= cur
->ordered
->ordered
; /* patch ordered list */
3118 cur
= xml
->parent
->child
; /* go back to head of subtag list */
3119 if (strcmp(cur
->name
, xml
->name
)) { /* not in first sibling list */
3120 while (strcmp(cur
->sibling
->name
, xml
->name
))
3122 if (cur
->sibling
== xml
) { /* first of a sibling list */
3123 cur
->sibling
= (xml
->next
) ? xml
->next
: cur
->sibling
->sibling
;
3125 cur
= cur
->sibling
; /* not first of a sibling list */
3128 while (cur
->next
&& cur
->next
!= xml
)
3131 cur
->next
= cur
->next
->next
; /* patch next list */
3134 xml
->ordered
= xml
->sibling
= xml
->next
= NULL
; /* prevent switch_xml_free() from clobbering ordered list */
3138 SWITCH_DECLARE(int) switch_xml_std_datetime_check(switch_xml_t xcond
, int *offset
, const char *tzname
)
3141 const char *xdt
= switch_xml_attr(xcond
, "date-time");
3142 const char *xyear
= switch_xml_attr(xcond
, "year");
3143 const char *xyday
= switch_xml_attr(xcond
, "yday");
3144 const char *xmon
= switch_xml_attr(xcond
, "mon");
3145 const char *xmday
= switch_xml_attr(xcond
, "mday");
3146 const char *xweek
= switch_xml_attr(xcond
, "week");
3147 const char *xmweek
= switch_xml_attr(xcond
, "mweek");
3148 const char *xwday
= switch_xml_attr(xcond
, "wday");
3149 const char *xhour
= switch_xml_attr(xcond
, "hour");
3150 const char *xminute
= switch_xml_attr(xcond
, "minute");
3151 const char *xminday
= switch_xml_attr(xcond
, "minute-of-day");
3152 const char *xtod
= switch_xml_attr(xcond
, "time-of-day");
3153 const char *tzoff
= switch_xml_attr(xcond
, "tz-offset");
3154 const char *isdst
= switch_xml_attr(xcond
, "dst");
3156 int loffset
= -1000;
3157 int eoffset
= -1000;
3159 switch_time_t ts
= switch_micro_time_now();
3160 int time_match
= -1;
3161 switch_time_exp_t tm
, tm2
;
3164 dst
= switch_true(isdst
);
3167 if (!zstr(tzoff
) && switch_is_number(tzoff
)) {
3168 loffset
= atoi(tzoff
);
3171 switch_time_exp_lt(&tm2
, ts
);
3175 switch_time_exp_tz(&tm
, ts
, *offset
* 3600);
3176 } else if (!zstr(tzname
)) {
3177 switch_time_exp_tz_name(tzname
, &tm
, ts
);
3182 if (eoffset
== -1000) {
3183 eoffset
= tm
.tm_gmtoff
/ 3600;
3186 if (loffset
== -1000) {
3191 if (time_match
&& tzoff
) {
3192 time_match
= loffset
== eoffset
;
3193 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_DEBUG9
,
3194 "XML DateTime Check: TZOFFSET[%d] == %d (%s)\n", eoffset
, loffset
, time_match
? "PASS" : "FAIL");
3198 if (time_match
&& dst
> -1) {
3199 time_match
= (tm2
.tm_isdst
> 0 && dst
> 0);
3200 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_DEBUG9
,
3201 "XML DateTime Check: DST[%s] == %s (%s)\n",
3202 tm2
.tm_isdst
> 0 ? "true" : "false", dst
> 0 ? "true" : "false", time_match
? "PASS" : "FAIL");
3206 if (time_match
&& xdt
) {
3208 switch_size_t retsize
;
3209 switch_strftime(tmpdate
, &retsize
, sizeof(tmpdate
), "%Y-%m-%d %H:%M:%S", &tm
);
3210 time_match
= switch_fulldate_cmp(xdt
, &ts
);
3211 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_DEBUG
,
3212 "XML DateTime Check: date time[%s] =~ %s (%s)\n", tmpdate
, xdt
, time_match
? "PASS" : "FAIL");
3215 if (time_match
&& xyear
) {
3216 int test
= tm
.tm_year
+ 1900;
3217 time_match
= switch_number_cmp(xyear
, test
);
3218 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_DEBUG9
,
3219 "XML DateTime Check: year[%d] =~ %s (%s)\n", test
, xyear
, time_match
? "PASS" : "FAIL");
3222 if (time_match
&& xyday
) {
3223 int test
= tm
.tm_yday
+ 1;
3224 time_match
= switch_number_cmp(xyday
, test
);
3225 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_DEBUG9
,
3226 "XML DateTime Check: day of year[%d] =~ %s (%s)\n", test
, xyday
, time_match
? "PASS" : "FAIL");
3229 if (time_match
&& xmon
) {
3230 int test
= tm
.tm_mon
+ 1;
3231 time_match
= switch_number_cmp(xmon
, test
);
3232 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_DEBUG9
,
3233 "XML DateTime Check: month[%d] =~ %s (%s)\n", test
, xmon
, time_match
? "PASS" : "FAIL");
3236 if (time_match
&& xmday
) {
3237 int test
= tm
.tm_mday
;
3238 time_match
= switch_number_cmp(xmday
, test
);
3239 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_DEBUG9
,
3240 "XML DateTime Check: day of month[%d] =~ %s (%s)\n", test
, xmday
, time_match
? "PASS" : "FAIL");
3243 if (time_match
&& xweek
) {
3244 int test
= (int) (tm
.tm_yday
/ 7 + 1);
3245 time_match
= switch_number_cmp(xweek
, test
);
3246 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_DEBUG9
,
3247 "XML DateTime Check: week of year[%d] =~ %s (%s)\n", test
, xweek
, time_match
? "PASS" : "FAIL");
3250 if (time_match
&& xmweek
) {
3251 /* calculate the day of the week of the first of the month (0-6) */
3252 int firstdow
= (int) (7 - (tm
.tm_mday
- (tm
.tm_wday
+ 1)) % 7) % 7;
3253 /* calculate the week of the month (1-6)*/
3254 int test
= (int) ceil((tm
.tm_mday
+ firstdow
) / 7.0);
3255 time_match
= switch_number_cmp(xmweek
, test
);
3256 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_DEBUG9
,
3257 "XML DateTime: week of month[%d] =~ %s (%s)\n", test
, xmweek
, time_match
? "PASS" : "FAIL");
3260 if (time_match
&& xwday
) {
3261 int test
= tm
.tm_wday
+ 1;
3262 time_match
= switch_dow_cmp(xwday
, test
);
3263 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_DEBUG9
,
3264 "XML DateTime Check: day of week[%s] =~ %s (%s)\n", switch_dow_int2str(test
), xwday
, time_match
? "PASS" : "FAIL");
3266 if (time_match
&& xhour
) {
3267 int test
= tm
.tm_hour
;
3268 time_match
= switch_number_cmp(xhour
, test
);
3269 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_DEBUG9
,
3270 "XML DateTime Check: hour[%d] =~ %s (%s)\n", test
, xhour
, time_match
? "PASS" : "FAIL");
3273 if (time_match
&& xminute
) {
3274 int test
= tm
.tm_min
;
3275 time_match
= switch_number_cmp(xminute
, test
);
3276 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_DEBUG9
,
3277 "XML DateTime Check: minute[%d] =~ %s (%s)\n", test
, xminute
, time_match
? "PASS" : "FAIL");
3280 if (time_match
&& xminday
) {
3281 int test
= (tm
.tm_hour
* 60) + (tm
.tm_min
+ 1);
3282 time_match
= switch_number_cmp(xminday
, test
);
3283 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_DEBUG9
,
3284 "XML DateTime Check: minute of day[%d] =~ %s (%s)\n", test
, xminday
, time_match
? "PASS" : "FAIL");
3287 if (time_match
&& xtod
) {
3288 int test
= (tm
.tm_hour
* 60 * 60) + (tm
.tm_min
* 60) + tm
.tm_sec
;
3290 switch_snprintf(tmpdate
, 10, "%d:%d:%d", tm
.tm_hour
, tm
.tm_min
, tm
.tm_sec
);
3291 time_match
= switch_tod_cmp(xtod
, test
);
3292 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_DEBUG9
,
3293 "XML DateTime Check: time of day[%s] =~ %s (%s)\n", tmpdate
, xtod
, time_match
? "PASS" : "FAIL");
3299 SWITCH_DECLARE(switch_status_t
) switch_xml_locate_language_ex(switch_xml_t
*root
, switch_xml_t
*node
, switch_event_t
*params
, switch_xml_t
*language
, switch_xml_t
*phrases
, switch_xml_t
*macros
, const char *str_language
) {
3300 switch_status_t status
= SWITCH_STATUS_FALSE
;
3302 if (switch_xml_locate("languages", NULL
, NULL
, NULL
, root
, node
, params
, SWITCH_TRUE
) != SWITCH_STATUS_SUCCESS
) {
3303 switch_xml_t sub_macros
;
3305 if (switch_xml_locate("phrases", NULL
, NULL
, NULL
, root
, node
, params
, SWITCH_TRUE
) != SWITCH_STATUS_SUCCESS
) {
3306 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_ERROR
, "Open of languages and phrases failed.\n");
3309 if (!(sub_macros
= switch_xml_child(*node
, "macros"))) {
3310 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_ERROR
, "Can't find macros tag.\n");
3311 switch_xml_free(*root
);
3316 if (!(*language
= switch_xml_find_child(sub_macros
, "language", "name", str_language
))) {
3317 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_ERROR
, "Can't find language %s.\n", str_language
);
3318 switch_xml_free(*root
);
3323 *macros
= *language
;
3325 if (!(*language
= switch_xml_find_child(*node
, "language", "name", str_language
))) {
3326 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_ERROR
, "Can't find language %s.\n", str_language
);
3327 switch_xml_free(*root
);
3331 if (!(*phrases
= switch_xml_child(*language
, "phrases"))) {
3332 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_ERROR
, "Can't find phrases tag.\n");
3333 switch_xml_free(*root
);
3340 if (!(*macros
= switch_xml_child(*phrases
, "macros"))) {
3341 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_ERROR
, "Can't find macros tag.\n");
3342 switch_xml_free(*root
);
3350 status
= SWITCH_STATUS_SUCCESS
;
3356 SWITCH_DECLARE(switch_status_t
) switch_xml_locate_language(switch_xml_t
*root
, switch_xml_t
*node
, switch_event_t
*params
, switch_xml_t
*language
, switch_xml_t
*phrases
, switch_xml_t
*macros
, const char *str_language
) {
3357 switch_status_t status
;
3359 if ((status
= switch_xml_locate_language_ex(root
, node
, params
, language
, phrases
, macros
, str_language
)) != SWITCH_STATUS_SUCCESS
) {
3360 char *str_language_dup
= strdup(str_language
);
3362 switch_assert(str_language_dup
);
3363 if ((secondary
= strchr(str_language_dup
, '-'))) {
3364 *secondary
++ = '\0';
3365 switch_log_printf(SWITCH_CHANNEL_LOG
, SWITCH_LOG_WARNING
,
3366 "language %s not found. trying %s by removing %s\n", str_language
, str_language_dup
, secondary
);
3367 switch_event_add_header_string(params
, SWITCH_STACK_BOTTOM
, "lang", str_language_dup
);
3368 status
= switch_xml_locate_language_ex(root
, node
, params
, language
, phrases
, macros
, str_language_dup
);
3370 switch_safe_free(str_language_dup
);
3378 * globbing functions for windows, part of libc on unix, this code was cut and paste from
3379 * freebsd lib and distilled a bit to work with windows
3383 * Copyright (c) 1989, 1993
3384 * The Regents of the University of California. All rights reserved.
3386 * This code is derived from software contributed to Berkeley by
3389 * Redistribution and use in source and binary forms, with or without
3390 * modification, are permitted provided that the following conditions
3392 * 1. Redistributions of source code must retain the above copyright
3393 * notice, this list of conditions and the following disclaimer.
3394 * 2. Redistributions in binary form must reproduce the above copyright
3395 * notice, this list of conditions and the following disclaimer in the
3396 * documentation and/or other materials provided with the distribution.
3397 * 4. Neither the name of the University nor the names of its contributors
3398 * may be used to endorse or promote products derived from this software
3399 * without specific prior written permission.
3401 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
3402 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
3403 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
3404 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
3405 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3406 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3407 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3408 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3409 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3410 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3417 #define LBRACKET '['
3419 #define QUESTION '?'
3421 #define RBRACKET ']'
3426 #define UNDERSCORE '_'
3432 #define M_QUOTE (char)0x80
3433 #define M_PROTECT (char)0x40
3434 #define M_MASK (char)0xff
3435 #define M_ASCII (char)0x7f
3437 #define CHAR(c) ((char)((c)&M_ASCII))
3438 #define META(c) ((char)((c)|M_QUOTE))
3439 #define M_ALL META('*')
3440 #define M_END META(']')
3441 #define M_NOT META('!')
3442 #define M_ONE META('?')
3443 #define M_RNG META('-')
3444 #define M_SET META('[')
3445 #define ismeta(c) (((c)&M_QUOTE) != 0)
3448 #define MAXPATHLEN 256
3451 static int compare(const void *, const void *);
3452 static int glob0(const char *, glob_t
*, size_t *);
3453 static int glob1(char *, glob_t
*, size_t *);
3454 static int glob2(char *, char *, char *, char *, glob_t
*, size_t *);
3455 static int glob3(char *, char *, char *, char *, char *, glob_t
*, size_t *);
3456 static int globextend(const char *, glob_t
*, size_t *);
3457 static int match(char *, char *, char *);
3459 #pragma warning(push)
3460 #pragma warning(disable:4310)
3462 int glob(const char *pattern
, int flags
, int (*errfunc
) (const char *, int), glob_t
*pglob
)
3464 const unsigned char *patnext
;
3467 char *bufnext
, *bufend
, patbuf
[MAXPATHLEN
];
3469 patnext
= (unsigned char *) pattern
;
3470 if (!(flags
& GLOB_APPEND
)) {
3471 pglob
->gl_pathc
= 0;
3472 pglob
->gl_pathv
= NULL
;
3473 if (!(flags
& GLOB_DOOFFS
))
3476 if (flags
& GLOB_LIMIT
) {
3477 limit
= pglob
->gl_matchc
;
3482 pglob
->gl_flags
= flags
& ~GLOB_MAGCHAR
;
3483 pglob
->gl_errfunc
= errfunc
;
3484 pglob
->gl_matchc
= 0;
3487 bufend
= bufnext
+ MAXPATHLEN
- 1;
3488 while (bufnext
< bufend
&& (c
= *patnext
++) != EOS
)
3492 return glob0(patbuf
, pglob
, &limit
);
3496 * The main glob() routine: compiles the pattern (optionally processing
3497 * quotes), calls glob1() to do the real pattern matching, and finally
3498 * sorts the list (unless unsorted operation is requested). Returns 0
3499 * if things went well, nonzero if errors occurred.
3501 static int glob0(const char *pattern
, glob_t
*pglob
, size_t *limit
)
3503 const char *qpatnext
;
3506 char *bufnext
, patbuf
[MAXPATHLEN
];
3509 oldpathc
= pglob
->gl_pathc
;
3512 /* We don't need to check for buffer overflow any more. */
3513 while ((c
= *qpatnext
++) != EOS
) {
3516 *bufnext
++ = WIN_SEP
;
3522 if (*qpatnext
== EOS
|| strchr((char *) qpatnext
+ 1, RBRACKET
) == NULL
) {
3523 *bufnext
++ = LBRACKET
;
3533 *bufnext
++ = CHAR(c
);
3534 if (*qpatnext
== RANGE
&& (c
= qpatnext
[1]) != RBRACKET
) {
3536 *bufnext
++ = CHAR(c
);
3539 } while ((c
= *qpatnext
++) != RBRACKET
);
3540 pglob
->gl_flags
|= GLOB_MAGCHAR
;
3544 pglob
->gl_flags
|= GLOB_MAGCHAR
;
3548 pglob
->gl_flags
|= GLOB_MAGCHAR
;
3549 /* collapse adjacent stars to one,
3550 * to avoid exponential behavior
3552 if (bufnext
== patbuf
|| bufnext
[-1] != M_ALL
)
3556 *bufnext
++ = CHAR(c
);
3562 if ((err
= glob1(patbuf
, pglob
, limit
)) != 0)
3566 * If there was no match we are going to append the pattern
3567 * if GLOB_NOCHECK was specified or if GLOB_NOMAGIC was specified
3568 * and the pattern did not contain any magic characters
3569 * GLOB_NOMAGIC is there just for compatibility with csh.
3571 if (pglob
->gl_pathc
== oldpathc
) {
3572 if (((pglob
->gl_flags
& GLOB_NOCHECK
) || ((pglob
->gl_flags
& GLOB_NOMAGIC
) && !(pglob
->gl_flags
& GLOB_MAGCHAR
))))
3573 return (globextend(pattern
, pglob
, limit
));
3575 return (GLOB_NOMATCH
);
3577 if (!(pglob
->gl_flags
& GLOB_NOSORT
))
3578 qsort(pglob
->gl_pathv
+ pglob
->gl_offs
+ oldpathc
, pglob
->gl_pathc
- oldpathc
, sizeof(char *), compare
);
3582 static int compare(const void *p
, const void *q
)
3584 return (strcmp(*(char **) p
, *(char **) q
));
3587 static int glob1(char *pattern
, glob_t
*pglob
, size_t *limit
)
3589 char pathbuf
[MAXPATHLEN
];
3591 /* A null pathname is invalid -- POSIX 1003.1 sect. 2.4. */
3592 if (*pattern
== EOS
)
3594 return (glob2(pathbuf
, pathbuf
, pathbuf
+ MAXPATHLEN
- 1, pattern
, pglob
, limit
));
3598 * The functions glob2 and glob3 are mutually recursive; there is one level
3599 * of recursion for each segment in the pattern that contains one or more
3602 static int glob2(char *pathbuf
, char *pathend
, char *pathend_last
, char *pattern
, glob_t
*pglob
, size_t *limit
)
3609 * Loop over pattern segments until end of pattern or until
3610 * segment with meta character found.
3612 for (anymeta
= 0;;) {
3613 if (*pattern
== EOS
) { /* End of pattern? */
3615 if (stat(pathbuf
, &sb
))
3618 if (((pglob
->gl_flags
& GLOB_MARK
) && pathend
[-1] != SEP
&& pathend
[-1] != WIN_SEP
) && (_S_IFDIR
& sb
.st_mode
)) {
3619 if (pathend
+ 1 > pathend_last
)
3620 return (GLOB_ABORTED
);
3621 *pathend
++ = WIN_SEP
;
3625 return (globextend(pathbuf
, pglob
, limit
));
3628 /* Find end of next segment, copy tentatively to pathend. */
3631 while (*p
!= EOS
&& *p
!= SEP
&& *p
!= WIN_SEP
) {
3634 if (q
+ 1 > pathend_last
)
3635 return (GLOB_ABORTED
);
3639 if (!anymeta
) { /* No expansion, do next segment. */
3642 while (*pattern
== SEP
|| *pattern
== WIN_SEP
) {
3643 if (pathend
+ 1 > pathend_last
)
3644 return (GLOB_ABORTED
);
3645 *pathend
++ = *pattern
++;
3647 } else /* Need expansion, recurse. */
3648 return (glob3(pathbuf
, pathend
, pathend_last
, pattern
, p
, pglob
, limit
));
3653 static int glob3(char *pathbuf
, char *pathend
, char *pathend_last
, char *pattern
, char *restpattern
, glob_t
*pglob
, size_t *limit
)
3659 fspr_pool_create(&pool
, NULL
);
3661 if (pathend
> pathend_last
)
3662 return (GLOB_ABORTED
);
3666 if (fspr_dir_open(&dirp
, pathbuf
, pool
) != APR_SUCCESS
) {
3667 /* TODO: don't call for ENOENT or ENOTDIR? */
3668 fspr_pool_destroy(pool
);
3669 if (pglob
->gl_errfunc
) {
3670 if (pglob
->gl_errfunc(pathbuf
, errno
) || pglob
->gl_flags
& GLOB_ERR
)
3671 return (GLOB_ABORTED
);
3678 /* Search directory for matching names. */
3684 if (fspr_dir_read(&dp
, APR_FINFO_NAME
, dirp
) != APR_SUCCESS
)
3686 if (!(dp
.valid
& APR_FINFO_NAME
) || !(dp
.name
) || !strlen(dp
.name
))
3689 /* Initial DOT must be matched literally. */
3690 if (dp
.name
[0] == DOT
&& *pattern
!= DOT
)
3693 sc
= (unsigned char *) dp
.name
;
3695 while (dc
< pathend_last
&& (*dc
++ = *sc
++) != EOS
);
3697 if (!match(pathend
, pattern
, restpattern
)) {
3701 err
= glob2(pathbuf
, --dc
, pathend_last
, restpattern
, pglob
, limit
);
3707 fspr_dir_close(dirp
);
3708 fspr_pool_destroy(pool
);
3714 * Extend the gl_pathv member of a glob_t structure to accommodate a new item,
3715 * add the new item, and update gl_pathc.
3717 * This assumes the BSD realloc, which only copies the block when its size
3718 * crosses a power-of-two boundary; for v7 realloc, this would cause quadratic
3721 * Return 0 if new item added, error code if memory couldn't be allocated.
3723 * Invariant of the glob_t structure:
3724 * Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and
3725 * gl_pathv points to (gl_offs + gl_pathc + 1) items.
3727 static int globextend(const char *path
, glob_t
*pglob
, size_t *limit
)
3732 size_t newsize
, len
;
3735 if (*limit
&& pglob
->gl_pathc
> *limit
) {
3737 return (GLOB_NOSPACE
);
3740 newsize
= sizeof(*pathv
) * (2 + pglob
->gl_pathc
+ pglob
->gl_offs
);
3741 pathv
= pglob
->gl_pathv
? switch_must_realloc((char *) pglob
->gl_pathv
, newsize
) : switch_must_malloc(newsize
);
3743 if (pglob
->gl_pathv
== NULL
&& pglob
->gl_offs
> 0) {
3744 /* first time around -- clear initial gl_offs items */
3745 pathv
+= pglob
->gl_offs
;
3746 for (i
= pglob
->gl_offs
; i
-- > 0;)
3749 pglob
->gl_pathv
= pathv
;
3751 for (p
= path
; *p
++;)
3753 len
= (size_t) (p
- path
);
3754 copy
= switch_must_malloc(len
);
3755 memcpy(copy
, path
, len
);
3756 pathv
[pglob
->gl_offs
+ pglob
->gl_pathc
++] = copy
;
3757 pathv
[pglob
->gl_offs
+ pglob
->gl_pathc
] = NULL
;
3758 return (copy
== NULL
? GLOB_NOSPACE
: 0);
3762 * pattern matching function for filenames. Each occurrence of the *
3763 * pattern causes a recursion level.
3765 static int match(char *name
, char *pat
, char *patend
)
3767 int ok
, negate_range
;
3771 while (pat
< patend
) {
3773 switch (c
& M_MASK
) {
3778 if (match(name
, pat
, patend
))
3780 while (*name
++ != EOS
);
3788 if ((k
= *name
++) == EOS
)
3790 if ((negate_range
= ((*pat
& M_MASK
) == M_NOT
)) != EOS
)
3792 while (((c
= *pat
++) & M_MASK
) != M_END
)
3793 if ((*pat
& M_MASK
) == M_RNG
) {
3794 memset(s1
, 0, sizeof(s1
));
3798 if (strcoll(&s1
[0], &s1
[2]) <= 0 && strcoll(&s1
[2], &s1
[4]) <= 0)
3803 if (ok
== negate_range
)
3812 return (*name
== EOS
);
3815 /* Free allocated data belonging to a glob_t structure. */
3816 void globfree(glob_t
*pglob
)
3821 if (pglob
->gl_pathv
!= NULL
) {
3822 pp
= pglob
->gl_pathv
+ pglob
->gl_offs
;
3823 for (i
= pglob
->gl_pathc
; i
--; ++pp
)
3826 free(pglob
->gl_pathv
);
3827 pglob
->gl_pathv
= NULL
;
3831 #pragma warning(pop)
3837 * indent-tabs-mode:t
3842 * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: