--- /tmp/asterisk-1.4.11/apps/app_voicemail.c 2007-08-20 10:34:43.000000000 -0500 +++ apps/app_voicemail.c 2007-10-05 12:33:13.000000000 -0500 @@ -199,6 +199,7 @@ OPT_PREPEND_MAILBOX = (1 << 4), OPT_PRIORITY_JUMP = (1 << 5), OPT_AUTOPLAY = (1 << 6), + OPT_DTMF_EXIT = (1 << 7), } vm_option_flags; enum { @@ -211,6 +212,7 @@ AST_APP_OPTIONS(vm_app_options, { AST_APP_OPTION('s', OPT_SILENT), AST_APP_OPTION('b', OPT_BUSY_GREETING), + AST_APP_OPTION('d', OPT_DTMF_EXIT), AST_APP_OPTION('u', OPT_UNAVAIL_GREETING), AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN), AST_APP_OPTION('p', OPT_PREPEND_MAILBOX), @@ -373,7 +375,7 @@ static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context); static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir, - signed char record_gain, struct vm_state *vms); + signed char record_gain, struct vm_state *vms, const char *dtmf_exit_codes); static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain); static int vm_play_folder_name(struct ast_channel *chan, char *mbox); static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname); @@ -451,6 +453,10 @@ " SUCCESS | USEREXIT | FAILED\n\n" " Options:\n" " b - Play the 'busy' greeting to the calling party.\n" +" d - Allow the caller to dial a 1 digit extension while listening to\n" +" the voicemail greeting or recording a voicemail message. Exit to\n" +" that extension if it exists in the current context, or the context\n" +" defined in the VMEXITCONTEXT variable, if it exists.\n" " g(#) - Use the specified amount of gain when recording the voicemail\n" " message. The units are whole-number decibels (dB).\n" " s - Skip the playback of instructions for leaving a message to the\n" @@ -2783,6 +2789,10 @@ struct ast_vm_user *vmu; struct ast_vm_user svm; const char *category = NULL; + int vm_exit_ext = 0; + char str_vm_exit_ext[2]; + char str_vm_exit_exts[10] = ""; + const char *vm_exit_context = NULL; ast_copy_string(tmp, ext, sizeof(tmp)); ext = tmp; @@ -2860,6 +2870,42 @@ ausemacro = 1; } + /* vm_exit setup */ + if (ast_test_flag(options, OPT_DTMF_EXIT)) { + vm_exit_context = pbx_builtin_getvar_helper(chan, "VMEXITCONTEXT"); + if (vm_exit_context != NULL) { + ast_verbose(VERBOSE_PREFIX_3 "Found VMEXITCONTEXT variable: %s\n", vm_exit_context); + } + + for (vm_exit_ext = 1; vm_exit_ext <= 9; vm_exit_ext++) { + snprintf(str_vm_exit_ext, sizeof(str_vm_exit_ext), "%d", vm_exit_ext); + + if (vm_exit_context != NULL) { // check in the VMEXITCONTEXT first + if (ast_exists_extension(chan, vm_exit_context, str_vm_exit_ext, 1, chan->cid.cid_num)) + strncat(str_vm_exit_exts, str_vm_exit_ext, sizeof(str_vm_exit_exts) - strlen(str_vm_exit_exts) - 1); + } else if (!ast_strlen_zero(vmu->exit)) { // then check default exit context + if (ast_exists_extension(chan, vmu->exit, str_vm_exit_ext, 1, chan->cid.cid_num)) + strncat(str_vm_exit_exts, str_vm_exit_ext, sizeof(str_vm_exit_exts) - strlen(str_vm_exit_exts) - 1); + } else if (ast_exists_extension(chan, chan->context, str_vm_exit_ext, 1, chan->cid.cid_num)) // then check actual context + strncat(str_vm_exit_exts, str_vm_exit_ext, sizeof(str_vm_exit_exts) - strlen(str_vm_exit_exts) - 1); + else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, str_vm_exit_ext, 1, chan->cid.cid_num)) { // then check macro context + strncat(str_vm_exit_exts, str_vm_exit_ext, sizeof(str_vm_exit_exts) - strlen(str_vm_exit_exts) - 1); + } + } + + strncat(ecodes, str_vm_exit_exts, sizeof(ecodes) - strlen(ecodes) - 1); + + ast_verbose(VERBOSE_PREFIX_3 "Looking for the following dtmf exit codes %s in the following context(s)\n", ecodes); + + if (vm_exit_context != NULL) { + ast_verbose(VERBOSE_PREFIX_3 " %s\n", vm_exit_context); + } else if (!ast_strlen_zero(vmu->exit)) { + ast_verbose(VERBOSE_PREFIX_3 " %s\n", vmu->exit); + } else { + ast_verbose(VERBOSE_PREFIX_3 " %s, %s\n", chan->context, chan->macrocontext); + } + } + /* Play the beginning intro if desired */ if (!ast_strlen_zero(prefile)) { #ifdef ODBC_STORAGE @@ -2921,6 +2967,22 @@ return 0; } + /* Check for 1-9 here*/ + ast_log(LOG_DEBUG, "Voicemail: res: %c\n", res); + switch (res) { + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + vm_exit_ext = res; + goto exit_to_extension; + } + /* Check for a '0' here */ if (res == '0') { transfer: @@ -3054,9 +3116,9 @@ } else ast_log(LOG_WARNING, "Error opening text file for output\n"); #ifdef IMAP_STORAGE - res = play_record_review(chan, NULL, tmptxtfile, vmmaxmessage, fmt, 1, vmu, &duration, NULL, options->record_gain, vms); + res = play_record_review(chan, NULL, tmptxtfile, vmmaxmessage, fmt, 1, vmu, &duration, NULL, options->record_gain, vms, str_vm_exit_exts); #else - res = play_record_review(chan, NULL, tmptxtfile, vmmaxmessage, fmt, 1, vmu, &duration, NULL, options->record_gain, NULL); + res = play_record_review(chan, NULL, tmptxtfile, vmmaxmessage, fmt, 1, vmu, &duration, NULL, options->record_gain, NULL, str_vm_exit_exts); #endif if (txt) { @@ -3129,10 +3191,26 @@ } } } + if (res == '0') { goto transfer; - } else if (res > 0) - res = 0; + } else { + switch (res) { // check to see if we hit a button from 1-9 to exit + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + vm_exit_ext = res; + goto exit_to_extension; + default: + res = 0; + } + } if (duration < vmminmessage) /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */ @@ -3141,6 +3219,24 @@ pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS"); } else ast_log(LOG_WARNING, "No format for saving voicemail?\n"); + +exit_to_extension: + chan->exten[0] = vm_exit_ext; + chan->exten[1] = '\0'; + ast_verbose(VERBOSE_PREFIX_3 "Exiting to extension %s\n", chan->exten); + if (vm_exit_context != NULL) { + ast_copy_string(chan->context, vm_exit_context, sizeof(chan->context)); + } else if (!ast_strlen_zero(vmu->exit)) { + ast_copy_string(chan->context, vmu->exit, sizeof(chan->context)); + } else if (!ast_strlen_zero(chan->macrocontext)) { + ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context)); + } + chan->priority = 0; + free_user(vmu); + pbx_builtin_setvar_helper(chan, "VMMAILBOX", vmu->mailbox); + pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT"); + return 0; + leave_vm_out: free_user(vmu); @@ -5886,9 +5982,9 @@ snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username); if (ast_fileexists(prefile, NULL, NULL) < 1) { #ifndef IMAP_STORAGE - cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL); + cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL, NULL); #else - cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms); + cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL); #endif if (cmd < 0 || cmd == 't' || cmd == '#') return cmd; @@ -5900,9 +5996,9 @@ snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username); if (ast_fileexists(prefile, NULL, NULL) < 1) { #ifndef IMAP_STORAGE - cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL); + cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL, NULL); #else - cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms); + cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL); #endif if (cmd < 0 || cmd == 't' || cmd == '#') return cmd; @@ -5911,9 +6007,9 @@ snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username); if (ast_fileexists(prefile, NULL, NULL) < 1) { #ifndef IMAP_STORAGE - cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL); + cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL, NULL); #else - cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms); + cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL); #endif if (cmd < 0 || cmd == 't' || cmd == '#') return cmd; @@ -5950,25 +6046,25 @@ case '1': snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username); #ifndef IMAP_STORAGE - cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL); + cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL, NULL); #else - cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms); + cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL); #endif break; case '2': snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username); #ifndef IMAP_STORAGE - cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL); + cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL, NULL); #else - cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms); + cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL); #endif break; case '3': snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username); #ifndef IMAP_STORAGE - cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL); + cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL, NULL); #else - cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms); + cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL); #endif break; case '4': @@ -6064,18 +6160,18 @@ RETRIEVE(prefile, -1); if (ast_fileexists(prefile, NULL, NULL) <= 0) { #ifndef IMAP_STORAGE - play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL); + play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL, NULL); #else - play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms); + play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL); #endif cmd = 't'; } else { switch (cmd) { case '1': #ifndef IMAP_STORAGE - cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL); + cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL, NULL); #else - cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms); + cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL); #endif break; case '2': @@ -6919,7 +7015,7 @@ ast_module_user_remove(u); return -1; } - ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_PRIORITY_JUMP); + ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_PRIORITY_JUMP | OPT_DTMF_EXIT); if (ast_test_flag(&flags, OPT_RECORDGAIN)) { int gain; @@ -8142,7 +8238,7 @@ static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir, - signed char record_gain, struct vm_state *vms) + signed char record_gain, struct vm_state *vms, const char *dtmf_exit_codes) { /* Record message & let caller review or re-record it, or set options if applicable */ int res = 0; @@ -8154,7 +8250,8 @@ signed char zero_gain = 0; char tempfile[PATH_MAX]; char *acceptdtmf = "#"; - char *canceldtmf = ""; + char operdtmf[2]; + char canceldtmf[16]; /* Note that urgent and private are for flagging messages as such in the future */ @@ -8217,7 +8314,11 @@ if (record_gain) ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0); if (ast_test_flag(vmu, VM_OPERATOR)) - canceldtmf = "0"; + snprintf(operdtmf, sizeof(operdtmf), "0"); + if (!ast_strlen_zero(dtmf_exit_codes)) + snprintf(canceldtmf, sizeof(canceldtmf), "%s%s", operdtmf, dtmf_exit_codes); + else + snprintf(canceldtmf, sizeof(canceldtmf), "%s", operdtmf); cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf); if (record_gain) ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0); @@ -8229,6 +8330,24 @@ } return cmd; } + + // We want to hand these numbers back to the calling function + if (!ast_strlen_zero(dtmf_exit_codes)) { + switch (cmd) { + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + ast_filedelete(tempfile, NULL); + return cmd; + } + } + if (cmd == '0') { break; } else if (cmd == '*') {