#!/bin/sh /etc/rc.common
#
# Copyright 2011 Iordan Iordanov <iiordanov (AT) gmail.com>
#
#    This file is part of luci-pbx.
#
#    luci-pbx is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    luci-pbx is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with luci-pbx.  If not, see <http://www.gnu.org/licenses/>.

. /lib/functions.sh

START=60

# Some global variables
MODULENAME=pbx
USERAGENT="PBX"
HANGUPCNTXT=hangup-call-context
GTALKUNVL=unavailable

ASTUSER=nobody
ASTGROUP=nogroup
ASTDIRSRECURSIVE="/var/run/asterisk /var/log/asterisk /var/spool/asterisk"
ASTDIRS="/usr/lib/asterisk"
ASTSOUNDSDIR="/usr/lib/asterisk/sounds"

TEMPLATEDIR=/etc/${MODULENAME}-asterisk
PBXSOUNDSDIR=$TEMPLATEDIR/sounds
VMTEMPLATEDIR=/etc/${MODULENAME}-voicemail
VMSOUNDSDIR=$VMTEMPLATEDIR/sounds
ASTERISKDIR=/etc/asterisk
WORKDIR=/tmp/$MODULENAME.$$
MD5SUMSFILE=/tmp/$MODULENAME-sums.$$

TMPL_ASTERISK=$TEMPLATEDIR/asterisk.conf.TEMPLATE
TMPL_GTALK=$TEMPLATEDIR/gtalk.conf.TEMPLATE
TMPL_INDICATIONS=$TEMPLATEDIR/indications.conf.TEMPLATE
TMPL_LOGGER=$TEMPLATEDIR/logger.conf.TEMPLATE
TMPL_MANAGER=$TEMPLATEDIR/manager.conf.TEMPLATE
TMPL_MODULES=$TEMPLATEDIR/modules.conf.TEMPLATE
TMPL_RTP=$TEMPLATEDIR/rtp.conf.TEMPLATE

TMPL_EXTCTHRUCHECKHDR=$TEMPLATEDIR/extensions_disa-check_header.conf.TEMPLATE
TMPL_EXTCTHRUCHECK=$TEMPLATEDIR/extensions_disa-check.conf.TEMPLATE
TMPL_EXTCTHRUCHECKFTR=$TEMPLATEDIR/extensions_disa-check_footer.conf.TEMPLATE
TMPL_EXTCTHRUHDR=$TEMPLATEDIR/extensions_disa_header.conf.TEMPLATE
TMPL_EXTCTHRU=$TEMPLATEDIR/extensions_disa.conf.TEMPLATE
TMPL_EXTCTHRUNOPIN=$TEMPLATEDIR/extensions_disa-nopin.conf.TEMPLATE

TMPL_EXTCBACKCHECKHDR=$TEMPLATEDIR/extensions_callback-check_header.conf.TEMPLATE
TMPL_EXTCBACKCHECK=$TEMPLATEDIR/extensions_callback-check.conf.TEMPLATE
TMPL_EXTCBACKCHECKFTR=$TEMPLATEDIR/extensions_callback-check_footer.conf.TEMPLATE
TMPL_EXTCBACKHDR=$TEMPLATEDIR/extensions_callback_header.conf.TEMPLATE
TMPL_EXTCBACKSIP=$TEMPLATEDIR/extensions_callback_sip.conf.TEMPLATE
TMPL_EXTCBACKGTALK=$TEMPLATEDIR/extensions_callback_gtalk.conf.TEMPLATE

TMPL_EXTENSIONS=$TEMPLATEDIR/extensions.conf.TEMPLATE

TMPL_EXTVMDISABLED=$TEMPLATEDIR/extensions_voicemail_disabled.conf.TEMPLATE
TMPL_EXTVMENABLED=$TEMPLATEDIR/extensions_voicemail_enabled.conf.TEMPLATE

TMPL_EXTBLKLIST=$TEMPLATEDIR/extensions_blacklist.conf.TEMPLATE
TMPL_EXTBLKLISTFTR=$TEMPLATEDIR/extensions_blacklist_footer.conf.TEMPLATE
TMPL_EXTBLKLISTHDR=$TEMPLATEDIR/extensions_blacklist_header.conf.TEMPLATE

TMPL_EXTDEFAULT=$TEMPLATEDIR/extensions_default.conf.TEMPLATE
TMPL_EXTDEFAULTUSER=$TEMPLATEDIR/extensions_default_user.conf.TEMPLATE

TMPL_EXTINCNTXTSIP=$TEMPLATEDIR/extensions_incoming_context_sip.conf.TEMPLATE
TMPL_EXTINCNTXTGTALKHDR=$TEMPLATEDIR/extensions_incoming_context_gtalk_header.conf.TEMPLATE
TMPL_EXTINCNTXTGTALK=$TEMPLATEDIR/extensions_incoming_context_gtalk.conf.TEMPLATE

TMPL_EXTUSERCNTXT=$TEMPLATEDIR/extensions_user_context.conf.TEMPLATE
TMPL_EXTUSERCNTXTFTR=$TEMPLATEDIR/extensions_user_context_footer.conf.TEMPLATE
TMPL_EXTUSERCNTXTHDR=$TEMPLATEDIR/extensions_user_context_header.conf.TEMPLATE

TMPL_EXTOUTHDR=$TEMPLATEDIR/extensions_default_outgoing_header.conf.TEMPLATE
TMPL_EXTOUTGTALK=$TEMPLATEDIR/extensions_outgoing_gtalk.conf.TEMPLATE
TMPL_EXTOUTLOCAL=$TEMPLATEDIR/extensions_outgoing_dial_local_user.conf.TEMPLATE
TMPL_EXTOUTSIP=$TEMPLATEDIR/extensions_outgoing_sip.conf.TEMPLATE

TMPL_JABBER=$TEMPLATEDIR/jabber.conf.TEMPLATE
TMPL_JABBERUSER=$TEMPLATEDIR/jabber_users.conf.TEMPLATE
TMPL_SIP=$TEMPLATEDIR/sip.conf.TEMPLATE
TMPL_SIPPEER=$TEMPLATEDIR/sip_peer.TEMPLATE
TMPL_SIPREG=$TEMPLATEDIR/sip_registration.TEMPLATE
TMPL_SIPUSR=$TEMPLATEDIR/sip_user.TEMPLATE

TMPL_MSMTPDEFAULT=$VMTEMPLATEDIR/pbx-msmtprc-defaults.TEMPLATE
TMPL_MSMTPACCOUNT=$VMTEMPLATEDIR/pbx-msmtprc-account.TEMPLATE
TMPL_MSMTPAUTH=$VMTEMPLATEDIR/pbx-msmtprc-account-auth.TEMPLATE
TMPL_MSMTPACCTDFLT=$VMTEMPLATEDIR/pbx-msmtprc-account-default.TEMPLATE


INCLUDED_FILES="$WORKDIR/extensions_blacklist.conf $WORKDIR/extensions_callthrough.conf\
                $WORKDIR/extensions_incoming.conf  $WORKDIR/extensions_incoming_gtalk.conf\
                $WORKDIR/extensions_user.conf      $WORKDIR/jabber_users.conf\
                $WORKDIR/sip_peers.conf            $WORKDIR/sip_registrations.conf\
                $WORKDIR/sip_users.conf            $WORKDIR/extensions_voicemail.conf\
                $WORKDIR/extensions_default.conf"


# In this string, we concatenate all local users enabled to receive calls
# readily formatted for the Dial command.
localusers_to_ring=""

# In this string, we keep a list of all users that are enabled for outgoing
# calls. It is used at the end to create the user contexts.
localusers_can_dial=""

# In this string, we put together a space-separated list of provider names
# (alphanumeric, with all non-alpha characters replaced with underscores),
# which will be used to dial out by default (whose outgoing contexts will
# be included in users' contexts by default.
outbound_providers=""
sip_outbound_providers=""
gtalk_outbound_providers=""

# Function which escapes non-alpha-numeric characters in a string
escape_non_alpha() {
    echo $@ | sed 's/\([^a-zA-Z0-9]\)/\\\1/g'
}

# Function which replaces non-alpha-numeric characters with an underscore
sub_underscore_for_non_alpha() {
    echo $@ | sed 's/[^a-zA-Z0-9]/_/g'
}

# Copies the template files which we don't edit.
copy_unedited_templates_over()
{
    cp $TMPL_ASTERISK      $WORKDIR/asterisk.conf
    cp $TMPL_GTALK         $WORKDIR/gtalk.conf
    cp $TMPL_INDICATIONS   $WORKDIR/indications.conf
    cp $TMPL_LOGGER        $WORKDIR/logger.conf
    cp $TMPL_MANAGER       $WORKDIR/manager.conf
    cp $TMPL_MODULES       $WORKDIR/modules.conf
    # If this file isn't present at this stage, voicemail is disabled.
    [ ! -f $WORKDIR/extensions_voicemail.conf ] && \
      cp $TMPL_EXTVMDISABLED $WORKDIR/extensions_voicemail.conf
}

# Touches all the included files, to prevent asterisk from refusing to
# start if a config item is missing and an included config file isn't created.
create_included_files()
{
    touch $INCLUDED_FILES
}

# Puts together all the extensions.conf related configuration.
pbx_create_extensions_config()
{
    local ringtime
    config_get ringtime advanced ringtime

    sed "s/|RINGTIME|/$ringtime/" $TMPL_EXTENSIONS > $WORKDIR/extensions.conf
    mv  $WORKDIR/inext.TMP                           $WORKDIR/extensions_incoming.conf
    cp  $TMPL_EXTINCNTXTGTALKHDR                     $WORKDIR/extensions_incoming_gtalk.conf
    cat $WORKDIR/outextgtalk.TMP                  >> $WORKDIR/extensions_incoming_gtalk.conf 2>/dev/null
    rm -f $WORKDIR/outextgtalk.TMP
    mv  $WORKDIR/blacklist.TMP                       $WORKDIR/extensions_blacklist.conf
    mv  $WORKDIR/userext.TMP                         $WORKDIR/extensions_user.conf

    cp  $TMPL_EXTCTHRUHDR                            $WORKDIR/extensions_callthrough.conf
    cat $WORKDIR/callthrough.TMP                  >> $WORKDIR/extensions_callthrough.conf 2>/dev/null
    rm -f $WORKDIR/callthrough.TMP
    cat $TMPL_EXTCTHRUCHECKHDR                    >> $WORKDIR/extensions_callthrough.conf 2>/dev/null
    cat $WORKDIR/callthroughcheck.TMP             >> $WORKDIR/extensions_callthrough.conf 2>/dev/null
    rm -f $WORKDIR/callthroughcheck.TMP
    cat $TMPL_EXTCTHRUCHECKFTR                    >> $WORKDIR/extensions_callthrough.conf 2>/dev/null

    cp  $TMPL_EXTCBACKHDR                            $WORKDIR/extensions_callback.conf
    cat $WORKDIR/callback.TMP                     >> $WORKDIR/extensions_callback.conf 2>/dev/null
    rm -f $WORKDIR/callback.TMP
    cat $TMPL_EXTCBACKCHECKHDR                    >> $WORKDIR/extensions_callback.conf 2>/dev/null
    cat $WORKDIR/callbackcheck.TMP                >> $WORKDIR/extensions_callback.conf 2>/dev/null
    rm -f $WORKDIR/callbackcheck.TMP
    cat $TMPL_EXTCBACKCHECKFTR                    >> $WORKDIR/extensions_callback.conf 2>/dev/null

    rm -f $WORKDIR/outext-*.TMP
    rm -f $WORKDIR/localext.TMP
    sed "s/|LOCALUSERS|/$localusers_to_ring/g" $TMPL_EXTDEFAULT \
                                                   > $WORKDIR/extensions_default.conf
    cat $WORKDIR/inextuser.TMP                    >> $WORKDIR/extensions_default.conf
    rm -f $WORKDIR/inextuser.TMP
}

# Puts together all the sip.conf related configuration.
pbx_create_sip_config()
{
    mv $WORKDIR/sip_regs.TMP    $WORKDIR/sip_registrations.conf
    mv $WORKDIR/sip_peers.TMP   $WORKDIR/sip_peers.conf
    mv $WORKDIR/sip_users.TMP   $WORKDIR/sip_users.conf
}

# Creates the jabber.conf related config
pbx_create_jabber_config()
{
    cp  $TMPL_JABBER        $WORKDIR/jabber.conf
    mv  $WORKDIR/jabber.TMP $WORKDIR/jabber_users.conf
}

# Gets rid of any config files from $ASTERISKDIR not found in $WORKDIR.
clean_up_asterisk_config_dir()
{
    for f in $ASTERISKDIR/* ; do
        basef="`basename $f`"
        if [ ! -e "$WORKDIR/$basef" ] ; then
            rm -rf "$f"
        fi
    done
}

# Compares md5sums of the config files in $WORKDIR to those
# in $ASTERISKDIR, and copies only changed files over to reduce
# wear on flash in embedded devices.
compare_configs_and_copy_changed()
{
    # First, compute md5sums of the config files in $WORKDIR.
    cd $WORKDIR/
    md5sum * > $MD5SUMSFILE

    # Now, check the files in $ASTERISKDIR against the md5sums.
    cd $ASTERISKDIR/
    changed_files="`md5sum -c $MD5SUMSFILE 2>/dev/null | fgrep ": FAILED" | awk -F: '{print $1}'`"

    rm -f $MD5SUMSFILE

    [ -z "$changed_files" ] && return

    # Now copy over the changed files.
    for f in $changed_files ; do
        cp "$WORKDIR/$f" "$ASTERISKDIR/$f"
    done
}

# Calls the functions that create the final config files
# Calls the function which compares which files have changed
# Puts the final touches on $ASTERISKDIR
# Gets rid of $WORKDIR
pbx_assemble_and_copy_config()
{
    mkdir -p $ASTERISKDIR

    copy_unedited_templates_over
    create_included_files
    pbx_create_extensions_config
    pbx_create_sip_config
    pbx_create_jabber_config

    touch    $WORKDIR/features.conf

    # At this point, $WORKDIR should contain a complete, working config.
    clean_up_asterisk_config_dir

    compare_configs_and_copy_changed

    [ ! -d $ASTERISKDIR/manager.d ] && mkdir -p $ASTERISKDIR/manager.d/

    # Get rid of the working directory
    rm -rf $WORKDIR/
}

# Creates configuration for a user and adds it to the temporary file that holds
# all users configured so far.
pbx_add_user()
{
    local fullname
    local defaultuser
    local rawdefaultuser
    local secret
    local ring
    local can_call

    config_get fullname    $1 fullname
    fullname=`escape_non_alpha $fullname`
    config_get rawdefaultuser $1 defaultuser
    defaultuser=`escape_non_alpha $rawdefaultuser`
    config_get secret      $1 secret
    secret=`escape_non_alpha $secret`
    config_get ring        $1 ring
    config_get can_call    $1 can_call

    [ -z "$defaultuser" -o -z "$secret" ] && return
    [ -z "$fullname" ] && fullname="$defaultuser"

    sed "s/|DEFAULTUSER|/$defaultuser/g" $TMPL_SIPUSR > $WORKDIR/sip_user.tmp

    if [ "$can_call" = "yes" ] ; then
        # Add user to list of all users that are allowed to make calls.
        localusers_can_dial="$localusers_can_dial $rawdefaultuser"
        sed -i "s/|CONTEXTNAME|/$defaultuser/g"     $WORKDIR/sip_user.tmp
    else
        sed -i "s/|CONTEXTNAME|/$HANGUPCNTXT/g"     $WORKDIR/sip_user.tmp
    fi

    # Add this user's configuration to the temp file containing all user configs.
    sed "s/|FULLNAME|/$fullname/" $WORKDIR/sip_user.tmp |\
             sed "s/|SECRET|/$secret/g"                       >> $WORKDIR/sip_users.TMP

    if [ "$ring" = "yes" ] ; then
        if [ -z "$localusers_to_ring" ] ; then
            localusers_to_ring="SIP\/$defaultuser"
        else
            localusers_to_ring="$localusers_to_ring\&SIP\/$defaultuser"
        fi
    fi

    # Add configuration which allows local users to call each other.
    sed "s/|DEFAULTUSER|/$defaultuser/g" $TMPL_EXTOUTLOCAL    >> $WORKDIR/localext.TMP

    # Add configuration which puts calls to users through the default
    # context, so that blacklists and voicemail take effect for this user.
    sed "s/|DEFAULTUSER|/$defaultuser/g" $TMPL_EXTDEFAULTUSER >> $WORKDIR/inextuser.TMP

    rm -f $WORKDIR/sip_user.tmp
}

# Creates configuration for a Google account, and adds it to the temporary file that holds
# all accounts configured so far.
# Also creates the outgoing extensions which are used in users' outgoing contexts.
pbx_add_jabber()
{
    local username
    local secret
    local numprefix
    local register
    local make_outgoing_calls
    local name
    local users_to_ring
    local status
    local statusmessage

    config_get username $1 username
    username=`escape_non_alpha $username`
    config_get secret $1 secret
    secret=`escape_non_alpha $secret`
    #TODO: Is this really necessary here? Numprefix is retrieved below.
    config_get numprefix $1 numprefix
    config_get register $1 register
    config_get make_outgoing_calls $1 make_outgoing_calls
    config_get name $1 name
    config_get status $1 status
    status=`escape_non_alpha $status`
    config_get statusmessage $1 statusmessage
    statusmessage=`escape_non_alpha $statusmessage`

    [ -z "$username" -o -z "$secret" ] && return

    # Construct a jabber entry for this provider.
    sed     "s/|USERNAME|/$username/g" $TMPL_JABBERUSER |\
            sed "s/|NAME|/$name/g"                            > $WORKDIR/jabber.tmp

    if [ "$register" = yes ] ; then
        # If this provider is enabled for incoming calls, we need to set the
        # status of the user to something other than unavailable in order to receive calls.
        sed -i "s/|STATUS|/$status/g"                   $WORKDIR/jabber.tmp
        sed -i "s/|STATUSMESSAGE|/\"$statusmessage\"/g" $WORKDIR/jabber.tmp

        users_to_ring="`uci -q get ${MODULENAME}-calls.incoming_calls.$name`"
        # If no users have been specified to ring, we ring all users enabled for incoming calls.
        if [ -z "$users_to_ring" ] ; then
            users_to_ring=$localusers_to_ring
        else
            # Else, we cook up a string formatted for the Dial command
            # with the specified users (SIP/user1&SIP/user2&...). We do it
            # with set, shift and a loop in order to be more tolerant of ugly whitespace
            # messes entered by users.
            set $users_to_ring
            users_to_ring="SIP\/$1" && shift
            for u in $@ ; do u=`escape_non_alpha $u` ; users_to_ring=$users_to_ring\\\&SIP\\\/$u ; done
        fi

        # Now, we add this account to the gtalk incoming context.
        sed    "s/|USERNAME|/$username/g" $TMPL_EXTINCNTXTGTALK |\
                   sed "s/|LOCALUSERS|/$users_to_ring/g"    >> $WORKDIR/outextgtalk.TMP
    else
        sed -i "s/|STATUS|/$GTALKUNVL/g"               $WORKDIR/jabber.tmp
        sed -i "s/|STATUSMESSAGE|/\"\"/g"              $WORKDIR/jabber.tmp
    fi

    # Add this account's configuration to the temp file containing all account configs.
    sed "s/|SECRET|/$secret/g" $WORKDIR/jabber.tmp      >> $WORKDIR/jabber.TMP

    # If this provider is enabled for outgoing calls.
    if [ "$make_outgoing_calls" = "yes" ] ; then

        numprefix="`uci -q get ${MODULENAME}-calls.outgoing_calls.$name`"

        # If no prefixes are specified, then we use "X" which matches any prefix.
        [ -z "$numprefix" ]  && numprefix="X"

        for p in $numprefix ; do
            p=`escape_non_alpha $p`
            sed "s/|NUMPREFIX|/$p/g" $TMPL_EXTOUTGTALK |\
                            sed "s/|NAME|/$name/g"            >> $WORKDIR/outext-$name.TMP
        done

        # Add this provider to the list of enabled outbound providers.
        if [ -z "$outbound_providers" ] ; then
            outbound_providers="$name"
        else
            outbound_providers="$outbound_providers $name"
        fi

        # Add this provider to the list of enabled gtalk outbound providers.
        if [ -z "$gtalk_outbound_providers" ] ; then
            gtalk_outbound_providers="$name"
        else
            gtalk_outbound_providers="$gtalk_outbound_providers $name"
        fi
    fi

    rm -f $WORKDIR/jabber.tmp
}

# Creates configuration for a SIP provider account, and adds it to the temporary file that holds
# all accounts configured so far.
# Also creates the outgoing extensions which are used in users' outgoing contexts.
pbx_add_peer()
{
    local defaultuser
    local secret
    local host
    local fromdomain
    local register
    local numprefix
    local make_outgoing_calls
    local name
    local users_to_ring
    local port
    local outboundproxy

    config_get defaultuser $1 defaultuser
    defaultuser=`escape_non_alpha $defaultuser`
    config_get secret $1 secret
    secret=`escape_non_alpha $secret`
    config_get host $1 host
    host=`escape_non_alpha $host`
    config_get port $1 port
    config_get outbountproxy $1 outboundproxy
    outbountproxy=`escape_non_alpha $outbountproxy`
    config_get fromdomain $1 fromdomain
    fromdomain=`escape_non_alpha $fromdomain`
    config_get register $1 register
    config_get numprefix $1 numprefix
    config_get make_outgoing_calls $1 make_outgoing_calls
    config_get name $1 name

    [ -z "$defaultuser" -o -z "$secret" -o -z "$host" ] && return
    [ -z "$fromdomain" ]    && fromdomain=$host
    [ -n "$port" ]          && port="port=$port"
    [ -n "$outboundproxy" ] && outboundproxy="outboundproxy=$outboundproxy"

    # Construct a sip peer entry for this provider.
    sed    "s/|DEFAULTUSER|/$defaultuser/" $TMPL_SIPPEER  >  $WORKDIR/sip_peer.tmp
    sed -i "s/|NAME|/$name/"                                 $WORKDIR/sip_peer.tmp
    sed -i "s/|FROMUSER|/$defaultuser/"                      $WORKDIR/sip_peer.tmp
    sed -i "s/|SECRET|/$secret/"                             $WORKDIR/sip_peer.tmp
    sed -i "s/|HOST|/$host/"                                 $WORKDIR/sip_peer.tmp
    sed -i "s/|PORT|/$port/"                                 $WORKDIR/sip_peer.tmp
    sed -i "s/|OUTBOUNDPROXY|/$outboundproxy/"               $WORKDIR/sip_peer.tmp
    # Add this account's configuration to the temp file containing all account configs.
    sed    "s/|FROMDOMAIN|/$host/" $WORKDIR/sip_peer.tmp >>  $WORKDIR/sip_peers.TMP

    # If this provider is enabled for incoming calls.
    if [ "$register" = "yes" ] ; then
        # Then we create a registration string for this provider.
        sed    "s/|DEFAULTUSER|/$defaultuser/g" $TMPL_SIPREG         > $WORKDIR/sip_reg.tmp
        sed -i "s/|SECRET|/$secret/g"                                  $WORKDIR/sip_reg.tmp
        sed    "s/|NAME|/$name/g" $WORKDIR/sip_reg.tmp              >> $WORKDIR/sip_regs.TMP

        users_to_ring="`uci -q get ${MODULENAME}-calls.incoming_calls.$name`"
        # If no users have been specified to ring, we ring all users enabled for incoming calls.
        if [ -z "$users_to_ring" ] ; then
            users_to_ring=$localusers_to_ring
        else
            # Else, we cook up a string formatted for the Dial command
            # with the specified users (SIP/user1&SIP/user2&...). We do it
            # with set, shift and a loop in order to be more tolerant of ugly whitespace
            # messes entered by users.
            set $users_to_ring
            users_to_ring="SIP\/$1" && shift
            for u in $@ ; do users_to_ring=$users_to_ring\\\&SIP\\\/$u ; done
        fi

        # And we create an incoming calls context for this provider.
        sed    "s/|NAME|/$name/g"                          $TMPL_EXTINCNTXTSIP |\
                    sed "s/|LOCALUSERS|/$users_to_ring/g"       >> $WORKDIR/inext.TMP
    fi

    # If this provider is enabled for outgoing calls.
    if [ "$make_outgoing_calls" = "yes" ] ; then

        numprefix="`uci -q get ${MODULENAME}-calls.outgoing_calls.$name`"
        # If no prefixes are specified, then we use "X" which matches any prefix.
        [ -z "$numprefix" ]  && numprefix="X"
        for p in $numprefix ; do
            p=`escape_non_alpha $p`
            sed "s/|NUMPREFIX|/$p/g" $TMPL_EXTOUTSIP |\
                            sed "s/|NAME|/$name/g"              >> $WORKDIR/outext-$name.TMP
        done

        # Add this provider to the list of enabled outbound providers.
        if [ -z "$outbound_providers" ] ; then
            outbound_providers="$name"
        else
            outbound_providers="$outbound_providers $name"
        fi

        # Add this provider to the list of enabled sip outbound providers.
        if [ -z "$sip_outbound_providers" ] ; then
            sip_outbound_providers="$name"
        else
            sip_outbound_providers="$sip_outbound_providers $name"
        fi
    fi

    rm -f $WORKDIR/sip_peer.tmp
    rm -f $WORKDIR/sip_reg.tmp
}

# For all local users enabled for outbound calls, creates a context
# containing the extensions for Google and SIP accounts this user is
# allowed to use.
pbx_create_user_contexts()
{
    local providers

    for u in $localusers_can_dial ; do
        u=`escape_non_alpha $u`
        sed "s/|DEFAULTUSER|/$u/g" $TMPL_EXTUSERCNTXTHDR                >> $WORKDIR/userext.TMP
        cat $WORKDIR/localext.TMP                                       >> $WORKDIR/userext.TMP
        providers="`uci -q get ${MODULENAME}-calls.providers_user_can_use.$u`"
        [ -z "$providers" ] && providers="$outbound_providers"

        # For each provider, cat the contents of outext-$name.TMP into the user's outgoing calls extension
        for p in $providers ; do
            [ -f $WORKDIR/outext-$p.TMP ] && cat $WORKDIR/outext-$p.TMP >> $WORKDIR/userext.TMP
        done
        cat $TMPL_EXTUSERCNTXTFTR                                       >> $WORKDIR/userext.TMP
    done
}

# Creates the blacklist context which hangs up on blacklisted numbers.
pbx_add_blacklist()
{
    local blacklist1
    local blacklist2

    config_get blacklist1 blacklisting blacklist1
    config_get blacklist2 blacklisting blacklist2

    # We create the blacklist context no matter whether the blacklist
    # actually contains entries or not, since the PBX will send calls
    # to the context for a check against the list anyway.
    cp $TMPL_EXTBLKLISTHDR                                   $WORKDIR/blacklist.TMP
    for n in $blacklist1 $blacklist2 ; do
        n=`escape_non_alpha $n`
        sed "s/|BLACKLISTITEM|/$n/g" $TMPL_EXTBLKLIST >> $WORKDIR/blacklist.TMP 
    done
    cat $TMPL_EXTBLKLISTFTR                               >> $WORKDIR/blacklist.TMP
}

# Creates the callthrough context which allows specified numbers to get
# into the PBX and dial out as the configured user.
pbx_add_callthrough()
{
    local callthrough_number_list
    local defaultuser
    local pin
    local enabled
    local F

    config_get callthrough_number_list $1 callthrough_number_list
    config_get defaultuser $1 defaultuser
    defaultuser=`escape_non_alpha $defaultuser`
    config_get pin $1 pin
    pin=`escape_non_alpha $pin`
    config_get enabled $1 enabled

    [ "$enabled" = "no" ]   && return
    [ "$defaultuser" = "" ] && return

    for callthrough_number in $callthrough_number_list ; do
        sed "s/|NUMBER|/$callthrough_number/g" $TMPL_EXTCTHRUCHECK >> $WORKDIR/callthroughcheck.TMP

	if [ -n "$pin" ] ; then F=$TMPL_EXTCTHRU ; else F=$TMPL_EXTCTHRUNOPIN ; fi
        sed "s/|NUMBER|/$callthrough_number/g" $F             |\
                    sed "s/|DEFAULTUSER|/$defaultuser/"       |\
                        sed "s/|PIN|/$pin/"                        >> $WORKDIR/callthrough.TMP
    done
}


# Creates the callback context which allows specified numbers to get
# a callback into the PBX and dial out as the configured user.
pbx_add_callback()
{
    local callback_number_list
    local defaultuser
    local pin
    local enabled
    local callback_provider
    local callback_hangup_delay
    local FB
    local FT

    config_get callback_number_list $1 callback_number_list
    config_get defaultuser $1 defaultuser
    defaultuser=`escape_non_alpha $defaultuser`
    config_get pin $1 pin
    pin=`escape_non_alpha $pin`
    config_get enabled $1 enabled
    config_get callback_provider $1 callback_provider
    callback_provider=`sub_underscore_for_non_alpha $callback_provider`
    config_get callback_hangup_delay $1 callback_hangup_delay

    [ "$enabled" = "no" ]   && return
    [ "$defaultuser" = "" ] && return

    # If the provider is a SIP provider, set the file to use to $TMPL_EXTCBACKSIP
    # otherwise, set it to $TMPL_EXTCBACKGTALK
    if echo $sip_outbound_providers | grep -q $callback_provider 2>/dev/null
    then
        FB=$TMPL_EXTCBACKSIP
    else
        FB=$TMPL_EXTCBACKGTALK
    fi

    for callback_number in $callback_number_list ; do
        sed "s/|NUMBER|/$callback_number/g" $TMPL_EXTCBACKCHECK    >> $WORKDIR/callbackcheck.TMP

        sed "s/|NUMBER|/$callback_number/g" $FB            |\
            sed "s/|CALLBACKPROVIDER|/$callback_provider/" |\
                sed "s/|CALLBACKHUPDELAY|/$callback_hangup_delay/" >> $WORKDIR/callback.TMP

        # Perhaps a bit confusingly, we create "callthrough" configuration for callback
        # numbers, because we use the same DISA construct as for callthrough.
        if [ -n "$pin" ] ; then FT=$TMPL_EXTCTHRU ; else FT=$TMPL_EXTCTHRUNOPIN ; fi
        sed "s/|NUMBER|/$callback_number/g" $FT            |\
            sed "s/|DEFAULTUSER|/$defaultuser/"            |\
                sed "s/|PIN|/$pin/"                                >> $WORKDIR/callthrough.TMP
    done
}


# Creates sip.conf from its template.
pbx_cook_sip_template()
{
    local useragent
    local externhost
    local bindport

    config_get useragent advanced useragent
    useragent=`escape_non_alpha $useragent`
    config_get externhost advanced externhost
    config_get bindport advanced bindport

    [ -z "$useragent" ] && useragent="$USERAGENT"

    sed "s/|USERAGENT|/$useragent/g" $TMPL_SIP  > $WORKDIR/sip.conf

    if [ -z "$externhost" ] ; then
        sed -i "s/externhost=|EXTERNHOST|//g" $WORKDIR/sip.conf
    else
        sed -i "s/|EXTERNHOST|/$externhost/g" $WORKDIR/sip.conf
    fi

    if [ -z "$bindport" ] ; then
        sed -i "s/bindport=|BINDPORT|//g"     $WORKDIR/sip.conf
    else
        sed -i "s/|BINDPORT|/$bindport/g"     $WORKDIR/sip.conf
    fi


}

# Creates rtp.conf from its template.
pbx_cook_rtp_template()
{
    local rtpstart
    local rtpend

    config_get rtpstart advanced rtpstart
    config_get rtpend   advanced rtpend

    sed "s/|RTPSTART|/$rtpstart/" $TMPL_RTP |\
            sed "s/|RTPEND|/$rtpend/"               > $WORKDIR/rtp.conf
}

# Links any sound files found in $PBXSOUNDSDIR and $VMSOUNDSDIR
# into $ASTSOUNDSDIR for use by Asterisk. Does not overwrite files.
pbx_link_sounds()
{
    mkdir -p $ASTSOUNDSDIR

    for dir in $PBXSOUNDSDIR $VMSOUNDSDIR ; do
        if [ -d $dir ] ; then
            for f in $dir/* ; do
                ln -s $f $ASTSOUNDSDIR 2>/dev/null
            done
        fi
    done
}


# Makes sure the ownership of specified directories is proper.
pbx_fix_ownership()
{
    chown $ASTUSER:$ASTGROUP    $ASTDIRS
    chown $ASTUSER:$ASTGROUP -R $ASTDIRSRECURSIVE
}


# Creates voicemail config if installed and enabled.
pbx_configure_voicemail()
{
    local enabled
    local global_timeout
    local global_email_addresses

    local smtp_tls
    local smtp_server
    local smtp_port
    local smtp_auth
    local smtp_user
    local smtp_password

    config_get enabled global_voicemail enabled

    # First check if voicemail is enabled.
    [ "$enabled" != "yes" ] && return

    config_get global_timeout         global_voicemail global_timeout
    #config_get global_email_addresses global_voicemail global_email_addresses
    config_get smtp_auth              voicemail_smtp   smtp_auth
    config_get smtp_tls               voicemail_smtp   smtp_tls
    config_get smtp_server            voicemail_smtp   smtp_server
    config_get smtp_port              voicemail_smtp   smtp_port
    config_get smtp_user              voicemail_smtp   smtp_user
    smtp_user=`escape_non_alpha $smtp_user`
    config_get smtp_password          voicemail_smtp   smtp_password
    smtp_password=`escape_non_alpha $smtp_password`

    sed "s/|AUTH|/$smtp_auth/"   $TMPL_MSMTPDEFAULT |\
             sed "s/|TLS|/$smtp_tls/"               >  $WORKDIR/pbx-msmtprc

    sed "s/|HOST|/$smtp_server/" $TMPL_MSMTPACCOUNT |\
             sed "s/|PORT|/$smtp_port/"             >> $WORKDIR/pbx-msmtprc

    if [ "$smtp_auth" = "on" ] ; then
        sed "s/|USER|/$smtp_user/"  $TMPL_MSMTPAUTH |\
             sed "s/|PASSWORD|/$smtp_password/"     >> $WORKDIR/pbx-msmtprc
    fi

    cat $TMPL_MSMTPACCTDFLT                         >> $WORKDIR/pbx-msmtprc

    [ ! -f /etc/pbx-msmtprc ] && cp $WORKDIR/pbx-msmtprc /etc/pbx-msmtprc
    cmp -s  $WORKDIR/pbx-msmtprc /etc/pbx-msmtprc 1>/dev/null \
                                || mv $WORKDIR/pbx-msmtprc /etc/pbx-msmtprc
    chmod 600                                              /etc/pbx-msmtprc
    chown nobody                                           /etc/pbx-msmtprc

    # Copy over the extensions file which has voicemail enabled.
    cp $TMPL_EXTVMENABLED                $WORKDIR/extensions_voicemail.conf

    # Create the voicemail directory in /tmp
    mkdir -p     /tmp/voicemail
    chown nobody /tmp/voicemail

    # Create the recordings directory
    mkdir -p     /etc/pbx-voicemail/recordings
    chown nobody /etc/pbx-voicemail/recordings

    # Working around a bug in OpenWRT 12.09-rc1
    # TODO: REMOVE AS SOON AS POSSIBLE
    chmod ugo+w /tmp
}


start() {
    mkdir -p $WORKDIR

    # Create the users.
    config_load ${MODULENAME}-users
    config_foreach pbx_add_user local_user

    # Create configuration for each google account.
    config_unset
    config_load ${MODULENAME}-google
    config_foreach pbx_add_jabber gtalk_jabber

    # Create configuration for each voip provider.
    config_unset
    config_load ${MODULENAME}-voip
    config_foreach pbx_add_peer voip_provider

    # Create the user contexts, callthroug/back, and phone blacklist.
    config_unset
    config_load ${MODULENAME}-calls
    pbx_create_user_contexts
    pbx_add_blacklist
    config_foreach pbx_add_callthrough callthrough_numbers
    config_foreach pbx_add_callback callback_numbers

    # Prepare sip.conf using settings from the "advanced" section.
    config_unset
    config_load ${MODULENAME}-advanced
    pbx_cook_sip_template
    pbx_cook_rtp_template

    # Prepare voicemail config.
    config_unset
    config_load ${MODULENAME}-voicemail
    pbx_configure_voicemail

    # Assemble the configuration, and copy changed files over.
    config_unset
    config_load ${MODULENAME}-advanced
    pbx_assemble_and_copy_config

    # Link sound files
    pbx_link_sounds

    # Enforce ownership of specified files and directories.
    pbx_fix_ownership
}