PageRenderTime 26ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/src/mod/dialplans/mod_dialplan_xml/mod_dialplan_xml.c

https://github.com/mzeena/FreeSWITCH-qmod
C | 430 lines | 321 code | 64 blank | 45 comment | 95 complexity | f0906ebd928cfbcb0954dd44c05ca63f MD5 | raw file
  1. /*
  2. * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
  3. * Copyright (C) 2005-2011, 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. *
  28. *
  29. * mod_dialplan_xml.c -- XML/Regex Dialplan Module
  30. *
  31. */
  32. #include <switch.h>
  33. #include <sys/types.h>
  34. #include <sys/stat.h>
  35. #include <fcntl.h>
  36. SWITCH_MODULE_LOAD_FUNCTION(mod_dialplan_xml_load);
  37. SWITCH_MODULE_DEFINITION(mod_dialplan_xml, mod_dialplan_xml_load, NULL, NULL);
  38. typedef enum {
  39. BREAK_ON_TRUE,
  40. BREAK_ON_FALSE,
  41. BREAK_ALWAYS,
  42. BREAK_NEVER
  43. } break_t;
  44. static switch_status_t exec_app(switch_core_session_t *session, const char *app, const char *arg)
  45. {
  46. switch_application_interface_t *application_interface;
  47. switch_status_t status = SWITCH_STATUS_SUCCESS;
  48. switch_channel_t *channel = switch_core_session_get_channel(session);
  49. switch_assert(channel);
  50. if ((application_interface = switch_loadable_module_get_application_interface(app)) == 0) {
  51. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Invalid Application %s\n", app);
  52. switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
  53. return SWITCH_STATUS_FALSE;
  54. }
  55. if (!application_interface->application_function) {
  56. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "No Function for %s\n", app);
  57. switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
  58. switch_goto_status(SWITCH_STATUS_FALSE, done);
  59. }
  60. if (!switch_test_flag(application_interface, SAF_ROUTING_EXEC)) {
  61. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "This application cannot be executed inline\n");
  62. switch_goto_status(SWITCH_STATUS_FALSE, done);
  63. }
  64. switch_core_session_exec(session, application_interface, arg);
  65. done:
  66. UNPROTECT_INTERFACE(application_interface);
  67. return status;
  68. }
  69. static int parse_exten(switch_core_session_t *session, switch_caller_profile_t *caller_profile, switch_xml_t xexten, switch_caller_extension_t **extension)
  70. {
  71. switch_xml_t xcond, xaction, xexpression;
  72. switch_channel_t *channel = switch_core_session_get_channel(session);
  73. char *exten_name = (char *) switch_xml_attr(xexten, "name");
  74. int proceed = 0;
  75. char *expression_expanded = NULL, *field_expanded = NULL;
  76. switch_regex_t *re = NULL;
  77. if (!exten_name) {
  78. exten_name = "_anon_";
  79. }
  80. for (xcond = switch_xml_child(xexten, "condition"); xcond; xcond = xcond->next) {
  81. char *field = NULL;
  82. char *do_break_a = NULL;
  83. char *expression = NULL;
  84. const char *field_data = NULL;
  85. int ovector[30];
  86. switch_bool_t anti_action = SWITCH_TRUE;
  87. break_t do_break_i = BREAK_ON_FALSE;
  88. int time_match = switch_xml_std_datetime_check(xcond);
  89. switch_safe_free(field_expanded);
  90. switch_safe_free(expression_expanded);
  91. if (switch_xml_child(xcond, "condition")) {
  92. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Nested conditions are not allowed!\n");
  93. proceed = 1;
  94. goto done;
  95. }
  96. field = (char *) switch_xml_attr(xcond, "field");
  97. if ((xexpression = switch_xml_child(xcond, "expression"))) {
  98. expression = switch_str_nil(xexpression->txt);
  99. } else {
  100. expression = (char *) switch_xml_attr_soft(xcond, "expression");
  101. }
  102. if ((expression_expanded = switch_channel_expand_variables(channel, expression)) == expression) {
  103. expression_expanded = NULL;
  104. } else {
  105. expression = expression_expanded;
  106. }
  107. if ((do_break_a = (char *) switch_xml_attr(xcond, "break"))) {
  108. if (!strcasecmp(do_break_a, "on-true")) {
  109. do_break_i = BREAK_ON_TRUE;
  110. } else if (!strcasecmp(do_break_a, "on-false")) {
  111. do_break_i = BREAK_ON_FALSE;
  112. } else if (!strcasecmp(do_break_a, "always")) {
  113. do_break_i = BREAK_ALWAYS;
  114. } else if (!strcasecmp(do_break_a, "never")) {
  115. do_break_i = BREAK_NEVER;
  116. } else {
  117. do_break_a = NULL;
  118. }
  119. }
  120. if (time_match == 1) {
  121. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG_CLEAN(session), SWITCH_LOG_DEBUG,
  122. "Dialplan: %s Date/Time Match (PASS) [%s] break=%s\n",
  123. switch_channel_get_name(channel), exten_name, do_break_a ? do_break_a : "on-false");
  124. anti_action = SWITCH_FALSE;
  125. } else if (time_match == 0) {
  126. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG_CLEAN(session), SWITCH_LOG_DEBUG,
  127. "Dialplan: %s Date/Time Match (FAIL) [%s] break=%s\n",
  128. switch_channel_get_name(channel), exten_name, do_break_a ? do_break_a : "on-false");
  129. }
  130. if (field) {
  131. if (strchr(field, '$')) {
  132. if ((field_expanded = switch_channel_expand_variables(channel, field)) == field) {
  133. field_expanded = NULL;
  134. field_data = field;
  135. } else {
  136. field_data = field_expanded;
  137. }
  138. } else {
  139. field_data = switch_caller_get_field_by_name(caller_profile, field);
  140. }
  141. if (!field_data) {
  142. field_data = "";
  143. }
  144. if ((proceed = switch_regex_perform(field_data, expression, &re, ovector, sizeof(ovector) / sizeof(ovector[0])))) {
  145. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG_CLEAN(session), SWITCH_LOG_DEBUG,
  146. "Dialplan: %s Regex (PASS) [%s] %s(%s) =~ /%s/ break=%s\n",
  147. switch_channel_get_name(channel), exten_name, field, field_data, expression, do_break_a ? do_break_a : "on-false");
  148. anti_action = SWITCH_FALSE;
  149. } else {
  150. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG_CLEAN(session), SWITCH_LOG_DEBUG,
  151. "Dialplan: %s Regex (FAIL) [%s] %s(%s) =~ /%s/ break=%s\n",
  152. switch_channel_get_name(channel), exten_name, field, field_data, expression, do_break_a ? do_break_a : "on-false");
  153. }
  154. } else if (time_match == -1) {
  155. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG_CLEAN(session), SWITCH_LOG_DEBUG,
  156. "Dialplan: %s Absolute Condition [%s]\n", switch_channel_get_name(channel), exten_name);
  157. anti_action = SWITCH_FALSE;
  158. }
  159. if (anti_action) {
  160. for (xaction = switch_xml_child(xcond, "anti-action"); xaction; xaction = xaction->next) {
  161. const char *application = switch_xml_attr_soft(xaction, "application");
  162. const char *loop = switch_xml_attr(xaction, "loop");
  163. const char *data;
  164. const char *inline_ = switch_xml_attr_soft(xaction, "inline");
  165. int xinline = switch_true(inline_);
  166. int loop_count = 1;
  167. if (!zstr(xaction->txt)) {
  168. data = xaction->txt;
  169. } else {
  170. data = (char *) switch_xml_attr_soft(xaction, "data");
  171. }
  172. if (!*extension) {
  173. if ((*extension = switch_caller_extension_new(session, exten_name, caller_profile->destination_number)) == 0) {
  174. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Memory Error!\n");
  175. proceed = 0;
  176. goto done;
  177. }
  178. }
  179. if (loop) {
  180. loop_count = atoi(loop);
  181. }
  182. for (;loop_count > 0; loop_count--) {
  183. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG_CLEAN(session), SWITCH_LOG_DEBUG,
  184. "Dialplan: %s ANTI-Action %s(%s) %s\n", switch_channel_get_name(channel), application, data, xinline ? "INLINE" : "");
  185. if (xinline) {
  186. exec_app(session, application, data);
  187. } else {
  188. switch_caller_extension_add_application(session, *extension, application, data);
  189. }
  190. }
  191. proceed = 1;
  192. }
  193. } else {
  194. if (field && strchr(expression, '(')) {
  195. switch_channel_set_variable(channel, "DP_MATCH", NULL);
  196. switch_capture_regex(re, proceed, field_data, ovector, "DP_MATCH", (switch_cap_callback_t)switch_regex_set_var_callback, session);
  197. }
  198. for (xaction = switch_xml_child(xcond, "action"); xaction; xaction = xaction->next) {
  199. char *application = (char *) switch_xml_attr_soft(xaction, "application");
  200. const char *loop = switch_xml_attr(xaction, "loop");
  201. char *data = NULL;
  202. char *substituted = NULL;
  203. uint32_t len = 0;
  204. char *app_data = NULL;
  205. const char *inline_ = switch_xml_attr_soft(xaction, "inline");
  206. int xinline = switch_true(inline_);
  207. int loop_count = 1;
  208. if (!zstr(xaction->txt)) {
  209. data = xaction->txt;
  210. } else {
  211. data = (char *) switch_xml_attr_soft(xaction, "data");
  212. }
  213. if (field && strchr(expression, '(')) {
  214. len = (uint32_t) (strlen(data) + strlen(field_data) + 10) * proceed;
  215. if (!(substituted = malloc(len))) {
  216. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Memory Error!\n");
  217. proceed = 0;
  218. goto done;
  219. }
  220. memset(substituted, 0, len);
  221. switch_perform_substitution(re, proceed, data, field_data, substituted, len, ovector);
  222. app_data = substituted;
  223. } else {
  224. app_data = data;
  225. }
  226. if (!*extension) {
  227. if ((*extension = switch_caller_extension_new(session, exten_name, caller_profile->destination_number)) == 0) {
  228. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Memory Error!\n");
  229. proceed = 0;
  230. goto done;
  231. }
  232. }
  233. if (loop) {
  234. loop_count = atoi(loop);
  235. }
  236. for (;loop_count > 0; loop_count--) {
  237. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG_CLEAN(session), SWITCH_LOG_DEBUG,
  238. "Dialplan: %s Action %s(%s) %s\n", switch_channel_get_name(channel), application, app_data, xinline ? "INLINE" : "");
  239. if (xinline) {
  240. exec_app(session, application, app_data);
  241. } else {
  242. switch_caller_extension_add_application(session, *extension, application, app_data);
  243. }
  244. }
  245. switch_safe_free(substituted);
  246. }
  247. }
  248. switch_regex_safe_free(re);
  249. if (((anti_action == SWITCH_FALSE && do_break_i == BREAK_ON_TRUE) ||
  250. (anti_action == SWITCH_TRUE && do_break_i == BREAK_ON_FALSE)) || do_break_i == BREAK_ALWAYS) {
  251. break;
  252. }
  253. }
  254. done:
  255. switch_regex_safe_free(re);
  256. switch_safe_free(field_expanded);
  257. switch_safe_free(expression_expanded);
  258. return proceed;
  259. }
  260. static switch_status_t dialplan_xml_locate(switch_core_session_t *session, switch_caller_profile_t *caller_profile, switch_xml_t *root,
  261. switch_xml_t *node)
  262. {
  263. switch_channel_t *channel = switch_core_session_get_channel(session);
  264. switch_status_t status = SWITCH_STATUS_GENERR;
  265. switch_event_t *params = NULL;
  266. switch_event_create(&params, SWITCH_EVENT_REQUEST_PARAMS);
  267. switch_assert(params);
  268. switch_channel_event_set_data(channel, params);
  269. switch_caller_profile_event_set_data(caller_profile, "Hunt", params);
  270. status = switch_xml_locate("dialplan", NULL, NULL, NULL, root, node, params, SWITCH_FALSE);
  271. switch_event_destroy(&params);
  272. return status;
  273. }
  274. SWITCH_STANDARD_DIALPLAN(dialplan_hunt)
  275. {
  276. switch_caller_extension_t *extension = NULL;
  277. switch_channel_t *channel = switch_core_session_get_channel(session);
  278. switch_xml_t alt_root = NULL, cfg, xml = NULL, xcontext, xexten = NULL;
  279. char *alt_path = (char *) arg;
  280. const char *hunt = NULL;
  281. if (!caller_profile) {
  282. if (!(caller_profile = switch_channel_get_caller_profile(channel))) {
  283. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error Obtaining Profile!\n");
  284. goto done;
  285. }
  286. }
  287. if (!caller_profile->context) {
  288. caller_profile->context = "default";
  289. }
  290. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Processing %s <%s>->%s in context %s\n",
  291. caller_profile->caller_id_name, caller_profile->caller_id_number, caller_profile->destination_number, caller_profile->context);
  292. /* get our handle to the "dialplan" section of the config */
  293. if (!zstr(alt_path)) {
  294. switch_xml_t conf = NULL, tag = NULL;
  295. if (!(alt_root = switch_xml_parse_file_simple(alt_path))) {
  296. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Open of [%s] failed\n", alt_path);
  297. goto done;
  298. }
  299. if ((conf = switch_xml_find_child(alt_root, "section", "name", "dialplan")) && (tag = switch_xml_find_child(conf, "dialplan", NULL, NULL))) {
  300. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Getting dialplan from alternate path: %s\n", alt_path);
  301. xml = alt_root;
  302. cfg = tag;
  303. } else {
  304. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Open of dialplan failed\n");
  305. goto done;
  306. }
  307. } else {
  308. if (dialplan_xml_locate(session, caller_profile, &xml, &cfg) != SWITCH_STATUS_SUCCESS) {
  309. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Open of dialplan failed\n");
  310. goto done;
  311. }
  312. }
  313. /* get a handle to the context tag */
  314. if (!(xcontext = switch_xml_find_child(cfg, "context", "name", caller_profile->context))) {
  315. if (!(xcontext = switch_xml_find_child(cfg, "context", "name", "global"))) {
  316. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Context %s not found\n", caller_profile->context);
  317. goto done;
  318. }
  319. }
  320. if ((hunt = switch_channel_get_variable(channel, "auto_hunt")) && switch_true(hunt)) {
  321. xexten = switch_xml_find_child(xcontext, "extension", "name", caller_profile->destination_number);
  322. }
  323. if (!xexten) {
  324. xexten = switch_xml_child(xcontext, "extension");
  325. }
  326. while (xexten) {
  327. int proceed = 0;
  328. const char *cont = switch_xml_attr(xexten, "continue");
  329. const char *exten_name = switch_xml_attr(xexten, "name");
  330. if (!exten_name) {
  331. exten_name = "UNKNOWN";
  332. }
  333. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG_CLEAN(session), SWITCH_LOG_DEBUG,
  334. "Dialplan: %s parsing [%s->%s] continue=%s\n",
  335. switch_channel_get_name(channel), caller_profile->context, exten_name, cont ? cont : "false");
  336. proceed = parse_exten(session, caller_profile, xexten, &extension);
  337. if (proceed && !switch_true(cont)) {
  338. break;
  339. }
  340. xexten = xexten->next;
  341. }
  342. switch_xml_free(xml);
  343. xml = NULL;
  344. done:
  345. switch_xml_free(xml);
  346. return extension;
  347. }
  348. SWITCH_MODULE_LOAD_FUNCTION(mod_dialplan_xml_load)
  349. {
  350. switch_dialplan_interface_t *dp_interface;
  351. /* connect my internal structure to the blank pointer passed to me */
  352. *module_interface = switch_loadable_module_create_module_interface(pool, modname);
  353. SWITCH_ADD_DIALPLAN(dp_interface, "XML", dialplan_hunt);
  354. /* indicate that the module should continue to be loaded */
  355. return SWITCH_STATUS_SUCCESS;
  356. }
  357. /* For Emacs:
  358. * Local Variables:
  359. * mode:c
  360. * indent-tabs-mode:t
  361. * tab-width:4
  362. * c-basic-offset:4
  363. * End:
  364. * For VIM:
  365. * vim:set softtabstop=4 shiftwidth=4 tabstop=4:
  366. */