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 * Marc Olivier Chouinard <mochouinard@moctel.com>
27 *
28 *
29 * menu.c -- VoiceMail Menu
30 *
31 */
32 #include <switch.h>
33
34 #include "ivr.h"
35 #include "menu.h"
36 #include "utils.h"
37 #include "config.h"
38
39 /* List of available menu */
40 vmivr_menu_function_t menu_list[] = {
41 {"std_authenticate", vmivr_menu_authenticate},
42 {"std_main_menu", vmivr_menu_main},
43 {"std_navigator", vmivr_menu_navigator},
44 {"std_record_name", vmivr_menu_record_name},
45 {"std_set_password", vmivr_menu_set_password},
46 {"std_select_greeting_slot", vmivr_menu_select_greeting_slot},
47 {"std_record_greeting_with_slot", vmivr_menu_record_greeting_with_slot},
48 {"std_preference", vmivr_menu_preference},
49 {"std_purge", vmivr_menu_purge},
50 {"std_forward", vmivr_menu_forward},
51 { NULL, NULL }
52 };
53
vmivr_menu_purge(switch_core_session_t * session,vmivr_profile_t * profile)54 void vmivr_menu_purge(switch_core_session_t *session, vmivr_profile_t *profile) {
55 vmivr_menu_t menu = { "std_menu_purge" };
56
57 /* Initialize Menu Configs */
58 menu_init(profile, &menu);
59
60 if (profile->id && profile->authorized) {
61 const char *exit_purge = switch_event_get_header(menu.event_settings, "Exit-Purge");
62 if (switch_true(exit_purge)) {
63 const char *cmd = switch_core_session_sprintf(session, "%s %s %s", profile->api_profile, profile->domain, profile->id);
64 vmivr_api_execute(session, profile->api_msg_purge, cmd);
65 }
66 }
67
68 menu_free(&menu);
69
70 }
71
vmivr_menu_main(switch_core_session_t * session,vmivr_profile_t * profile)72 void vmivr_menu_main(switch_core_session_t *session, vmivr_profile_t *profile) {
73 switch_channel_t *channel = switch_core_session_get_channel(session);
74 vmivr_menu_t menu = { "std_main_menu" };
75 int retry;
76 switch_bool_t action_on_new_message_occured = SWITCH_FALSE;
77
78 /* Initialize Menu Configs */
79 menu_init(profile, &menu);
80
81 if (!menu.event_keys_dtmf || !menu.event_phrases) {
82 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing Menu Phrases or Keys in menu '%s'\n", menu.name);
83 goto end;
84 }
85
86 for (retry = menu.ivr_maximum_attempts; switch_channel_ready(channel) && retry > 0; retry--) {
87 char *cmd = NULL;
88 const char *action = NULL;
89 const char *action_on_new_message = switch_event_get_header(menu.event_settings, "Action-On-New-Message");
90
91 menu_instance_init(&menu);
92
93 switch_event_add_header(menu.phrase_params, SWITCH_STACK_BOTTOM, "IVR-Retry-Left", "%d", retry);
94
95 ivre_init(&menu.ivre_d, menu.dtmfa);
96
97 cmd = switch_core_session_sprintf(session, "json %s %s %s %s", profile->api_profile, profile->domain, profile->id, profile->folder_name);
98 jsonapi_populate_event(session, menu.phrase_params, profile->api_msg_count, cmd);
99
100 /* Verify that phrases returned values, if not, exit */
101 if (!switch_event_get_header(menu.phrase_params, "VM-Total-New-Messages")) {
102 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Return from API is invalid. Check that the context exist on your DB backend\n");
103 menu_instance_free(&menu);
104 break;
105 }
106
107 ivre_playback(session, &menu.ivre_d, switch_event_get_header(menu.event_phrases, "msg_count"), NULL, menu.phrase_params, NULL, 0);
108
109 if (atoi(switch_event_get_header(menu.phrase_params, "VM-Total-New-Messages")) > 0 && menu.ivre_d.result == RES_WAITFORMORE && !action_on_new_message_occured && action_on_new_message) {
110 menu.ivre_d.result = RES_FOUND;
111 action = action_on_new_message;
112 action_on_new_message_occured = SWITCH_TRUE;
113
114 } else {
115 ivre_playback(session, &menu.ivre_d, switch_event_get_header(menu.event_phrases, "menu_options"), NULL, menu.phrase_params, NULL, menu.ivr_entry_timeout);
116 }
117
118 if (menu.ivre_d.result == RES_TIMEOUT) {
119 ivre_playback_dtmf_buffered(session, switch_event_get_header(menu.event_phrases, "timeout"), NULL, NULL, NULL, 0);
120 } else if (menu.ivre_d.result == RES_INVALID) {
121 ivre_playback_dtmf_buffered(session, switch_event_get_header(menu.event_phrases, "invalid"), NULL, NULL, NULL, 0);
122 } else if (menu.ivre_d.result == RES_FOUND) { /* Matching DTMF Key Pressed */
123 if (!action) {
124 action = switch_event_get_header(menu.event_keys_dtmf, menu.ivre_d.dtmf_stored);
125 }
126
127 /* Reset the try count */
128 retry = menu.ivr_maximum_attempts;
129
130 if (action) {
131 if (!strncasecmp(action, "new_msg:", 8)) {
132 void (*fPtr)(switch_core_session_t *session, vmivr_profile_t *profile) = vmivr_get_menu_function(action+8);
133 profile->folder_filter = VM_MSG_NEW;
134
135 if (fPtr) {
136 fPtr(session, profile);
137 }
138 } else if (!strncasecmp(action, "saved_msg:", 10)) {
139 void (*fPtr)(switch_core_session_t *session, vmivr_profile_t *profile) = vmivr_get_menu_function(action+10);
140 profile->folder_filter = VM_MSG_SAVED;
141
142 if (fPtr) {
143 fPtr(session, profile);
144 }
145 } else if (!strcasecmp(action, "return")) { /* Return to the previous menu */
146 retry = -1;
147 } else if (!strncasecmp(action, "menu:", 5)) { /* Sub Menu */
148 void (*fPtr)(switch_core_session_t *session, vmivr_profile_t *profile) = vmivr_get_menu_function(action+5);
149 if (fPtr) {
150 fPtr(session, profile);
151 }
152 }
153 }
154 }
155 menu_instance_free(&menu);
156
157
158 }
159
160 end:
161 menu_free(&menu);
162 }
163
164
vmivr_menu_navigator(switch_core_session_t * session,vmivr_profile_t * profile)165 void vmivr_menu_navigator(switch_core_session_t *session, vmivr_profile_t *profile) {
166 switch_channel_t *channel = switch_core_session_get_channel(session);
167 switch_event_t *msg_list_params = NULL;
168 size_t msg_count = 0;
169 size_t current_msg = 1;
170 size_t next_msg = current_msg;
171 size_t previous_msg;
172 char *cmd = NULL;
173 int retry;
174
175 /* Different switch to control playback of phrases */
176 switch_bool_t initial_count_played = SWITCH_FALSE;
177 switch_bool_t skip_header = SWITCH_FALSE;
178 switch_bool_t skip_playback = SWITCH_FALSE;
179 switch_bool_t msg_deleted = SWITCH_FALSE;
180 switch_bool_t msg_undeleted = SWITCH_FALSE;
181 switch_bool_t msg_saved = SWITCH_FALSE;
182
183 vmivr_menu_t menu = { "std_navigator" };
184
185 /* Initialize Menu Configs */
186 menu_init(profile, &menu);
187
188 if (!menu.event_keys_dtmf || !menu.event_phrases) {
189 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing Menu Phrases or Keys in menu '%s'\n", menu.name);
190 goto done;
191 }
192
193 /* Get VoiceMail List And update msg count */
194 cmd = switch_core_session_sprintf(session, "json %s %s %s %s %s", profile->api_profile, profile->domain, profile->id, profile->folder_name, profile->folder_filter);
195 msg_list_params = jsonapi2event(session, profile->api_msg_list, cmd);
196 if (msg_list_params) {
197 msg_count = atol(switch_event_get_header(msg_list_params,"VM-List-Count"));
198 if (msg_count == 0) {
199 goto done;
200 }
201 } else {
202 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "API message list return invalid result : %s(%s)\n", profile->api_msg_list, cmd);
203 goto done;
204 }
205
206
207 /* TODO Add Detection of new message and notify the user */
208
209 for (retry = menu.ivr_maximum_attempts; switch_channel_ready(channel) && retry > 0; retry--) {
210 switch_core_session_message_t msg = { 0 };
211 char cid_buf[1024] = "";
212
213 menu_instance_init(&menu);
214
215 switch_event_add_header(menu.phrase_params, SWITCH_STACK_BOTTOM, "IVR-Retry-Left", "%d", retry);
216
217 previous_msg = current_msg;
218
219 ivre_init(&menu.ivre_d, menu.dtmfa);
220
221 /* Prompt related to previous Message here */
222 append_event_message(session, profile, menu.phrase_params, msg_list_params, previous_msg);
223 if (msg_deleted) {
224 msg_deleted = SWITCH_FALSE;
225 ivre_playback(session, &menu.ivre_d, switch_event_get_header(menu.event_phrases, "ack"), "deleted", menu.phrase_params, NULL, 0);
226 }
227 if (msg_undeleted) {
228 msg_undeleted = SWITCH_FALSE;
229 ivre_playback(session, &menu.ivre_d, switch_event_get_header(menu.event_phrases, "ack"), "undeleted", menu.phrase_params, NULL, 0);
230 }
231 if (msg_saved) {
232 msg_saved = SWITCH_FALSE;
233 ivre_playback(session, &menu.ivre_d, switch_event_get_header(menu.event_phrases, "ack"), "saved", menu.phrase_params, NULL, 0);
234 }
235 switch_event_del_header(menu.phrase_params, "VM-Message-Flags");
236
237 /* Simple Protection to not go out of msg list scope */
238 if (next_msg == 0) {
239 next_msg = 1;
240 } else if (next_msg > msg_count) {
241 next_msg = msg_count;
242 ivre_playback_dtmf_buffered(session, switch_event_get_header(menu.event_phrases, "no_more_messages"), NULL, NULL, NULL, 0);
243 }
244
245 current_msg = next_msg;
246
247 /* Prompt related the current message */
248 append_event_message(session, profile, menu.phrase_params, msg_list_params, current_msg);
249
250 /* Used for extra control in phrases */
251 switch_event_add_header(menu.phrase_params, SWITCH_STACK_BOTTOM, "VM-List-Count", "%"SWITCH_SIZE_T_FMT, msg_count);
252
253 /* Display MSG CID/Name to caller */
254 switch_snprintf(cid_buf, sizeof(cid_buf), "%s|%s", switch_str_nil(switch_event_get_header(menu.phrase_params, "VM-Message-Caller-Number")), switch_str_nil(switch_event_get_header(menu.phrase_params, "VM-Message-Caller-Name")));
255
256 msg.from = __FILE__;
257 msg.string_arg = cid_buf;
258 msg.message_id = SWITCH_MESSAGE_INDICATE_DISPLAY;
259 switch_core_session_receive_message(session, &msg);
260
261 /* Save in profile the current msg info for other menu processing AND restoration of our current position */
262 profile->current_msg = current_msg;
263 profile->current_msg_uuid = switch_core_session_strdup(session, switch_event_get_header(menu.phrase_params, "VM-Message-UUID"));
264
265 /* TODO check if msg is gone (purged by another session, notify user and auto jump to next message or something) */
266 if (!skip_header) {
267 if (!initial_count_played) {
268 cmd = switch_core_session_sprintf(session, "json %s %s %s", profile->api_profile, profile->domain, profile->id);
269 jsonapi_populate_event(session, menu.phrase_params, profile->api_msg_count, cmd);
270 initial_count_played = SWITCH_TRUE;
271 // TODO ivre_playback(session, &menu.ivre_d, switch_event_get_header(menu.event_phrases, "msg_count"), NULL, menu.phrase_params, NULL, 0);
272 }
273 if (msg_count > 0) {
274 ivre_playback(session, &menu.ivre_d, switch_event_get_header(menu.event_phrases, "say_msg_number"), NULL, menu.phrase_params, NULL, 0);
275 ivre_playback(session, &menu.ivre_d, switch_event_get_header(menu.event_phrases, "say_date"), NULL, menu.phrase_params, NULL, 0);
276 }
277 }
278 if (msg_count > 0 && !skip_playback) {
279 /* TODO Update the Read date of a message (When msg start, or when it listen compleatly ??? To be determined */
280 ivre_playback(session, &menu.ivre_d, switch_event_get_header(menu.event_phrases, "play_message"), NULL, menu.phrase_params, NULL, 0);
281 }
282 skip_header = SWITCH_FALSE;
283 skip_playback = SWITCH_FALSE;
284
285 ivre_playback(session, &menu.ivre_d, switch_event_get_header(menu.event_phrases, "menu_options"), NULL, menu.phrase_params, NULL, menu.ivr_entry_timeout);
286
287 if (menu.ivre_d.result == RES_TIMEOUT) {
288 ivre_playback_dtmf_buffered(session, switch_event_get_header(menu.event_phrases, "timeout"), NULL, NULL, NULL, 0);
289 } else if (menu.ivre_d.result == RES_INVALID) {
290 ivre_playback_dtmf_buffered(session, switch_event_get_header(menu.event_phrases, "invalid"), NULL, NULL, NULL, 0);
291 } else if (menu.ivre_d.result == RES_FOUND) { /* Matching DTMF Key Pressed */
292 const char *action = switch_event_get_header(menu.event_keys_dtmf, menu.ivre_d.dtmf_stored);
293
294 /* Reset the try count */
295 retry = menu.ivr_maximum_attempts;
296 action:
297 if (action) {
298 if (!strcasecmp(action, "skip_intro")) { /* Skip Header / Play the recording again */
299 skip_header = SWITCH_TRUE;
300 } else if (!strcasecmp(action, "next_msg")) { /* Next Message */
301 next_msg++;
302 } else if (!strcasecmp(action, "prev_msg")) { /* Previous Message */
303 next_msg--;
304 } else if (!strcasecmp(action, "delete_msg")) { /* Delete / Undelete Message */
305 const char *msg_flags = switch_event_get_header(menu.phrase_params, "VM-Message-Flags");
306 if (!msg_flags || strncasecmp(msg_flags, "delete", 6)) {
307 const char *action_on_delete = switch_event_get_header(menu.event_settings, "Nav-Action-On-Delete");
308 cmd = switch_core_session_sprintf(session, "%s %s %s %s", profile->api_profile, profile->domain, profile->id, switch_event_get_header(menu.phrase_params, "VM-Message-UUID"));
309 vmivr_api_execute(session, profile->api_msg_delete, cmd);
310
311 msg_deleted = SWITCH_TRUE;
312
313 if (action_on_delete) {
314 action = action_on_delete;
315 goto action;
316 } else {
317 skip_header = skip_playback = SWITCH_TRUE;
318 }
319 } else {
320 cmd = switch_core_session_sprintf(session, "%s %s %s %s", profile->api_profile, profile->domain, profile->id, switch_event_get_header(menu.phrase_params, "VM-Message-UUID"));
321 vmivr_api_execute(session, profile->api_msg_undelete, cmd);
322
323 msg_undeleted = SWITCH_TRUE;
324 }
325 } else if (!strcasecmp(action, "save_msg")) { /* Save Message */
326 cmd = switch_core_session_sprintf(session, "%s %s %s %s", profile->api_profile, profile->domain, profile->id, switch_event_get_header(menu.phrase_params, "VM-Message-UUID"));
327 vmivr_api_execute(session, profile->api_msg_save, cmd);
328
329 msg_saved = SWITCH_TRUE;
330 } else if (!strcasecmp(action, "callback")) { /* CallBack caller */
331 const char *cid_num = switch_event_get_header(menu.phrase_params, "VM-Message-Caller-Number");
332 if (cid_num) {
333 /* TODO add detection for private number */
334 switch_core_session_execute_exten(session, cid_num, "XML", profile->domain);
335 } else {
336 /* TODO Some error msg that the msg doesn't contain a caller number */
337 }
338 } else if (!strncasecmp(action, "menu:", 5)) { /* Sub Menu */
339 void (*fPtr)(switch_core_session_t *session, vmivr_profile_t *profile) = vmivr_get_menu_function(action+5);
340 if (fPtr) {
341 fPtr(session, profile);
342 }
343 } else if (!strcasecmp(action, "return")) { /* Return */
344 retry = -1;
345 }
346 }
347 }
348
349 /* IF the API to get the message returned us a COPY of the file menu.ivre_dally (temp file create from a DB or from a web server), delete it */
350 if (switch_true(switch_event_get_header(menu.phrase_params, "VM-Message-Private-Local-Copy"))) {
351 const char *file_path = switch_event_get_header(menu.phrase_params, "VM-Message-File-Path");
352 if (file_path && unlink(file_path) != 0) {
353 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Failed to delete temp file [%s]\n", file_path);
354 }
355 }
356 menu_instance_free(&menu);
357 }
358 done:
359 switch_event_destroy(&msg_list_params);
360
361 menu_free(&menu);
362
363 return;
364 }
365
vmivr_menu_forward(switch_core_session_t * session,vmivr_profile_t * profile)366 void vmivr_menu_forward(switch_core_session_t *session, vmivr_profile_t *profile) {
367
368 vmivr_menu_t menu = { "std_forward_ask_prepend" };
369 switch_channel_t *channel = switch_core_session_get_channel(session);
370 const char *prepend_filepath = NULL;
371 int retry;
372 switch_bool_t forward_msg = SWITCH_FALSE;
373
374 /* Initialize Menu Configs */
375 menu_init(profile, &menu);
376
377 if (!menu.event_keys_dtmf || !menu.event_phrases) {
378 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing Menu Phrases or Keys in menu '%s'\n", menu.name);
379 goto end;
380 }
381
382 for (retry = menu.ivr_maximum_attempts; switch_channel_ready(channel) && retry > 0; retry--) {
383
384 menu_instance_init(&menu);
385
386 switch_event_add_header(menu.phrase_params, SWITCH_STACK_BOTTOM, "IVR-Retry-Left", "%d", retry);
387
388 ivre_init(&menu.ivre_d, menu.dtmfa);
389
390 ivre_playback(session, &menu.ivre_d, switch_event_get_header(menu.event_phrases, "menu_options"), NULL, menu.phrase_params, NULL, menu.ivr_entry_timeout);
391
392 if (menu.ivre_d.result == RES_TIMEOUT) {
393 ivre_playback_dtmf_buffered(session, switch_event_get_header(menu.event_phrases, "timeout"), NULL, NULL, NULL, 0);
394 } else if (menu.ivre_d.result == RES_INVALID) {
395 ivre_playback_dtmf_buffered(session, switch_event_get_header(menu.event_phrases, "invalid"), NULL, NULL, NULL, 0);
396 } else if (menu.ivre_d.result == RES_FOUND) { /* Matching DTMF Key Pressed */
397 const char *action = switch_event_get_header(menu.event_keys_dtmf, menu.ivre_d.dtmf_stored);
398
399 /* Reset the try count */
400 retry = menu.ivr_maximum_attempts;
401
402 if (action) {
403 if (!strcasecmp(action, "return")) { /* Return to the previous menu */
404 retry = -1;
405 forward_msg = SWITCH_FALSE;
406 } else if (!strcasecmp(action, "prepend")) { /* Prepend record msg */
407 vmivr_menu_t sub_menu = { "std_record_message" };
408
409 const char *tmp_filepath = NULL;
410 const char *record_format = NULL;
411
412 switch_status_t status;
413
414 /* Initialize Menu Configs */
415 menu_init(profile, &sub_menu);
416
417 record_format = switch_event_get_header(sub_menu.event_settings, "Record-Format");
418
419 tmp_filepath = generate_random_file_name(session, "voicemail_ivr", record_format);
420
421 status = vmivr_menu_record(session, profile, &sub_menu, tmp_filepath);
422
423 if (status == SWITCH_STATUS_SUCCESS) {
424 //char *cmd = switch_core_session_sprintf(session, "%s %s %s %d %s", profile->api_profile, profile->domain, profile->id, gnum, tmp_filepath);
425 //char *str_num = switch_core_session_sprintf(session, "%d", gnum);
426 //vmivr_api_execute(session, profile->api_pref_greeting_set, cmd);
427 //ivre_playback_dtmf_buffered(session, switch_event_get_header(menu.event_phrases, "selected_slot"), str_num, NULL, NULL, 0);
428 prepend_filepath = tmp_filepath;
429 retry = -1;
430 forward_msg = SWITCH_TRUE;
431 } else {
432 ivre_playback_dtmf_buffered(session, switch_event_get_header(menu.event_phrases, "record_failed"), NULL, NULL, NULL, 0);
433 }
434 menu_free(&sub_menu);
435
436 } else if (!strcasecmp(action, "forward")) { /* Forward without prepend msg */
437 retry = -1;
438 forward_msg = SWITCH_TRUE;
439 } else if (!strncasecmp(action, "menu:", 5)) { /* Sub Menu */
440 void (*fPtr)(switch_core_session_t *session, vmivr_profile_t *profile) = vmivr_get_menu_function(action+5);
441 if (fPtr) {
442 fPtr(session, profile);
443 }
444 }
445 }
446 }
447 menu_instance_free(&menu);
448
449
450 }
451
452 /* Ask Extension to Forward */
453 if (forward_msg) {
454 for (retry = menu.ivr_maximum_attempts; switch_channel_ready(channel) && retry > 0; retry--) {
455 const char *id = NULL;
456 vmivr_menu_t sub_menu = { "std_forward_ask_extension" };
457
458 /* Initialize Menu Configs */
459 menu_init(profile, &sub_menu);
460 switch_event_add_header(sub_menu.phrase_params, SWITCH_STACK_BOTTOM, "IVR-Retry-Left", "%d", retry);
461
462 id = vmivr_menu_get_input_set(session, profile, &sub_menu, "X.");
463
464 if (id) {
465 const char *cmd = switch_core_session_sprintf(session, "%s %s %s %s %s %s %s%s%s", profile->api_profile, profile->domain, profile->id, profile->current_msg_uuid, profile->domain, id, prepend_filepath?" ":"", prepend_filepath?prepend_filepath:"" );
466 if (vmivr_api_execute(session, profile->api_msg_forward, cmd) == SWITCH_STATUS_SUCCESS) {
467 ivre_playback_dtmf_buffered(session, switch_event_get_header(sub_menu.event_phrases, "ack"), "saved", NULL, NULL, 0);
468 retry = -1;
469 } else {
470 ivre_playback_dtmf_buffered(session, switch_event_get_header(sub_menu.event_phrases, "invalid_extension"), NULL, NULL, NULL, 0);
471 }
472 } else {
473 ivre_playback_dtmf_buffered(session, switch_event_get_header(menu.event_phrases, "invalid_input"), NULL, NULL, NULL, 0);
474 }
475 menu_free(&sub_menu);
476 /* TODO add Confirmation of the transfered number */
477 }
478 /* TODO Ask if we want to transfer the msg to more person */
479
480 }
481
482 end:
483 menu_free(&menu);
484 }
485
486
vmivr_menu_record_name(switch_core_session_t * session,vmivr_profile_t * profile)487 void vmivr_menu_record_name(switch_core_session_t *session, vmivr_profile_t *profile) {
488 switch_status_t status;
489 vmivr_menu_t menu = { "std_record_name" };
490
491 const char *tmp_filepath = NULL;
492 const char *record_format = NULL;
493
494 /* Initialize Menu Configs */
495 menu_init(profile, &menu);
496
497 record_format = switch_event_get_header(menu.event_settings, "Record-Format");
498
499 tmp_filepath = generate_random_file_name(session, "voicemail_ivr", record_format);
500
501 status = vmivr_menu_record(session, profile, &menu, tmp_filepath);
502
503 if (status == SWITCH_STATUS_SUCCESS) {
504 char *cmd = switch_core_session_sprintf(session, "%s %s %s %s", profile->api_profile, profile->domain, profile->id, tmp_filepath);
505 vmivr_api_execute(session, profile->api_pref_recname_set, cmd);
506 }
507 }
508
vmivr_menu_set_password(switch_core_session_t * session,vmivr_profile_t * profile)509 void vmivr_menu_set_password(switch_core_session_t *session, vmivr_profile_t *profile) {
510 char *password;
511 vmivr_menu_t menu = { "std_set_password" };
512 const char *password_mask = NULL;
513
514 /* Initialize Menu Configs */
515 menu_init(profile, &menu);
516
517 password_mask = switch_event_get_header(menu.event_settings, "Password-Mask");
518
519 password = vmivr_menu_get_input_set(session, profile, &menu, password_mask);
520
521 if (password) {
522 char *cmd = switch_core_session_sprintf(session, "%s %s %s %s", profile->api_profile, profile->domain, profile->id, password);
523 if (vmivr_api_execute(session, profile->api_pref_password_set, cmd)) {
524 ivre_playback_dtmf_buffered(session, switch_event_get_header(menu.event_phrases, "password_set"), NULL, NULL, NULL, 0);
525 } else {
526 ivre_playback_dtmf_buffered(session, switch_event_get_header(menu.event_phrases, "password_not_set"), NULL, NULL, NULL, 0);
527 }
528 }
529
530 menu_free(&menu);
531 }
532
vmivr_menu_authenticate(switch_core_session_t * session,vmivr_profile_t * profile)533 void vmivr_menu_authenticate(switch_core_session_t *session, vmivr_profile_t *profile) {
534 switch_channel_t *channel = switch_core_session_get_channel(session);
535 vmivr_menu_t menu = { "std_authenticate" };
536 int retry;
537 const char *auth_var = NULL;
538 /* Initialize Menu Configs */
539 menu_init(profile, &menu);
540
541 if (profile->id && (auth_var = switch_channel_get_variable(channel, "voicemail_authorized")) && switch_true(auth_var)) {
542 profile->authorized = SWITCH_TRUE;
543 }
544
545 for (retry = menu.ivr_maximum_attempts; switch_channel_ready(channel) && retry > 0 && profile->authorized == SWITCH_FALSE; retry--) {
546 const char *id = profile->id, *password = NULL;
547 char *cmd = NULL;
548 const char *password_mask = switch_event_get_header(menu.event_settings, "Password-Mask");
549 const char *user_mask = switch_event_get_header(menu.event_settings, "User-Mask");
550 if (!id) {
551 vmivr_menu_t sub_menu = { "std_authenticate_ask_user" };
552 /* Initialize Menu Configs */
553 menu_init(profile, &sub_menu);
554
555 switch_event_add_header(sub_menu.phrase_params, SWITCH_STACK_BOTTOM, "IVR-Retry-Left", "%d", retry);
556
557 id = vmivr_menu_get_input_set(session, profile, &sub_menu, user_mask);
558 menu_free(&sub_menu);
559 }
560 if (!password) {
561 vmivr_menu_t sub_menu = { "std_authenticate_ask_password" };
562 /* Initialize Menu Configs */
563 menu_init(profile, &sub_menu);
564
565 switch_event_add_header(sub_menu.phrase_params, SWITCH_STACK_BOTTOM, "IVR-Retry-Left", "%d", retry);
566
567 password = vmivr_menu_get_input_set(session, profile, &sub_menu, password_mask);
568 menu_free(&sub_menu);
569 }
570 cmd = switch_core_session_sprintf(session, "%s %s %s %s", profile->api_profile, profile->domain, id, password);
571
572 if (vmivr_api_execute(session, profile->api_auth_login, cmd) == SWITCH_STATUS_SUCCESS) {
573 profile->id = id;
574 profile->authorized = SWITCH_TRUE;
575 } else {
576 ivre_playback_dtmf_buffered(session, switch_event_get_header(menu.event_phrases, "fail_auth"), NULL, NULL, NULL, 0);
577 }
578 }
579 menu_free(&menu);
580 }
581
vmivr_menu_select_greeting_slot(switch_core_session_t * session,vmivr_profile_t * profile)582 void vmivr_menu_select_greeting_slot(switch_core_session_t *session, vmivr_profile_t *profile) {
583 vmivr_menu_t menu = { "std_select_greeting_slot" };
584
585 const char *result;
586 int gnum = -1;
587
588 /* Initialize Menu Configs */
589 menu_init(profile, &menu);
590
591 result = vmivr_menu_get_input_set(session, profile, &menu, "X");
592
593 if (result)
594 gnum = atoi(result);
595 if (gnum != -1) {
596 char * cmd = switch_core_session_sprintf(session, "%s %s %s %d", profile->api_profile, profile->domain, profile->id, gnum);
597 if (vmivr_api_execute(session, profile->api_pref_greeting_set, cmd) == SWITCH_STATUS_SUCCESS) {
598 char *str_num = switch_core_session_sprintf(session, "%d", gnum);
599 char *cmd = switch_core_session_sprintf(session, "json %s %s %s %d %s", profile->api_profile, profile->domain, profile->id);
600 switch_event_t *phrases = jsonapi2event(session, profile->api_pref_greeting_get, cmd);
601
602 ivre_playback_dtmf_buffered(session, switch_event_get_header(menu.event_phrases, "selected_slot"), str_num, phrases, NULL, 0);
603
604 if (switch_true(switch_event_get_header(phrases, "VM-Message-Private-Local-Copy"))) {
605 const char *file_path = switch_event_get_header(phrases, "VM-Preference-Greeting-File-Path");
606 if (file_path && unlink(file_path) != 0) {
607 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Failed to delete temp file [%s]\n", file_path);
608 }
609 }
610
611 switch_event_destroy(&phrases);
612 } else {
613 ivre_playback_dtmf_buffered(session, switch_event_get_header(menu.event_phrases, "invalid_slot"), NULL, NULL, NULL, 0);
614 }
615 }
616 menu_free(&menu);
617 }
618
vmivr_menu_record_greeting_with_slot(switch_core_session_t * session,vmivr_profile_t * profile)619 void vmivr_menu_record_greeting_with_slot(switch_core_session_t *session, vmivr_profile_t *profile) {
620
621 vmivr_menu_t menu = { "std_record_greeting_with_slot" };
622
623 const char *result;
624 int gnum = -1;
625
626 /* Initialize Menu Configs */
627 menu_init(profile, &menu);
628
629 result = vmivr_menu_get_input_set(session, profile, &menu, "X");
630
631 if (result)
632 gnum = atoi(result);
633
634 /* If user entered 0, we don't accept it */
635 if (gnum > 0) {
636 vmivr_menu_t sub_menu = { "std_record_greeting" };
637 char *tmp_filepath = NULL;
638 const char *record_format = NULL;
639
640 switch_status_t status;
641
642 /* Initialize Menu Configs */
643 menu_init(profile, &sub_menu);
644
645 record_format = switch_event_get_header(menu.event_settings, "Record-Format");
646
647 tmp_filepath = generate_random_file_name(session, "voicemail_ivr", record_format);
648
649 status = vmivr_menu_record(session, profile, &sub_menu, tmp_filepath);
650
651 if (status == SWITCH_STATUS_SUCCESS) {
652 char *cmd = switch_core_session_sprintf(session, "%s %s %s %d %s", profile->api_profile, profile->domain, profile->id, gnum, tmp_filepath);
653 char *str_num = switch_core_session_sprintf(session, "%d", gnum);
654 vmivr_api_execute(session, profile->api_pref_greeting_set, cmd);
655 ivre_playback_dtmf_buffered(session, switch_event_get_header(menu.event_phrases, "selected_slot"), str_num, NULL, NULL, 0);
656 }
657 menu_free(&sub_menu);
658
659 }
660
661 menu_free(&menu);
662
663 }
664
vmivr_menu_preference(switch_core_session_t * session,vmivr_profile_t * profile)665 void vmivr_menu_preference(switch_core_session_t *session, vmivr_profile_t *profile) {
666 switch_channel_t *channel = switch_core_session_get_channel(session);
667
668 int retry;
669
670 vmivr_menu_t menu = { "std_preference" };
671
672 /* Initialize Menu Configs */
673 menu_init(profile, &menu);
674
675 if (!menu.event_keys_dtmf || !menu.event_phrases) {
676 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing Menu Phrases or Keys in menu '%s'\n", menu.name);
677 goto end;
678 }
679
680 for (retry = menu.ivr_maximum_attempts; switch_channel_ready(channel) && retry > 0; retry--) {
681
682 menu_instance_init(&menu);
683
684 switch_event_add_header(menu.phrase_params, SWITCH_STACK_BOTTOM, "IVR-Retry-Left", "%d", retry);
685
686 ivre_init(&menu.ivre_d, menu.dtmfa);
687
688 ivre_playback(session, &menu.ivre_d, switch_event_get_header(menu.event_phrases, "menu_options"), NULL, menu.phrase_params, NULL, menu.ivr_entry_timeout);
689
690 if (menu.ivre_d.result == RES_TIMEOUT) {
691 ivre_playback_dtmf_buffered(session, switch_event_get_header(menu.event_phrases, "timeout"), NULL, NULL, NULL, 0);
692 } else if (menu.ivre_d.result == RES_INVALID) {
693 ivre_playback_dtmf_buffered(session, switch_event_get_header(menu.event_phrases, "invalid"), NULL, NULL, NULL, 0);
694 } else if (menu.ivre_d.result == RES_FOUND) { /* Matching DTMF Key Pressed */
695 const char *action = switch_event_get_header(menu.event_keys_dtmf, menu.ivre_d.dtmf_stored);
696
697 /* Reset the try count */
698 retry = menu.ivr_maximum_attempts;
699
700 if (action) {
701 if (!strcasecmp(action, "return")) { /* Return to the previous menu */
702 retry = -1;
703 } else if (!strncasecmp(action, "menu:", 5)) { /* Sub Menu */
704 void (*fPtr)(switch_core_session_t *session, vmivr_profile_t *profile) = vmivr_get_menu_function(action+5);
705 if (fPtr) {
706 fPtr(session, profile);
707 }
708 }
709 }
710 }
711 menu_instance_free(&menu);
712 }
713
714 end:
715 menu_free(&menu);
716 }
717
vmivr_menu_get_input_set(switch_core_session_t * session,vmivr_profile_t * profile,vmivr_menu_t * menu,const char * input_mask)718 char *vmivr_menu_get_input_set(switch_core_session_t *session, vmivr_profile_t *profile, vmivr_menu_t *menu, const char *input_mask) {
719 char *result = NULL;
720 int retry;
721 const char *terminate_key = NULL;
722 switch_channel_t *channel = switch_core_session_get_channel(session);
723
724 if (!menu->event_keys_dtmf || !menu->event_phrases) {
725 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing Menu Phrases or Keys in menu '%s'\n", menu->name);
726 goto end;
727 }
728
729 terminate_key = switch_event_get_header(menu->event_keys_action, "ivrengine:terminate_entry");
730
731 for (retry = menu->ivr_maximum_attempts; switch_channel_ready(channel) && retry > 0; retry--) {
732 int i;
733
734 menu_instance_init(menu);
735
736 switch_event_add_header(menu->phrase_params, SWITCH_STACK_BOTTOM, "IVR-Retry-Left", "%d", retry);
737
738 /* Find the last entry and append this one to it */
739 for (i=0; i < 16 && menu->dtmfa[i]; i++){
740 }
741 menu->dtmfa[i] = (char *) input_mask;
742
743 ivre_init(&menu->ivre_d, menu->dtmfa);
744 if (terminate_key) {
745 menu->ivre_d.terminate_key = terminate_key[0];
746 }
747 ivre_playback(session, &menu->ivre_d, switch_event_get_header(menu->event_phrases, "instructions"), NULL, menu->phrase_params, NULL, menu->ivr_entry_timeout);
748
749 if (menu->ivre_d.result == RES_TIMEOUT) {
750 ivre_playback_dtmf_buffered(session, switch_event_get_header(menu->event_phrases, "timeout"), NULL, NULL, NULL, 0);
751 } else if (menu->ivre_d.result == RES_INVALID) {
752 ivre_playback_dtmf_buffered(session, switch_event_get_header(menu->event_phrases, "invalid"), NULL, NULL, NULL, 0);
753 } else if (menu->ivre_d.result == RES_FOUND) { /* Matching DTMF Key Pressed */
754
755 /* Reset the try count */
756 retry = menu->ivr_maximum_attempts;
757
758 if (!strncasecmp(menu->ivre_d.completeMatch, input_mask, 1)) {
759 result = switch_core_session_strdup(session, menu->ivre_d.dtmf_stored);
760 retry = -1;
761
762 }
763 }
764 menu_instance_free(menu);
765 }
766 end:
767 return result;
768 }
769
vmivr_menu_record(switch_core_session_t * session,vmivr_profile_t * profile,vmivr_menu_t * menu,const char * file_name)770 switch_status_t vmivr_menu_record(switch_core_session_t *session, vmivr_profile_t *profile, vmivr_menu_t *menu, const char *file_name) {
771 switch_status_t status = SWITCH_STATUS_FALSE;
772 switch_channel_t *channel = switch_core_session_get_channel(session);
773 int retry;
774
775 switch_bool_t record_prompt = SWITCH_TRUE;
776 switch_bool_t listen_recording = SWITCH_FALSE;
777 switch_bool_t play_instruction = SWITCH_TRUE;
778
779 if (!menu->event_keys_dtmf || !menu->event_phrases) {
780 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing Menu Phrases or Keys in menu '%s'\n", menu->name);
781 goto end;
782 }
783
784 for (retry = menu->ivr_maximum_attempts; switch_channel_ready(channel) && retry > 0; retry--) {
785 switch_file_handle_t fh = { 0 };
786 const char *rec_silence_hits = switch_event_get_header(menu->event_settings, "Record-Silence-Hits");
787 const char *rec_silence_threshold = switch_event_get_header(menu->event_settings, "Record-Silence-Threshold");
788 const char *rec_silence_samplerate = switch_event_get_header(menu->event_settings, "Record-Sample-Rate");
789 const char *rec_maximum_length = switch_event_get_header(menu->event_settings, "Record-Maximum-Length");
790 const char *rec_minimum_length = switch_event_get_header(menu->event_settings, "Record-Minimum-Length");
791 switch_size_t record_length = 0;
792
793 /* Prepare Recording File Handle */
794 fh.thresh = atoi(rec_silence_threshold);
795 fh.silence_hits = atoi(rec_silence_hits);
796 if (rec_silence_samplerate) {
797 fh.samplerate = atoi(rec_silence_samplerate);
798 }
799
800 menu_instance_init(menu);
801
802 switch_event_add_header(menu->phrase_params, SWITCH_STACK_BOTTOM, "IVR-Retry-Left", "%d", retry);
803
804 ivre_init(&menu->ivre_d, menu->dtmfa);
805
806 if (record_prompt) {
807 if (play_instruction) {
808 ivre_playback(session, &menu->ivre_d, switch_event_get_header(menu->event_phrases, "instructions"), NULL, menu->phrase_params, NULL, 0);
809 }
810 play_instruction = SWITCH_TRUE;
811
812 ivre_record(session, &menu->ivre_d, menu->phrase_params, file_name, &fh, atoi(rec_maximum_length), &record_length);
813 } else {
814 if (listen_recording) {
815 switch_event_add_header(menu->phrase_params, SWITCH_STACK_BOTTOM, "VM-Record-File-Path", "%s", file_name);
816 ivre_playback(session, &menu->ivre_d, switch_event_get_header(menu->event_phrases, "play_recording"), NULL, menu->phrase_params, NULL, 0);
817 listen_recording = SWITCH_FALSE;
818
819 }
820 ivre_playback(session, &menu->ivre_d, switch_event_get_header(menu->event_phrases, "menu_options"), NULL, menu->phrase_params, NULL, menu->ivr_entry_timeout);
821 }
822
823 if (menu->ivre_d.recorded_audio) {
824 /* Reset the try count */
825 retry = menu->ivr_maximum_attempts;
826
827 if (rec_minimum_length && record_length < atoi(rec_minimum_length)) {
828 ivre_playback_dtmf_buffered(session, switch_event_get_header(menu->event_phrases, "too_short"), NULL, NULL, NULL, 0);
829 unlink(file_name);
830 } else {
831 record_prompt = SWITCH_FALSE;
832 }
833
834 } else if (menu->ivre_d.result == RES_TIMEOUT) {
835 ivre_playback_dtmf_buffered(session, switch_event_get_header(menu->event_phrases, "timeout"), NULL, NULL, NULL, 0);
836 } else if (menu->ivre_d.result == RES_INVALID) {
837 ivre_playback_dtmf_buffered(session, switch_event_get_header(menu->event_phrases, "invalid"), NULL, NULL, NULL, 0);
838 } else if (menu->ivre_d.result == RES_FOUND) { /* Matching DTMF Key Pressed */
839 const char *action = switch_event_get_header(menu->event_keys_dtmf, menu->ivre_d.dtmf_stored);
840
841 /* Reset the try count */
842 retry = menu->ivr_maximum_attempts;
843
844 if (action) {
845 if (!strcasecmp(action, "listen")) { /* Listen */
846 listen_recording = SWITCH_TRUE;
847
848 } else if (!strcasecmp(action, "save")) {
849 retry = -1;
850 status = SWITCH_STATUS_SUCCESS;
851
852 } else if (!strcasecmp(action, "rerecord")) {
853 record_prompt = SWITCH_TRUE;
854
855 } else if (!strcasecmp(action, "skip_instruction")) { /* Skip Recording Greeting */
856 play_instruction = SWITCH_FALSE;
857
858 } else if (!strncasecmp(action, "menu:", 5)) { /* Sub Menu */
859 void (*fPtr)(switch_core_session_t *session, vmivr_profile_t *profile) = vmivr_get_menu_function(action+5);
860 if (fPtr) {
861 fPtr(session, profile);
862 }
863 } else if (!strcasecmp(action, "return")) { /* Return */
864 retry = -1;
865 }
866 }
867 }
868 menu_instance_free(menu);
869 }
870
871 end:
872 return status;
873 }
874
875
vmivr_get_menu_function(const char * menu_name)876 void (*vmivr_get_menu_function(const char *menu_name))(switch_core_session_t *session, vmivr_profile_t *profile) {
877 int i = 0;
878
879 if (menu_name) {
880 for (i=0; menu_list[i].name ; i++) {
881 if (!strcasecmp(menu_list[i].name, menu_name)) {
882 return menu_list[i].pt2Func;
883 }
884 }
885 }
886 return NULL;
887 }
888
889
890
891 /* For Emacs:
892 * Local Variables:
893 * mode:c
894 * indent-tabs-mode:t
895 * tab-width:4
896 * c-basic-offset:4
897 * End:
898 * For VIM:
899 * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet
900 */
901