; extensions.conf - the Asterisk dial plan ; ; Static extension configuration file, used by ; the pbx_config module. This is where you configure all your ; inbound and outbound calls in Asterisk. ; ; This configuration file is reloaded ; - With the "dialplan reload" command in the CLI ; - With the "reload" command (that reloads everything) in the CLI ; ; The "General" category is for certain variables. ; [general] ; ; If static is set to no, or omitted, then the pbx_config will rewrite ; this file when extensions are modified. Remember that all comments ; made in the file will be lost when that happens. ; ; XXX Not yet implemented XXX ; static=yes ; ; if static=yes and writeprotect=no, you can save dialplan by ; CLI command "dialplan save" too ; writeprotect=no ; ; If autofallthrough is set, then if an extension runs out of ; things to do, it will terminate the call with BUSY, CONGESTION ; or HANGUP depending on Asterisk's best guess. This is the default. ; ; If autofallthrough is not set, then if an extension runs out of ; things to do, Asterisk will wait for a new extension to be dialed ; (this is the original behavior of Asterisk 1.0 and earlier). ; ;autofallthrough=no ; ; If clearglobalvars is set, global variables will be cleared ; and reparsed on an extensions reload, or Asterisk reload. ; ; If clearglobalvars is not set, then global variables will persist ; through reloads, and even if deleted from the extensions.conf or ; one of its included files, will remain set to the previous value. ; ; NOTE: A complication sets in, if you put your global variables into ; the AEL file, instead of the extensions.conf file. With clearglobalvars ; set, a "reload" will often leave the globals vars cleared, because it ; is not unusual to have extensions.conf (which will have no globals) ; load after the extensions.ael file (where the global vars are stored). ; So, with "reload" in this particular situation, first the AEL file will ; clear and then set all the global vars, then, later, when the extensions.conf ; file is loaded, the global vars are all cleared, and then not set, because ; they are not stored in the extensions.conf file. ; clearglobalvars=no ; ; If priorityjumping is set to 'yes', then applications that support ; 'jumping' to a different priority based on the result of their operations ; will do so (this is backwards compatible behavior with pre-1.2 releases ; of Asterisk). Individual applications can also be requested to do this ; by passing a 'j' option in their arguments. ; ;priorityjumping=yes ; ; User context is where entries from users.conf are registered. The ; default value is 'default' ; ;userscontext=default ; ; You can include other config files, use the #include command ; (without the ';'). Note that this is different from the "include" command ; that includes contexts within other contexts. The #include command works ; in all asterisk configuration files. ;#include "filename.conf" ; The "Globals" category contains global variables that can be referenced ; in the dialplan with the GLOBAL dialplan function: ; ${GLOBAL(VARIABLE)} ; ${${GLOBAL(VARIABLE)}} or ${text${GLOBAL(VARIABLE)}} or any hybrid ; Unix/Linux environmental variables can be reached with the ENV dialplan ; function: ${ENV(VARIABLE)} ; [globals] VM_HOME=/var/spool/asterisk/voicemail ; root directory of the voicemail application RELOAD_VM_CONFIGURATION=0 ;FUNCTION_TEST=${SHELL(echo "foo_bar")} ; currently causes a segfault of Asterisk [phones] ; Testing Area! ; SAMPLE: Creation of new voicemail envelope exten => 5,1,Answer() exten => 5,n,GoSub(setup_mailbox,s,1(default,100,INBOX)) exten => 5,n,GoSub(get_msg_count,s,1()) exten => 5,n,Set(MSG_COUNT=${GOSUB_RETVAL}) exten => 5,n,GoSub(set_msg_number,s,1(${MSG_COUNT})) exten => 5,n,Set(MSGNUM=${GOSUB_RETVAL}) exten => 5,n,System(touch ${PWD}/msg${MSGNUM}.txt) ; VoiceMail(mailbox[@context][&mailbox[@context]][...][|options]) exten => 100,1,GoSub(voicemail,s,1()) exten => 101,1,Voicemail() exten => 200,1,GoSub(voicemail,s,1(100)) exten => 201,1,Voicemail(100) exten => 300,1,GoSub(voicemail,s,1(100,u)) exten => 301,1,Voicemail(100,u) exten => 400,1,GoSub(voicemail,s,1(100,su)) exten => 401,1,Voicemail(100,us) ; Invalid voicemail exten => 500,1,GoSub(voicemail,s,1(101,u)) exten => 500,n,DumpChan() exten => 501,1,Voicemail(101,u) exten => 501,n,DumpChan() ; VoiceMailMain([mailbox][@context][|options]) exten => 1100,1,GoSub(voicemailmain,s,1()) exten => 1101,1,VoicemailMain() exten => 1200,1,GoSub(voicemailmain,s,(100)) exten => 1201,1,VoicemailMain(100) exten => 1,1,GoSub(read_voicemail_conf,s,1()) exten => 2,1,Set(LINE_NUMBER=2) exten => 2,n,NoOp(${SHELL(sed -e '/\\\;/d' /etc/asterisk/voicemail.conf \| grep '.' \| tr "\\\n"\, "&" \| cut -d \\\& -f ${LINE_NUMBER})}) exten => 2,n,NoOp(${COMMAND}) exten => 3,1,Set(information=foo_bar) exten => 3,n,Set(RES=${SET(information2=foo_bar)}) exten => 3,n,DumpChan() exten => 3,n,Answer() exten => 4,1,NoOp() exten => 4,n,Set(LINE_NUMBER=1) exten => 4,n,While($[${LEN(${SHELL(sed -e '/\\\;/d' /etc/asterisk/voicemail.conf \| grep '.' \| tr "\\\n" "~" \| cut -d \\\~ -f ${LINE_NUMBER})})} > 1]) exten => 4,n,Verbose(1|${SHELL(sed -e '/\\\;/d' /etc/asterisk/voicemail.conf \| grep '.' \| tr "\\\n" "~" \| cut -d \\\~ -f ${LINE_NUMBER})}) exten => 4,n,Set(LINE_NUMBER=$[${LINE_NUMBER} + 1]) exten => 4,n,EndWhile() [voicemail] ; Leif Madsen ;-- [Description] VoiceMail(mailbox[@context][&mailbox[@context]][...][|options]): This application allows the calling party to leave a message for the specified list of mailboxes. When multiple mailboxes are specified, the greeting will be taken from the first mailbox specified. Dialplan execution will stop if the specified mailbox does not exist. The Voicemail application will exit if any of the following DTMF digits are received: 0 - Jump to the 'o' extension in the current dialplan context. * - Jump to the 'a' extension in the current dialplan context. This application will set the following channel variable upon completion: VMSTATUS - This indicates the status of the execution of the VoiceMail application. The possible values are: SUCCESS | USEREXIT | FAILED Options: b - Play the 'busy' greeting to the calling party. g(#) - Use the specified amount of gain when recording the voicemail message. The units are whole-number decibels (dB). s - Skip the playback of instructions for leaving a message to the calling party. u - Play the 'unavailable' greeting. j - Jump to priority n+101 if the mailbox is not found or some other error occurs. --; exten => s,1,NoOp() ; Get the value of VM_RELOAD_CONFIGURATION so we can determine if it needs to be reset exten => s,n,Set(RESET_RELOAD_VM_CONFIGURATION=${GLOBAL(RELOAD_VM_CONFIGURATION)}) exten => s,n,GoSubIf($[${RELOAD_VM_CONFIGURATION}]?read_voicemail_conf,s,1()) ; Load the voicemail.conf file into the AstDB ; Reset the Global variable VM_RELOAD_CONFIGURATION so we don't load it again next time a call comes in exten => s,n,Exec(${IF($[${RESET_RELOAD_VM_CONFIGURATION}]?Set(GLOBAL(RELOAD_VM_CONFIGURATION)=0):NoOp())}) ; Define some starter variables exten => s,n,Set(MAX_TRIES=3) ; default tries for when we ask the user for input exten => s,n,Set(MAILBOX=${ARG1}) ; set the mailbox number exten => s,n,Set(VM_CTX=${IF($[${EXISTS(${CUT(MAILBOX,@,2)})}]?${CUT(MAILBOX,@,2)}:default)}) ; set the voicemail context exten => s,n,Set(OPTIONS=${ARG2}) ; save the options requested for Voicemail() ; Check if the voicemail exists in our configuration exten => s,n,GotoIf($[${DB_EXISTS(voicemail/conf/[${VM_CTX}]/${MAILBOX})}]?set_options) exten => s,n,Set(VMSTATUS=FAILED) ; invalid voicemail box, return with status exten => s,n,Return() ; Save status of options exten => s,n(set_options),NoOp() ; if we have a valid mailbox, we can continue exten => s,n,Set(b_option=${REGEX("b" ${OPTIONS})}) exten => s,n,Set(g_option=${REGEX("g" ${OPTIONS})}) exten => s,n,Set(s_option=${REGEX("s" ${OPTIONS})}) exten => s,n,Set(u_option=${REGEX("u" ${OPTIONS})}) exten => s,n,Set(j_option=${REGEX("j" ${OPTIONS})}) exten => s,n,GoSubIf($[${ISNULL(${MAILBOX})}]?get_mailbox,1) ; if no mailbox passed to us, get it from the user exten => s,n,GoSub(setup_mailbox,s,1(${VM_CTX},${MAILBOX})) ; setup the mailbox present working directory (PWD) ; Check options so we can determine which greeting to play exten => s,n,GoSubIf($[${u_option} & !$[0${STAT(e,${PWD}/unavail.gsm)} = 0 & 0${STAT(e,${PWD}/unavail.wav)} = 0 & 0${STAT(e,${PWD}/unavail.WAV)} = 0]]?cust_greeting,1(unavail)) exten => s,n,GotoIf($[${EXISTS(${RECORDED_FILE})}]?goodbye) ; if a file exists, we recorded something, and we're done exten => s,n,GoSubIf($[${b_option} & !$[0${STAT(e,${PWD}/busy.gsm)} = 0 & 0${STAT(e,${PWD}/busy.wav)} = 0 & 0${STAT(e,${PWD}/busy.WAV)} = 0]]?cust_greeting,1(busy)) exten => s,n,GotoIf($[${EXISTS(${RECORDED_FILE})}]?goodbye) ; we're done, file was recorded exten => s,n,GoSub(std_greeting,1(${IF($[${u_option}]?unavail:${IF($[${b_option}]?busy)})})) exten => s,n,GotoIf($[${EXISTS(${RECORDED_FILE})}]?goodbye) ; we're done, file was recorded exten => s,n,Verbose(1|File recording: ${IF($[${EXISTED(${RECORDED_FILE})}]?SUCCESSFUL:FAILED)}) exten => s,n(goodbye),NoOp() exten => s,n,Set(VMSTATUS=SUCCESS) exten => s,n,Return() exten => get_mailbox,1,NoOp() exten => get_mailbox,n,Read(MAILBOX|vm-whichbox|||${MAX_TRIES}) exten => get_mailbox,n,Return() exten => cust_greeting,1,NoOp() exten => cust_greeting,n,Set(GREETING=${ARG1}) exten => cust_greeting,n,Playback(${PWD}/${GREETING}) exten => cust_greeting,n,Exec(${IF($[${s_option} = 0]?Playback(vm-intro):NoOp())}) exten => cust_greeting,n,GoSub(record_voicemail,s,1()) exten => cust_greeting,n,Return() exten => std_greeting,1,NoOp() exten => std_greeting,n,Set(GREETING=${ARG1}) exten => std_greeting,n,GotoIf($["${GREETING}" != "unavail" | "${GREETING}" != "busy"]?no_greeting) ; if no greeting requested, just play an intro file exten => std_greeting,n,Background(vm-theperson) exten => std_greeting,n,Set(X=0) exten => std_greeting,n,While($[${X} < ${LEN(${MAILBOX})}]) exten => std_greeting,n,Background(digits/${MAILBOX:${X}:1}) exten => std_greeting,n,Set(X=$[${X} + 1]) exten => std_greeting,n,EndWhile() exten => std_greeting,n,Background(vm-is${GREETING}) exten => std_greeting,n,Exec(${IF($[${s_option} = 0]?Background(vm-intro):NoOp())}) exten => std_greeting,n,Goto(record) ; don't play the intro file, just record ; If we don't request a greeting type, then just play the intro exten => std_greeting,n(no_greeting),Background(vm-intro) exten => std_greeting,n(record),GoSub(record_voicemail,s,1()) exten => std_greeting,n,Return() [voicemailmain] ; Drop in replacement for the dialplan application VoicemailMain() ; Leif Madsen ;-- [Description] VoiceMailMain([mailbox][@context][|options]): This application allows the calling party to check voicemail messages. A specific mailbox, and optional corresponding context, may be specified. If a mailbox is not provided, the calling party will be prompted to enter one. If a context is not specified, the 'default' context will be used. Options: p - Consider the mailbox parameter as a prefix to the mailbox that is entered by the caller. g(#) - Use the specified amount of gain when recording a voicemail message. The units are whole-number decibels (dB). s - Skip checking the passcode for the mailbox. a(#) - Skip folder prompt and go directly to folder specified. Defaults to INBOX --; exten => s,1,Answer() exten => s,n,GoSub(setup_mailbox,s,1(default,100,INBOX) exten => s,n,GoSub(get_msg_count,s,1()) exten => s,n,Set(TOTAL_MSGS=${GOSUB_RETVAL}) exten => s,n,GoSub(play_msg,s,1(0)) exten => s,n,Playback(vm-goodbye) [read_voicemail_conf] ; Purpose: To read the voicemail.conf file into AstDB ; LIMITATION: The regex's in the SHELL() command will remove all lines that contain a comment (even if the start of the line is uncommented) ; That means you'll have to make sure your comments are on separate lines from the options you want activated. ; ; Leif Madsen ; exten => s,1,NoOp() exten => s,n,Set(LINE_NUMBER=1) ; While we have data, loop through the file, pulling it in one line at a time exten => s,n,While($[${LEN(${SHELL(sed -e '/\\\;/d' /etc/asterisk/voicemail.conf \| grep '.' \| tr "\\\n" "~" \| cut -d \\\~ -f ${LINE_NUMBER})})} > 1]) ; Save the data to the VM_FILE_DATA channel variable (one line from voicemail.conf) exten => s,n,NoOp(${SET(VM_FILE_DATA=${SHELL(sed -e '/\\\;/d' /etc/asterisk/voicemail.conf \| grep '.' \| tr "\\\n" "~" \| cut -d \\\~ -f ${LINE_NUMBER})})}) ; If the line starts with an alphanumeric string (not a square brace), then goto the write_data label, otherwise, save the data to the SECTION_NAME channel var exten => s,n,Exec(${IF(${REGEX("^[a-z A-Z 0-9]" ${VM_FILE_DATA})}?Goto(write_data):Set(SECTION_NAME=${VM_FILE_DATA}))}) exten => s,n,Goto(next_iteration) ; if we hit here, then we're going to go to the next iteration of the loop ; because we've already set the SECTION_NAME variable, which will be used ; as part of the family keyname in AstDB ; Write the database to the AstDB exten => s,n(write_data),NoOp() ; NOTE: We're using the SET() function because the Set() application will take commas and pipes as argument separators ; In order to make it literally assign the whole string to the AstDB, we have to write it out with the SET() function ; which does not have multiple arguments ; ; Split the data we're getting into 2 separate variables (left and right of the equals sign) exten => s,n,NoOp(${SET(VARNAME=${CUT(VM_FILE_DATA,=,1)})}) exten => s,n,NoOp(${SET(VARVALUE=${CUT(VM_FILE_DATA,=,2)})}) ; Save the value to the AstDB because once we parse it again, Asterisk likes to forget the values for some reason... exten => s,n,NoOp(${SET(DB(voicemail/conf/${SECTION_NAME}/${VARNAME})=${VARVALUE})}) ; If the start of the VARVALUE variable contains a >, then we want to strip it out in the next priority. Otherwise, go to the next loop iteration exten => s,n,Exec(${IF($[${EXISTS(${CUT(VARVALUE,>,2)})}]?NoOp():Goto(next_iteration))}) ; If we make it here, then we've gotta strip the > from the beginning of the line and assign the remainder to the AstDB exten => s,n,NoOp(${SET(DB(voicemail/conf/${SECTION_NAME}/${VARNAME})=${CUT(VARVALUE,>,2)})}) exten => s,n(next_iteration),Set(LINE_NUMBER=$[${LINE_NUMBER} + 1]) exten => s,n,EndWhile() exten => s,n,Return() [get_msg_count] ; get_msg_count: Get the number of messages in the current folder ; Leif Madsen ; exten => s,1,Verbose(1|Getting number of messages in the mailbox) exten => s,n,Set(X=0000) exten => s,n,While($[${STAT(e,${PWD}/msg${X}.txt)}]) exten => s,n,Verbose(1|Current message count: ${X}) exten => s,n,Set(X=$[${X} + 1]) exten => s,n,GoSub(set_msg_number,s,1(${X})) exten => s,n,Set(X=${GOSUB_RETVAL}) exten => s,n,EndWhile() exten => s,n,Return(${X}) [set_msg_number] ; set_msg_number: Pad numbers with a LEN < 4 with zeros ; Leif Madsen ; exten => s,1,NoOp() exten => s,n,Set(MSGNUM=${IF($[${LEN(${ARG1})} = 1]?000:${IF($[${LEN(${ARG1})} = 2]?00:${IF($[${LEN(${ARG1})} = 3]?0)})})}${ARG1}) exten => s,n,Return(${MSGNUM}) [setup_mailbox] ; setup_mailbox: Setup the path to a folder in a mailbox to work in ; Leif Madsen ; exten => s,1,NoOp() exten => s,n,Set(VM_CTX=${ARG1}) exten => s,n,Set(MAILBOX=${ARG2}) exten => s,n,Set(FOLDER=${ARG3}) exten => s,n,Set(PWD=${VM_HOME}/${VM_CTX}/${MAILBOX}${IF($[${EXISTS(${ARG3})}]?/${FOLDER})}) exten => s,n,Return() [get_envelope] ; get_envelope: Get the contents of an envelope an assign the contents to variables ; Leif Madsen ; ; Returns and Sets: ; ; - origmailbox ; - context ; - macrocontext ; - exten ; - priority ; - callerchan ; - callerid ; - origdate ; - origtime ; - category ; - duration6 ; exten => s,1,NoOp() exten => s,n,Set(LINE_NUMBER=5) exten => s,n,While($[${LEN(${SHELL(tr "\\\n"\, "&" < ${PWD}/msg${ARG1}.txt \| cut -d \\\& -f ${LINE_NUMBER})})} > 1]) exten => s,n,Set(DATA=${SHELL(tr "\\\n"\, "&" < ${PWD}/msg${ARG1}.txt \| cut -d \\\& -f ${LINE_NUMBER})}) exten => s,n,Set(${CUT(DATA,=,1)}=${CUT(DATA,=,2)}) exten => s,n,Set(LINE_NUMBER=$[${LINE_NUMBER} + 1]) exten => s,n,EndWhile() exten => s,n,Return() [record_voicemail] ; record_voicemail: Record audio and save to disk (saved to INBOX) ; Leif Madsen exten => s,1,NoOp() exten => s,n,GoSub(setup_mailbox,s,1(${VM_CTX},${MAILBOX},INBOX)) ; save new voicemails to the INBOX exten => s,n,GoSub(get_msg_count,s,1(${MAILBOX})) exten => s,n,Set(TOTAL_MSGS=${GOSUB_RETVAL}) exten => s,n,GoSub(set_msg_number,s,1(${TOTAL_MSGS})) exten => s,n,Set(MSGNUM=${GOSUB_RETVAL}) ; ; LIMITATIONS: ; * Can't record to multiple formats ; * Can't save the file if the user hangs up (have to press #) ; * Need duration of recording ; exten => s,n,Record(${PWD}/msg${MSGNUM}.wav) exten => s,n,Set(RECORDED_FILE=${PWD}/msg${MSGNUM}.wav) exten => s,n,GoSub(create_envelope,s,1()) exten => s,n,Playback(auth-thankyou) exten => s,n,Return() [create_envelope] ; create_envelope: Create the message envelope in the filesystem ; Leif Madsen ; exten => s,1,NoOp() exten => s,n,GoSub(setup_mailbox,s,1(default,100,INBOX)) exten => s,n,GoSub(get_msg_count,s,1()) exten => s,n,Set(MSG_COUNT=${GOSUB_RETVAL}) exten => s,n,GoSub(set_msg_number,s,1(${MSG_COUNT})) exten => s,n,Set(MSGNUM=${GOSUB_RETVAL}) exten => s,n,System(touch ${PWD}/msg${MSGNUM}.txt) exten => s,n,System(echo "\;" >> ${PWD}/msg${MSGNUM}.txt) exten => s,n,System(echo "\; Message Information file" >> ${PWD}/msg${MSGNUM}.txt) exten => s,n,System(echo "\;" >> ${PWD}/msg${MSGNUM}.txt) exten => s,n,System(echo "[message]" >> ${PWD}/msg${MSGNUM}.txt) exten => s,n,System(echo "origmailbox=${MAILBOX}" >> ${PWD}/msg${MSGNUM}.txt) exten => s,n,System(echo "context=INACCURATE" >> ${PWD}/msg${MSGNUM}.txt) exten => s,n,System(echo "exten=INACCURATE" >> ${PWD}/msg${MSGNUM}.txt) exten => s,n,System(echo "priority=INACCURATE" >> ${PWD}/msg${MSGNUM}.txt) exten => s,n,System(echo "callerchan=INACCURATE" >> ${PWD}/msg${MSGNUM}.txt) exten => s,n,System(echo "callerid=\\"${CALLERID(name)}\\" ${CALLERID(num)}" >> ${PWD}/msg${MSGNUM}.txt) exten => s,n,System(echo "origdate=${STRFTIME(${EPOCH},,%d%m%Y-%H:%M:%S)}" >> ${PWD}/msg${MSGNUM}.txt) exten => s,n,System(echo "origtime=${EPOCH}" >> ${PWD}/msg${MSGNUM}.txt) exten => s,n,System(echo "category=" >> ${PWD}/msg${MSGNUM}.txt) exten => s,n,System(echo "duration=${ANSWEREDTIME}" >> ${PWD}/msg${MSGNUM}.txt) exten => s,n,Return()