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