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