mirror of
https://github.com/signalwire/freeswitch.git
synced 2025-02-23 18:04:07 +00:00
530 lines
13 KiB
C
530 lines
13 KiB
C
/*
|
|
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
|
* Copyright (C) 2005-2011, Anthony Minessale II <anthm@freeswitch.org>
|
|
*
|
|
* Version: MPL 1.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Anthony Minessale II <anthm@freeswitch.org>
|
|
* Portions created by the Initial Developer are Copyright (C)
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
* Anthony Minessale II <anthm@freeswitch.org>
|
|
|
|
*
|
|
* mod_yaml.c -- YAML Module
|
|
*
|
|
*/
|
|
#include <switch.h>
|
|
#include <yaml.h>
|
|
|
|
SWITCH_MODULE_LOAD_FUNCTION(mod_yaml_load);
|
|
SWITCH_MODULE_DEFINITION(mod_yaml, mod_yaml_load, NULL, NULL);
|
|
|
|
static void print_error(yaml_parser_t *parser)
|
|
{
|
|
switch (parser->error) {
|
|
case YAML_MEMORY_ERROR:
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory error: Not enough memory for parsing\n");
|
|
break;
|
|
|
|
case YAML_READER_ERROR:
|
|
if (parser->problem_value != -1) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Reader error: %s: #%X at %d\n", parser->problem,
|
|
parser->problem_value, (int) parser->problem_offset);
|
|
} else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Reader error: %s at %d\n", parser->problem, (int) parser->problem_offset);
|
|
}
|
|
break;
|
|
|
|
case YAML_SCANNER_ERROR:
|
|
if (parser->context) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Scanner error: %s at line %d, column %d\n"
|
|
"%s at line %d, column %d\n", parser->context,
|
|
(int) parser->context_mark.line + 1, (int) parser->context_mark.column + 1,
|
|
parser->problem, (int) parser->problem_mark.line + 1, (int) parser->problem_mark.column + 1);
|
|
} else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Scanner error: %s at line %d, column %d\n",
|
|
parser->problem, (int) parser->problem_mark.line + 1, (int) parser->problem_mark.column + 1);
|
|
}
|
|
break;
|
|
|
|
case YAML_PARSER_ERROR:
|
|
if (parser->context) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Parser error: %s at line %d, column %d\n"
|
|
"%s at line %d, column %d\n", parser->context,
|
|
(int) parser->context_mark.line + 1, (int) parser->context_mark.column + 1,
|
|
parser->problem, (int) parser->problem_mark.line + 1, (int) parser->problem_mark.column + 1);
|
|
} else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Parser error: %s at line %d, column %d\n",
|
|
parser->problem, (int) parser->problem_mark.line + 1, (int) parser->problem_mark.column + 1);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
/* Couldn't happen. */
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Internal error\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static switch_xml_t parse_file(FILE * input, const char *file_name)
|
|
{
|
|
yaml_parser_t parser;
|
|
yaml_event_t event = { 0 };
|
|
char *scalar_data;
|
|
int done = 0;
|
|
int depth = 0;
|
|
char name[128] = "";
|
|
char value[128] = "";
|
|
char category[128] = "";
|
|
int nv = 0, p_off = 0;
|
|
switch_xml_t xml, param, top, current = NULL;
|
|
|
|
yaml_parser_initialize(&parser);
|
|
yaml_parser_set_input_file(&parser, input);
|
|
|
|
|
|
if (!(xml = switch_xml_new("document"))) {
|
|
return NULL;
|
|
}
|
|
|
|
switch_xml_set_attr_d(xml, "type", "freeswitch/xml");
|
|
current = switch_xml_add_child_d(xml, "section", 0);
|
|
switch_xml_set_attr_d(current, "name", "configuration");
|
|
|
|
top = switch_xml_add_child_d(current, "configuration", 0);
|
|
switch_xml_set_attr_d(top, "name", file_name);
|
|
|
|
while (!done) {
|
|
if (!yaml_parser_parse(&parser, &event)) {
|
|
print_error(&parser);
|
|
break;
|
|
} else {
|
|
switch (event.type) {
|
|
case YAML_MAPPING_START_EVENT:
|
|
depth++;
|
|
break;
|
|
case YAML_MAPPING_END_EVENT:
|
|
depth--;
|
|
break;
|
|
case YAML_STREAM_END_EVENT:
|
|
done = 1;
|
|
break;
|
|
case YAML_SCALAR_EVENT:
|
|
scalar_data = (char *) event.data.scalar.value;
|
|
switch (depth) {
|
|
case 1:
|
|
if (!(current = switch_xml_add_child_d(top, scalar_data, depth - 1))) {
|
|
done = 1;
|
|
}
|
|
switch_set_string(category, scalar_data);
|
|
nv = 0;
|
|
p_off = 0;
|
|
break;
|
|
case 2:
|
|
if (current) {
|
|
if (nv == 0) {
|
|
switch_set_string(name, scalar_data);
|
|
nv++;
|
|
} else {
|
|
switch_set_string(value, scalar_data);
|
|
param = switch_xml_add_child_d(current, "param", p_off++);
|
|
switch_xml_set_attr_d_buf(param, "name", name);
|
|
switch_xml_set_attr_d(param, "value", scalar_data);
|
|
nv = 0;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
yaml_event_delete(&event);
|
|
}
|
|
|
|
yaml_parser_delete(&parser);
|
|
|
|
if (input) {
|
|
fclose(input);
|
|
}
|
|
#ifdef DEBUG_XML
|
|
if (xml) {
|
|
char *foo = switch_xml_toxml(xml, SWITCH_FALSE);
|
|
printf("%s\n", foo);
|
|
free(foo);
|
|
}
|
|
#endif
|
|
|
|
return xml;
|
|
|
|
}
|
|
|
|
static switch_xml_t yaml_fetch(const char *section,
|
|
const char *tag_name, const char *key_name, const char *key_value, switch_event_t *params, void *user_data)
|
|
{
|
|
char *path;
|
|
FILE *input;
|
|
switch_xml_t xml = NULL;
|
|
|
|
path = switch_mprintf("%s/yaml/%s.yaml", SWITCH_GLOBAL_dirs.conf_dir, key_value);
|
|
if ((input = fopen(path, "r"))) {
|
|
xml = parse_file(input, key_value);
|
|
} else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "cannot open %s\n", path);
|
|
}
|
|
|
|
switch_safe_free(path);
|
|
return xml;
|
|
}
|
|
|
|
|
|
static switch_caller_extension_t *parse_dp(FILE * input, switch_core_session_t *session, switch_caller_profile_t *caller_profile)
|
|
{
|
|
yaml_parser_t parser;
|
|
yaml_event_t event = { 0 };
|
|
char *scalar_data;
|
|
int done = 0;
|
|
int depth = 0;
|
|
char name[128] = "";
|
|
char value[128] = "";
|
|
char category[128] = "";
|
|
char *last_field = NULL;
|
|
int nv = 0;
|
|
switch_caller_extension_t *extension = NULL;
|
|
switch_channel_t *channel = switch_core_session_get_channel(session);
|
|
int context_hit = 0;
|
|
int proceed = 0;
|
|
switch_regex_t *re = NULL;
|
|
int ovector[30];
|
|
int parens = 0;
|
|
|
|
if (!caller_profile) {
|
|
if (!(caller_profile = switch_channel_get_caller_profile(channel))) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Obtaining Profile!\n");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (!caller_profile->context) {
|
|
caller_profile->context = "default";
|
|
}
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Processing %s->%s@%s\n",
|
|
caller_profile->caller_id_name, caller_profile->destination_number, caller_profile->context);
|
|
|
|
yaml_parser_initialize(&parser);
|
|
yaml_parser_set_input_file(&parser, input);
|
|
|
|
while (!done) {
|
|
if (!yaml_parser_parse(&parser, &event)) {
|
|
print_error(&parser);
|
|
break;
|
|
} else {
|
|
switch (event.type) {
|
|
case YAML_MAPPING_START_EVENT:
|
|
depth++;
|
|
break;
|
|
case YAML_MAPPING_END_EVENT:
|
|
depth--;
|
|
break;
|
|
case YAML_STREAM_END_EVENT:
|
|
done = 1;
|
|
break;
|
|
case YAML_SCALAR_EVENT:
|
|
scalar_data = (char *) event.data.scalar.value;
|
|
switch (depth) {
|
|
case 1:
|
|
switch_set_string(category, scalar_data);
|
|
context_hit = (!strcasecmp(category, caller_profile->context));
|
|
nv = 0;
|
|
break;
|
|
case 2:
|
|
if (context_hit) {
|
|
char *field = switch_core_session_strdup(session, scalar_data);
|
|
char *p, *e, *expression = NULL, *field_expanded = NULL, *expression_expanded = NULL;
|
|
const char *field_data = NULL;
|
|
|
|
parens = 0;
|
|
proceed = 0;
|
|
switch_regex_safe_free(re);
|
|
|
|
if ((p = strstr(field, "=~"))) {
|
|
*p = '\0';
|
|
e = p - 1;
|
|
while (*e == ' ') {
|
|
*e-- = '\0';
|
|
}
|
|
e = p + 2;
|
|
while (*e == ' ') {
|
|
*e++ = '\0';
|
|
}
|
|
expression = e;
|
|
}
|
|
|
|
if (field && expression) {
|
|
if ((expression_expanded = switch_channel_expand_variables(channel, expression)) == expression) {
|
|
expression_expanded = NULL;
|
|
} else {
|
|
expression = expression_expanded;
|
|
}
|
|
|
|
if (strchr(field, '$')) {
|
|
if ((field_expanded = switch_channel_expand_variables(channel, field)) == field) {
|
|
field_expanded = NULL;
|
|
field_data = field;
|
|
} else {
|
|
field_data = field_expanded;
|
|
}
|
|
} else {
|
|
field_data = switch_caller_get_field_by_name(caller_profile, field);
|
|
}
|
|
if (!field_data) {
|
|
field_data = "";
|
|
}
|
|
switch_safe_free(last_field);
|
|
last_field = strdup(field_data);
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "test conditions %s(%s) =~ /%s/\n", field, field_data, expression);
|
|
if (!(proceed = switch_regex_perform(field_data, expression, &re, ovector, sizeof(ovector) / sizeof(ovector[0])))) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Regex mismatch\n");
|
|
}
|
|
|
|
if (strchr(expression, '(')) {
|
|
parens++;
|
|
}
|
|
|
|
switch_safe_free(field_expanded);
|
|
switch_safe_free(expression_expanded);
|
|
}
|
|
}
|
|
break;
|
|
case 3:
|
|
if (nv == 0) {
|
|
if (!strcasecmp(scalar_data, "exit")) {
|
|
yaml_event_delete(&event);
|
|
goto end;
|
|
}
|
|
switch_set_string(name, scalar_data);
|
|
nv++;
|
|
} else {
|
|
switch_set_string(value, scalar_data);
|
|
nv = 0;
|
|
if (proceed) {
|
|
uint32_t len = 0;
|
|
char *substituted = NULL;
|
|
char *app_data;
|
|
|
|
|
|
if (!extension) {
|
|
extension = switch_caller_extension_new(session, "YAML", caller_profile->destination_number);
|
|
switch_assert(extension);
|
|
}
|
|
|
|
if (parens) {
|
|
len = (uint32_t) (strlen(value) + strlen(last_field) + 10) * proceed;
|
|
switch_zmalloc(substituted, len);
|
|
switch_perform_substitution(re, proceed, value, last_field, substituted, len, ovector);
|
|
app_data = substituted;
|
|
} else {
|
|
app_data = value;
|
|
}
|
|
|
|
switch_caller_extension_add_application(session, extension, name, app_data);
|
|
switch_safe_free(substituted);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
yaml_event_delete(&event);
|
|
}
|
|
|
|
end:
|
|
|
|
switch_safe_free(last_field);
|
|
switch_regex_safe_free(re);
|
|
yaml_parser_delete(&parser);
|
|
|
|
if (input) {
|
|
fclose(input);
|
|
}
|
|
#ifdef DEBUG_XML
|
|
if (xml) {
|
|
char *foo = switch_xml_toxml(xml, SWITCH_FALSE);
|
|
printf("%s\n", foo);
|
|
free(foo);
|
|
}
|
|
#endif
|
|
|
|
return extension;
|
|
|
|
}
|
|
|
|
SWITCH_STANDARD_DIALPLAN(yaml_dialplan_hunt)
|
|
{
|
|
switch_caller_extension_t *extension = NULL;
|
|
char *alt_path = (char *) arg;
|
|
char *path = NULL;
|
|
FILE *input;
|
|
|
|
if (!zstr(alt_path)) {
|
|
path = strdup(alt_path);
|
|
} else {
|
|
path = switch_mprintf("%s/yaml/extensions.yaml", SWITCH_GLOBAL_dirs.conf_dir);
|
|
}
|
|
|
|
if ((input = fopen(path, "r"))) {
|
|
extension = parse_dp(input, session, caller_profile);
|
|
} else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error opening %s\n", path);
|
|
}
|
|
|
|
switch_safe_free(path);
|
|
return extension;
|
|
}
|
|
|
|
|
|
static switch_status_t do_config(void)
|
|
{
|
|
yaml_parser_t parser;
|
|
yaml_event_t event = { 0 };
|
|
char *path;
|
|
const char *cfg = "mod_yaml.yaml";
|
|
FILE *input;
|
|
switch_status_t status = SWITCH_STATUS_FALSE;
|
|
char *scalar_data;
|
|
int done = 0;
|
|
int depth = 0;
|
|
char name[128] = "";
|
|
char value[128] = "";
|
|
char category[128] = "";
|
|
int nv = 0;
|
|
|
|
path = switch_mprintf("%s/yaml/%s", SWITCH_GLOBAL_dirs.conf_dir, cfg);
|
|
|
|
if (!(input = fopen(path, "r"))) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error opening %s\n", path);
|
|
goto end;
|
|
}
|
|
|
|
yaml_parser_initialize(&parser);
|
|
yaml_parser_set_input_file(&parser, input);
|
|
|
|
while (!done) {
|
|
if (!yaml_parser_parse(&parser, &event)) {
|
|
print_error(&parser);
|
|
break;
|
|
} else {
|
|
switch (event.type) {
|
|
case YAML_MAPPING_START_EVENT:
|
|
depth++;
|
|
break;
|
|
case YAML_MAPPING_END_EVENT:
|
|
depth--;
|
|
break;
|
|
case YAML_STREAM_END_EVENT:
|
|
done = 1;
|
|
break;
|
|
case YAML_SCALAR_EVENT:
|
|
scalar_data = (char *) event.data.scalar.value;
|
|
switch (depth) {
|
|
case 1:
|
|
switch_set_string(category, scalar_data);
|
|
nv = 0;
|
|
break;
|
|
case 2:
|
|
if (nv == 0) {
|
|
switch_set_string(name, scalar_data);
|
|
nv++;
|
|
} else {
|
|
switch_set_string(value, scalar_data);
|
|
if (!strcasecmp(category, "settings")) {
|
|
if (!strcasecmp(name, "bind_config") && switch_true_buf(value)) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Binding To XML Config\n");
|
|
switch_xml_bind_search_function(yaml_fetch, switch_xml_parse_section_string("config"), NULL);
|
|
}
|
|
}
|
|
nv = 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
yaml_event_delete(&event);
|
|
}
|
|
|
|
yaml_parser_delete(&parser);
|
|
status = SWITCH_STATUS_SUCCESS;
|
|
|
|
end:
|
|
|
|
if (input) {
|
|
fclose(input);
|
|
}
|
|
|
|
switch_safe_free(path);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
SWITCH_MODULE_LOAD_FUNCTION(mod_yaml_load)
|
|
{
|
|
switch_dialplan_interface_t *dp_interface;
|
|
|
|
/* connect my internal structure to the blank pointer passed to me */
|
|
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
|
|
|
|
if (do_config() != SWITCH_STATUS_SUCCESS) {
|
|
return SWITCH_STATUS_TERM;
|
|
}
|
|
|
|
SWITCH_ADD_DIALPLAN(dp_interface, "YAML", yaml_dialplan_hunt);
|
|
|
|
/* indicate that the module should continue to be loaded */
|
|
return SWITCH_STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* For Emacs:
|
|
* Local Variables:
|
|
* mode:c
|
|
* indent-tabs-mode:t
|
|
* tab-width:4
|
|
* c-basic-offset:4
|
|
* End:
|
|
* For VIM:
|
|
* vim:set softtabstop=4 shiftwidth=4 tabstop=4:
|
|
*/
|