FreeSWITCH API Documentation  1.7.0
switch_ivr_menu.c
Go to the documentation of this file.
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 
33 #include <switch.h>
34 
36 
38  char *name;
42  char *exit_sound;
44  char *buf;
45  char *ptr;
47  char *confirm_key;
48  char *tts_engine;
49  char *tts_voice;
51  int digit_len;
54  int timeout;
59  uint32_t flags;
64  char *pin;
66  char *bad_pin_file;
67 };
68 
72  char *arg;
73  char *bind;
74  int re;
76 };
77 
78 #define MENU_EVENT_ENTER "menu::enter"
79 #define MENU_EVENT_EXIT "menu::exit"
80 
81 static void ivr_send_event(switch_core_session_t *session, char *event_type, switch_ivr_menu_t *menu)
82 {
84  switch_event_t *event = NULL;
86  switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Menu-Name", menu->name);
87  switch_channel_event_set_data(channel, event);
88  switch_event_fire(&event);
89  }
90 }
91 
92 static switch_ivr_menu_t *switch_ivr_menu_find(switch_ivr_menu_t *stack, const char *name)
93 {
94  switch_ivr_menu_t *ret;
95  for (ret = stack; ret; ret = ret->next) {
96  if (!name || !strcmp(ret->name, name))
97  break;
98  }
99  return ret;
100 }
101 
103 {
104  switch_ivr_menu_t *ptr;
105 
106  for (ptr = *top; ptr && ptr->next; ptr = ptr->next);
107 
108  if (ptr) {
109  ptr->next = bottom;
110  } else {
111  *top = bottom;
112  }
113 
114 }
115 
118  const char *name,
119  const char *greeting_sound,
120  const char *short_greeting_sound,
121  const char *invalid_sound,
122  const char *exit_sound,
123  const char *transfer_sound,
124  const char *confirm_macro,
125  const char *confirm_key,
126  const char *tts_engine,
127  const char *tts_voice,
128  int confirm_attempts,
129  int inter_timeout,
130  int digit_len, int timeout, int max_failures, int max_timeouts, switch_memory_pool_t *pool)
131 {
132  switch_ivr_menu_t *menu;
133  uint8_t newpool = 0;
134 
135  if (!pool) {
138  return SWITCH_STATUS_MEMERR;
139  }
140  newpool = 1;
141  }
142 
143  if (!(menu = switch_core_alloc(pool, sizeof(*menu)))) {
144  if (newpool) {
147  return SWITCH_STATUS_MEMERR;
148  }
149  }
150 
151  menu->pool = pool;
152 
153  if (!confirm_attempts) {
154  confirm_attempts = 3;
155  }
156 
157  if (!inter_timeout) {
158  inter_timeout = timeout / 2;
159  }
160 
161  if (!zstr(name)) {
162  menu->name = switch_core_strdup(menu->pool, name);
163  }
164 
165  if (!zstr(greeting_sound)) {
166  menu->greeting_sound = switch_core_strdup(menu->pool, greeting_sound);
167  }
168 
169  if (!zstr(short_greeting_sound)) {
170  menu->short_greeting_sound = switch_core_strdup(menu->pool, short_greeting_sound);
171  }
172 
173  if (!zstr(invalid_sound)) {
174  menu->invalid_sound = switch_core_strdup(menu->pool, invalid_sound);
175  }
176 
177  if (!zstr(transfer_sound)) {
178  menu->transfer_sound = switch_core_strdup(menu->pool, transfer_sound);
179  }
180 
181  if (!zstr(exit_sound)) {
182  menu->exit_sound = switch_core_strdup(menu->pool, exit_sound);
183  }
184 
185  if (!zstr(confirm_key)) {
186  menu->confirm_key = switch_core_strdup(menu->pool, confirm_key);
187  }
188 
189  if (!zstr(confirm_macro)) {
190  menu->confirm_macro = switch_core_strdup(menu->pool, confirm_macro);
191  }
192 
193  if (!zstr(tts_engine)) {
194  menu->tts_engine = switch_core_strdup(menu->pool, tts_engine);
195  }
196 
197  if (!zstr(tts_voice)) {
198  menu->tts_voice = switch_core_strdup(menu->pool, tts_voice);
199  }
200 
201  menu->confirm_attempts = confirm_attempts;
202 
203  menu->inlen = digit_len;
204 
205  if (max_failures > 0) {
206  menu->max_failures = max_failures;
207  } else {
208  menu->max_failures = 3;
209  }
210 
211  if (max_timeouts > 0) {
212  menu->max_timeouts = max_timeouts;
213  } else {
214  menu->max_timeouts = 3;
215  }
216 
217  menu->timeout = timeout;
218 
219  menu->inter_timeout = inter_timeout;
220 
221  menu->actions = NULL;
222 
223  if (newpool) {
225  }
226 
227  if (menu->timeout <= 0) {
228  menu->timeout = 10000;
229  }
230 
231  if (main) {
232  switch_ivr_menu_stack_add(&main, menu);
233  } else {
235  }
236 
237  menu->buf = switch_core_alloc(menu->pool, 1024);
238 
239  *new_menu = menu;
240 
241  return SWITCH_STATUS_SUCCESS;
242 }
243 
245 {
246  switch_ivr_menu_action_t *action, *ap;
247  uint32_t len;
248 
249  if ((action = switch_core_alloc(menu->pool, sizeof(*action)))) {
250  action->bind = switch_core_strdup(menu->pool, bind);
251  action->arg = switch_core_strdup(menu->pool, arg);
252  if (*action->bind == '/') {
253  action->re = 1;
254  } else {
255  len = (uint32_t) strlen(action->bind);
256  if (len > menu->inlen) {
257  menu->inlen = len;
258  }
259  }
260  action->ivr_action = ivr_action;
261 
262  if (menu->actions) {
263  for(ap = menu->actions; ap && ap->next; ap = ap->next);
264  ap->next = action;
265  } else {
266  menu->actions = action;
267  }
268 
269  return SWITCH_STATUS_SUCCESS;
270  }
271 
272  return SWITCH_STATUS_MEMERR;
273 }
274 
276  switch_ivr_menu_action_function_t *function, const char *arg, const char *bind)
277 {
278  switch_ivr_menu_action_t *action, *ap;
279  uint32_t len;
280 
281  if ((action = switch_core_alloc(menu->pool, sizeof(*action)))) {
282  action->bind = switch_core_strdup(menu->pool, bind);
283  action->arg = switch_core_strdup(menu->pool, arg);
284 
285  if (*action->bind == '/') {
286  action->re = 1;
287  } else {
288  len = (uint32_t) strlen(action->bind);
289  if (len > menu->inlen) {
290  menu->inlen = len;
291  }
292  }
293 
294  action->function = function;
295 
296  if (menu->actions) {
297  for(ap = menu->actions; ap && ap->next; ap = ap->next);
298  ap->next = action;
299  } else {
300  menu->actions = action;
301  }
302 
303  return SWITCH_STATUS_SUCCESS;
304  }
305 
306  return SWITCH_STATUS_MEMERR;
307 }
308 
310 {
312 
313  if (stack != NULL && stack->pool != NULL) {
316  switch_memory_pool_t *pool = stack->pool;
317  status = switch_core_destroy_memory_pool(&pool);
318  } else {
319  status = SWITCH_STATUS_SUCCESS;
320  }
321  }
322 
323  return status;
324 }
325 
327 {
328  char terminator;
329  uint32_t len;
330  char *ptr;
332  switch_input_args_t args = { 0 };
333  switch_channel_t *channel;
334  char *sound_expanded = sound;
335  switch_size_t menu_buf_len = 0;
336  const char *terminator_str = "#";
337 
338  if (!session || !menu || zstr(sound)) {
339  return status;
340  }
341 
342  if ((channel = switch_core_session_get_channel(session))) {
343  const char *tmp;
344  sound_expanded = switch_channel_expand_variables(channel, sound);
345  if ((tmp = switch_channel_get_variable(channel, "ivr_menu_terminator")) && !zstr(tmp)) {
346  terminator_str = tmp;
347  }
348  }
349 
350  memset(menu->buf, 0, menu->inlen + 1);
351  menu->ptr = menu->buf;
352 
353  if (!need) {
354  len = 1;
355  ptr = NULL;
356  } else {
357  len = (uint32_t) menu->inlen + 1;
358  ptr = menu->ptr;
359  }
360  args.buf = ptr;
361  args.buflen = len;
362 
363  status = switch_ivr_play_file(session, NULL, sound_expanded, &args);
364 
365  if (sound_expanded != sound) {
366  switch_safe_free(sound_expanded);
367  }
368 
369  if (!need) {
370  return status;
371  }
372 
373  menu_buf_len = strlen(menu->buf);
374 
375  menu->ptr += menu_buf_len;
376  if (menu_buf_len < need) {
377  switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "waiting for %u/%u digits t/o %d\n",
378  (uint32_t) (menu->inlen - strlen(menu->buf)), (uint32_t) need, menu->inter_timeout);
379  status = switch_ivr_collect_digits_count(session, menu->ptr, menu->inlen - strlen(menu->buf),
380  need, terminator_str, &terminator, menu_buf_len ? menu->inter_timeout : menu->timeout,
381  menu->inter_timeout, menu->timeout);
382  }
383 
384  if (menu->confirm_macro && status == SWITCH_STATUS_SUCCESS && *menu->buf != '\0') {
385  switch_input_args_t confirm_args = { 0 }, *ap = NULL;
386  char buf[10] = "";
387  char terminator_key;
388  int att = menu->confirm_attempts;
389 
390  while (att) {
391  confirm_args.buf = buf;
392  confirm_args.buflen = sizeof(buf);
393  memset(buf, 0, confirm_args.buflen);
394 
395  if (menu->confirm_key) {
396  ap = &confirm_args;
397  }
398 
399  switch_ivr_phrase_macro(session, menu->confirm_macro, menu->buf, NULL, ap);
400 
401  if (menu->confirm_key && *buf == '\0') {
402  switch_ivr_collect_digits_count(session, buf, sizeof(buf), 1, terminator_str, &terminator_key, menu->timeout, 0, 0);
403  }
404 
405  if (menu->confirm_key && *buf != '\0') {
406  if (*menu->confirm_key == *buf) {
408  "approving digits '%s' via confirm key %s\n", menu->buf, menu->confirm_key);
409  break;
410  } else {
411  att = 0;
412  break;
413  }
414  }
415  att--;
416  }
417  if (!att) {
418  switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "rejecting digits '%s' via confirm key %s\n", menu->buf,
419  menu->confirm_key);
420  *menu->buf = '\0';
421  }
422  }
423 
424  switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "digits '%s'\n", menu->buf);
425 
426  return status;
427 }
428 
429 static void exec_app(switch_core_session_t *session, char *app_str)
430 {
432  char *app = switch_core_session_strdup(session, app_str);
433  char *data = strchr(app, ' ');
434  char *expanded = NULL;
435 
436  if (data) {
437  *data++ = '\0';
438  }
439 
440  expanded = switch_channel_expand_variables(channel, data);
441 
442  switch_core_session_execute_application(session, app, expanded);
443 
444  if (expanded && expanded != data) {
445  free(expanded);
446  }
447 
448 }
449 
451 {
452  int reps = 0, errs = 0, timeouts = 0, match = 0, running = 1;
453  char *greeting_sound = NULL, *aptr = NULL;
454  char arg[512];
457  switch_ivr_menu_t *menu = NULL;
458  switch_channel_t *channel;
460 
461  if (++stack->stack_count > 12) {
462  switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Too many levels of recursion.\n");
464  }
465 
466  if (!session || !stack || zstr(name)) {
467  switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Invalid menu context\n");
469  }
470 
471  channel = switch_core_session_get_channel(session);
472 
473  if (!(menu = switch_ivr_menu_find(stack, name))) {
474  switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Invalid Menu!\n");
476  }
477 
478  if (!zstr(menu->tts_engine) && !zstr(menu->tts_voice)) {
479  switch_channel_set_variable(channel, "tts_engine", menu->tts_engine);
480  switch_channel_set_variable(channel, "tts_voice", menu->tts_voice);
481  }
482 
483  switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Executing IVR menu %s\n", menu->name);
484  switch_channel_set_variable(channel, "ivr_menu_status", "success");
485 
486  ivr_send_event(session, MENU_EVENT_ENTER, menu);
487 
488  if (!zstr(menu->pin)) {
489  char digit_buffer[128] = "";
490  char *digits_regex = switch_core_session_sprintf(session, "^%s$", menu->pin);
491 
492  if (switch_play_and_get_digits(session, (uint32_t)strlen(menu->pin), (uint32_t)strlen(menu->pin), 3, 3000, "#",
493  menu->prompt_pin_file, menu->bad_pin_file, NULL, digit_buffer, sizeof(digit_buffer),
494  digits_regex, 10000, NULL) != SWITCH_STATUS_SUCCESS) {
496  }
497  }
498 
499 
500  for (reps = 0; running && status == SWITCH_STATUS_SUCCESS; reps++) {
501  if (!switch_channel_ready(channel)) {
502  break;
503  }
504  if (errs == menu->max_failures) {
505  switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Maximum failures\n");
506  switch_channel_set_variable(channel, "ivr_menu_status", "failure");
507  if (!zstr(menu->exec_on_max_fail)) {
508  exec_app(session, menu->exec_on_max_fail);
509  }
510  break;
511  }
512  if (timeouts == menu->max_timeouts) {
513  switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Maximum timeouts\n");
514  switch_channel_set_variable(channel, "ivr_menu_status", "timeout");
515  if (!zstr(menu->exec_on_max_timeout)) {
516  exec_app(session, menu->exec_on_max_timeout);
517  }
518  break;
519  }
520 
521  if (reps > 0 && menu->short_greeting_sound) {
522  greeting_sound = menu->short_greeting_sound;
523  } else {
524  greeting_sound = menu->greeting_sound;
525  }
526 
527  match = 0;
528  aptr = NULL;
529 
530  memset(arg, 0, sizeof(arg));
531 
532  memset(menu->buf, 0, menu->inlen + 1);
533 
534  if (play_and_collect(session, menu, greeting_sound, menu->inlen) == SWITCH_STATUS_TIMEOUT && *menu->buf == '\0') {
535  timeouts++;
536  continue;
537  }
538 
539  if (*menu->buf != '\0') {
540 
541  for (ap = menu->actions; ap; ap = ap->next) {
542  int ok = 0;
543  char substituted[1024];
544  char *use_arg = ap->arg;
545 
546  if (!zstr(menu->tts_engine) && !zstr(menu->tts_voice)) {
547  switch_channel_set_variable(channel, "tts_engine", menu->tts_engine);
548  switch_channel_set_variable(channel, "tts_voice", menu->tts_voice);
549  }
550 
551  if (ap->re) {
552  switch_regex_t *re = NULL;
553  int ovector[30];
554 
555  if ((ok = switch_regex_perform(menu->buf, ap->bind, &re, ovector, sizeof(ovector) / sizeof(ovector[0])))) {
556  switch_perform_substitution(re, ok, ap->arg, menu->buf, substituted, sizeof(substituted), ovector);
557  use_arg = substituted;
558  }
559  switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "action regex [%s] [%s] [%d]\n", menu->buf, ap->bind, ok);
560 
562  } else {
563  ok = !strcmp(menu->buf, ap->bind);
564  }
565 
566  if (ok) {
567  match++;
568  errs = 0;
569  if (ap->function) {
571  "IVR function on menu '%s' matched '%s' param '%s'\n", menu->name, menu->buf, use_arg);
572  todo = ap->function(menu, use_arg, arg, sizeof(arg), obj);
573  aptr = arg;
574  } else {
575  todo = ap->ivr_action;
576  aptr = use_arg;
578  "IVR action on menu '%s' matched '%s' param '%s'\n", menu->name, menu->buf, aptr);
579  }
580 
581  switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "switch_ivr_menu_execute todo=[%d]\n", todo);
582 
583  switch (todo) {
585  status = SWITCH_STATUS_FALSE;
586  break;
588  status = switch_ivr_play_file(session, NULL, aptr, NULL);
589  break;
591  if (!strcmp(aptr, menu->name)) {
592  status = SWITCH_STATUS_SUCCESS;
593  } else {
594  reps = -1;
595  ivr_send_event(session, MENU_EVENT_EXIT, menu);
596  status = switch_ivr_menu_execute(session, stack, aptr, obj);
597  ivr_send_event(session, MENU_EVENT_ENTER, menu);
598  }
599  break;
601  {
602  switch_application_interface_t *application_interface;
603  char *app_name;
604  char *app_arg = NULL;
605 
606  status = SWITCH_STATUS_FALSE;
607 
608  if (!zstr(aptr)) {
609  app_name = switch_core_session_strdup(session, aptr);
610  if ((app_arg = strchr(app_name, ' '))) {
611  *app_arg++ = '\0';
612  }
613 
614  if ((application_interface = switch_loadable_module_get_application_interface(app_name))) {
615  if (!zstr(menu->transfer_sound) && !strcmp(app_name, "transfer")) {
616  status = play_and_collect(session, menu, menu->transfer_sound, 0);
617  }
618 
619  switch_core_session_exec(session, application_interface, app_arg);
620  UNPROTECT_INTERFACE(application_interface);
621  status = SWITCH_STATUS_SUCCESS;
622  }
623  }
624  }
625  break;
627  running = 0;
628  status = SWITCH_STATUS_SUCCESS;
629  break;
632  status = SWITCH_STATUS_BREAK;
633  break;
635  status = SWITCH_STATUS_SUCCESS;
636  break;
637  default:
639  break;
640  }
641  }
642  }
643 
644  if (switch_test_flag(menu, SWITCH_IVR_MENU_FLAG_STACK)) { /* top level */
645  if (switch_test_flag(stack, SWITCH_IVR_MENU_FLAG_FALLTOMAIN)) { /* catch the fallback and recover */
647  status = SWITCH_STATUS_SUCCESS;
648  running = 1;
649  continue;
650  }
651  }
652  }
653  if (!match) {
654  if (*menu->buf) {
655  switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "IVR menu '%s' caught invalid input '%s'\n", menu->name,
656  menu->buf);
657  if (menu->invalid_sound) {
658  play_and_collect(session, menu, menu->invalid_sound, 0);
659  }
660  } else {
661  switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "IVR menu '%s' no input detected\n", menu->name);
662  }
663  errs++;
664 
665  /* breaks are ok too */
666  if (SWITCH_STATUS_IS_BREAK(status)) {
667  status = SWITCH_STATUS_SUCCESS;
668  }
669  }
670  }
671 
672  if (stack->stack_count == 1) {
673  switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "exit-sound '%s'\n", menu->exit_sound);
674  if (!zstr(menu->exit_sound)) {
675  status = play_and_collect(session, menu, menu->exit_sound, 0);
676  }
677  }
678 
679  end:
680 
681  stack->stack_count--;
682 
683  if (menu) {
684  ivr_send_event(session, MENU_EVENT_EXIT, menu);
685  }
686 
687  return status;
688 }
689 
690 /******************************************************************************************************/
691 
692 typedef struct switch_ivr_menu_xml_map {
693  char *name;
698 
703 };
704 
706 {
707  switch_ivr_menu_xml_map_t *map = (xml_ctx != NULL ? xml_ctx->map : NULL);
708  int rc = -1;
709 
710  while (map != NULL && (rc = strcasecmp(map->name, name)) != 0) {
711  map = map->next;
712  }
713 
714  return (rc == 0 ? map : NULL);
715 }
716 
719 {
721 
722  /* if this action/function does not exist yet */
723  if (xml_ctx != NULL && name != NULL && xml_ctx->pool != NULL && switch_ivr_menu_stack_xml_find(xml_ctx, name) == NULL) {
725 
726  if (map != NULL) {
727  map->name = switch_core_strdup(xml_ctx->pool, name);
728  map->action = action;
729  map->function = function;
730 
731  if (map->name != NULL) {
732  /* insert map item at top of list */
733  map->next = xml_ctx->map;
734  xml_ctx->map = map;
735  status = SWITCH_STATUS_SUCCESS;
736  } else {
737  status = SWITCH_STATUS_MEMERR;
738  }
739  } else {
740  status = SWITCH_STATUS_MEMERR;
741  }
742 
743  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "switch_ivr_menu_stack_xml_add binding '%s'\n", name);
744  } else {
745  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to add binding %s\n", name);
746  }
747 
748  return status;
749 }
750 
751 static struct iam_s {
752  const char *name;
754 } iam[] = {
755  {
756  "menu-exit", SWITCH_IVR_ACTION_DIE}, {
757  "menu-sub", SWITCH_IVR_ACTION_EXECMENU}, {
758  "menu-exec-app", SWITCH_IVR_ACTION_EXECAPP}, {
759  "menu-play-sound", SWITCH_IVR_ACTION_PLAYSOUND}, {
760  "menu-back", SWITCH_IVR_ACTION_BACK}, {
761  "menu-top", SWITCH_IVR_ACTION_TOMAIN}, {
762  NULL, 0}
763 };
764 
766 {
767  int i;
768 
769  if (!zstr(action_name)) {
770  for (i = 0;; i++) {
771  if (!iam[i].name) {
772  break;
773  }
774 
775  if (!strcasecmp(iam[i].name, action_name)) {
776  *action = iam[i].action;
777  return SWITCH_STATUS_SUCCESS;
778  }
779  }
780  }
781 
782  return SWITCH_STATUS_FALSE;
783 }
784 
785 static switch_bool_t is_valid_action(const char *action)
786 {
787  int i;
788 
789  if (!zstr(action)) {
790  for (i = 0;; i++) {
791  if (!iam[i].name) {
792  break;
793  }
794 
795  if (!strcmp(iam[i].name, action)) {
796  return SWITCH_TRUE;
797  }
798  }
799  }
800 
801  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid Action [%s]\n", switch_str_nil(action));
802  return SWITCH_FALSE;
803 }
804 
806 {
808  int autocreated = 0;
809 
810  /* build a memory pool ? */
811  if (pool == NULL) {
812  status = switch_core_new_memory_pool(&pool);
813  autocreated = 1;
814  }
815  /* allocate the xml context */
816  if (xml_menu_ctx != NULL && pool != NULL) {
817  *xml_menu_ctx = switch_core_alloc(pool, sizeof(switch_ivr_menu_xml_ctx_t));
818  if (*xml_menu_ctx != NULL) {
819  (*xml_menu_ctx)->pool = pool;
820  (*xml_menu_ctx)->autocreated = autocreated;
821  (*xml_menu_ctx)->map = NULL;
822  status = SWITCH_STATUS_SUCCESS;
823  } else {
824  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to alloc xml_ctx\n");
825  status = SWITCH_STATUS_FALSE;
826  }
827  }
828  /* build the standard/default xml menu handler mappings */
829  if (status == SWITCH_STATUS_SUCCESS && xml_menu_ctx != NULL && *xml_menu_ctx != NULL) {
830  int i;
831 
832  for (i = 0; iam[i].name && status == SWITCH_STATUS_SUCCESS; i++) {
833  status = switch_ivr_menu_stack_xml_add(*xml_menu_ctx, iam[i].name, iam[i].action, NULL);
834  }
835  }
836 
837  return status;
838 }
839 
841  const char *name, switch_ivr_menu_action_function_t *function)
842 {
843  return switch_ivr_menu_stack_xml_add(xml_menu_ctx, name, -1, function);
844 }
845 
847  switch_ivr_menu_t ** menu_stack, switch_xml_t xml_menus, switch_xml_t xml_menu)
848 {
850 
851  if (xml_menu_ctx != NULL && menu_stack != NULL && xml_menu != NULL) {
852  const char *menu_name = switch_xml_attr_soft(xml_menu, "name"); /* if the attr doesn't exist, return "" */
853  const char *greet_long = switch_xml_attr(xml_menu, "greet-long"); /* if the attr doesn't exist, return NULL */
854  const char *greet_short = switch_xml_attr(xml_menu, "greet-short"); /* if the attr doesn't exist, return NULL */
855  const char *invalid_sound = switch_xml_attr(xml_menu, "invalid-sound"); /* if the attr doesn't exist, return NULL */
856  const char *exit_sound = switch_xml_attr(xml_menu, "exit-sound"); /* if the attr doesn't exist, return NULL */
857  const char *transfer_sound = switch_xml_attr(xml_menu, "transfer-sound"); /* if the attr doesn't exist, return NULL */
858  const char *timeout = switch_xml_attr_soft(xml_menu, "timeout"); /* if the attr doesn't exist, return "" */
859  const char *max_failures = switch_xml_attr_soft(xml_menu, "max-failures"); /* if the attr doesn't exist, return "" */
860  const char *max_timeouts = switch_xml_attr_soft(xml_menu, "max-timeouts");
861  const char *exec_on_max_fail = switch_xml_attr(xml_menu, "exec-on-max-failures");
862  const char *exec_on_max_timeout = switch_xml_attr(xml_menu, "exec-on-max-timeouts");
863  const char *confirm_macro = switch_xml_attr(xml_menu, "confirm-macro");
864  const char *confirm_key = switch_xml_attr(xml_menu, "confirm-key");
865  const char *tts_engine = switch_xml_attr(xml_menu, "tts-engine");
866  const char *tts_voice = switch_xml_attr(xml_menu, "tts-voice");
867  const char *confirm_attempts = switch_xml_attr_soft(xml_menu, "confirm-attempts");
868  const char *digit_len = switch_xml_attr_soft(xml_menu, "digit-len");
869  const char *inter_timeout = switch_xml_attr_soft(xml_menu, "inter-digit-timeout");
870  const char *pin = switch_xml_attr_soft(xml_menu, "pin");
871  const char *prompt_pin_file = switch_xml_attr_soft(xml_menu, "pin-file");
872  const char *bad_pin_file = switch_xml_attr_soft(xml_menu, "bad-pin-file");
873 
874  switch_ivr_menu_t *menu = NULL;
875 
876  if (zstr(max_timeouts)) {
877  max_timeouts = max_failures;
878  }
879 
880  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "building menu '%s'\n", menu_name);
881 
882  status = switch_ivr_menu_init(&menu,
883  *menu_stack,
884  menu_name,
885  greet_long,
886  greet_short,
887  invalid_sound,
888  exit_sound,
889  transfer_sound,
890  confirm_macro,
891  confirm_key,
892  tts_engine,
893  tts_voice,
894  atoi(confirm_attempts),
895  atoi(inter_timeout),
896  atoi(digit_len),
897  atoi(timeout),
898  strlen(max_failures) ? atoi(max_failures) : 0, strlen(max_timeouts) ? atoi(max_timeouts) : 0, xml_menu_ctx->pool);
899 
900 
901  if (!zstr(exec_on_max_fail)) {
902  menu->exec_on_max_fail = switch_core_strdup(menu->pool, exec_on_max_fail);
903  }
904 
905  if (!zstr(exec_on_max_timeout)) {
906  menu->exec_on_max_timeout = switch_core_strdup(menu->pool, exec_on_max_timeout);
907  }
908 
909  if (!zstr(pin)) {
910  if (zstr(prompt_pin_file)) {
911  prompt_pin_file = "ivr/ivr-please_enter_pin_followed_by_pound.wav";
912  }
913  if (zstr(bad_pin_file)) {
914  bad_pin_file = "conference/conf-bad-pin.wav";
915  }
916  menu->pin = switch_core_strdup(menu->pool, pin);
917  menu->prompt_pin_file = switch_core_strdup(menu->pool, prompt_pin_file);
918  menu->bad_pin_file = switch_core_strdup(menu->pool, bad_pin_file);
919  }
920 
921  /* set the menu_stack for the caller */
922  if (status == SWITCH_STATUS_SUCCESS && *menu_stack == NULL) {
923  *menu_stack = menu;
924 
925  if (xml_menu_ctx->autocreated) {
927  }
928  }
929 
930  if (status == SWITCH_STATUS_SUCCESS && menu != NULL) {
931  switch_xml_t xml_kvp;
932 
933  /* build menu entries */
934  for (xml_kvp = switch_xml_child(xml_menu, "entry"); xml_kvp != NULL && status == SWITCH_STATUS_SUCCESS; xml_kvp = xml_kvp->next) {
935  const char *action = switch_xml_attr(xml_kvp, "action");
936  const char *digits = switch_xml_attr(xml_kvp, "digits");
937  const char *param = switch_xml_attr_soft(xml_kvp, "param");
938 
939  if (is_valid_action(action) && !zstr(digits)) {
940  switch_ivr_menu_xml_map_t *xml_map = xml_menu_ctx->map;
941  int found = 0;
942 
943  /* find and appropriate xml handler */
944  while (xml_map != NULL && !found) {
945  if (!(found = (strcasecmp(xml_map->name, action) == 0))) {
946  xml_map = xml_map->next;
947  }
948  }
949 
950  if (found && xml_map != NULL) {
951  /* do we need to build a new sub-menu ? */
952  if (xml_map->action == SWITCH_IVR_ACTION_EXECMENU && switch_ivr_menu_find(*menu_stack, param) == NULL) {
953  if ((xml_menu = switch_xml_find_child(xml_menus, "menu", "name", param)) != NULL) {
954  status = switch_ivr_menu_stack_xml_build(xml_menu_ctx, menu_stack, xml_menus, xml_menu);
955  }
956  }
957  /* finally bind the menu entry */
958  if (status == SWITCH_STATUS_SUCCESS) {
959  if (xml_map->function != NULL) {
961  "binding menu caller control '%s'/'%s' to '%s'\n", xml_map->name, param, digits);
962  status = switch_ivr_menu_bind_function(menu, xml_map->function, param, digits);
963  } else {
964  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "binding menu action '%s' to '%s'\n", xml_map->name, digits);
965  status = switch_ivr_menu_bind_action(menu, xml_map->action, param, digits);
966  }
967  }
968  }
969  } else {
970  status = SWITCH_STATUS_FALSE;
971  }
972  }
973  }
974  }
975 
976  if (status != SWITCH_STATUS_SUCCESS) {
977  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to build xml menu\n");
978  }
979 
980  return status;
981 }
982 
983 /* For Emacs:
984  * Local Variables:
985  * mode:c
986  * indent-tabs-mode:t
987  * tab-width:4
988  * c-basic-offset:4
989  * End:
990  * For VIM:
991  * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
992  */
#define switch_event_fire(event)
Fire an event filling in most of the arguements with obvious values.
Definition: switch_event.h:412
#define switch_regex_safe_free(re)
Definition: switch_regex.h:79
#define switch_core_new_memory_pool(p)
Create a new sub memory pool from the core's master pool.
Definition: switch_core.h:631
A module interface to implement an application.
const char * switch_xml_attr_soft(_In_ switch_xml_t xml, _In_z_ const char *attr)
returns the value of the requested tag attribute, or "" if not found
char * switch_core_session_sprintf(_In_ switch_core_session_t *session, _In_z_ _Printf_format_string_ const char *fmt,...)
printf-style style printing routine. The data is output to a string allocated from the session ...
switch_ivr_menu_action_function_t * function
#define SWITCH_CHANNEL_SESSION_LOG(x)
#define switch_set_flag(obj, flag)
Set a flag on an arbitrary object.
Definition: switch_utils.h:631
const char * name
#define SWITCH_CHANNEL_LOG
switch_xml_t switch_xml_find_child(_In_ switch_xml_t node, _In_z_ const char *childname, _In_opt_z_ const char *attrname, _In_opt_z_ const char *value)
find a child tag in a node called 'childname' with an attribute 'attrname' which equals 'value' ...
const char * switch_xml_attr(_In_opt_ switch_xml_t xml, _In_opt_z_ const char *attr)
returns the value of the requested tag attribute, or NULL if not found
switch_status_t switch_play_and_get_digits(switch_core_session_t *session, uint32_t min_digits, uint32_t max_digits, uint32_t max_tries, uint32_t timeout, const char *valid_terminators, const char *audio_file, const char *bad_input_audio_file, const char *var_name, char *digit_buffer, uint32_t digit_buffer_length, const char *digits_regex, uint32_t digit_timeout, const char *transfer_on_failure)
Play a sound and gather digits with the number of retries specified if the user doesn't give digits i...
#define SWITCH_STATUS_IS_BREAK(x)
Definition: switch_utils.h:564
void switch_perform_substitution(switch_regex_t *re, int match_count, const char *data, const char *field_data, char *substituted, switch_size_t len, int *ovector)
Definition: switch_regex.c:131
void switch_channel_event_set_data(_In_ switch_channel_t *channel, _In_ switch_event_t *event)
Add information about a given channel to an event object.
switch_bool_t
Definition: switch_types.h:405
#define switch_core_strdup(_pool, _todup)
Copy a string using memory allocation from a given pool.
Definition: switch_core.h:729
struct switch_ivr_menu_xml_map switch_ivr_menu_xml_map_t
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)
switch_ivr_menu_bind_action: Bind a keystroke to an action.
#define switch_core_destroy_memory_pool(p)
Returns a subpool back to the main pool.
Definition: switch_core.h:640
switch_memory_pool_t * pool
Representation of an event.
Definition: switch_event.h:80
#define switch_channel_ready(_channel)
switch_status_t switch_core_session_exec(_In_ switch_core_session_t *session, _In_ const switch_application_interface_t *application_interface, _In_opt_z_ const char *arg)
Execute an application on a session.
switch_status_t switch_ivr_play_file(switch_core_session_t *session, switch_file_handle_t *fh, const char *file, switch_input_args_t *args)
play a file from the disk to the session
A representation of an XML tree.
Definition: switch_xml.h:76
struct real_pcre switch_regex_t
Definition: switch_regex.h:43
#define switch_event_create_subclass(_e, _eid, _sn)
Definition: switch_event.h:153
switch_status_t switch_ivr_menu_execute(switch_core_session_t *session, switch_ivr_menu_t *stack, char *name, void *obj)
Execute a menu.
switch_status_t switch_ivr_menu_stack_xml_add_custom(switch_ivr_menu_xml_ctx_t *xml_menu_ctx, const char *name, switch_ivr_menu_action_function_t *function)
struct switch_ivr_menu_action * actions
#define zstr(x)
Definition: switch_utils.h:281
switch_size_t inlen
static void ivr_send_event(switch_core_session_t *session, char *event_type, switch_ivr_menu_t *menu)
#define switch_core_session_execute_application(_a, _b, _c)
Execute an application on a session.
Definition: switch_core.h:1103
_Ret_ switch_channel_t * switch_core_session_get_channel(_In_ switch_core_session_t *session)
Retrieve a pointer to the channel object associated with a given session.
#define UNPROTECT_INTERFACE(_it)
#define switch_clear_flag(obj, flag)
Clear a flag on an arbitrary object while locked.
Definition: switch_utils.h:655
int main(int argc, char *argv[])
Definition: fs_encode.c:56
switch_xml_t next
Definition: switch_xml.h:88
switch_status_t switch_ivr_collect_digits_count(switch_core_session_t *session, char *buf, switch_size_t buflen, switch_size_t maxdigits, const char *terminators, char *terminator, uint32_t first_timeout, uint32_t digit_timeout, uint32_t abs_timeout)
Wait for specified number of DTMF digits, untile terminator is received or until the channel hangs up...
Definition: switch_ivr.c:1287
switch_byte_t switch_byte_t * buf
static void exec_app(switch_core_session_t *session, char *app_str)
static switch_ivr_menu_xml_map_t * switch_ivr_menu_stack_xml_find(switch_ivr_menu_xml_ctx_t *xml_ctx, const char *name)
switch_application_interface_t * switch_loadable_module_get_application_interface(const char *name)
Retrieve the application interface by it's registered name.
#define switch_core_alloc(_pool, _mem)
Allocate memory directly from a memory pool.
Definition: switch_core.h:682
#define switch_channel_get_variable(_c, _v)
switch_status_t switch_event_add_header_string(switch_event_t *event, switch_stack_t stack, const char *header_name, const char *data)
Add a string header to an event.
switch_ivr_action_t ivr_action
#define switch_safe_free(it)
Free a pointer and set it to NULL unless it already is NULL.
Definition: switch_utils.h:789
uintptr_t switch_size_t
switch_ivr_action_t action
static switch_status_t play_and_collect(switch_core_session_t *session, switch_ivr_menu_t *menu, char *sound, switch_size_t need)
switch_memory_pool_t * pool
int switch_regex_perform(const char *field, const char *expression, switch_regex_t **new_re, int *ovector, uint32_t olen)
Definition: switch_regex.c:55
#define MENU_EVENT_EXIT
switch_ivr_action_t switch_ivr_menu_action_function_t(struct switch_ivr_menu *, char *, char *, size_t, void *)
Definition: switch_ivr.h:783
static int32_t running
struct switch_ivr_menu_xml_map * next
#define switch_str_nil(s)
Make a null string a blank string instead.
Definition: switch_utils.h:903
static switch_bool_t is_valid_action(const char *action)
#define switch_channel_expand_variables(_channel, _in)
switch_status_t switch_ivr_menu_init(switch_ivr_menu_t **new_menu, switch_ivr_menu_t *main, const char *name, const char *greeting_sound, const char *short_greeting_sound, const char *invalid_sound, const char *exit_sound, const char *transfer_sound, const char *confirm_macro, const char *confirm_key, const char *tts_engine, const char *tts_voice, int confirm_attempts, int inter_timeout, int digit_len, int timeout, int max_failures, int max_timeouts, switch_memory_pool_t *pool)
Create a new menu object.
switch_status_t
Common return values.
switch_status_t switch_ivr_menu_stack_xml_build(switch_ivr_menu_xml_ctx_t *xml_menu_ctx, switch_ivr_menu_t **menu_stack, switch_xml_t xml_menus, switch_xml_t xml_menu)
Build a menu stack from an xml source.
#define switch_goto_status(_status, _label)
Definition: switch_utils.h:256
char * exec_on_max_timeout
Main Library Header.
switch_ivr_action_t
Definition: switch_ivr.h:773
switch_memory_pool_t * pool
#define SWITCH_DECLARE(type)
switch_xml_t switch_xml_child(_In_ switch_xml_t xml, _In_z_ const char *name)
returns the first child tag (one level deeper) with the given name or NULL \ if not found ...
switch_status_t switch_ivr_menu_str2action(const char *action_name, switch_ivr_action_t *action)
static struct iam_s iam[]
switch_ivr_menu_action_function_t * function
struct apr_pool_t switch_memory_pool_t
#define switch_test_flag(obj, flag)
Test for the existance of a flag on an arbitary object.
Definition: switch_utils.h:624
void switch_log_printf(_In_ switch_text_channel_t channel, _In_z_ const char *file, _In_z_ const char *func, _In_ int line, _In_opt_z_ const char *userdata, _In_ switch_log_level_t level, _In_z_ _Printf_format_string_ const char *fmt,...) PRINTF_FUNCTION(7
Write log data to the logging engine.
#define MENU_EVENT_ENTER
switch_status_t switch_ivr_menu_stack_xml_init(switch_ivr_menu_xml_ctx_t **xml_menu_ctx, switch_memory_pool_t *pool)
static void switch_ivr_menu_stack_add(switch_ivr_menu_t **top, switch_ivr_menu_t *bottom)
#define switch_ivr_phrase_macro(session, macro_name, data, lang, args)
Definition: switch_ivr.h:903
#define switch_core_session_strdup(_session, _todup)
Copy a string using memory allocation from a session's pool.
Definition: switch_core.h:717
struct switch_ivr_menu_action * next
#define switch_channel_set_variable(_channel, _var, _val)
static switch_status_t switch_ivr_menu_stack_xml_add(switch_ivr_menu_xml_ctx_t *xml_ctx, const char *name, int action, switch_ivr_menu_action_function_t *function)
struct switch_ivr_menu_xml_map * map
memset(buf, 0, buflen)
struct switch_ivr_menu * next
char * short_greeting_sound
switch_ivr_action_t action
static switch_ivr_menu_t * switch_ivr_menu_find(switch_ivr_menu_t *stack, const char *name)
switch_status_t switch_ivr_menu_bind_function(switch_ivr_menu_t *menu, switch_ivr_menu_action_function_t *function, const char *arg, const char *bind)
Bind a keystroke to a callback function.
switch_status_t switch_ivr_menu_stack_free(switch_ivr_menu_t *stack)
free a stack of menu objects.