switch_ivr_menu.c 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996
  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. * Neal Horman <neal at wanlink dot com>
  28. *
  29. * switch_ivr_menu.c -- IVR Library (menu code)
  30. *
  31. */
  32. #include <switch.h>
  33. struct switch_ivr_menu_action;
  34. struct switch_ivr_menu {
  35. char *name;
  36. char *greeting_sound;
  37. char *short_greeting_sound;
  38. char *invalid_sound;
  39. char *exit_sound;
  40. char *transfer_sound;
  41. char *buf;
  42. char *ptr;
  43. char *confirm_macro;
  44. char *confirm_key;
  45. char *tts_engine;
  46. char *tts_voice;
  47. int confirm_attempts;
  48. int digit_len;
  49. int max_failures;
  50. int max_timeouts;
  51. int timeout;
  52. int inter_timeout;
  53. char *exec_on_max_fail;
  54. char *exec_on_max_timeout;
  55. switch_size_t inlen;
  56. uint32_t flags;
  57. struct switch_ivr_menu_action *actions;
  58. struct switch_ivr_menu *next;
  59. switch_memory_pool_t *pool;
  60. int stack_count;
  61. char *pin;
  62. char *prompt_pin_file;
  63. char *bad_pin_file;
  64. };
  65. struct switch_ivr_menu_action {
  66. switch_ivr_menu_action_function_t *function;
  67. switch_ivr_action_t ivr_action;
  68. char *arg;
  69. char *bind;
  70. int re;
  71. struct switch_ivr_menu_action *next;
  72. };
  73. #define MENU_EVENT_ENTER "menu::enter"
  74. #define MENU_EVENT_EXIT "menu::exit"
  75. static void ivr_send_event(switch_core_session_t *session, char *event_type, switch_ivr_menu_t *menu)
  76. {
  77. switch_channel_t *channel = switch_core_session_get_channel(session);
  78. switch_event_t *event = NULL;
  79. if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, event_type) == SWITCH_STATUS_SUCCESS) {
  80. switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Menu-Name", menu->name);
  81. switch_channel_event_set_data(channel, event);
  82. switch_event_fire(&event);
  83. }
  84. }
  85. static switch_ivr_menu_t *switch_ivr_menu_find(switch_ivr_menu_t *stack, const char *name)
  86. {
  87. switch_ivr_menu_t *ret;
  88. for (ret = stack; ret; ret = ret->next) {
  89. if (!name || !strcmp(ret->name, name))
  90. break;
  91. }
  92. return ret;
  93. }
  94. static void switch_ivr_menu_stack_add(switch_ivr_menu_t ** top, switch_ivr_menu_t *bottom)
  95. {
  96. switch_ivr_menu_t *ptr;
  97. for (ptr = *top; ptr && ptr->next; ptr = ptr->next);
  98. if (ptr) {
  99. ptr->next = bottom;
  100. } else {
  101. *top = bottom;
  102. }
  103. }
  104. SWITCH_DECLARE(switch_status_t) switch_ivr_menu_init(switch_ivr_menu_t ** new_menu,
  105. switch_ivr_menu_t *main,
  106. const char *name,
  107. const char *greeting_sound,
  108. const char *short_greeting_sound,
  109. const char *invalid_sound,
  110. const char *exit_sound,
  111. const char *transfer_sound,
  112. const char *confirm_macro,
  113. const char *confirm_key,
  114. const char *tts_engine,
  115. const char *tts_voice,
  116. int confirm_attempts,
  117. int inter_timeout,
  118. int digit_len, int timeout, int max_failures, int max_timeouts, switch_memory_pool_t *pool)
  119. {
  120. switch_ivr_menu_t *menu;
  121. uint8_t newpool = 0;
  122. if (!pool) {
  123. if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
  124. switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "OH OH no pool\n");
  125. return SWITCH_STATUS_MEMERR;
  126. }
  127. newpool = 1;
  128. }
  129. if (!(menu = switch_core_alloc(pool, sizeof(*menu)))) {
  130. if (newpool) {
  131. switch_core_destroy_memory_pool(&pool);
  132. }
  133. switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error!\n");
  134. return SWITCH_STATUS_MEMERR;
  135. }
  136. menu->pool = pool;
  137. if (!confirm_attempts) {
  138. confirm_attempts = 3;
  139. }
  140. if (!inter_timeout) {
  141. inter_timeout = timeout / 2;
  142. }
  143. if (!zstr(name)) {
  144. menu->name = switch_core_strdup(menu->pool, name);
  145. }
  146. if (!zstr(greeting_sound)) {
  147. menu->greeting_sound = switch_core_strdup(menu->pool, greeting_sound);
  148. }
  149. if (!zstr(short_greeting_sound)) {
  150. menu->short_greeting_sound = switch_core_strdup(menu->pool, short_greeting_sound);
  151. }
  152. if (!zstr(invalid_sound)) {
  153. menu->invalid_sound = switch_core_strdup(menu->pool, invalid_sound);
  154. }
  155. if (!zstr(transfer_sound)) {
  156. menu->transfer_sound = switch_core_strdup(menu->pool, transfer_sound);
  157. }
  158. if (!zstr(exit_sound)) {
  159. menu->exit_sound = switch_core_strdup(menu->pool, exit_sound);
  160. }
  161. if (!zstr(confirm_key)) {
  162. menu->confirm_key = switch_core_strdup(menu->pool, confirm_key);
  163. }
  164. if (!zstr(confirm_macro)) {
  165. menu->confirm_macro = switch_core_strdup(menu->pool, confirm_macro);
  166. }
  167. if (!zstr(tts_engine)) {
  168. menu->tts_engine = switch_core_strdup(menu->pool, tts_engine);
  169. }
  170. if (!zstr(tts_voice)) {
  171. menu->tts_voice = switch_core_strdup(menu->pool, tts_voice);
  172. }
  173. menu->confirm_attempts = confirm_attempts;
  174. menu->inlen = digit_len;
  175. if (max_failures > 0) {
  176. menu->max_failures = max_failures;
  177. } else {
  178. menu->max_failures = 3;
  179. }
  180. if (max_timeouts > 0) {
  181. menu->max_timeouts = max_timeouts;
  182. } else {
  183. menu->max_timeouts = 3;
  184. }
  185. menu->timeout = timeout;
  186. menu->inter_timeout = inter_timeout;
  187. menu->actions = NULL;
  188. if (newpool) {
  189. switch_set_flag(menu, SWITCH_IVR_MENU_FLAG_FREEPOOL);
  190. }
  191. if (menu->timeout <= 0) {
  192. menu->timeout = 10000;
  193. }
  194. if (main) {
  195. switch_ivr_menu_stack_add(&main, menu);
  196. } else {
  197. switch_set_flag(menu, SWITCH_IVR_MENU_FLAG_STACK);
  198. }
  199. menu->buf = switch_core_alloc(menu->pool, 1024);
  200. *new_menu = menu;
  201. return SWITCH_STATUS_SUCCESS;
  202. }
  203. SWITCH_DECLARE(switch_status_t) switch_ivr_menu_bind_action(switch_ivr_menu_t *menu, switch_ivr_action_t ivr_action, const char *arg, const char *bind)
  204. {
  205. switch_ivr_menu_action_t *action, *ap;
  206. uint32_t len;
  207. if ((action = switch_core_alloc(menu->pool, sizeof(*action)))) {
  208. action->bind = switch_core_strdup(menu->pool, bind);
  209. action->arg = switch_core_strdup(menu->pool, arg);
  210. if (*action->bind == '/') {
  211. action->re = 1;
  212. } else {
  213. len = (uint32_t) strlen(action->bind);
  214. if (len > menu->inlen) {
  215. menu->inlen = len;
  216. }
  217. }
  218. action->ivr_action = ivr_action;
  219. if (menu->actions) {
  220. for(ap = menu->actions; ap && ap->next; ap = ap->next);
  221. ap->next = action;
  222. } else {
  223. menu->actions = action;
  224. }
  225. return SWITCH_STATUS_SUCCESS;
  226. }
  227. return SWITCH_STATUS_MEMERR;
  228. }
  229. SWITCH_DECLARE(switch_status_t) switch_ivr_menu_bind_function(switch_ivr_menu_t *menu,
  230. switch_ivr_menu_action_function_t *function, const char *arg, const char *bind)
  231. {
  232. switch_ivr_menu_action_t *action, *ap;
  233. uint32_t len;
  234. if ((action = switch_core_alloc(menu->pool, sizeof(*action)))) {
  235. action->bind = switch_core_strdup(menu->pool, bind);
  236. action->arg = switch_core_strdup(menu->pool, arg);
  237. if (*action->bind == '/') {
  238. action->re = 1;
  239. } else {
  240. len = (uint32_t) strlen(action->bind);
  241. if (len > menu->inlen) {
  242. menu->inlen = len;
  243. }
  244. }
  245. action->function = function;
  246. if (menu->actions) {
  247. for(ap = menu->actions; ap && ap->next; ap = ap->next);
  248. ap->next = action;
  249. } else {
  250. menu->actions = action;
  251. }
  252. return SWITCH_STATUS_SUCCESS;
  253. }
  254. return SWITCH_STATUS_MEMERR;
  255. }
  256. SWITCH_DECLARE(switch_status_t) switch_ivr_menu_stack_free(switch_ivr_menu_t *stack)
  257. {
  258. switch_status_t status = SWITCH_STATUS_FALSE;
  259. if (stack != NULL && stack->pool != NULL) {
  260. if (switch_test_flag(stack, SWITCH_IVR_MENU_FLAG_STACK)
  261. && switch_test_flag(stack, SWITCH_IVR_MENU_FLAG_FREEPOOL)) {
  262. switch_memory_pool_t *pool = stack->pool;
  263. status = switch_core_destroy_memory_pool(&pool);
  264. } else {
  265. status = SWITCH_STATUS_SUCCESS;
  266. }
  267. }
  268. return status;
  269. }
  270. static switch_status_t play_and_collect(switch_core_session_t *session, switch_ivr_menu_t *menu, char *sound, switch_size_t need)
  271. {
  272. char terminator;
  273. uint32_t len;
  274. char *ptr;
  275. switch_status_t status = SWITCH_STATUS_FALSE;
  276. switch_input_args_t args = { 0 };
  277. switch_channel_t *channel;
  278. char *sound_expanded = sound;
  279. switch_size_t menu_buf_len = 0;
  280. const char *terminator_str = "#";
  281. if (!session || !menu || zstr(sound)) {
  282. return status;
  283. }
  284. if ((channel = switch_core_session_get_channel(session))) {
  285. const char *tmp;
  286. sound_expanded = switch_channel_expand_variables(channel, sound);
  287. if ((tmp = switch_channel_get_variable(channel, "ivr_menu_terminator")) && !zstr(tmp)) {
  288. terminator_str = tmp;
  289. }
  290. }
  291. memset(menu->buf, 0, menu->inlen + 1);
  292. menu->ptr = menu->buf;
  293. if (!need) {
  294. len = 1;
  295. ptr = NULL;
  296. } else {
  297. len = (uint32_t) menu->inlen + 1;
  298. ptr = menu->ptr;
  299. }
  300. args.buf = ptr;
  301. args.buflen = len;
  302. status = switch_ivr_play_file(session, NULL, sound_expanded, &args);
  303. if (sound_expanded != sound) {
  304. switch_safe_free(sound_expanded);
  305. }
  306. if (!need) {
  307. return status;
  308. }
  309. menu_buf_len = strlen(menu->buf);
  310. menu->ptr += menu_buf_len;
  311. if (menu_buf_len < need) {
  312. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "waiting for %u/%u digits t/o %d\n",
  313. (uint32_t) (menu->inlen - strlen(menu->buf)), (uint32_t) need, menu->inter_timeout);
  314. status = switch_ivr_collect_digits_count(session, menu->ptr, menu->inlen - strlen(menu->buf),
  315. need, terminator_str, &terminator, menu_buf_len ? menu->inter_timeout : menu->timeout,
  316. menu->inter_timeout, menu->timeout);
  317. }
  318. if (menu->confirm_macro && status == SWITCH_STATUS_SUCCESS && *menu->buf != '\0') {
  319. switch_input_args_t confirm_args = { 0 }, *ap = NULL;
  320. char buf[10] = "";
  321. char terminator_key;
  322. int att = menu->confirm_attempts;
  323. while (att) {
  324. confirm_args.buf = buf;
  325. confirm_args.buflen = sizeof(buf);
  326. memset(buf, 0, confirm_args.buflen);
  327. if (menu->confirm_key) {
  328. ap = &confirm_args;
  329. }
  330. switch_ivr_phrase_macro(session, menu->confirm_macro, menu->buf, NULL, ap);
  331. if (menu->confirm_key && *buf == '\0') {
  332. switch_ivr_collect_digits_count(session, buf, sizeof(buf), 1, terminator_str, &terminator_key, menu->timeout, 0, 0);
  333. }
  334. if (menu->confirm_key && *buf != '\0') {
  335. if (*menu->confirm_key == *buf) {
  336. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG,
  337. "approving digits '%s' via confirm key %s\n", menu->buf, menu->confirm_key);
  338. break;
  339. } else {
  340. att = 0;
  341. break;
  342. }
  343. }
  344. att--;
  345. }
  346. if (!att) {
  347. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "rejecting digits '%s' via confirm key %s\n", menu->buf,
  348. menu->confirm_key);
  349. *menu->buf = '\0';
  350. }
  351. }
  352. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "digits '%s'\n", menu->buf);
  353. return status;
  354. }
  355. static void exec_app(switch_core_session_t *session, char *app_str)
  356. {
  357. switch_channel_t *channel = switch_core_session_get_channel(session);
  358. char *app = switch_core_session_strdup(session, app_str);
  359. char *data = strchr(app, ' ');
  360. char *expanded = NULL;
  361. if (data) {
  362. *data++ = '\0';
  363. }
  364. expanded = switch_channel_expand_variables(channel, data);
  365. switch_core_session_execute_application(session, app, expanded);
  366. if (expanded && expanded != data) {
  367. free(expanded);
  368. }
  369. }
  370. SWITCH_DECLARE(switch_status_t) switch_ivr_menu_execute(switch_core_session_t *session, switch_ivr_menu_t *stack, char *name, void *obj)
  371. {
  372. int reps = 0, errs = 0, timeouts = 0, match = 0, running = 1;
  373. char *greeting_sound = NULL, *aptr = NULL;
  374. char arg[512];
  375. switch_ivr_action_t todo = SWITCH_IVR_ACTION_DIE;
  376. switch_ivr_menu_action_t *ap;
  377. switch_ivr_menu_t *menu = NULL;
  378. switch_channel_t *channel;
  379. switch_status_t status = SWITCH_STATUS_SUCCESS;
  380. switch_assert(stack);
  381. if (++stack->stack_count > 12) {
  382. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Too many levels of recursion.\n");
  383. switch_goto_status(SWITCH_STATUS_FALSE, end);
  384. }
  385. if (!session || zstr(name)) {
  386. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Invalid menu context\n");
  387. switch_goto_status(SWITCH_STATUS_FALSE, end);
  388. }
  389. channel = switch_core_session_get_channel(session);
  390. if (!(menu = switch_ivr_menu_find(stack, name))) {
  391. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Invalid Menu!\n");
  392. switch_goto_status(SWITCH_STATUS_FALSE, end);
  393. }
  394. if (!zstr(menu->tts_engine) && !zstr(menu->tts_voice)) {
  395. switch_channel_set_variable(channel, "tts_engine", menu->tts_engine);
  396. switch_channel_set_variable(channel, "tts_voice", menu->tts_voice);
  397. }
  398. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Executing IVR menu %s\n", menu->name);
  399. switch_channel_set_variable(channel, "ivr_menu_status", "success");
  400. ivr_send_event(session, MENU_EVENT_ENTER, menu);
  401. if (!zstr(menu->pin)) {
  402. char digit_buffer[128] = "";
  403. char *digits_regex = switch_core_session_sprintf(session, "^%s$", menu->pin);
  404. if (switch_play_and_get_digits(session, (uint32_t)strlen(menu->pin), (uint32_t)strlen(menu->pin), 3, 3000, "#",
  405. menu->prompt_pin_file, menu->bad_pin_file, NULL, digit_buffer, sizeof(digit_buffer),
  406. digits_regex, 10000, NULL) != SWITCH_STATUS_SUCCESS) {
  407. switch_goto_status(SWITCH_STATUS_FALSE, end);
  408. }
  409. }
  410. for (reps = 0; running && status == SWITCH_STATUS_SUCCESS; reps++) {
  411. if (!switch_channel_ready(channel)) {
  412. break;
  413. }
  414. if (errs == menu->max_failures) {
  415. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Maximum failures\n");
  416. switch_channel_set_variable(channel, "ivr_menu_status", "failure");
  417. if (!zstr(menu->exec_on_max_fail)) {
  418. exec_app(session, menu->exec_on_max_fail);
  419. }
  420. break;
  421. }
  422. if (timeouts == menu->max_timeouts) {
  423. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Maximum timeouts\n");
  424. switch_channel_set_variable(channel, "ivr_menu_status", "timeout");
  425. if (!zstr(menu->exec_on_max_timeout)) {
  426. exec_app(session, menu->exec_on_max_timeout);
  427. }
  428. break;
  429. }
  430. if (reps > 0 && menu->short_greeting_sound) {
  431. greeting_sound = menu->short_greeting_sound;
  432. } else {
  433. greeting_sound = menu->greeting_sound;
  434. }
  435. match = 0;
  436. aptr = NULL;
  437. memset(arg, 0, sizeof(arg));
  438. memset(menu->buf, 0, menu->inlen + 1);
  439. if (play_and_collect(session, menu, greeting_sound, menu->inlen) == SWITCH_STATUS_TIMEOUT && *menu->buf == '\0') {
  440. timeouts++;
  441. continue;
  442. }
  443. if (*menu->buf != '\0') {
  444. for (ap = menu->actions; ap; ap = ap->next) {
  445. int ok = 0;
  446. char substituted[1024];
  447. char *use_arg = ap->arg;
  448. if (!zstr(menu->tts_engine) && !zstr(menu->tts_voice)) {
  449. switch_channel_set_variable(channel, "tts_engine", menu->tts_engine);
  450. switch_channel_set_variable(channel, "tts_voice", menu->tts_voice);
  451. }
  452. if (ap->re) {
  453. switch_regex_t *re = NULL;
  454. int ovector[30];
  455. if ((ok = switch_regex_perform(menu->buf, ap->bind, &re, ovector, sizeof(ovector) / sizeof(ovector[0])))) {
  456. switch_perform_substitution(re, ok, ap->arg, menu->buf, substituted, sizeof(substituted), ovector);
  457. use_arg = substituted;
  458. }
  459. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "action regex [%s] [%s] [%d]\n", menu->buf, ap->bind, ok);
  460. switch_regex_safe_free(re);
  461. } else {
  462. ok = !strcmp(menu->buf, ap->bind);
  463. }
  464. if (ok) {
  465. match++;
  466. errs = 0;
  467. if (ap->function) {
  468. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG,
  469. "IVR function on menu '%s' matched '%s' param '%s'\n", menu->name, menu->buf, use_arg);
  470. todo = ap->function(menu, use_arg, arg, sizeof(arg), obj);
  471. aptr = arg;
  472. } else {
  473. todo = ap->ivr_action;
  474. aptr = use_arg;
  475. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG,
  476. "IVR action on menu '%s' matched '%s' param '%s'\n", menu->name, menu->buf, aptr);
  477. }
  478. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "switch_ivr_menu_execute todo=[%d]\n", todo);
  479. switch (todo) {
  480. case SWITCH_IVR_ACTION_DIE:
  481. status = SWITCH_STATUS_FALSE;
  482. break;
  483. case SWITCH_IVR_ACTION_PLAYSOUND:
  484. status = switch_ivr_play_file(session, NULL, aptr, NULL);
  485. break;
  486. case SWITCH_IVR_ACTION_EXECMENU:
  487. if (!strcmp(aptr, menu->name)) {
  488. status = SWITCH_STATUS_SUCCESS;
  489. } else {
  490. reps = -1;
  491. ivr_send_event(session, MENU_EVENT_EXIT, menu);
  492. status = switch_ivr_menu_execute(session, stack, aptr, obj);
  493. ivr_send_event(session, MENU_EVENT_ENTER, menu);
  494. }
  495. break;
  496. case SWITCH_IVR_ACTION_EXECAPP:
  497. {
  498. switch_application_interface_t *application_interface;
  499. char *app_name;
  500. char *app_arg = NULL;
  501. status = SWITCH_STATUS_FALSE;
  502. if (!zstr(aptr)) {
  503. app_name = switch_core_session_strdup(session, aptr);
  504. if ((app_arg = strchr(app_name, ' '))) {
  505. *app_arg++ = '\0';
  506. }
  507. if ((application_interface = switch_loadable_module_get_application_interface(app_name))) {
  508. if (!zstr(menu->transfer_sound) && !strcmp(app_name, "transfer")) {
  509. play_and_collect(session, menu, menu->transfer_sound, 0);
  510. }
  511. switch_core_session_exec(session, application_interface, app_arg);
  512. UNPROTECT_INTERFACE(application_interface);
  513. status = SWITCH_STATUS_SUCCESS;
  514. }
  515. }
  516. }
  517. break;
  518. case SWITCH_IVR_ACTION_BACK:
  519. running = 0;
  520. status = SWITCH_STATUS_SUCCESS;
  521. break;
  522. case SWITCH_IVR_ACTION_TOMAIN:
  523. switch_set_flag(stack, SWITCH_IVR_MENU_FLAG_FALLTOMAIN);
  524. status = SWITCH_STATUS_BREAK;
  525. break;
  526. case SWITCH_IVR_ACTION_NOOP:
  527. status = SWITCH_STATUS_SUCCESS;
  528. break;
  529. default:
  530. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Invalid TODO!\n");
  531. break;
  532. }
  533. }
  534. }
  535. if (switch_test_flag(menu, SWITCH_IVR_MENU_FLAG_STACK)) { /* top level */
  536. if (switch_test_flag(stack, SWITCH_IVR_MENU_FLAG_FALLTOMAIN)) { /* catch the fallback and recover */
  537. switch_clear_flag(stack, SWITCH_IVR_MENU_FLAG_FALLTOMAIN);
  538. status = SWITCH_STATUS_SUCCESS;
  539. running = 1;
  540. continue;
  541. }
  542. }
  543. }
  544. if (!match) {
  545. if (*menu->buf) {
  546. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "IVR menu '%s' caught invalid input '%s'\n", menu->name,
  547. menu->buf);
  548. if (menu->invalid_sound) {
  549. play_and_collect(session, menu, menu->invalid_sound, 0);
  550. }
  551. } else {
  552. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "IVR menu '%s' no input detected\n", menu->name);
  553. }
  554. errs++;
  555. /* breaks are ok too */
  556. if (SWITCH_STATUS_IS_BREAK(status)) {
  557. status = SWITCH_STATUS_SUCCESS;
  558. }
  559. }
  560. }
  561. if (stack->stack_count == 1) {
  562. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "exit-sound '%s'\n", menu->exit_sound);
  563. if (!zstr(menu->exit_sound)) {
  564. status = play_and_collect(session, menu, menu->exit_sound, 0);
  565. }
  566. }
  567. end:
  568. stack->stack_count--;
  569. if (menu) {
  570. ivr_send_event(session, MENU_EVENT_EXIT, menu);
  571. }
  572. return status;
  573. }
  574. /******************************************************************************************************/
  575. typedef struct switch_ivr_menu_xml_map {
  576. char *name;
  577. switch_ivr_action_t action;
  578. switch_ivr_menu_action_function_t *function;
  579. struct switch_ivr_menu_xml_map *next;
  580. } switch_ivr_menu_xml_map_t;
  581. struct switch_ivr_menu_xml_ctx {
  582. switch_memory_pool_t *pool;
  583. struct switch_ivr_menu_xml_map *map;
  584. int autocreated;
  585. };
  586. static switch_ivr_menu_xml_map_t *switch_ivr_menu_stack_xml_find(switch_ivr_menu_xml_ctx_t *xml_ctx, const char *name)
  587. {
  588. switch_ivr_menu_xml_map_t *map = (xml_ctx != NULL ? xml_ctx->map : NULL);
  589. int rc = -1;
  590. while (map != NULL && (rc = strcasecmp(map->name, name)) != 0) {
  591. map = map->next;
  592. }
  593. return (rc == 0 ? map : NULL);
  594. }
  595. static switch_status_t switch_ivr_menu_stack_xml_add(switch_ivr_menu_xml_ctx_t *xml_ctx, const char *name, int action,
  596. switch_ivr_menu_action_function_t *function)
  597. {
  598. switch_status_t status = SWITCH_STATUS_FALSE;
  599. /* if this action/function does not exist yet */
  600. if (xml_ctx != NULL && name != NULL && xml_ctx->pool != NULL && switch_ivr_menu_stack_xml_find(xml_ctx, name) == NULL) {
  601. switch_ivr_menu_xml_map_t *map = switch_core_alloc(xml_ctx->pool, sizeof(switch_ivr_menu_xml_map_t));
  602. if (map != NULL) {
  603. map->name = switch_core_strdup(xml_ctx->pool, name);
  604. map->action = action;
  605. map->function = function;
  606. if (map->name != NULL) {
  607. /* insert map item at top of list */
  608. map->next = xml_ctx->map;
  609. xml_ctx->map = map;
  610. status = SWITCH_STATUS_SUCCESS;
  611. } else {
  612. status = SWITCH_STATUS_MEMERR;
  613. }
  614. } else {
  615. status = SWITCH_STATUS_MEMERR;
  616. }
  617. switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "switch_ivr_menu_stack_xml_add binding '%s'\n", name);
  618. } else {
  619. switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to add binding %s\n", name);
  620. }
  621. return status;
  622. }
  623. static struct iam_s {
  624. const char *name;
  625. switch_ivr_action_t action;
  626. } iam[] = {
  627. {
  628. "menu-exit", SWITCH_IVR_ACTION_DIE}, {
  629. "menu-sub", SWITCH_IVR_ACTION_EXECMENU}, {
  630. "menu-exec-app", SWITCH_IVR_ACTION_EXECAPP}, {
  631. "menu-play-sound", SWITCH_IVR_ACTION_PLAYSOUND}, {
  632. "menu-back", SWITCH_IVR_ACTION_BACK}, {
  633. "menu-top", SWITCH_IVR_ACTION_TOMAIN}, {
  634. NULL, 0}
  635. };
  636. SWITCH_DECLARE(switch_status_t) switch_ivr_menu_str2action(const char *action_name, switch_ivr_action_t *action)
  637. {
  638. int i;
  639. if (!zstr(action_name)) {
  640. for (i = 0;; i++) {
  641. if (!iam[i].name) {
  642. break;
  643. }
  644. if (!strcasecmp(iam[i].name, action_name)) {
  645. *action = iam[i].action;
  646. return SWITCH_STATUS_SUCCESS;
  647. }
  648. }
  649. }
  650. return SWITCH_STATUS_FALSE;
  651. }
  652. static switch_bool_t is_valid_action(const char *action)
  653. {
  654. int i;
  655. if (!zstr(action)) {
  656. for (i = 0;; i++) {
  657. if (!iam[i].name) {
  658. break;
  659. }
  660. if (!strcmp(iam[i].name, action)) {
  661. return SWITCH_TRUE;
  662. }
  663. }
  664. }
  665. switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid Action [%s]\n", switch_str_nil(action));
  666. return SWITCH_FALSE;
  667. }
  668. SWITCH_DECLARE(switch_status_t) switch_ivr_menu_stack_xml_init(switch_ivr_menu_xml_ctx_t ** xml_menu_ctx, switch_memory_pool_t *pool)
  669. {
  670. switch_status_t status = SWITCH_STATUS_FALSE;
  671. int autocreated = 0;
  672. /* build a memory pool ? */
  673. if (pool == NULL) {
  674. status = switch_core_new_memory_pool(&pool);
  675. autocreated = 1;
  676. }
  677. /* allocate the xml context */
  678. if (xml_menu_ctx != NULL && pool != NULL) {
  679. *xml_menu_ctx = switch_core_alloc(pool, sizeof(switch_ivr_menu_xml_ctx_t));
  680. if (*xml_menu_ctx != NULL) {
  681. (*xml_menu_ctx)->pool = pool;
  682. (*xml_menu_ctx)->autocreated = autocreated;
  683. (*xml_menu_ctx)->map = NULL;
  684. status = SWITCH_STATUS_SUCCESS;
  685. } else {
  686. switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to alloc xml_ctx\n");
  687. status = SWITCH_STATUS_FALSE;
  688. }
  689. }
  690. /* build the standard/default xml menu handler mappings */
  691. if (status == SWITCH_STATUS_SUCCESS && xml_menu_ctx != NULL && *xml_menu_ctx != NULL) {
  692. int i;
  693. for (i = 0; iam[i].name && status == SWITCH_STATUS_SUCCESS; i++) {
  694. status = switch_ivr_menu_stack_xml_add(*xml_menu_ctx, iam[i].name, iam[i].action, NULL);
  695. }
  696. }
  697. return status;
  698. }
  699. SWITCH_DECLARE(switch_status_t) switch_ivr_menu_stack_xml_add_custom(switch_ivr_menu_xml_ctx_t *xml_menu_ctx,
  700. const char *name, switch_ivr_menu_action_function_t *function)
  701. {
  702. return switch_ivr_menu_stack_xml_add(xml_menu_ctx, name, -1, function);
  703. }
  704. SWITCH_DECLARE(switch_status_t) switch_ivr_menu_stack_xml_build(switch_ivr_menu_xml_ctx_t *xml_menu_ctx,
  705. switch_ivr_menu_t ** menu_stack, switch_xml_t xml_menus, switch_xml_t xml_menu)
  706. {
  707. switch_status_t status = SWITCH_STATUS_FALSE;
  708. if (xml_menu_ctx != NULL && menu_stack != NULL && xml_menu != NULL) {
  709. const char *menu_name = switch_xml_attr_soft(xml_menu, "name"); /* if the attr doesn't exist, return "" */
  710. const char *greet_long = switch_xml_attr(xml_menu, "greet-long"); /* if the attr doesn't exist, return NULL */
  711. const char *greet_short = switch_xml_attr(xml_menu, "greet-short"); /* if the attr doesn't exist, return NULL */
  712. const char *invalid_sound = switch_xml_attr(xml_menu, "invalid-sound"); /* if the attr doesn't exist, return NULL */
  713. const char *exit_sound = switch_xml_attr(xml_menu, "exit-sound"); /* if the attr doesn't exist, return NULL */
  714. const char *transfer_sound = switch_xml_attr(xml_menu, "transfer-sound"); /* if the attr doesn't exist, return NULL */
  715. const char *timeout = switch_xml_attr_soft(xml_menu, "timeout"); /* if the attr doesn't exist, return "" */
  716. const char *max_failures = switch_xml_attr_soft(xml_menu, "max-failures"); /* if the attr doesn't exist, return "" */
  717. const char *max_timeouts = switch_xml_attr_soft(xml_menu, "max-timeouts");
  718. const char *exec_on_max_fail = switch_xml_attr(xml_menu, "exec-on-max-failures");
  719. const char *exec_on_max_timeout = switch_xml_attr(xml_menu, "exec-on-max-timeouts");
  720. const char *confirm_macro = switch_xml_attr(xml_menu, "confirm-macro");
  721. const char *confirm_key = switch_xml_attr(xml_menu, "confirm-key");
  722. const char *tts_engine = switch_xml_attr(xml_menu, "tts-engine");
  723. const char *tts_voice = switch_xml_attr(xml_menu, "tts-voice");
  724. const char *confirm_attempts = switch_xml_attr_soft(xml_menu, "confirm-attempts");
  725. const char *digit_len = switch_xml_attr_soft(xml_menu, "digit-len");
  726. const char *inter_timeout = switch_xml_attr_soft(xml_menu, "inter-digit-timeout");
  727. const char *pin = switch_xml_attr_soft(xml_menu, "pin");
  728. const char *prompt_pin_file = switch_xml_attr_soft(xml_menu, "pin-file");
  729. const char *bad_pin_file = switch_xml_attr_soft(xml_menu, "bad-pin-file");
  730. switch_ivr_menu_t *menu = NULL;
  731. if (zstr(max_timeouts)) {
  732. max_timeouts = max_failures;
  733. }
  734. switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "building menu '%s'\n", menu_name);
  735. status = switch_ivr_menu_init(&menu,
  736. *menu_stack,
  737. menu_name,
  738. greet_long,
  739. greet_short,
  740. invalid_sound,
  741. exit_sound,
  742. transfer_sound,
  743. confirm_macro,
  744. confirm_key,
  745. tts_engine,
  746. tts_voice,
  747. atoi(confirm_attempts),
  748. atoi(inter_timeout),
  749. atoi(digit_len),
  750. atoi(timeout),
  751. strlen(max_failures) ? atoi(max_failures) : 0, strlen(max_timeouts) ? atoi(max_timeouts) : 0, xml_menu_ctx->pool);
  752. switch_assert(menu);
  753. if (!zstr(exec_on_max_fail)) {
  754. menu->exec_on_max_fail = switch_core_strdup(menu->pool, exec_on_max_fail);
  755. }
  756. if (!zstr(exec_on_max_timeout)) {
  757. menu->exec_on_max_timeout = switch_core_strdup(menu->pool, exec_on_max_timeout);
  758. }
  759. if (!zstr(pin)) {
  760. if (zstr(prompt_pin_file)) {
  761. prompt_pin_file = "ivr/ivr-please_enter_pin_followed_by_pound.wav";
  762. }
  763. if (zstr(bad_pin_file)) {
  764. bad_pin_file = "conference/conf-bad-pin.wav";
  765. }
  766. menu->pin = switch_core_strdup(menu->pool, pin);
  767. menu->prompt_pin_file = switch_core_strdup(menu->pool, prompt_pin_file);
  768. menu->bad_pin_file = switch_core_strdup(menu->pool, bad_pin_file);
  769. }
  770. /* set the menu_stack for the caller */
  771. if (status == SWITCH_STATUS_SUCCESS && *menu_stack == NULL) {
  772. *menu_stack = menu;
  773. if (xml_menu_ctx->autocreated) {
  774. switch_set_flag(menu, SWITCH_IVR_MENU_FLAG_FREEPOOL);
  775. }
  776. }
  777. if (status == SWITCH_STATUS_SUCCESS) {
  778. switch_xml_t xml_kvp;
  779. /* build menu entries */
  780. for (xml_kvp = switch_xml_child(xml_menu, "entry"); xml_kvp != NULL && status == SWITCH_STATUS_SUCCESS; xml_kvp = xml_kvp->next) {
  781. const char *action = switch_xml_attr(xml_kvp, "action");
  782. const char *digits = switch_xml_attr(xml_kvp, "digits");
  783. const char *param = switch_xml_attr_soft(xml_kvp, "param");
  784. if (is_valid_action(action) && !zstr(digits)) {
  785. switch_ivr_menu_xml_map_t *xml_map = xml_menu_ctx->map;
  786. int found = 0;
  787. /* find and appropriate xml handler */
  788. while (xml_map != NULL && !found) {
  789. if (!(found = (strcasecmp(xml_map->name, action) == 0))) {
  790. xml_map = xml_map->next;
  791. }
  792. }
  793. if (found && xml_map != NULL) {
  794. /* do we need to build a new sub-menu ? */
  795. if (xml_map->action == SWITCH_IVR_ACTION_EXECMENU && switch_ivr_menu_find(*menu_stack, param) == NULL) {
  796. if ((xml_menu = switch_xml_find_child(xml_menus, "menu", "name", param)) != NULL) {
  797. status = switch_ivr_menu_stack_xml_build(xml_menu_ctx, menu_stack, xml_menus, xml_menu);
  798. }
  799. }
  800. /* finally bind the menu entry */
  801. if (status == SWITCH_STATUS_SUCCESS) {
  802. if (xml_map->function != NULL) {
  803. switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,
  804. "binding menu caller control '%s'/'%s' to '%s'\n", xml_map->name, param, digits);
  805. status = switch_ivr_menu_bind_function(menu, xml_map->function, param, digits);
  806. } else {
  807. switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "binding menu action '%s' to '%s'\n", xml_map->name, digits);
  808. status = switch_ivr_menu_bind_action(menu, xml_map->action, param, digits);
  809. }
  810. }
  811. }
  812. } else {
  813. status = SWITCH_STATUS_FALSE;
  814. }
  815. }
  816. }
  817. }
  818. if (status != SWITCH_STATUS_SUCCESS) {
  819. switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to build xml menu\n");
  820. }
  821. return status;
  822. }
  823. /* For Emacs:
  824. * Local Variables:
  825. * mode:c
  826. * indent-tabs-mode:t
  827. * tab-width:4
  828. * c-basic-offset:4
  829. * End:
  830. * For VIM:
  831. * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
  832. */