예제 #1
0
function core_get_config($engine)
{
    global $ext;
    // is this the best way to pass this?
    global $version;
    // this is not the best way to pass this, this should be passetd together with $engine
    global $engineinfo;
    global $amp_conf;
    global $core_conf;
    global $chan_dahdi;
    global $chan_dahdi_loaded;
    global $astman;
    $modulename = "core";
    switch ($engine) {
        case "asterisk":
            $ast_ge_14 = version_compare($version, '1.4', 'ge');
            $ast_lt_16 = version_compare($version, '1.6', 'lt');
            $ast_lt_161 = version_compare($version, '1.6.1', 'lt');
            // Now add to sip_general_addtional.conf
            //
            if (isset($core_conf) && is_a($core_conf, "core_conf")) {
                $core_conf->addSipGeneral('disallow', 'all');
                $core_conf->addSipGeneral('allow', 'ulaw');
                $core_conf->addSipGeneral('allow', 'alaw');
                $core_conf->addSipGeneral('context', 'from-sip-external');
                $core_conf->addSipGeneral('callerid', 'Unknown');
                $core_conf->addSipGeneral('notifyringing', 'yes');
                if ($ast_ge_14) {
                    $core_conf->addSipGeneral('notifyhold', 'yes');
                    $core_conf->addSipGeneral('tos_sip', 'cs3');
                    // Recommended setting from doc/ip-tos.txt
                    $core_conf->addSipGeneral('tos_audio', 'ef');
                    // Recommended setting from doc/ip-tos.txt
                    $core_conf->addSipGeneral('tos_video', 'af41');
                    // Recommended setting from doc/ip-tos.txt
                    $core_conf->addSipGeneral('alwaysauthreject', 'yes');
                    if ($ast_lt_161) {
                        $core_conf->addSipGeneral('limitonpeers', 'yes');
                    }
                } else {
                    $core_conf->addSipGeneral('tos', '0x68');
                    // This really doesn't do anything with astersk not running as root
                }
                $useragent = "FPBX-" . getversion() . "({$version})";
                $core_conf->addSipGeneral('useragent', $useragent);
                $core_conf->addIaxGeneral('disallow', 'all');
                $core_conf->addIaxGeneral('allow', 'ulaw');
                $core_conf->addIaxGeneral('allow', 'alaw');
                $core_conf->addIaxGeneral('allow', 'gsm');
                $core_conf->addIaxGeneral('mailboxdetail', 'yes');
                if ($ast_ge_14) {
                    $core_conf->addIaxGeneral('tos', 'ef');
                    // Recommended setting from doc/ip-tos.txt
                }
                $fcc = new featurecode($modulename, 'blindxfer');
                $code = $fcc->getCodeActive();
                unset($fcc);
                if ($code != '') {
                    $core_conf->addFeatureMap('blindxfer', $code);
                }
                $fcc = new featurecode($modulename, 'atxfer');
                $code = $fcc->getCodeActive();
                unset($fcc);
                if ($code != '') {
                    $core_conf->addFeatureMap('atxfer', $code);
                }
                $fcc = new featurecode($modulename, 'automon');
                $code = $fcc->getCodeActive();
                unset($fcc);
                if ($code != '') {
                    $core_conf->addFeatureMap('automon', $code);
                }
                $fcc = new featurecode($modulename, 'disconnect');
                $code = $fcc->getCodeActive();
                unset($fcc);
                if ($code != '') {
                    $core_conf->addFeatureMap('disconnect', $code);
                }
                $fcc = new featurecode($modulename, 'pickupexten');
                $code = $fcc->getCodeActive();
                unset($fcc);
                if ($code != '') {
                    $core_conf->addFeatureGeneral('pickupexten', $code);
                }
            }
            // FeatureCodes
            $fcc = new featurecode($modulename, 'userlogon');
            $fc_userlogon = $fcc->getCodeActive();
            unset($fcc);
            $fcc = new featurecode($modulename, 'userlogoff');
            $fc_userlogoff = $fcc->getCodeActive();
            unset($fcc);
            $fcc = new featurecode($modulename, 'zapbarge');
            $fc_zapbarge = $fcc->getCodeActive();
            unset($fcc);
            $fcc = new featurecode($modulename, 'chanspy');
            $fc_chanspy = $fcc->getCodeActive();
            unset($fcc);
            $fcc = new featurecode($modulename, 'simu_pstn');
            $fc_simu_pstn = $fcc->getCodeActive();
            unset($fcc);
            $fcc = new featurecode($modulename, 'pickup');
            $fc_pickup = $fcc->getCodeActive();
            unset($fcc);
            // Log on / off -- all in one context
            if ($fc_userlogoff != '' || $fc_userlogon != '') {
                $ext->addInclude('from-internal-additional', 'app-userlogonoff');
                // Add the include from from-internal
                if ($fc_userlogoff != '') {
                    $ext->add('app-userlogonoff', $fc_userlogoff, '', new ext_macro('user-logoff'));
                    $ext->add('app-userlogonoff', $fc_userlogoff, 'hook_off', new ext_hangup(''));
                }
                if ($fc_userlogon != '') {
                    $ext->add('app-userlogonoff', $fc_userlogon, '', new ext_macro('user-logon'));
                    $ext->add('app-userlogonoff', $fc_userlogon, 'hook_on_1', new ext_hangup(''));
                    $clen = strlen($fc_userlogon);
                    $fc_userlogon = "_{$fc_userlogon}.";
                    $ext->add('app-userlogonoff', $fc_userlogon, '', new ext_macro('user-logon,${EXTEN:' . $clen . '}'));
                    $ext->add('app-userlogonoff', $fc_userlogon, 'hook_on_2', new ext_hangup(''));
                }
            }
            // Call pickup using app_pickup - Note that '**xtn' is hard-coded into the GXPs and SNOMs as a number to dial
            // when a user pushes a flashing BLF.
            //
            // We need to add ringgoups to this so that if an extension is part of a ringgroup, we can try to pickup that
            // extension by trying the ringgoup which is what the pickup application is going to respond to.
            //
            // NOTICE: this may be confusing, we check if this is a BRI build of Asterisk and use dpickup instead of pickup
            //         if it is. So we simply assign the varaible $ext_pickup which one it is, and use that variable when
            //         creating all the extnesions below. So those are "$ext_pickup" on purpose!
            //
            if ($fc_pickup != '' && $ast_ge_14) {
                $ext->addInclude('from-internal-additional', 'app-pickup');
                $fclen = strlen($fc_pickup);
                $ext_pickup = strstr($engineinfo['raw'], 'BRI') ? 'ext_dpickup' : 'ext_pickup';
                $fcc = new featurecode('paging', 'intercom-prefix');
                $intercom_code = $fcc->getCodeActive();
                unset($fcc);
                $picklist = '${EXTEN:' . $fclen . '}';
                $picklist .= '&${EXTEN:' . $fclen . '}@ext-local';
                $picklist .= '&${EXTEN:' . $fclen . '}@from-internal';
                $picklist .= '&${EXTEN:' . $fclen . '}@from-internal-xfer';
                $picklist .= '&${EXTEN:' . $fclen . '}@from-did-direct';
                $picklist .= '&LC-${EXTEN:' . $fclen . '}@from-internal';
                $picklist .= '&LC-${EXTEN:' . $fclen . '}@from-internal-xfer';
                $picklist .= '&FMPR-${EXTEN:' . $fclen . '}@from-internal';
                $picklist .= '&FMPR-${EXTEN:' . $fclen . '}@from-internal-xfer';
                $picklist .= '&FMPR-${EXTEN:' . $fclen . '}@from-did-direct';
                $ext->add('app-pickup', "_{$fc_pickup}.", '', new $ext_pickup($picklist));
                $ext->add('app-pickup', "_{$fc_pickup}.", '', new ext_hangup(''));
                if ($intercom_code != '') {
                    $len = strlen($fc_pickup . $intercom_code);
                    $picklist = '${EXTEN:' . $len . '}';
                    $picklist .= '&${EXTEN:' . $len . '}@ext-local';
                    $picklist .= '&${EXTEN:' . $len . '}@from-internal';
                    $picklist .= '&${EXTEN:' . $len . '}@from-internal-xfer';
                    $picklist .= '&${EXTEN:' . $len . '}@from-did-direct';
                    $picklist .= '&LC-${EXTEN:' . $len . '}@from-internal';
                    $picklist .= '&LC-${EXTEN:' . $len . '}@from-internal-xfer';
                    $picklist .= '&FMPR-${EXTEN:' . $len . '}@from-internal';
                    $picklist .= '&FMPR-${EXTEN:' . $len . '}@from-internal-xfer';
                    $picklist .= '&FMPR-${EXTEN:' . $len . '}@from-did-direct';
                    $ext->add('app-pickup', "_{$fc_pickup}{$intercom_code}.", '', new $ext_pickup($picklist));
                    $ext->add('app-pickup', "_{$fc_pickup}{$intercom_code}.", '', new ext_hangup(''));
                }
                // In order to do call pickup in ringgroups, we will need to try the ringgoup number
                // when doing call pickup for that ringgoup so we must see who is a member of what ringgroup
                // and then generate the dialplan
                //
                $rg_members = array();
                if (function_exists('ringgroups_list')) {
                    $rg_list = ringgroups_list(true);
                    foreach ($rg_list as $item) {
                        $thisgrp = ringgroups_get($item['grpnum']);
                        $grpliststr = $thisgrp['grplist'];
                        $grplist = explode("-", $grpliststr);
                        foreach ($grplist as $exten) {
                            if (strpos($exten, "#") === false) {
                                $rg_members[$exten][] = $item['grpnum'];
                            }
                        }
                    }
                }
                // Now we have a hash of extensions and what ringgoups they are members of
                // so we need to generate the callpickup dialplan for these specific extensions
                // to try the ringgoup.
                foreach ($rg_members as $exten => $grps) {
                    $picklist = $exten;
                    $picklist .= '&' . $exten . '@ext-local';
                    $picklist .= '&' . $exten . '@from-internal';
                    $picklist .= '&' . $exten . '@from-internal-xfer';
                    $picklist .= '&' . $exten . '@from-did-direct';
                    $picklist .= '&LC-' . $exten . '@from-internal';
                    $picklist .= '&LC-' . $exten . '@from-internal-xfer';
                    $picklist .= '&FMPR-' . $exten . '@from-internal';
                    $picklist .= '&FMPR-' . $exten . '@from-internal-xfer';
                    $picklist .= '&FMPR-' . $exten . '@from-did-direct';
                    foreach ($grps as $grp) {
                        $picklist .= '&' . $grp . '@from-internal';
                        $picklist .= '&' . $grp . '@from-internal-xfer';
                        $picklist .= '&' . $grp . '@ext-group';
                    }
                    $ext->add('app-pickup', "{$fc_pickup}" . $exten, '', new $ext_pickup($picklist));
                    $ext->add('app-pickup', "{$fc_pickup}" . $exten, '', new ext_hangup(''));
                    if ($intercom_code != '') {
                        $ext->add('app-pickup', "{$fc_pickup}" . $intercom_code . $exten, '', new $ext_pickup($picklist));
                        $ext->add('app-pickup', "{$fc_pickup}" . $intercom_code . $exten, '', new ext_hangup(''));
                    }
                }
            } elseif ($fc_pickup != '') {
                $ext->addInclude('from-internal-additional', 'app-pickup');
                $fclen = strlen($fc_pickup);
                $ext_pickup = strstr($engineinfo['raw'], 'BRI') ? 'ext_dpickup' : 'ext_pickup';
                $fcc = new featurecode('paging', 'intercom-prefix');
                $intercom_code = $fcc->getCodeActive();
                unset($fcc);
                $ext->add('app-pickup', "_{$fc_pickup}.", '', new ext_NoOp('Attempt to Pickup ${EXTEN:' . $fclen . '} by ${CALLERID(num)}'));
                $ext->add('app-pickup', "_{$fc_pickup}.", '', new $ext_pickup('${EXTEN:' . $fclen . '}'));
                $ext->add('app-pickup', "_{$fc_pickup}.", '', new $ext_pickup('${EXTEN:' . $fclen . '}@ext-local'));
                $ext->add('app-pickup', "_{$fc_pickup}.", '', new $ext_pickup('${EXTEN:' . $fclen . '}@from-internal'));
                $ext->add('app-pickup', "_{$fc_pickup}.", '', new $ext_pickup('${EXTEN:' . $fclen . '}@from-internal-xfer'));
                $ext->add('app-pickup', "_{$fc_pickup}.", '', new $ext_pickup('${EXTEN:' . $fclen . '}@from-did-direct'));
                $ext->add('app-pickup', "_{$fc_pickup}.", '', new $ext_pickup('FMPR-${EXTEN:' . $fclen . '}'));
                $ext->add('app-pickup', "_{$fc_pickup}.", '', new $ext_pickup('LC-${EXTEN:' . $fclen . '}@from-internal'));
                $ext->add('app-pickup', "_{$fc_pickup}.", '', new $ext_pickup('LC-${EXTEN:' . $fclen . '}@from-internal-xfer'));
                $ext->add('app-pickup', "_{$fc_pickup}.", '', new $ext_pickup('FMPR-${EXTEN:' . $fclen . '}@from-internal'));
                $ext->add('app-pickup', "_{$fc_pickup}.", '', new $ext_pickup('FMPR-${EXTEN:' . $fclen . '}@from-internal-xfer'));
                $ext->add('app-pickup', "_{$fc_pickup}.", '', new $ext_pickup('FMPR-${EXTEN:' . $fclen . '}@from-did-direct'));
                if ($intercom_code != '') {
                    $ext->add('app-pickup', "_{$fc_pickup}{$intercom_code}.", '', new $ext_pickup('${EXTEN:' . strlen($fc_pickup . $intercom_code) . '}'));
                    $ext->add('app-pickup', "_{$fc_pickup}{$intercom_code}.", '', new $ext_pickup('${EXTEN:' . strlen($fc_pickup . $intercom_code) . '}@from-internal'));
                    $ext->add('app-pickup', "_{$fc_pickup}{$intercom_code}.", '', new $ext_pickup('${EXTEN:' . strlen($fc_pickup . $intercom_code) . '}@from-internal-xfer'));
                    $ext->add('app-pickup', "_{$fc_pickup}{$intercom_code}.", '', new $ext_pickup('${EXTEN:' . strlen($fc_pickup . $intercom_code) . '}@from-did-direct'));
                    $ext->add('app-pickup', "_{$fc_pickup}{$intercom_code}.", '', new $ext_pickup('FMPR-${EXTEN:' . strlen($fc_pickup . $intercom_code) . '}'));
                    $ext->add('app-pickup', "_{$fc_pickup}{$intercom_code}.", '', new $ext_pickup('FMPR-${EXTEN:' . strlen($fc_pickup . $intercom_code) . '}@from-internal'));
                    $ext->add('app-pickup', "_{$fc_pickup}{$intercom_code}.", '', new $ext_pickup('FMPR-${EXTEN:' . strlen($fc_pickup . $intercom_code) . '}@from-internal-xfer'));
                    $ext->add('app-pickup', "_{$fc_pickup}{$intercom_code}.", '', new $ext_pickup('FMPR-${EXTEN:' . strlen($fc_pickup . $intercom_code) . '}@from-did-direct'));
                }
                $ext->add('app-pickup', "_{$fc_pickup}.", '', new ext_hangup(''));
                // In order to do call pickup in ringgroups, we will need to try the ringgoup number
                // when doing call pickup for that ringgoup so we must see who is a member of what ringgroup
                // and then generate the dialplan
                //
                $rg_members = array();
                if (function_exists('ringgroups_list')) {
                    $rg_list = ringgroups_list(true);
                    foreach ($rg_list as $item) {
                        $thisgrp = ringgroups_get($item['grpnum']);
                        $grpliststr = $thisgrp['grplist'];
                        $grplist = explode("-", $grpliststr);
                        foreach ($grplist as $exten) {
                            if (strpos($exten, "#") === false) {
                                $rg_members[$exten][] = $item['grpnum'];
                            }
                        }
                    }
                }
                // Now we have a hash of extensions and what ringgoups they are members of
                // so we need to generate the callpickup dialplan for these specific extensions
                // to try the ringgoup.
                foreach ($rg_members as $exten => $grps) {
                    $ext->add('app-pickup', "{$fc_pickup}" . $exten, '', new $ext_pickup($exten));
                    $ext->add('app-pickup', "{$fc_pickup}" . $exten, '', new $ext_pickup($exten . '@ext-local'));
                    $ext->add('app-pickup', "{$fc_pickup}" . $exten, '', new $ext_pickup($exten . '@from-internal'));
                    $ext->add('app-pickup', "{$fc_pickup}" . $exten, '', new $ext_pickup($exten . '@from-internal-xfer'));
                    $ext->add('app-pickup', "{$fc_pickup}" . $exten, '', new $ext_pickup($exten . '@from-did-direct'));
                    $ext->add('app-pickup', "{$fc_pickup}" . $exten, '', new $ext_pickup('LC-' . $exten . '@from-internal'));
                    $ext->add('app-pickup', "{$fc_pickup}" . $exten, '', new $ext_pickup('LC-' . $exten . '@from-internal-xfer'));
                    $ext->add('app-pickup', "{$fc_pickup}" . $exten, '', new $ext_pickup('FMPR-' . $exten));
                    $ext->add('app-pickup', "{$fc_pickup}" . $exten, '', new $ext_pickup('FMPR-' . $exten . '@from-internal'));
                    $ext->add('app-pickup', "{$fc_pickup}" . $exten, '', new $ext_pickup('FMPR-' . $exten . '@from-internal-xfer'));
                    $ext->add('app-pickup', "{$fc_pickup}" . $exten, '', new $ext_pickup('FMPR-' . $exten . '@from-did-direct'));
                    foreach ($grps as $grp) {
                        $ext->add('app-pickup', "{$fc_pickup}" . $exten, '', new $ext_pickup($grp . '@from-internal'));
                        $ext->add('app-pickup', "{$fc_pickup}" . $exten, '', new $ext_pickup($grp . '@from-internal-xfer'));
                        $ext->add('app-pickup', "{$fc_pickup}" . $exten, '', new $ext_pickup($grp . '@ext-group'));
                    }
                    $ext->add('app-pickup', "{$fc_pickup}" . $exten, '', new ext_hangup(''));
                }
            }
            // zap barge
            if ($fc_zapbarge != '') {
                $ext->addInclude('from-internal-additional', 'app-zapbarge');
                // Add the include from from-internal
                $ext->add('app-zapbarge', $fc_zapbarge, '', new ext_macro('user-callerid'));
                $ext->add('app-zapbarge', $fc_zapbarge, '', new ext_setvar('GROUP()', '${CALLERID(number)}'));
                $ext->add('app-zapbarge', $fc_zapbarge, '', new ext_answer(''));
                $ext->add('app-zapbarge', $fc_zapbarge, '', new ext_wait(1));
                $ext->add('app-zapbarge', $fc_zapbarge, '', new ext_zapbarge(''));
                $ext->add('app-zapbarge', $fc_zapbarge, '', new ext_hangup(''));
            }
            // chan spy
            if ($fc_chanspy != '') {
                $ext->addInclude('from-internal-additional', 'app-chanspy');
                // Add the include from from-internal
                $ext->add('app-chanspy', $fc_chanspy, '', new ext_macro('user-callerid'));
                $ext->add('app-chanspy', $fc_chanspy, '', new ext_answer(''));
                $ext->add('app-chanspy', $fc_chanspy, '', new ext_wait(1));
                $ext->add('app-chanspy', $fc_chanspy, '', new ext_chanspy(''));
                $ext->add('app-chanspy', $fc_chanspy, '', new ext_hangup(''));
            }
            // Simulate options (ext-test)
            if ($fc_simu_pstn != '') {
                $ext->addInclude('from-internal-additional', 'ext-test');
                // Add the include from from-internal
                if ($fc_simu_pstn != '') {
                    if (ctype_digit($fc_simu_pstn)) {
                        $ext->add('ext-test', $fc_simu_pstn, '', new ext_goto('1', '${EXTEN}', 'from-pstn'));
                    } else {
                        $ext->add('ext-test', $fc_simu_pstn, '', new ext_goto('1', 's', 'from-pstn'));
                    }
                }
                $ext->add('ext-test', 'h', '', new ext_macro('hangupcall'));
            }
            $ext->addInclude('ext-did', 'ext-did-0001');
            // Add the include from from-internal
            $ext->addInclude('ext-did', 'ext-did-0002');
            // Add the include from from-internal
            $ext->add('ext-did', 'foo', '', new ext_noop('bar'));
            /* inbound routing extensions */
            $didlist = core_did_list();
            if (is_array($didlist)) {
                $catchall = false;
                $catchall_context = 'ext-did-catchall';
                foreach ($didlist as $item) {
                    if (trim($item['destination']) == '') {
                        continue;
                    }
                    $exten = trim($item['extension']);
                    $cidnum = trim($item['cidnum']);
                    // If the user put in just a cid number for routing, we add _. pattern to catch
                    // all DIDs with that CID number. Asterisk will complain about _. being dangerous
                    // but we don't want to limit this to just numberic as someone may be trying to
                    // route a non-numeric did
                    //
                    $cidroute = false;
                    if ($cidnum != '' && $exten == '') {
                        $exten = '_.';
                        $pricid = $item['pricid'] ? true : false;
                        $cidroute = true;
                    } else {
                        if ($cidnum != '' && $exten != '' || $cidnum == '' && $exten == '') {
                            $pricid = true;
                        } else {
                            $pricid = false;
                        }
                    }
                    $context = $pricid ? "ext-did-0001" : "ext-did-0002";
                    $exten = $exten == "" ? "s" : $exten;
                    $exten = $exten . ($cidnum == "" ? "" : "/" . $cidnum);
                    //if a CID num is defined, add it
                    if ($cidroute) {
                        $ext->add($context, $exten, '', new ext_setvar('__FROM_DID', '${EXTEN}'));
                        $ext->add($context, $exten, '', new ext_goto('1', 's'));
                        $exten = "s/{$cidnum}";
                        $ext->add($context, $exten, '', new ext_execif('$["${FROM_DID}" = ""]', 'Set', '__FROM_DID=${EXTEN}'));
                    } else {
                        $ext->add($context, $exten, '', new ext_setvar('__FROM_DID', '${EXTEN}'));
                    }
                    // always set callerID name
                    $ext->add($context, $exten, '', new ext_execif('$[ "${CALLERID(name)}" = "" ] ', 'Set', 'CALLERID(name)=${CALLERID(num)}'));
                    if (!empty($item['mohclass']) && trim($item['mohclass']) != 'default') {
                        $ext->add($context, $exten, '', new ext_setmusiconhold($item['mohclass']));
                        $ext->add($context, $exten, '', new ext_setvar('__MOHCLASS', $item['mohclass']));
                    }
                    // If we require RINGING, signal it as soon as we enter.
                    if ($item['ringing'] === "CHECKED") {
                        $ext->add($context, $exten, '', new ext_ringing(''));
                    }
                    if ($item['delay_answer']) {
                        $ext->add($context, $exten, '', new ext_wait($item['delay_answer']));
                    }
                    if ($exten == "s") {
                        //if the exten is s, then also make a catchall for undefined DIDs
                        $catchaccount = "_." . (empty($cidnum) ? "" : "/" . $cidnum);
                        if ($catchaccount == "_." && !$catchall) {
                            $catchall = true;
                            $ext->add($catchall_context, $catchaccount, '', new ext_NoOp('Catch-All DID Match - Found ${EXTEN} - You probably want a DID for this.'));
                            $ext->add($catchall_context, $catchaccount, '', new ext_goto('1', 's', 'ext-did'));
                        }
                    }
                    if ($item['privacyman'] == "1") {
                        $ext->add($context, $exten, '', new ext_macro('privacy-mgr', $item['pmmaxretries'] . ',' . $item['pmminlength']));
                    } else {
                        // if privacymanager is used, this is not necessary as it will not let blocked/anonymous calls through
                        // otherwise, we need to save the caller presence to set it properly if we forward the call back out the pbx
                        // note - the indirect table could go away as of 1.4.20 where it is fixed so that SetCallerPres can take
                        // the raw format.
                        //
                        if ($ast_lt_16) {
                            $ext->add($context, $exten, '', new ext_setvar('__CALLINGPRES_SV', '${CALLINGPRES_${CALLINGPRES}}'));
                        } else {
                            $ext->add($context, $exten, '', new ext_setvar('__CALLINGPRES_SV', '${CALLERPRES()}'));
                        }
                        $ext->add($context, $exten, '', new ext_setcallerpres('allowed_not_screened'));
                    }
                    if (!empty($item['alertinfo'])) {
                        $ext->add($context, $exten, '', new ext_setvar("__ALERT_INFO", str_replace(';', '\\;', $item['alertinfo'])));
                    }
                    // Add CID prefix, no need to do checks for existing pre-pends, this is an incoming did so this should
                    // be the first time the CID is manipulated. We set _RGPREFIX which is the same used throughout the different
                    // modules.
                    //
                    // TODO: If/When RGPREFIX is added to trunks, then see code in ringgroups to strip prefix if added here.
                    //
                    // TODO: core FreePBX documentation about this standard. (and probably rename from RGPREFIX to CIDPREFIX)
                    //
                    if (!empty($item['grppre'])) {
                        $ext->add($context, $exten, '', new ext_setvar('_RGPREFIX', $item['grppre']));
                        $ext->add($context, $exten, '', new ext_setvar('CALLERID(name)', '${RGPREFIX}${CALLERID(name)}'));
                    }
                    //the goto destination
                    // destination field in 'incoming' database is backwards from what ext_goto expects
                    $goto_context = strtok($item['destination'], ',');
                    $goto_exten = strtok(',');
                    $goto_pri = strtok(',');
                    $ext->add($context, $exten, 'dest-ext', new ext_goto($goto_pri, $goto_exten, $goto_context));
                }
                // If there's not a catchall, make one with an error message
                if (!$catchall) {
                    $ext->add($catchall_context, 's', '', new ext_noop("No DID or CID Match"));
                    $ext->add($catchall_context, 's', 'a2', new ext_answer(''));
                    $ext->add($catchall_context, 's', '', new ext_wait('2'));
                    $ext->add($catchall_context, 's', '', new ext_playback('ss-noservice'));
                    $ext->add($catchall_context, 's', '', new ext_sayalpha('${FROM_DID}'));
                    $ext->add($catchall_context, 's', '', new ext_hangup(''));
                    $ext->add($catchall_context, '_.', '', new ext_setvar('__FROM_DID', '${EXTEN}'));
                    $ext->add($catchall_context, '_.', '', new ext_noop('Received an unknown call with DID set to ${EXTEN}'));
                    $ext->add($catchall_context, '_.', '', new ext_goto('a2', 's'));
                    $ext->add($catchall_context, 'h', '', new ext_hangup(''));
                }
            }
            // Now create macro-from-zaptel-nnn or macro-from-dahdi-nnn for each defined channel to route it to the DID routing
            // Send it to from-trunk so it is handled as other dids would be handled.
            //
            // to this point we have both zap and dahdi configuration options. At generation though they can't co-exists. If compatibility
            // mode then it's still from-zaptel, otherwise it is which ever is present. We cant use ast_with_dahdi() (chan_dadi) because
            // it is for detection with compatibility mode. We need to actually determine if chan_dahdi is present or not at this point
            //
            if (!isset($chan_dahdi_loaded)) {
                if ($ast_ge_14 && isset($astman) && $astman->connected()) {
                    $response = $astman->send_request('Command', array('Command' => 'module show like chan_dahdi'));
                    $chan_dahdi_loaded = preg_match('/1 modules loaded/', $response['data']) > 0;
                }
            }
            foreach (core_zapchandids_list() as $row) {
                $channel = $row['channel'];
                $did = $row['did'];
                $this_context = "macro-from-dahdi-{$channel}";
                $ext->add($this_context, 's', '', new ext_noop('Entering ' . $this_context . ' with DID = ${DID} and setting to: ' . $did));
                $ext->add($this_context, 's', '', new ext_setvar('__FROM_DID', $did));
                $ext->add($this_context, 's', '', new ext_goto('1', $did, 'from-trunk'));
            }
            /* user extensions */
            $ext->addInclude('from-internal-additional', 'ext-local');
            // If running in Dynamic mode, this will insert the hints through an Asterisk #exec call.
            // which require "execincludes=yes" to be set in the [options] section of asterisk.conf
            //
            $fcc = new featurecode('paging', 'intercom-prefix');
            $intercom_code = $fcc->getCodeActive();
            unset($fcc);
            $intercom_code = $intercom_code == '' ? 'nointercom' : $intercom_code;
            // Pass the code so agi scripts like user_login_logout know to generate hints
            //
            $ext->addGlobal('INTERCOMCODE', $intercom_code);
            if ($amp_conf['DYNAMICHINTS']) {
                if ($amp_conf['USEDEVSTATE'] && function_exists('donotdisturb_get_config')) {
                    $add_dnd = ' dnd';
                } else {
                    $add_dnd = '';
                }
                $ext->addExec('ext-local', $amp_conf['AMPBIN'] . '/generate_hints.php ' . $intercom_code . $add_dnd);
            }
            $userlist = core_users_list();
            if (is_array($userlist)) {
                foreach ($userlist as $item) {
                    $exten = core_users_get($item[0]);
                    $vm = $exten['voicemail'] == "novm" || $exten['voicemail'] == "disabled" || $exten['voicemail'] == "" ? "novm" : $exten['extension'];
                    if (isset($exten['ringtimer']) && $exten['ringtimer'] != 0) {
                        $ext->add('ext-local', $exten['extension'], '', new ext_setvar('__RINGTIMER', $exten['ringtimer']));
                    }
                    $ext->add('ext-local', $exten['extension'], '', new ext_macro('exten-vm', $vm . "," . $exten['extension']));
                    if ($vm != "novm") {
                        $ext->add('ext-local', $exten['extension'], '', new ext_goto('1', 'vmret'));
                        $ext->add('ext-local', '${VM_PREFIX}' . $exten['extension'], '', new ext_macro('vm', $vm . ',DIRECTDIAL,${IVR_RETVM}'));
                        $ext->add('ext-local', '${VM_PREFIX}' . $exten['extension'], '', new ext_goto('1', 'vmret'));
                        $ext->add('ext-local', 'vmb' . $exten['extension'], '', new ext_macro('vm', $vm . ',BUSY,${IVR_RETVM}'));
                        $ext->add('ext-local', 'vmb' . $exten['extension'], '', new ext_goto('1', 'vmret'));
                        $ext->add('ext-local', 'vmu' . $exten['extension'], '', new ext_macro('vm', $vm . ',NOANSWER,${IVR_RETVM}'));
                        $ext->add('ext-local', 'vmu' . $exten['extension'], '', new ext_goto('1', 'vmret'));
                        $ext->add('ext-local', 'vms' . $exten['extension'], '', new ext_macro('vm', $vm . ',NOMESSAGE,${IVR_RETVM}'));
                        $ext->add('ext-local', 'vms' . $exten['extension'], '', new ext_goto('1', 'vmret'));
                    } else {
                        // If we return from teh macro, it means we are suppose to return to the IVR
                        //
                        $ext->add('ext-local', $exten['extension'], '', new ext_goto('1', 'return', '${IVR_CONTEXT}'));
                    }
                    // Create the hints if running in normal mode
                    //
                    if (!$amp_conf['DYNAMICHINTS']) {
                        $hint = core_hint_get($exten['extension']);
                        $dnd_string = $amp_conf['USEDEVSTATE'] && function_exists('donotdisturb_get_config') ? "&Custom:DND" . $exten['extension'] : '';
                        if (!empty($hint)) {
                            $ext->addHint('ext-local', $exten['extension'], $hint . $dnd_string);
                            if ($intercom_code != '') {
                                $ext->addHint('ext-local', $intercom_code . $exten['extension'], $hint . $dnd_string);
                            }
                        } else {
                            if ($dnd_string) {
                                $ext->addHint('ext-local', $exten['extension'], "&Custom:DND" . $exten['extension']);
                                if ($intercom_code != '') {
                                    $ext->addHint('ext-local', $intercom_code . $exten['extension'], "&Custom:DND" . $exten['extension']);
                                }
                            }
                        }
                    }
                    if ($exten['sipname']) {
                        $ext->add('ext-local', $exten['sipname'], '', new ext_goto('1', $item[0], 'from-internal'));
                    }
                    // Now make a special context for the IVR inclusions of local extension dialing so that
                    // when people use the Queues breakout ability, and break out to someone's extensions, voicemail
                    // works.
                    //
                    $ivr_context = 'from-did-direct-ivr';
                    if ($ast_lt_16) {
                        $ext->add($ivr_context, $exten['extension'], '', new ext_execif('$["${BLKVM_OVERRIDE}" != ""]', 'dbDel', '${BLKVM_OVERRIDE}'));
                    } else {
                        $ext->add($ivr_context, $exten['extension'], '', new ext_execif('$["${BLKVM_OVERRIDE}" != ""]', 'Noop', 'Deleting: ${BLKVM_OVERRIDE}: ${DB_DELETE(${BLKVM_OVERRIDE})}'));
                    }
                    $ext->add($ivr_context, $exten['extension'], '', new ext_setvar('__NODEST', ''));
                    $ext->add($ivr_context, $exten['extension'], '', new ext_goto('1', $exten['extension'], 'from-did-direct'));
                    if ($vm != "novm") {
                        if ($ast_lt_16) {
                            $ext->add($ivr_context, '${VM_PREFIX}' . $exten['extension'], '', new ext_execif('$["${BLKVM_OVERRIDE}" != ""]', 'dbDel', '${BLKVM_OVERRIDE}'));
                        } else {
                            $ext->add($ivr_context, '${VM_PREFIX}' . $exten['extension'], '', new ext_execif('$["${BLKVM_OVERRIDE}" != ""]', 'Noop', 'Deleting: ${BLKVM_OVERRIDE}: ${DB_DELETE(${BLKVM_OVERRIDE})}'));
                        }
                        $ext->add($ivr_context, '${VM_PREFIX}' . $exten['extension'], '', new ext_setvar('__NODEST', ''));
                        $ext->add($ivr_context, '${VM_PREFIX}' . $exten['extension'], '', new ext_macro('vm', $vm . ',DIRECTDIAL,${IVR_RETVM}'));
                        $ext->add($ivr_context, '${VM_PREFIX}' . $exten['extension'], '', new ext_gotoif('$["${IVR_RETVM}" = "RETURN" & "${IVR_CONTEXT}" != ""]', 'ext-local,vmret,playret'));
                    }
                }
                $ext->add('ext-local', 'vmret', '', new ext_gotoif('$["${IVR_RETVM}" = "RETURN" & "${IVR_CONTEXT}" != ""]', 'playret'));
                $ext->add('ext-local', 'vmret', '', new ext_hangup(''));
                $ext->add('ext-local', 'vmret', 'playret', new ext_playback('exited-vm-will-be-transfered&silence/1'));
                $ext->add('ext-local', 'vmret', '', new ext_goto('1', 'return', '${IVR_CONTEXT}'));
                $ext->add('ext-local', 'h', '', new ext_macro('hangupcall'));
            }
            /* Create the from-trunk-tech-chanelid context that can be used for inbound group counting
             * Create the DUNDI macros for DUNDI trunks
             * Create the ext-trunk context for direct trunk dialing TODO: should this be its own module?
             */
            $trunklist = core_trunks_listbyid();
            if (is_array($trunklist) && count($trunklist)) {
                $tcontext = 'ext-trunk';
                $texten = 'tdial';
                $tcustom = 'tcustom';
                $generate_texten = false;
                $generate_tcustom = false;
                foreach ($trunklist as $trunkprops) {
                    if (trim($trunkprops['disabled']) == 'on') {
                        continue;
                    }
                    $trunkgroup = 'OUT_' . $trunkprops['trunkid'];
                    switch ($trunkprops['tech']) {
                        case 'dundi':
                            $macro_name = 'macro-dundi-' . $trunkprops['trunkid'];
                            $ext->addSwitch($macro_name, 'DUNDI/' . $trunkprops['channelid']);
                            $ext->add($macro_name, 's', '', new ext_goto('1', '${ARG1}'));
                            $trunkcontext = "from-trunk-" . $trunkprops['tech'] . "-" . $trunkprops['channelid'];
                            $ext->add($trunkcontext, '_.', '', new ext_set('GROUP()', $trunkgroup));
                            $ext->add($trunkcontext, '_.', '', new ext_goto('1', '${EXTEN}', 'from-trunk'));
                            $ext->add($tcontext, $trunkprops['trunkid'], '', new ext_set('OUTBOUND_GROUP', $trunkgroup));
                            $ext->add($tcontext, $trunkprops['trunkid'], '', new ext_gotoif('$["${OUTMAXCHANS_' . $trunkprops['trunkid'] . '}" = ""]', 'nomax'));
                            $ext->add($tcontext, $trunkprops['trunkid'], '', new ext_gotoif('$[${GROUP_COUNT(' . $trunkgroup . ')} >= ${OUTMAXCHANS_${DIAL_TRUNK}}]', 'hangit'));
                            if ($ast_lt_16) {
                                $ext->add($tcontext, $trunkprops['trunkid'], 'nomax', new ext_execif('$["${CALLINGPRES_SV}" != ""]', 'SetCallerPres', '${CALLINGPRES_SV}'));
                            } else {
                                $ext->add($tcontext, $trunkprops['trunkid'], 'nomax', new ext_execif('$["${CALLINGPRES_SV}" != ""]', 'Set', 'CALLERPRES()=${CALLINGPRES_SV}'));
                            }
                            $ext->add($tcontext, $trunkprops['trunkid'], '', new ext_set('DIAL_NUMBER', '${FROM_DID}'));
                            $ext->add($tcontext, $trunkprops['trunkid'], '', new ext_gosubif('$["${PREFIX_TRUNK_' . $trunkprops['trunkid'] . '}" != ""]', 'sub-flp-' . $trunkprops['trunkid'] . ',s,1'));
                            $ext->add($tcontext, $trunkprops['trunkid'], '', new ext_set('OUTNUM', '${OUTPREFIX_${DIAL_TRUNK}}${DIAL_NUMBER}'));
                            // OUTNUM is the final dial number
                            $ext->add($tcontext, $trunkprops['trunkid'], '', new ext_macro('dundi-${DIAL_TRUNK}', '${OUTNUM}'));
                            $ext->add($tcontext, $trunkprops['trunkid'], 'hangit', new ext_hangup());
                            break;
                        case 'iax':
                            $trunkprops['tech'] = 'iax2';
                            // fall-through
                        // fall-through
                        case 'iax2':
                        case 'sip':
                            $trunkcontext = "from-trunk-" . $trunkprops['tech'] . "-" . $trunkprops['channelid'];
                            $ext->add($trunkcontext, '_.', '', new ext_set('GROUP()', $trunkgroup));
                            $ext->add($trunkcontext, '_.', '', new ext_goto('1', '${EXTEN}', 'from-trunk'));
                            // fall-through
                        // fall-through
                        case 'zap':
                        case 'dahdi':
                            $ext->add($tcontext, $trunkprops['trunkid'], '', new ext_set('TDIAL_STRING', strtoupper($trunkprops['tech']) . '/' . $trunkprops['channelid']));
                            $ext->add($tcontext, $trunkprops['trunkid'], '', new ext_set('DIAL_TRUNK', $trunkprops['trunkid']));
                            $ext->add($tcontext, $trunkprops['trunkid'], '', new ext_goto('1', $texten, 'ext-trunk'));
                            $generate_texten = true;
                            break;
                            // TODO we don't have the OUTNUM until later so fix this...
                        // TODO we don't have the OUTNUM until later so fix this...
                        case 'custom':
                            $dial_string = str_replace('$OUTNUM$', '\\\\$\\\\{OUTNUM\\\\}', $trunkprops['channelid']);
                            $ext->add($tcontext, $trunkprops['trunkid'], '', new ext_set('TDIAL_STRING', $dial_string));
                            $ext->add($tcontext, $trunkprops['trunkid'], '', new ext_set('DIAL_TRUNK', $trunkprops['trunkid']));
                            $ext->add($tcontext, $trunkprops['trunkid'], '', new ext_goto('1', $tcustom, 'ext-trunk'));
                            $generate_tcustom = true;
                            break;
                        case 'enum':
                            // Not Supported
                            break;
                        default:
                    }
                }
                if ($generate_tcustom) {
                    $ext->add($tcontext, $tcustom, '', new ext_set('OUTBOUND_GROUP', 'OUT_${DIAL_TRUNK}'));
                    $ext->add($tcontext, $tcustom, '', new ext_gotoif('$["${OUTMAXCHANS_${DIAL_TRUNK}}" = ""]', 'nomax'));
                    $ext->add($tcontext, $tcustom, '', new ext_gotoif('$[${GROUP_COUNT(OUT_${DIAL_TRUNK})} >= ${OUTMAXCHANS_${DIAL_TRUNK}}]', 'hangit'));
                    if ($ast_lt_16) {
                        $ext->add($tcontext, $tcustom, 'nomax', new ext_execif('$["${CALLINGPRES_SV}" != ""]', 'SetCallerPres', '${CALLINGPRES_SV}'));
                    } else {
                        $ext->add($tcontext, $tcustom, 'nomax', new ext_execif('$["${CALLINGPRES_SV}" != ""]', 'Set', 'CALLERPRES()=${CALLINGPRES_SV}'));
                    }
                    $ext->add($tcontext, $tcustom, '', new ext_set('DIAL_NUMBER', '${FROM_DID}'));
                    $ext->add($tcontext, $tcustom, '', new ext_gosubif('$["${PREFIX_TRUNK_${DIAL_TRUNK}}" != ""]', 'sub-flp-${DIAL_TRUNK},s,1'));
                    $ext->add($tcontext, $tcustom, '', new ext_set('OUTNUM', '${OUTPREFIX_${DIAL_TRUNK}}${DIAL_NUMBER}'));
                    // OUTNUM is the final dial number
                    // Address Security Vulnerability in many earlier versions of Asterisk from an external source tranmitting a
                    // malicious CID that can cause overflows in the Asterisk code.
                    //
                    $ext->add($tcontext, $tcustom, '', new ext_set('CALLERID(number)', '${CALLERID(number):0:40}'));
                    $ext->add($tcontext, $tcustom, '', new ext_set('CALLERID(name)', '${CALLERID(name):0:40}'));
                    $ext->add($tcontext, $tcustom, '', new ext_dial('${EVAL(${TDIAL_STRING})}', '300,${DIAL_TRUNK_OPTIONS}'));
                    $ext->add($tcontext, $tcustom, 'hangit', new ext_hangup());
                }
                if ($generate_texten) {
                    $ext->add($tcontext, $texten, '', new ext_set('OUTBOUND_GROUP', 'OUT_${DIAL_TRUNK}'));
                    $ext->add($tcontext, $texten, '', new ext_gotoif('$["${OUTMAXCHANS_${DIAL_TRUNK}}" = ""]', 'nomax'));
                    $ext->add($tcontext, $texten, '', new ext_gotoif('$[${GROUP_COUNT(OUT_${DIAL_TRUNK})} >= ${OUTMAXCHANS_${DIAL_TRUNK}}]', 'hangit'));
                    if ($ast_lt_16) {
                        $ext->add($tcontext, $texten, 'nomax', new ext_execif('$["${CALLINGPRES_SV}" != ""]', 'SetCallerPres', '${CALLINGPRES_SV}'));
                    } else {
                        $ext->add($tcontext, $texten, 'nomax', new ext_execif('$["${CALLINGPRES_SV}" != ""]', 'Set', 'CALLERPRES()=${CALLINGPRES_SV}'));
                    }
                    $ext->add($tcontext, $texten, '', new ext_set('DIAL_NUMBER', '${FROM_DID}'));
                    $ext->add($tcontext, $texten, '', new ext_gosubif('$["${PREFIX_TRUNK_${DIAL_TRUNK}}" != ""]', 'sub-flp-${DIAL_TRUNK},s,1'));
                    $ext->add($tcontext, $texten, '', new ext_set('OUTNUM', '${OUTPREFIX_${DIAL_TRUNK}}${DIAL_NUMBER}'));
                    // OUTNUM is the final dial number
                    $ext->add($tcontext, $texten, '', new ext_dial('${TDIAL_STRING}/${OUTNUM}', '300,${DIAL_TRUNK_OPTIONS}'));
                    // Address Security Vulnerability in many earlier versions of Asterisk from an external source tranmitting a
                    // malicious CID that can cause overflows in the Asterisk code.
                    //
                    $ext->add($tcontext, $texten, '', new ext_set('CALLERID(number)', '${CALLERID(number):0:40}'));
                    $ext->add($tcontext, $texten, '', new ext_set('CALLERID(name)', '${CALLERID(name):0:40}'));
                    $ext->add($tcontext, $texten, 'hangit', new ext_hangup());
                }
            }
            /* dialplan globals */
            // modules should NOT use the globals table to store anything!
            // modules should use $ext->addGlobal("testvar","testval"); in their module_get_config() function instead
            // I'm cheating for core functionality - do as I say, not as I do ;-)
            // Auto add these globals to give access to agi scripts and other needs, unless defined in the global table.
            //
            $amp_conf_globals = array("ASTETCDIR", "ASTMODDIR", "ASTVARLIBDIR", "ASTAGIDIR", "ASTSPOOLDIR", "ASTRUNDIR", "ASTLOGDIR", "CWINUSEBUSY", "AMPMGRUSER", "AMPMGRPASS", "AMPDBENGINE", "AMPDBHOST", "AMPDBNAME", "AMPDBUSER", "AMPDBPASS", "AMPDBFILE");
            $disable_recording = false;
            $sql = "SELECT * FROM globals";
            $globals = sql($sql, "getAll", DB_FETCHMODE_ASSOC);
            foreach ($globals as $global) {
                $value = $global['value'];
                $ext->addGlobal($global['variable'], $value);
                // now if for some reason we have a variable in the global table
                // that is in our $amp_conf_globals list, then remove it so we
                // don't duplicate, the sql table will take precedence
                //
                if (array_key_exists($global['variable'], $amp_conf_globals)) {
                    $rm_keys = array_keys($amp_conf_globals, $global['variable']);
                    foreach ($rm_keys as $index) {
                        unset($amp_conf_globals[$index]);
                    }
                }
                if ($global['variable'] == 'RECORDING_STATE' && strtoupper($global['value']) == 'DISABLED') {
                    $disable_recording = true;
                }
            }
            foreach ($amp_conf_globals as $global) {
                if (isset($amp_conf[$global])) {
                    $value = $amp_conf[$global];
                    if ($value === true || $value === false) {
                        $value = $value ? 'true' : 'false';
                    }
                    $ext->addGlobal($global, $value);
                    out("Added to globals: {$global} = {$value}");
                }
            }
            // Put the asterisk version in a global for agi etc.
            $ext->addGlobal('ASTVERSION', $version);
            // Put the use of chan_dahdi in a global for dialparties
            $ext->addGlobal('ASTCHANDAHDI', $chan_dahdi ? '1' : '0');
            // Create CallingPresTable to deal with difference that ${CALINGPRES} returns vs. what
            // SetCallerPres() accepts. This is a workaround that gets resolved in 1.6 where
            // function CALLINGPRES() is consistent.
            // This should be fixed in 1.4.20 but for now we keep it in until 1.6
            //
            if ($ast_lt_16) {
                $ext->addGlobal('CALLINGPRES_0', 'allowed_not_screened');
                $ext->addGlobal('CALLINGPRES_1', 'allowed_passed_screen');
                $ext->addGlobal('CALLINGPRES_2', 'allowed_failed_screen');
                $ext->addGlobal('CALLINGPRES_3', 'allowed');
                $ext->addGlobal('CALLINGPRES_32', 'prohib_not_screened');
                $ext->addGlobal('CALLINGPRES_33', 'prohib_passed_screen');
                $ext->addGlobal('CALLINGPRES_34', 'prohib_failed_screen');
                $ext->addGlobal('CALLINGPRES_35', 'prohib');
                $ext->addGlobal('CALLINGPRES_67', 'unavailable');
            }
            // This checks if we have func_extstate loaded, if so we set the global which dialparties
            // can use (and any other AGI script) to determine that this function exists and not connect
            // to the manager to get the information
            //
            $has_extension_state = !$ast_lt_16;
            if ($ast_ge_14 && $ast_lt_16) {
                $response = $astman->send_request('Command', array('Command' => 'module show like func_extstate'));
                if (preg_match('/1 modules loaded/', $response['data'])) {
                    $ext->addGlobal('HAS_EXTENSION_STATE', 'TRUE');
                    $has_extension_state = true;
                }
            }
            // Now let's create the required globals for the trunks so outbound routes work. These used to
            // be stored in the globals table but are not generated by retrieve conf and pulled from the
            // trunks table
            //
            $sqlstr = "\n\t\t\t\tSELECT `trunkid`, `tech`, `outcid`, `keepcid`, `maxchans`, `failscript`, `dialoutprefix`, `channelid`, `disabled` \n\t\t\t\tFROM `trunks` ORDER BY `trunkid`\n\t\t\t";
            $trunks = sql($sqlstr, "getAll", DB_FETCHMODE_ASSOC);
            $trunk_hash = core_trunks_list_dialrules();
            // $has_keepcid_cnum is used when macro-outbound-callerid is generated to determine if we need to insert the
            // final execif() statement so it is important to be set before then and here
            //
            $has_keepcid_cnum = false;
            foreach ($trunks as $trunk) {
                $tid = $trunk['trunkid'];
                $tech = strtoupper($trunk['tech']);
                if ($tech == 'IAX') {
                    $tech = 'IAX2';
                } elseif ($tech == 'ZAP' && $chan_dahdi) {
                    $tech = 'DAHDI';
                }
                if ($tech == 'CUSTOM') {
                    $ext->addGlobal('OUT_' . $tid, 'AMP:' . $trunk['channelid']);
                } else {
                    $ext->addGlobal('OUT_' . $tid, $tech . "/" . $trunk['channelid']);
                }
                $ext->addGlobal('OUTCID_' . $tid, $trunk['outcid']);
                $ext->addGlobal('OUTMAXCHANS_' . $tid, $trunk['maxchans']);
                $ext->addGlobal('OUTFAIL_' . $tid, $trunk['failscript']);
                $ext->addGlobal('OUTPREFIX_' . $tid, $trunk['dialoutprefix']);
                $ext->addGlobal('OUTDISABLE_' . $tid, $trunk['disabled']);
                $ext->addGlobal('OUTKEEPCID_' . $tid, $trunk['keepcid']);
                $ext->addGlobal('FORCEDOUTCID_' . $tid, $trunk['keepcid'] == 'all' ? $trunk['outcid'] : "");
                if ($trunk['keepcid'] == 'cnum') {
                    $has_keepcid_cnum = true;
                }
                // Generate PREFIX_TRUNK_$tid even if 0 since globals will persist and cause crashes
                if (isset($trunk_hash[$tid]) && count($trunk_hash)) {
                    $patterns = $trunk_hash[$tid];
                    // First, generate the global referencing how many there are
                    $ext->addGlobal("PREFIX_TRUNK_{$tid}", count($patterns));
                    $context = 'sub-flp-' . $tid;
                    $target = 'TARGET_FLP_' . $tid;
                    $exten = 's';
                    foreach ($patterns as $pattern) {
                        $prepend = $pattern['prepend_digits'];
                        $offset = strlen(preg_replace('/(\\[[^\\]]*\\])/', 'X', $pattern['match_pattern_prefix']));
                        $regex_base = $pattern['match_pattern_prefix'] . $pattern['match_pattern_pass'];
                        // convert asterisk pattern matching into perl regular expression
                        //  - two steps, use $ in place of +
                        //  - next replace $ with +
                        // if you don't do this, the str_replace() walks over itself
                        $regex_intermediate = str_replace(array('X', 'Z', 'N', '.', '*', '+'), array('[0-9]', '[1-9]', '[2-9]', '[0-9#*\\\\$]$', '\\\\*', '\\\\$'), $pattern['match_pattern_prefix'] . $pattern['match_pattern_pass']);
                        $regex = strtr($regex_intermediate, "\$", "+");
                        if ($pattern['prepend_digits'] == '' && $offset == 0) {
                            $ext->add($context, $exten, '', new ext_execif('$[${REGEX("^' . $regex . '$" ${DIAL_NUMBER})} = 1]', 'Return'));
                        } else {
                            $offset = $offset ? ':' . $offset : '';
                            $ext->add($context, $exten, '', new ext_execif('$[${REGEX("^' . $regex . '$" ${DIAL_NUMBER})} = 1]', 'Set', $target . '=' . $pattern['prepend_digits'] . '${DIAL_NUMBER' . $offset . '}'));
                            $ext->add($context, $exten, '', new ext_gotoif('$[${LEN(${' . $target . '})} != 0]', 'match'));
                        }
                    }
                    $ext->add($context, $exten, '', new ext_return(''));
                    $ext->add($context, $exten, 'match', new ext_set('DIAL_NUMBER', '${' . $target . '}'));
                    $ext->add($context, $exten, '', new ext_return(''));
                } else {
                    $ext->addGlobal("PREFIX_TRUNK_{$tid}", '');
                }
            }
            // Generate macro-record-enable, if recording is disabled then we just make it a stub
            // Otherwise we make it right
            //
            $context = 'macro-record-enable';
            $exten = 's';
            if ($disable_recording) {
                $ext->add($context, $exten, '', new ext_macroexit());
            } else {
                $ext->add($context, $exten, '', new ext_gotoif('$["${BLINDTRANSFER}" = ""]', 'check'));
                $ext->add($context, $exten, '', new ext_resetcdr('w'));
                if ($ast_ge_14) {
                    $ext->add($context, $exten, '', new ext_stopmixmonitor());
                } else {
                    $ext->add($context, $exten, '', new ext_stopmonitor());
                }
                $ext->add($context, $exten, 'check', new ext_execif('$["${ARG1}"=""]', 'MacroExit'));
                $ext->add($context, $exten, '', new ext_gotoif('$["${ARG2}"="Group"]', 'Group', 'OUT'));
                $ext->add($context, $exten, 'Group', new ext_set('LOOPCNT', '${FIELDQTY(ARG1,-)}'));
                $ext->add($context, $exten, '', new ext_set('ITER', '1'));
                $ext->add($context, $exten, 'begin', new ext_gotoif('$["${CUT(DB(AMPUSER/${CUT(ARG1,-,${ITER})}/recording),=,3)}" != "Always"]', 'continue'));
                $ext->add($context, $exten, '', new ext_set('TEXTEN', '${CUT(ARG1,-,${ITER})}'));
                $ext->add($context, $exten, '', new ext_noop('Recording enable for ${TEXTEN}'));
                $ext->add($context, $exten, '', new ext_set('CALLFILENAME', 'g${TEXTEN}-${STRFTIME(${EPOCH},,%Y%m%d-%H%M%S)}-${UNIQUEID}'));
                $ext->add($context, $exten, '', new ext_goto('record'));
                $ext->add($context, $exten, 'continue', new ext_set('ITER', '$[${ITER}+1]'));
                $ext->add($context, $exten, '', new ext_gotoif('$[${ITER}<=${LOOPCNT}]', 'begin'));
                $ext->add($context, $exten, 'OUT', new ext_gotoif('$["${ARG2}"="IN"]', 'IN'));
                $ext->add($context, $exten, '', new ext_execif('$["${CUT(DB(AMPUSER/${ARG1}/recording),\\\\\\|,1):4}" != "Always"]', 'MacroExit'));
                $ext->add($context, $exten, '', new ext_noop('Recording enable for ${ARG1}'));
                $ext->add($context, $exten, '', new ext_set('CALLFILENAME', 'OUT${ARG1}-${STRFTIME(${EPOCH},,%Y%m%d-%H%M%S)}-${UNIQUEID}'));
                $ext->add($context, $exten, '', new ext_goto('record'));
                $ext->add($context, $exten, 'IN', new ext_execif('$["${CUT(DB(AMPUSER/${ARG1}/recording),\\\\\\|,2):3}" != "Always"]', 'MacroExit'));
                $ext->add($context, $exten, '', new ext_noop('Recording enable for ${ARG1}'));
                $ext->add($context, $exten, '', new ext_set('CALLFILENAME', '${STRFTIME(${EPOCH},,%Y%m%d-%H%M%S)}-${UNIQUEID}'));
                $ext->add($context, $exten, 'record', new ext_mixmonitor('${MIXMON_DIR}${CALLFILENAME}.${MIXMON_FORMAT}', '', '${MIXMON_POST}'));
                $ext->add($context, $exten, '', new ext_macroexit());
            }
            /* outbound routes */
            $ext->addInclude('from-internal-additional', 'outbound-allroutes');
            //$ext->add('outbound-allroutes', '_!', '', new ext_macro('user-callerid,SKIPTTL'));
            $ext->add('outbound-allroutes', 'foo', '', new ext_noop('bar'));
            $routes = core_routing_list();
            $trunk_table = core_trunks_listbyid();
            $delim = $ast_lt_16 ? '|' : ',';
            foreach ($routes as $route) {
                $add_extra_pri1 = array();
                $context = 'outrt-' . $route['route_id'];
                $comment = $route['name'];
                $ext->addSectionComment($context, $comment);
                if (function_exists('timeconditions_timegroups_get_times') && $route['time_group_id'] !== null) {
                    $times = timeconditions_timegroups_get_times($route['time_group_id'], true);
                    if (is_array($times) && count($times)) {
                        foreach ($times as $time) {
                            $ext->addInclude('outbound-allroutes', $context . $delim . $time[1], $comment);
                        }
                    } else {
                        $ext->addInclude('outbound-allroutes', $context, $comment);
                    }
                } else {
                    $ext->addInclude('outbound-allroutes', $context, $comment);
                }
                $patterns = core_routing_getroutepatternsbyid($route['route_id']);
                $trunks = core_routing_getroutetrunksbyid($route['route_id']);
                foreach ($patterns as $pattern) {
                    // returns:
                    // array('prepend_digits' => $pattern['prepend_digits'], 'dial_pattern' => $exten, 'offset' => $pos);
                    //
                    $fpattern = core_routing_formatpattern($pattern);
                    $exten = $fpattern['dial_pattern'];
                    $offset = $fpattern['offset'] == 0 ? '' : ':' . $fpattern['offset'];
                    // This will not get called, but it fixes some things like custom-context or other possible custom uses of these
                    // generated contexts that don't have an 'outbound-allroutes' wrapper around them, of course in those cases the
                    // CID part of the dialplan will not get executed
                    if (!isset($add_extra_pri1[$fpattern['base_pattern']])) {
                        $ext->add($context, $fpattern['base_pattern'], '', new ext_macro('user-callerid,SKIPTTL'));
                        $add_extra_pri1[$fpattern['base_pattern']] = true;
                    }
                    if ($fpattern['base_pattern'] != $exten) {
                        $ext->add($context, $exten, '', new ext_macro('user-callerid,SKIPTTL'));
                    }
                    $ext->add($context, $exten, '', new ext_noop(sprintf(_('Calling Out Route: %s'), $route['name'])));
                    if ($route['emergency_route'] != '') {
                        $ext->add($context, $exten, '', new ext_set("EMERGENCYROUTE", $route['emergency_route']));
                    }
                    if ($route['intracompany_route'] != '') {
                        $ext->add($context, $exten, '', new ext_set("INTRACOMPANYROUTE", $route['intracompany_route']));
                    }
                    if ($route['mohclass'] != '') {
                        $ext->add($context, $exten, '', new ext_set("MOHCLASS", '${IF($["${MOHCLASS}"=""]?' . $route['mohclass'] . ':${MOHCLASS})}'));
                    }
                    if ($route['outcid'] != '') {
                        if ($route['outcid_mode'] != '') {
                            $ext->add($context, $exten, '', new ext_execif('$["${KEEPCID}"!="TRUE" & ${LEN(${TRUNKCIDOVERRIDE})}=0]', 'Set', 'TRUNKCIDOVERRIDE=' . $route['outcid']));
                        } else {
                            $ext->add($context, $exten, '', new ext_execif('$["${KEEPCID}"!="TRUE" & ${LEN(${DB(AMPUSER/${AMPUSER}/outboundcid)})}=0 & ${LEN(${TRUNKCIDOVERRIDE})}=0]', 'Set', 'TRUNKCIDOVERRIDE=' . $route['outcid']));
                        }
                    }
                    $ext->add($context, $exten, '', new ext_set("_NODEST", ""));
                    $ext->add($context, $exten, '', new ext_macro('record-enable,${AMPUSER},OUT'));
                    $password = $route['password'];
                    foreach ($trunks as $trunk_id) {
                        if (isset($trunk_table[$trunk_id])) {
                            switch (strtolower($trunk_table[$trunk_id]['tech'])) {
                                case 'dundi':
                                    $trunk_macro = 'dialout-dundi';
                                    break;
                                case 'enum':
                                    $trunk_macro = 'dialout-enum';
                                    break;
                                default:
                                    $trunk_macro = 'dialout-trunk';
                                    break;
                            }
                        }
                        $ext->add($context, $exten, '', new ext_macro($trunk_macro, $trunk_id . ',' . $pattern['prepend_digits'] . '${EXTEN' . $offset . '},' . $password));
                        $password = '';
                    }
                    $ext->add($context, $exten, '', new ext_macro("outisbusy"));
                }
                unset($add_extra_pri1);
            }
            general_generate_indications();
            // "blackhole" destinations
            $ext->add('app-blackhole', 'hangup', '', new ext_noop('Blackhole Dest: Hangup'));
            $ext->add('app-blackhole', 'hangup', '', new ext_hangup());
            $ext->add('app-blackhole', 'zapateller', '', new ext_noop('Blackhole Dest: Play SIT Tone'));
            $ext->add('app-blackhole', 'zapateller', '', new ext_answer());
            $ext->add('app-blackhole', 'zapateller', '', new ext_zapateller());
            // Should hangup ?
            // $ext->add('app-blackhole', 'zapateller', '', new ext_hangup());
            $ext->add('app-blackhole', 'musiconhold', '', new ext_noop('Blackhole Dest: Put caller on hold forever'));
            $ext->add('app-blackhole', 'musiconhold', '', new ext_answer());
            $ext->add('app-blackhole', 'musiconhold', '', new ext_musiconhold());
            $ext->add('app-blackhole', 'congestion', '', new ext_noop('Blackhole Dest: Congestion'));
            $ext->add('app-blackhole', 'congestion', '', new ext_answer());
            $ext->add('app-blackhole', 'congestion', '', new ext_playtones('congestion'));
            $ext->add('app-blackhole', 'congestion', '', new ext_congestion());
            $ext->add('app-blackhole', 'congestion', '', new ext_hangup());
            $ext->add('app-blackhole', 'busy', '', new ext_noop('Blackhole Dest: Busy'));
            $ext->add('app-blackhole', 'busy', '', new ext_answer());
            $ext->add('app-blackhole', 'busy', '', new ext_playtones('busy'));
            $ext->add('app-blackhole', 'busy', '', new ext_busy());
            $ext->add('app-blackhole', 'busy', '', new ext_hangup());
            $ext->add('app-blackhole', 'ring', '', new ext_noop('Blackhole Dest: Ring'));
            $ext->add('app-blackhole', 'ring', '', new ext_answer());
            $ext->add('app-blackhole', 'ring', '', new ext_playtones('ring'));
            $ext->add('app-blackhole', 'ring', '', new ext_wait(300));
            $ext->add('app-blackhole', 'ring', '', new ext_hangup());
            if ($amp_conf['AMPBADNUMBER'] !== false) {
                $context = 'bad-number';
                $exten = '_X.';
                $ext->add($context, $exten, '', new extension('ResetCDR()'));
                $ext->add($context, $exten, '', new extension('NoCDR()'));
                $ext->add($context, $exten, '', new ext_progress());
                $ext->add($context, $exten, '', new ext_wait('1'));
                $ext->add($context, $exten, '', new ext_progress());
                $ext->add($context, $exten, '', new ext_playback('silence/1&cannot-complete-as-dialed&check-number-dial-again,noanswer'));
                $ext->add($context, $exten, '', new ext_wait('1'));
                $ext->add($context, $exten, '', new ext_congestion('20'));
                $ext->add($context, $exten, '', new ext_hangup());
            }
            /*
            ;------------------------------------------------------------------------
            ; [macro-confirm]
            ;------------------------------------------------------------------------
            ; CONTEXT:      macro-confirm                                                                                                              
            ; PURPOSE:      added default message if none supplied
            ;
            ; Follom-Me and Ringgroups provide an option to supply a message to be
            ; played as part of the confirmation. These changes have added a default
            ; message if none is supplied.
            ;
            ;------------------------------------------------------------------------
            */
            $context = 'macro-confirm';
            $exten = 's';
            $ext->add($context, $exten, '', new ext_setvar('LOOPCOUNT', '0'));
            $ext->add($context, $exten, '', new ext_setvar('__MACRO_RESULT', 'ABORT'));
            $ext->add($context, $exten, '', new ext_setvar('MSG1', '${IF($["foo${ARG1}" != "foo"]?${ARG1}:"incoming-call-1-accept-2-decline")}'));
            if ($ast_ge_14) {
                $ext->add($context, $exten, 'start', new ext_background('${MSG1},m,${CHANNEL(language)},macro-confirm'));
            } else {
                $ext->add($context, $exten, 'start', new ext_background('${MSG1},m,${LANGUAGE},macro-confirm'));
            }
            $ext->add($context, $exten, '', new ext_read('INPUT', '', 1, '', '', 4));
            $ext->add($context, $exten, '', new ext_gotoif('$[${LEN(${INPUT})} > 0]', '${INPUT},1', 't,1'));
            $exten = '1';
            $ext->add($context, $exten, '', new ext_gotoif('$["${DB_EXISTS(RG/${ARG3}/${UNIQCHAN})}" = "0"]', 'toolate,1'));
            $ext->add($context, $exten, '', new ext_dbdel('RG/${ARG3}/${UNIQCHAN}'));
            $ext->add($context, $exten, '', new ext_dbdel('${BLKVM_OVERRIDE}'));
            $ext->add($context, $exten, '', new ext_setvar('__MACRO_RESULT', ''));
            $ext->add($context, $exten, 'exitopt1', new ext_macroexit());
            $exten = '2';
            $ext->add($context, $exten, '', new ext_goto(1, 'noanswer'));
            $exten = '3';
            $ext->add($context, $exten, '', new ext_saydigits('${CALLCONFIRMCID}'));
            $ext->add($context, $exten, '', new ext_gotoif('$["${DB_EXISTS(RG/${ARG3}/${UNIQCHAN})}" = "0"]', 'toolate,1', 's,start'));
            $exten = 't';
            $ext->add($context, $exten, '', new ext_gotoif('$["${DB_EXISTS(RG/${ARG3}/${UNIQCHAN})}" = "0"]', 'toolate,1'));
            $ext->add($context, $exten, '', new ext_setvar('LOOPCOUNT', '$[ ${LOOPCOUNT} + 1 ]'));
            $ext->add($context, $exten, '', new ext_gotoif('$[ ${LOOPCOUNT} < 5 ]', 's,start', 'noanswer,1'));
            $exten = '_X';
            if ($ast_ge_14) {
                $ext->add($context, $exten, '', new ext_background('invalid,m,${CHANNEL(language)},macro-confirm'));
            } else {
                $ext->add($context, $exten, '', new ext_background('invalid,m,${LANGUAGE},macro-confirm'));
            }
            $ext->add($context, $exten, '', new ext_gotoif('$["${DB_EXISTS(RG/${ARG3}/${UNIQCHAN})}" = "0"]', 'toolate,1'));
            $ext->add($context, $exten, '', new ext_setvar('LOOPCOUNT', '$[ ${LOOPCOUNT} + 1 ]'));
            $ext->add($context, $exten, '', new ext_gotoif('$[ ${LOOPCOUNT} < 5 ]', 's,start', 'noanswer,1'));
            $exten = 'noanswer';
            $ext->add($context, $exten, '', new ext_setvar('__MACRO_RESULT', 'ABORT'));
            $ext->add($context, $exten, 'exitnoanswer', new ext_macroexit());
            $exten = 'toolate';
            $ext->add($context, $exten, '', new ext_setvar('MSG2', '${IF($["foo${ARG2}" != "foo"]?${ARG2}:"incoming-call-no-longer-avail")}'));
            $ext->add($context, $exten, '', new ext_playback('${MSG2}'));
            $ext->add($context, $exten, '', new ext_setvar('__MACRO_RESULT', 'ABORT'));
            $ext->add($context, $exten, 'exittoolate', new ext_macroexit());
            $exten = 'h';
            $ext->add($context, $exten, '', new ext_macro('hangupcall'));
            /*
            ;------------------------------------------------------------------------
            ; [macro-auto-confirm]
            ;------------------------------------------------------------------------
            ; This macro is called from ext-local-confirm to auto-confirm a call so that other extensions
            ; are aware that the call has been answered.
            ;
            ;------------------------------------------------------------------------
            */
            $context = 'macro-auto-confirm';
            $exten = 's';
            $ext->add($context, $exten, '', new ext_setvar('__MACRO_RESULT', ''));
            $ext->add($context, $exten, '', new ext_dbdel('${BLKVM_OVERRIDE}'));
            $ext->add($context, $exten, '', new ext_dbdel('RG/${ARG1}/${UNIQCHAN}'));
            /*
            ;------------------------------------------------------------------------
            ; [macro-auto-blkvm]
            ;------------------------------------------------------------------------
            ; This macro is called for any extension dialed form a queue, ringgroup
            ; or followme, so that the answering extension can clear the voicemail block
            ; override allow subsequent transfers to properly operate.
            ;
            ;------------------------------------------------------------------------
            */
            $context = 'macro-auto-blkvm';
            $exten = 's';
            $ext->add($context, $exten, '', new ext_setvar('__MACRO_RESULT', ''));
            $ext->add($context, $exten, '', new ext_dbdel('${BLKVM_OVERRIDE}'));
            /*
            ;------------------------------------------------------------------------
            ; [sub-pincheck]
            ;------------------------------------------------------------------------
            ; This subroutine checks the pincode and then resets the CDR from that point
            ; if the pincode passes. This way the billsec and duration fields are set
            ; properly for pin dialing.
            ;
            ; ${ARG3} is the pincode if this was called, used by dialout-trunk, dialout-enum
            ; and dialout-dundi
            ;
            ;------------------------------------------------------------------------
            */
            $context = 'sub-pincheck';
            $exten = 's';
            $ext->add($context, $exten, '', new ext_authenticate('${ARG3}'));
            $ext->add($context, $exten, '', new ext_resetcdr(''));
            $ext->add($context, $exten, '', new ext_return(''));
            /*
             * dialout using a trunk, using pattern matching (don't strip any prefix)
             * arg1 = trunk number, arg2 = number, arg3 = route password
             *
             * MODIFIED (PL)
             *
             * Modified both Dial() commands to include the new TRUNK_OPTIONS from the general
             * screen of AMP
             */
            if (function_exists('outroutemsg_get')) {
                $trunkreportmsg_ids = outroutemsg_get();
            } else {
                if (!defined('DEFAULT_MSG')) {
                    define('DEFAULT_MSG', -1);
                }
                if (!defined('CONGESTION_TONE')) {
                    define('CONGESTION_TONE', -2);
                }
                $trunkreportmsg_ids = array('no_answer_msg_id' => -1, 'invalidnmbr_msg_id' => -1, 'unalloc_msg_id' => -1);
            }
            $context = 'macro-dialout-trunk';
            $exten = 's';
            $ext->add($context, $exten, '', new ext_set('DIAL_TRUNK', '${ARG1}'));
            $ext->add($context, $exten, '', new ext_gosubif('$[$["${ARG3}" != ""] & $["${DB(AMPUSER/${AMPUSER}/pinless)}" != "NOPASSWD"]]', 'sub-pincheck,s,1'));
            $ext->add($context, $exten, '', new ext_gotoif('$["x${OUTDISABLE_${DIAL_TRUNK}}" = "xon"]', 'disabletrunk,1'));
            $ext->add($context, $exten, '', new ext_set('DIAL_NUMBER', '${ARG2}'));
            // fixlocalprefix depends on this
            $ext->add($context, $exten, '', new ext_set('DIAL_TRUNK_OPTIONS', '${DIAL_OPTIONS}'));
            // will be reset to TRUNK_OPTIONS if not intra-company
            $ext->add($context, $exten, '', new ext_set('OUTBOUND_GROUP', 'OUT_${DIAL_TRUNK}'));
            $ext->add($context, $exten, '', new ext_gotoif('$["${OUTMAXCHANS_${DIAL_TRUNK}}foo" = "foo"]', 'nomax'));
            $ext->add($context, $exten, '', new ext_gotoif('$[ ${GROUP_COUNT(OUT_${DIAL_TRUNK})} >= ${OUTMAXCHANS_${DIAL_TRUNK}} ]', 'chanfull'));
            $ext->add($context, $exten, 'nomax', new ext_gotoif('$["${INTRACOMPANYROUTE}" = "YES"]', 'skipoutcid'));
            // Set to YES if treated like internal
            $ext->add($context, $exten, '', new ext_set('DIAL_TRUNK_OPTIONS', '${TRUNK_OPTIONS}'));
            $ext->add($context, $exten, '', new ext_macro('outbound-callerid', '${DIAL_TRUNK}'));
            $ext->add($context, $exten, 'skipoutcid', new ext_gosubif('$["${PREFIX_TRUNK_${DIAL_TRUNK}}" != ""]', 'sub-flp-${DIAL_TRUNK},s,1'));
            // this sets DIAL_NUMBER to the proper dial string for this trunk
            $ext->add($context, $exten, '', new ext_set('OUTNUM', '${OUTPREFIX_${DIAL_TRUNK}}${DIAL_NUMBER}'));
            // OUTNUM is the final dial number
            $ext->add($context, $exten, '', new ext_set('custom', '${CUT(OUT_${DIAL_TRUNK},:,1)}'));
            // Custom trunks are prefixed with "AMP:"
            // Back to normal processing, whether intracompany or not.
            // But add the macro-setmusic if we don't want music on this outbound call
            $ext->add($context, $exten, '', new ext_execif('$[$["${MOHCLASS}" != "default"] & $["${MOHCLASS}" != ""]]', 'Set', 'DIAL_TRUNK_OPTIONS=M(setmusic^${MOHCLASS})${DIAL_TRUNK_OPTIONS}'));
            // This macro call will always be blank and is provided as a hook for customization required prior to making a call
            // such as adding SIP header information or other requirements. All the channel variables from above are present
            $ext->add($context, $exten, 'gocall', new ext_macro('dialout-trunk-predial-hook'));
            $ext->add($context, $exten, '', new ext_gotoif('$["${PREDIAL_HOOK_RET}" = "BYPASS"]', 'bypass,1'));
            $ext->add($context, $exten, '', new ext_gotoif('$["${custom}" = "AMP"]', 'customtrunk'));
            $ext->add($context, $exten, '', new ext_dial('${OUT_${DIAL_TRUNK}}/${OUTNUM}', '300,${DIAL_TRUNK_OPTIONS}'));
            // Regular Trunk Dial
            $ext->add($context, $exten, '', new ext_noop('Dial failed for some reason with DIALSTATUS = ${DIALSTATUS} and HANGUPCAUSE = ${HANGUPCAUSE}'));
            $ext->add($context, $exten, '', new ext_goto(1, 's-${DIALSTATUS}'));
            $ext->add($context, $exten, 'customtrunk', new ext_set('pre_num', '${CUT(OUT_${DIAL_TRUNK},$,1)}'));
            $ext->add($context, $exten, '', new ext_set('the_num', '${CUT(OUT_${DIAL_TRUNK},$,2)}'));
            // this is where we expect to find string OUTNUM
            $ext->add($context, $exten, '', new ext_set('post_num', '${CUT(OUT_${DIAL_TRUNK},$,3)}'));
            $ext->add($context, $exten, '', new ext_gotoif('$["${the_num}" = "OUTNUM"]', 'outnum', 'skipoutnum'));
            // if we didn't find "OUTNUM", then skip to Dial
            $ext->add($context, $exten, 'outnum', new ext_set('the_num', '${OUTNUM}'));
            // replace "OUTNUM" with the actual number to dial
            $ext->add($context, $exten, 'skipoutnum', new ext_dial('${pre_num:4}${the_num}${post_num}', '300,${DIAL_TRUNK_OPTIONS}'));
            $ext->add($context, $exten, '', new ext_noop('Dial failed for some reason with DIALSTATUS = ${DIALSTATUS} and HANGUPCAUSE = ${HANGUPCAUSE}'));
            $ext->add($context, $exten, '', new ext_goto(1, 's-${DIALSTATUS}'));
            $ext->add($context, $exten, 'chanfull', new ext_noop('max channels used up'));
            $exten = 's-BUSY';
            /*
             * HANGUPCAUSE 17 = Busy, or SIP 486 Busy everywhere
             */
            $ext->add($context, $exten, '', new ext_noop('Dial failed due to trunk reporting BUSY - giving up'));
            $ext->add($context, $exten, '', new ext_playtones('busy'));
            $ext->add($context, $exten, '', new ext_busy(20));
            /*
             * There are reported bugs in Asterisk Blind Trasfers that result in Dial() returning and continuing
             * execution with a status of ANSWER. So we hangup at this point
             */
            $exten = 's-ANSWER';
            $ext->add($context, $exten, '', new ext_noop('Call successfully answered - Hanging up now'));
            $ext->add($context, $exten, '', new ext_macro('hangupcall'));
            $exten = 's-NOANSWER';
            /*
             * HANGUPCAUSE 18 = No User Responding, or SIP 408 Request Timeout
             * HANGUPCAUSE 19 = No Answer From The User, or SIP 480 Temporarily unavailable, SIP 483 To many hops
             */
            $ext->add($context, $exten, '', new ext_noop('Dial failed due to trunk reporting NOANSWER - giving up'));
            $ext->add($context, $exten, '', new ext_progress());
            switch ($trunkreportmsg_ids['no_answer_msg_id']) {
                case DEFAULT_MSG:
                    $ext->add($context, $exten, '', new ext_playback('number-not-answering,noanswer'));
                    break;
                case CONGESTION_TONE:
                    $ext->add($context, $exten, '', new ext_playtones('congestion'));
                    break;
                default:
                    $message = recordings_get_file($trunkreportmsg_ids['no_answer_msg_id']);
                    $message = $message != "" ? $message : "number-not-answering";
                    $ext->add($context, $exten, '', new ext_playback("{$message}, noanswer"));
            }
            $ext->add($context, $exten, '', new ext_congestion(20));
            $exten = 's-INVALIDNMBR';
            /*
             * HANGUPCAUSE 28 = Address Incomplete, or SIP 484 Address Incomplete
             */
            $ext->add($context, $exten, '', new ext_noop('Dial failed due to trunk reporting Address Incomplete - giving up'));
            $ext->add($context, $exten, '', new ext_progress());
            switch ($trunkreportmsg_ids['invalidnmbr_msg_id']) {
                case DEFAULT_MSG:
                    $ext->add($context, $exten, '', new ext_playback('ss-noservice,noanswer'));
                    break;
                case CONGESTION_TONE:
                    $ext->add($context, $exten, '', new ext_playtones('congestion'));
                    break;
                default:
                    $message = recordings_get_file($trunkreportmsg_ids['invalidnmbr_msg_id']);
                    $message = $message != "" ? $message : "ss-noservice";
                    $ext->add($context, $exten, '', new ext_playback("{$message}, noanswer"));
            }
            $ext->add($context, $exten, '', new ext_busy(20));
            $exten = "s-CHANGED";
            $ext->add($context, $exten, '', new ext_noop('Dial failed due to trunk reporting Number Changed - giving up'));
            $ext->add($context, $exten, '', new ext_playtones('busy'));
            $ext->add($context, $exten, '', new ext_busy(20));
            $exten = '_s-.';
            $ext->add($context, $exten, '', new ext_set('RC', '${IF($[${ISNULL(${HANGUPCAUSE})}]?0:${HANGUPCAUSE})}'));
            $ext->add($context, $exten, '', new ext_goto('1', '${RC}'));
            $ext->add($context, '17', '', new ext_goto('1', 's-BUSY'));
            $ext->add($context, '18', '', new ext_goto('1', 's-NOANSWER'));
            $ext->add($context, '22', '', new ext_goto('1', 's-CHANGED'));
            $ext->add($context, '23', '', new ext_goto('1', 's-CHANGED'));
            $ext->add($context, '28', '', new ext_goto('1', 's-INVALIDNMBR'));
            $ext->add($context, '_X', '', new ext_goto('1', 'continue'));
            $ext->add($context, '_X.', '', new ext_goto('1', 'continue'));
            $exten = 'continue';
            $ext->add($context, $exten, '', new ext_gotoif('$["${OUTFAIL_${ARG1}}" = ""]', 'noreport'));
            $ext->add($context, $exten, '', new ext_agi('${OUTFAIL_${ARG1}}'));
            $ext->add($context, $exten, 'noreport', new ext_noop('TRUNK Dial failed due to ${DIALSTATUS} HANGUPCAUSE: ${HANGUPCAUSE} - failing through to other trunks'));
            $ext->add($context, $exten, '', new ext_set('CALLERID(number)', '${AMPUSER}'));
            $ext->add($context, 'disabletrunk', '', new ext_noop('TRUNK: ${OUT_${DIAL_TRUNK}} DISABLED - falling through to next trunk'));
            $ext->add($context, 'bypass', '', new ext_noop('TRUNK: ${OUT_${DIAL_TRUNK}} BYPASSING because dialout-trunk-predial-hook'));
            $ext->add($context, 'h', '', new ext_macro('hangupcall'));
            $context = 'macro-dialout-dundi';
            $exten = 's';
            /*
             * Dialout Dundi Trunk
             */
            $ext->add($context, $exten, '', new ext_set('DIAL_TRUNK', '${ARG1}'));
            $ext->add($context, $exten, '', new ext_gosubif('$[$["${ARG3}" != ""] & $["${DB(AMPUSER/${AMPUSER}/pinless)}" != "NOPASSWD"]]', 'sub-pincheck,s,1'));
            $ext->add($context, $exten, '', new ext_gotoif('$["x${OUTDISABLE_${DIAL_TRUNK}}" = "xon"]', 'disabletrunk,1'));
            $ext->add($context, $exten, '', new ext_set('DIAL_NUMBER', '${ARG2}'));
            // fixlocalprefix depends on this
            $ext->add($context, $exten, '', new ext_set('DIAL_TRUNK_OPTIONS', '${DIAL_OPTIONS}'));
            // will be reset to TRUNK_OPTIONS if not intra-company
            $ext->add($context, $exten, '', new ext_set('OUTBOUND_GROUP', 'OUT_${DIAL_TRUNK}'));
            $ext->add($context, $exten, '', new ext_gotoif('$["${OUTMAXCHANS_${DIAL_TRUNK}}foo" = "foo"]', 'nomax'));
            $ext->add($context, $exten, '', new ext_gotoif('$[ ${GROUP_COUNT(OUT_${DIAL_TRUNK})} >= ${OUTMAXCHANS_${DIAL_TRUNK}} ]', 'chanfull'));
            $ext->add($context, $exten, 'nomax', new ext_gotoif('$["${INTRACOMPANYROUTE}" = "YES"]', 'skipoutcid'));
            // Set to YES if treated like internal
            $ext->add($context, $exten, '', new ext_set('DIAL_TRUNK_OPTIONS', '${TRUNK_OPTIONS}'));
            $ext->add($context, $exten, '', new ext_macro('outbound-callerid', '${DIAL_TRUNK}'));
            $ext->add($context, $exten, 'skipoutcid', new ext_gosubif('$["${PREFIX_TRUNK_${DIAL_TRUNK}}" != ""]', 'sub-flp-${DIAL_TRUNK},s,1'));
            // manipulate DIAL_NUMBER
            $ext->add($context, $exten, '', new ext_set('OUTNUM', '${OUTPREFIX_${DIAL_TRUNK}}${DIAL_NUMBER}'));
            // OUTNUM is the final dial number
            // Back to normal processing, whether intracompany or not.
            // But add the macro-setmusic if we don't want music on this outbound call
            $ext->add($context, $exten, '', new ext_execif('$[$["${MOHCLASS}" != "default"] & $["${MOHCLASS}" != ""]]', 'Set', 'DIAL_TRUNK_OPTIONS=M(setmusic^${MOHCLASS})${DIAL_TRUNK_OPTIONS}'));
            // This macro call will always be blank and is provided as a hook for customization required prior to making a call
            // such as adding SIP header information or other requirements. All the channel variables from above are present
            $ext->add($context, $exten, 'gocall', new ext_macro('dialout-dundi-predial-hook'));
            $ext->add($context, $exten, '', new ext_gotoif('$["${PREDIAL_HOOK_RET}" = "BYPASS"]', 'bypass,1'));
            $ext->add($context, $exten, '', new ext_macro('dundi-${DIAL_TRUNK}', '${OUTNUM}'));
            $ext->add($context, $exten, '', new ext_goto(1, 's-${DIALSTATUS}'));
            $ext->add($context, $exten, 'chanfull', new ext_noop('max channels used up'));
            $exten = 's-BUSY';
            /*
             * HANGUPCAUSE 17 = Busy, or SIP 486 Busy everywhere
             */
            $ext->add($context, $exten, '', new ext_noop('Dial failed due to trunk reporting BUSY - giving up'));
            $ext->add($context, $exten, '', new ext_playtones('busy'));
            $ext->add($context, $exten, '', new ext_busy(20));
            /*
             * There are reported bugs in Asterisk Blind Trasfers that result in Dial() returning and continuing
             * execution with a status of ANSWER. So we hangup at this point
             */
            $exten = 's-ANSWER';
            $ext->add($context, $exten, '', new ext_noop('Call successfully answered - Hanging up now'));
            $ext->add($context, $exten, '', new ext_macro('hangupcall'));
            $exten = 's-NOANSWER';
            /*
             * HANGUPCAUSE 18 = No User Responding, or SIP 408 Request Timeout
             * HANGUPCAUSE 19 = No Answer From The User, or SIP 480 Temporarily unavailable, SIP 483 To many hops
             */
            $ext->add($context, $exten, '', new ext_noop('Dial failed due to trunk reporting NOANSWER - giving up'));
            $ext->add($context, $exten, '', new ext_progress());
            switch ($trunkreportmsg_ids['no_answer_msg_id']) {
                case DEFAULT_MSG:
                    $ext->add($context, $exten, '', new ext_playback('number-not-answering,noanswer'));
                    break;
                case CONGESTION_TONE:
                    $ext->add($context, $exten, '', new ext_playtones('congestion'));
                    break;
                default:
                    $message = recordings_get_file($trunkreportmsg_ids['no_answer_msg_id']);
                    $message = $message != "" ? $message : "number-not-answering";
                    $ext->add($context, $exten, '', new ext_playback("{$message}, noanswer"));
            }
            $ext->add($context, $exten, '', new ext_congestion(20));
            $exten = 's-INVALIDNMBR';
            /*
             * HANGUPCAUSE 28 = Address Incomplete, or SIP 484 Address Incomplete
             */
            $ext->add($context, $exten, '', new ext_noop('Dial failed due to trunk reporting Address Incomplete - giving up'));
            $ext->add($context, $exten, '', new ext_progress());
            switch ($trunkreportmsg_ids['invalidnmbr_msg_id']) {
                case DEFAULT_MSG:
                    $ext->add($context, $exten, '', new ext_playback('ss-noservice,noanswer'));
                    break;
                case CONGESTION_TONE:
                    $ext->add($context, $exten, '', new ext_playtones('congestion'));
                    break;
                default:
                    $message = recordings_get_file($trunkreportmsg_ids['invalidnmbr_msg_id']);
                    $message = $message != "" ? $message : "ss-noservice";
                    $ext->add($context, $exten, '', new ext_playback("{$message}, noanswer"));
            }
            $ext->add($context, $exten, '', new ext_busy(20));
            $exten = "s-CHANGED";
            $ext->add($context, $exten, '', new ext_noop('Dial failed due to trunk reporting Number Changed - giving up'));
            $ext->add($context, $exten, '', new ext_playtones('busy'));
            $ext->add($context, $exten, '', new ext_busy(20));
            $exten = '_s-.';
            $ext->add($context, $exten, '', new ext_set('RC', '${IF($[${ISNULL(${HANGUPCAUSE})}]?0:${HANGUPCAUSE})}'));
            $ext->add($context, $exten, '', new ext_goto('1', '${RC}'));
            $ext->add($context, '17', '', new ext_goto('1', 's-BUSY'));
            $ext->add($context, '18', '', new ext_goto('1', 's-NOANSWER'));
            $ext->add($context, '22', '', new ext_goto('1', 's-CHANGED'));
            $ext->add($context, '23', '', new ext_goto('1', 's-CHANGED'));
            $ext->add($context, '28', '', new ext_goto('1', 's-INVALIDNMBR'));
            $ext->add($context, '_X', '', new ext_goto('1', 'continue'));
            $ext->add($context, '_X.', '', new ext_goto('1', 'continue'));
            $exten = 'continue';
            $ext->add($context, $exten, '', new ext_gotoif('$["${OUTFAIL_${ARG1}}" = ""]', 'noreport'));
            $ext->add($context, $exten, '', new ext_agi('${OUTFAIL_${ARG1}}'));
            $ext->add($context, $exten, 'noreport', new ext_noop('TRUNK Dial failed due to ${DIALSTATUS} HANGUPCAUSE: ${HANGUPCAUSE} - failing through to other trunks'));
            $ext->add($context, $exten, '', new ext_set('CALLERID(number)', '${AMPUSER}'));
            $ext->add($context, 'disabletrunk', '', new ext_noop('TRUNK: ${OUT_${DIAL_TRUNK}} DISABLED - falling through to next trunk'));
            $ext->add($context, 'bypass', '', new ext_noop('TRUNK: ${OUT_${DIAL_TRUNK}} BYPASSING because dialout-dundi-predial-hook'));
            $ext->add($context, 'h', '', new ext_macro('hangupcall'));
            /*
             * sets the callerid of the device to that of the logged in user
             *
             * ${AMPUSER} is set upon return to the real user despite any aliasing that may
             * have been set as a result of the AMPUSER/<nnn>/cidnum field. This is used by
             * features like DND, CF, etc. to set the proper structure on aliased instructions 
             */
            $context = 'macro-user-callerid';
            $exten = 's';
            //$ext->add($context, $exten, '', new ext_noop('user-callerid: ${CALLERID(name)} ${CALLERID(number)}'));
            // make sure AMPUSER is set if it doesn't get set below
            $ext->add($context, $exten, '', new ext_set('AMPUSER', '${IF($["foo${AMPUSER}" = "foo"]?${CALLERID(number)}:${AMPUSER})}'));
            $ext->add($context, $exten, '', new ext_gotoif('$["${CHANNEL:0:5}" = "Local"]', 'report'));
            $ext->add($context, $exten, '', new ext_execif('$["${REALCALLERIDNUM:1:2}" = ""]', 'Set', 'REALCALLERIDNUM=${CALLERID(number)}'));
            //$ext->add($context, $exten, 'start', new ext_noop('REALCALLERIDNUM is ${REALCALLERIDNUM}'));
            $ext->add($context, $exten, '', new ext_set('AMPUSER', '${DB(DEVICE/${REALCALLERIDNUM}/user)}'));
            $ext->add($context, $exten, '', new ext_set('AMPUSERCIDNAME', '${DB(AMPUSER/${AMPUSER}/cidname)}'));
            $ext->add($context, $exten, '', new ext_gotoif('$["x${AMPUSERCIDNAME:1:2}" = "x"]', 'report'));
            // user may masquerade as a different user internally, so set the internal cid as indicated
            // but keep the REALCALLERID which is used to determine their true identify and lookup info
            // during outbound calls.
            $ext->add($context, $exten, '', new ext_set('AMPUSERCID', '${IF($["${DB_EXISTS(AMPUSER/${AMPUSER}/cidnum)}" = "1"]?${DB_RESULT}:${AMPUSER})}'));
            $ext->add($context, $exten, '', new ext_set('CALLERID(all)', '"${AMPUSERCIDNAME}" <${AMPUSERCID}>'));
            /*
             * This is where to splice in things like setting the language based on a user's astdb setting,
             * or where you might set the CID account code based on a user instead of the device settings.
             */
            $ext->add($context, $exten, 'report', new ext_gotoif('$[ "${ARG1}" = "SKIPTTL" ]', 'continue'));
            $ext->add($context, $exten, 'report2', new ext_set('__TTL', '${IF($["foo${TTL}" = "foo"]?64:$[ ${TTL} - 1 ])}'));
            $ext->add($context, $exten, '', new ext_gotoif('$[ ${TTL} > 0 ]', 'continue'));
            $ext->add($context, $exten, '', new ext_wait('${RINGTIMER}'));
            // wait for a while, to give it a chance to be picked up by voicemail
            $ext->add($context, $exten, '', new ext_answer());
            $ext->add($context, $exten, '', new ext_wait('2'));
            $ext->add($context, $exten, '', new ext_playback('im-sorry&an-error-has-occured&with&call-forwarding'));
            $ext->add($context, $exten, '', new ext_macro('hangupcall'));
            $ext->add($context, $exten, '', new ext_congestion(20));
            // Address Security Vulnerability in many earlier versions of Asterisk from an external source tranmitting a
            // malicious CID that can cause overflows in the Asterisk code.
            //
            $ext->add($context, $exten, 'continue', new ext_set('CALLERID(number)', '${CALLERID(number):0:40}'));
            $ext->add($context, $exten, '', new ext_set('CALLERID(name)', '${CALLERID(name):0:40}'));
            $ext->add($context, $exten, '', new ext_noop('Using CallerID ${CALLERID(all)}'));
            $ext->add($context, 'h', '', new ext_macro('hangupcall'));
            /*
             * arg1 = trunk number, arg2 = number
             * 
             * Re-written to use enumlookup.agi
             */
            // Is this the best place to put it in?
            // Check if we are using Google DNS for ENUM-lookups,
            // enable it as a global variable so we can use it in the agi
            if ($amp_conf['USEGOOGLEDNSFORENUM']) {
                $ext->addGlobal('ENUMUSEGOOGLEDNS', 'TRUE');
            }
            $context = 'macro-dialout-enum';
            $exten = 's';
            $ext->add($context, $exten, '', new ext_gosubif('$[$["${ARG3}" != ""] & $["${DB(AMPUSER/${AMPUSER}/pinless)}" != "NOPASSWD"]]', 'sub-pincheck,s,1'));
            $ext->add($context, $exten, '', new ext_macro('outbound-callerid', '${ARG1}'));
            $ext->add($context, $exten, '', new ext_set('OUTBOUND_GROUP', 'OUT_${ARG1}'));
            $ext->add($context, $exten, '', new ext_gotoif('$["${OUTMAXCHANS_${ARG1}}foo" = "foo"]', 'nomax'));
            $ext->add($context, $exten, '', new ext_gotoif('$[ ${GROUP_COUNT(OUT_${ARG1})} >= ${OUTMAXCHANS_${ARG1}} ]', 'nochans'));
            $ext->add($context, $exten, 'nomax', new ext_set('DIAL_NUMBER', '${ARG2}'));
            $ext->add($context, $exten, '', new ext_set('DIAL_TRUNK', '${ARG1}'));
            $ext->add($context, $exten, '', new ext_gosubif('$["${PREFIX_TRUNK_${DIAL_TRUNK}}" != ""]', 'sub-flp-${DIAL_TRUNK},s,1'));
            // manimpulate DIAL_NUMBER
            //  Replacement for asterisk's ENUMLOOKUP function
            $ext->add($context, $exten, '', new ext_agi('enumlookup.agi'));
            // Now we have the variable DIALARR set to a list of URI's that can be called, in order of priority
            // Loop through them trying them in order.
            $ext->add($context, $exten, 'dialloop', new ext_gotoif('$["foo${DIALARR}"="foo"]', 's-${DIALSTATUS},1'));
            $ext->add($context, $exten, '', new ext_set('TRYDIAL', '${CUT(DIALARR,%,1)}'));
            $ext->add($context, $exten, '', new ext_set('DIALARR', '${CUT(DIALARR,%,2-)}'));
            $ext->add($context, $exten, '', new ext_dial('${TRYDIAL}', ''));
            // Now, if we're still here, that means the Dial failed for some reason.
            // If it's CONGESTION or CHANUNAVAIL we want to try again on a different
            // different channel. If there's no more left, the dialloop tag will exit.
            $ext->add($context, $exten, '', new ext_gotoif('$[ $[ "${DIALSTATUS}" = "CHANUNAVAIL" ] | $[ "${DIALSTATUS}" = "CONGESTION" ] ]', 'dialloop', 's-${DIALSTATUS},1'));
            // Here are the exit points for the macro.
            $ext->add($context, $exten, 'nochans', new ext_noop('max channels used up'));
            $exten = 's-BUSY';
            /*
             * HANGUPCAUSE 17 = Busy, or SIP 486 Busy everywhere
             */
            $ext->add($context, $exten, '', new ext_noop('Dial failed due to trunk reporting BUSY - giving up'));
            $ext->add($context, $exten, '', new ext_playtones('busy'));
            $ext->add($context, $exten, '', new ext_busy(20));
            /*
             * There are reported bugs in Asterisk Blind Trasfers that result in Dial() returning and continuing
             * execution with a status of ANSWER. So we hangup at this point
             */
            $exten = 's-ANSWER';
            $ext->add($context, $exten, '', new ext_noop('Call successfully answered - Hanging up now'));
            $ext->add($context, $exten, '', new ext_macro('hangupcall'));
            $exten = 's-NOANSWER';
            /*
             * HANGUPCAUSE 18 = No User Responding, or SIP 408 Request Timeout
             * HANGUPCAUSE 19 = No Answer From The User, or SIP 480 Temporarily unavailable, SIP 483 To many hops
             */
            $ext->add($context, $exten, '', new ext_noop('Dial failed due to trunk reporting NOANSWER - giving up'));
            $ext->add($context, $exten, '', new ext_progress());
            switch ($trunkreportmsg_ids['no_answer_msg_id']) {
                case DEFAULT_MSG:
                    $ext->add($context, $exten, '', new ext_playback('number-not-answering,noanswer'));
                    break;
                case CONGESTION_TONE:
                    $ext->add($context, $exten, '', new ext_playtones('congestion'));
                    break;
                default:
                    $message = recordings_get_file($trunkreportmsg_ids['no_answer_msg_id']);
                    $message = $message != "" ? $message : "number-not-answering";
                    $ext->add($context, $exten, '', new ext_playback("{$message}, noanswer"));
            }
            $ext->add($context, $exten, '', new ext_congestion(20));
            $exten = 's-INVALIDNMBR';
            /*
             * HANGUPCAUSE 28 = Address Incomplete, or SIP 484 Address Incomplete
             */
            $ext->add($context, $exten, '', new ext_noop('Dial failed due to trunk reporting Address Incomplete - giving up'));
            $ext->add($context, $exten, '', new ext_progress());
            switch ($trunkreportmsg_ids['invalidnmbr_msg_id']) {
                case DEFAULT_MSG:
                    $ext->add($context, $exten, '', new ext_playback('ss-noservice,noanswer'));
                    break;
                case CONGESTION_TONE:
                    $ext->add($context, $exten, '', new ext_playtones('congestion'));
                    break;
                default:
                    $message = recordings_get_file($trunkreportmsg_ids['invalidnmbr_msg_id']);
                    $message = $message != "" ? $message : "ss-noservice";
                    $ext->add($context, $exten, '', new ext_playback("{$message}, noanswer"));
            }
            $ext->add($context, $exten, '', new ext_busy(20));
            $exten = "s-CHANGED";
            $ext->add($context, $exten, '', new ext_noop('Dial failed due to trunk reporting Number Changed - giving up'));
            $ext->add($context, $exten, '', new ext_playtones('busy'));
            $ext->add($context, $exten, '', new ext_busy(20));
            $exten = '_s-.';
            $ext->add($context, $exten, '', new ext_set('RC', '${IF($[${ISNULL(${HANGUPCAUSE})}]?0:${HANGUPCAUSE})}'));
            $ext->add($context, $exten, '', new ext_goto('1', '${RC}'));
            $ext->add($context, '17', '', new ext_goto('1', 's-BUSY'));
            $ext->add($context, '18', '', new ext_goto('1', 's-NOANSWER'));
            $ext->add($context, '22', '', new ext_goto('1', 's-CHANGED'));
            $ext->add($context, '23', '', new ext_goto('1', 's-CHANGED'));
            $ext->add($context, '28', '', new ext_goto('1', 's-INVALIDNMBR'));
            $ext->add($context, '_X', '', new ext_goto('1', 'continue'));
            $ext->add($context, '_X.', '', new ext_goto('1', 'continue'));
            $exten = 'continue';
            $ext->add($context, $exten, '', new ext_gotoif('$["${OUTFAIL_${ARG1}}" = ""]', 'noreport'));
            $ext->add($context, $exten, '', new ext_agi('${OUTFAIL_${ARG1}}'));
            $ext->add($context, $exten, 'noreport', new ext_noop('TRUNK Dial failed due to ${DIALSTATUS} HANGUPCAUSE: ${HANGUPCAUSE} - failing through to other trunks'));
            $ext->add($context, $exten, '', new ext_set('CALLERID(number)', '${AMPUSER}'));
            $ext->add($context, 'disabletrunk', '', new ext_noop('TRUNK: ${OUT_${DIAL_TRUNK}} DISABLED - falling through to next trunk'));
            $ext->add($context, 'bypass', '', new ext_noop('TRUNK: ${OUT_${DIAL_TRUNK}} BYPASSING because dialout-trunk-predial-hook'));
            $ext->add($context, 'h', '', new ext_macro('hangupcall'));
            /*
             * overrides callerid out trunks
             * arg1 is trunk
             * macro-user-callerid should be called _before_ using this macro
             */
            $context = 'macro-outbound-callerid';
            $exten = 's';
            // If we modified the caller presence, set it back. This allows anonymous calls to be internally prepended but keep
            // their status if forwarded back out. Not doing this can result in the trunk CID being displayed vs. 'blocked call'
            //
            if ($ast_lt_16) {
                $ext->add($context, $exten, '', new ext_execif('$["${CALLINGPRES_SV}" != ""]', 'SetCallerPres', '${CALLINGPRES_SV}'));
            } else {
                $ext->add($context, $exten, '', new ext_execif('$["${CALLINGPRES_SV}" != ""]', 'Set', 'CALLERPRES()=${CALLINGPRES_SV}'));
            }
            // Keep the original CallerID number, for failover to the next trunk.
            $ext->add($context, $exten, '', new ext_execif('$["${REALCALLERIDNUM:1:2}" = ""]', 'Set', 'REALCALLERIDNUM=${CALLERID(number)}'));
            // If this came through a ringgroup or CF, then we want to retain original CID unless
            // OUTKEEPCID_${trunknum} is set.
            // Save then CIDNAME while it is still intact in case we end up sending out this same CID
            $ext->add($context, $exten, 'start', new ext_gotoif('$[ $["${REALCALLERIDNUM}" = ""] | $["${KEEPCID}" != "TRUE"] | $["${OUTKEEPCID_${ARG1}}" = "on"] ]', 'normcid'));
            // Set to TRUE if coming from ringgroups, CF, etc.
            $ext->add($context, $exten, '', new ext_set('USEROUTCID', '${REALCALLERIDNUM}'));
            //$ext->add($context, $exten, '', new ext_set('REALCALLERIDNAME', '${CALLERID(name)}'));
            // We now have to make sure the CID is valid. If we find an AMPUSER with the same CID, we assume it is an internal
            // call (would be quite a conincidence if not) and go through the normal processing to get that CID. If a device
            // is set for this CID, then it must be internal
            // If we end up using USEROUTCID at the end, it may still be the REALCALLERIDNUM we saved above. That is determined
            // if the two are equal, AND there is no CALLERID(name) present since it has been removed by the CALLERID(all)=${USEROUTCID}
            // setting. If this is the case, then we put the orignal name back in to send out. Although the CNAME is not honored by most
            // carriers, there are cases where it is so this preserves that information to be used by those carriers who do honor it.
            $ext->add($context, $exten, '', new ext_gotoif('$["foo${DB(AMPUSER/${REALCALLERIDNUM}/device)}" = "foo"]', 'bypass'));
            $ext->add($context, $exten, 'normcid', new ext_set('USEROUTCID', '${DB(AMPUSER/${AMPUSER}/outboundcid)}'));
            $ext->add($context, $exten, 'bypass', new ext_set('EMERGENCYCID', '${DB(DEVICE/${REALCALLERIDNUM}/emergency_cid)}'));
            $ext->add($context, $exten, '', new ext_set('TRUNKOUTCID', '${OUTCID_${ARG1}}'));
            $ext->add($context, $exten, '', new ext_gotoif('$["${EMERGENCYROUTE:1:2}" = "" | "${EMERGENCYCID:1:2}" = ""]', 'trunkcid'));
            // check EMERGENCY ROUTE
            $ext->add($context, $exten, '', new ext_set('CALLERID(all)', '${EMERGENCYCID}'));
            // emergency cid for device
            $ext->add($context, $exten, 'exit', new ext_macroexit());
            $ext->add($context, $exten, 'trunkcid', new ext_execif('$[${LEN(${TRUNKOUTCID})} != 0]', 'Set', 'CALLERID(all)=${TRUNKOUTCID}'));
            $ext->add($context, $exten, 'usercid', new ext_execif('$[${LEN(${USEROUTCID})} != 0]', 'Set', 'CALLERID(all)=${USEROUTCID}'));
            // check CID override for extension
            /* TRUNKCIDOVERRIDE is used by followme and can be used by other functions. It forces the specified CID except for the case of 
             * an Emergency CID on an Emergency Route
             */
            $ext->add($context, $exten, '', new ext_execif('$[${LEN(${TRUNKCIDOVERRIDE})} != 0 | ${LEN(${FORCEDOUTCID_${ARG1}})} != 0]', 'Set', 'CALLERID(all)=${IF($[${LEN(${FORCEDOUTCID_${ARG1}})}=0]?${TRUNKCIDOVERRIDE}:${FORCEDOUTCID_${ARG1}})}'));
            if ($ast_lt_16) {
                $ext->add($context, $exten, 'hidecid', new ext_execif('$["${CALLERID(name)}"="hidden"]', 'SetCallerPres', 'prohib_passed_screen'));
            } else {
                $ext->add($context, $exten, 'hidecid', new ext_execif('$["${CALLERID(name)}"="hidden"]', 'Set', 'CALLERPRES()=prohib_passed_screen'));
            }
            // $has_keepcid_cnum is checked and set when the globals are being generated above
            //
            if ($has_keepcid_cnum) {
                $ext->add($context, $exten, '', new ext_execif('$["${OUTKEEPCID_${ARG1}}" = "cnum"]', 'Set', 'CALLERID(name)='));
            }
            // Combined from-zpatel / from-dahdi and all macros now from-dahdi-channum
            //
            $ext->addInclude('from-zaptel', 'from-dahdi');
            $ext->add('from-zaptel', 'foo', '', new ext_noop('bar'));
            $context = 'from-dahdi';
            $exten = '_X.';
            $ext->add($context, $exten, '', new ext_set('DID', '${EXTEN}'));
            $ext->add($context, $exten, '', new ext_goto(1, 's'));
            $exten = 's';
            $ext->add($context, $exten, '', new ext_noop('Entering from-dahdi with DID == ${DID}'));
            // Some trunks _require_ a RINGING be sent before an Answer.
            $ext->add($context, $exten, '', new ext_ringing());
            // If ($did == "") { $did = "s"; }
            $ext->add($context, $exten, '', new ext_set('DID', '${IF($["${DID}"= ""]?s:${DID})}'));
            $ext->add($context, $exten, '', new ext_noop('DID is now ${DID}'));
            $ext->add($context, $exten, '', new ext_gotoif('$["${CHANNEL:0:5}"="DAHDI"]', 'dahdiok', 'checkzap'));
            $ext->add($context, $exten, 'checkzap', new ext_gotoif('$["${CHANNEL:0:3}"="Zap"]', 'zapok', 'neither'));
            $ext->add($context, $exten, 'neither', new ext_goto('1', '${DID}', 'from-pstn'));
            // If there's no ext-did,s,1, that means there's not a no did/no cid route. Hangup.
            $ext->add($context, $exten, '', new ext_macro('Hangupcall', 'dummy'));
            $ext->add($context, $exten, 'dahdiok', new ext_noop('Is a DAHDI Channel'));
            $ext->add($context, $exten, '', new ext_set('CHAN', '${CHANNEL:6}'));
            $ext->add($context, $exten, '', new ext_set('CHAN', '${CUT(CHAN,-,1)}'));
            $ext->add($context, $exten, '', new ext_macro('from-dahdi-${CHAN}', '${DID},1'));
            // If nothing there, then treat it as a DID
            $ext->add($context, $exten, '', new ext_noop('Returned from Macro from-dahdi-${CHAN}'));
            $ext->add($context, $exten, '', new ext_goto(1, '${DID}', 'from-pstn'));
            $ext->add($context, $exten, 'zapok', new ext_noop('Is a Zaptel Channel'));
            $ext->add($context, $exten, '', new ext_set('CHAN', '${CHANNEL:4}'));
            $ext->add($context, $exten, '', new ext_set('CHAN', '${CUT(CHAN,-,1)}'));
            $ext->add($context, $exten, '', new ext_macro('from-dahdi-${CHAN}', '${DID},1'));
            $ext->add($context, $exten, '', new ext_noop('Returned from Macro from-dahdi-${CHAN}'));
            $ext->add($context, $exten, '', new ext_goto(1, '${DID}', 'from-pstn'));
            /*
             * vm-callme context plays voicemail over telephone for web click-to-call
             * MSG and MBOX are channel variables that must be set when originating the call
             */
            $context = 'vm-callme';
            $ext->add($context, 's', '', new ext_answer());
            $ext->add($context, 's', '', new ext_wait(1));
            $ext->add($context, 's', 'repeat', new ext_background('${MSG}&silence/2&vm-repeat&vm-starmain'));
            $ext->add($context, 's', '', new ext_waitexten(15));
            $ext->add($context, '5', '', new ext_goto('repeat', 's'));
            $ext->add($context, '#', '', new ext_playback('vm-goodbye'));
            $ext->add($context, '#', '', new ext_hangup());
            $ext->add($context, '*', '', new ext_macro('get-vmcontext', '${MBOX}'));
            $ext->add($context, '*', '', new ext_vmmain('${MBOX}@${VMCONTEXT},s'));
            $ext->add($context, 'i', '', new ext_playback('pm-invalid-option'));
            $ext->add($context, 'i', '', new ext_goto('repeat', 's'));
            $ext->add($context, 't', '', new ext_playback('vm-goodbye'));
            $ext->add($context, 't', '', new ext_hangup());
            $ext->add($context, 'h', '', new ext_hangup());
            /* end vm-callme context  */
            /*
             * macro-vm 
             */
            /*
                                    ;------------------------------------------------------------------------
                                    ; [macro-vm]
                                    ;------------------------------------------------------------------------
                                    ; CONTEXT:      macro-vm
                                    ; PURPOSE:      call voicemail system and extend with personal ivr
                                    ;
                                    ; Under normal use, this macro will call the voicemail system with the extension and
                                    ; desired greeting mode of busy, unavailable or as specified with direct voicemail
                                    ; calls (usually unavailable) when entered from destinations.
                                    ;
                                    ; The voicemail system's two greetings have been 'hijacked' as follows to extend the
                                    ; system by giving the option of a private 'ivr' for each voicemail user. The following
                                    ; applies to both the busy and unavailable modes of voicemail and can be applied to one
                                    ; or both, and differently.
                                    ;
                                    ; Global Defaults:
                                    ;
                                    ; The following are default values, used in both busy and unavail modes if no specific
                                    ; values are specified.
                                    ;
                                    ; VMX_REPEAT
                                    ;                                       The number of times to repeat the users message if no option is pressed.
                                    ; VMX_TIMEOUT
                                    ;                                       The timeout to wait after playing message before repeating or giving up.
                                    ; VMX_LOOPS
                                    ;                                       The number of times it should replay the message and check for an option when
                                    ;                                       an invalid option is pressed.
                                    ;
                                    ; VMX_OPTS_DOVM
                                    ;                                       Default voicemail option to use if vm is chosen as an option. No options will
                                    ;                                       cause Allison's generic message, 's' will go straight to beep.
                                    ; VMX_OPTS_TIMEOUT
                                    ;                                       Default voicemail option to use if it times out with no options. No options will
                                    ;                                       cause Allison's generic message, 's' will go straight to beep.
                                    ;                                       IF THE USER PRESSES # - it will look like a timeout as well since no option will
                                    ;                                       be presented. If the user wishes to enable a mode where a caller can press #
                                    ;                                       during their message and it goes straight to voicemail with only a 'beep' then
                                    ;                                       this should be set to 's'.
                                    ; VMX_OPTS_LOOPS
                                    ;                                       Default voicemail option to use if to many wrong options occur. No options will
                                    ;                                       cause Allison's generic message, 's' will go straight to beep.
                                    ;
                                    ; VMX_CONTEXT
                                    ;                                       Default context for user destinations if not supplied in the user's settings
                                    ; VMX_PRI
                                    ;                                       Default priority for user destinations if not supplied in the user's settings
                                    ;
                                    ; VMX_TIMEDEST_CONTEXT
                                    ;                                       Default context for timeout destination if not supplied in the user's settings
                                    ; VMX_TIMEDEST_EXT
                                    ;                                       Default extension for timeout destination if not supplied in the user's settings
                                    ; VMX_TIMEDEST_PRI
                                    ;                                       Default priority for timeout destination if not supplied in the user's settings
                                    ;
                                    ; VMX_LOOPDEST_CONTEXT
                                    ;                                       Default context for loops  destination if not supplied in the user's settings
                                    ; VMX_LOOPDEST_EXT
                                    ;                                       Default extension for loops  destination if not supplied in the user's settings
                                    ; VMX_LOOPDEST_PRI
                                    ;                                       Default priority for loops  destination if not supplied in the user's settings
                                    ;
                                    ;
                                    ; The AMPUSER database variable has been extended with a 'vmx' tree (vm-extension). A
                                    ; duplicate set is included for both unavail and busy. You could choose for to have an
                                    ; ivr when unavail is taken, but not with busy - or a different once with busy.
                                    ; The full list is below, each specific entry is futher described:
                                    ;
                                    ; state:                Whether teh current mode is enabled or disabled. Anything but 'enabled' is
                                    ;                                               treated as disabled.
                                    ; repeat:               This is the number of times that the users message should be played after the
                                    ;                                               timeout if the user has not entered anything. It is just a variable to the
                                    ;                                               Read() function which will do the repeating.
                                    ; timeout:      This is how long to wait after the message has been read for a response from
                                    ;                                               the user. A caller can enter a digit any time during the playback.
                                    ; loops:                This is the number of loops that the system will allow a caller to retry if
                                    ;                                               they enter a bad menu choice, before going to the loop failover destination
                                    ; vmxopts:      This is the vm options to send to the voicemail command used when a specific
                                    ;                                               voicemail destination is chosen (inidcated by 'dovm' in the ext field). This is
                                    ;                                               typically either set to 's' or left blank. When set to 's' there will be no
                                    ;                                               message played when entering the voicemail, just a beep. When blank, you will
                                    ;                                               have Allison's generic message played. It is not typical to play the greetings
                                    ;                                               since they have been 'hijacked' for these IVR's and from a caller's perspecitive
                                    ;                                               this system appears interconnected with the voicemail so instructions can be
                                    ;                                               left there.
                                    ; timedest: The three variables: ext, context and pri are the goto destination if the caller
                                    ;                                               enters no options and it timesout. None have to be set and a system default
                                    ;                                               will be used. If just ext is set, then defaults will be used for context and
                                    ;                                               pri, etc.
                                    ; loopdest:     This is identical to timedest but used if the caller exceeds the maximum invalid
                                    ;                                               menu choices.
                                    ; [0-9*]:               The user can specify up to 11 ivr options, all as single digits from 0-9 or *. The
                                    ;                                               # key can not be used since it is used as a terminator key for the Read command
                                    ;                                               and will never be returned. A minimum of the ext must be specified for each valid
                                    ;                                               option and as above, the context and priority can also be specified if the default
                                    ;                                               is not to be used.
                                    ;                                               Option '0' takes on a special meaning. Since a user is able to break out of the
                                    ;                                               voicemail command once entering it with a 0, if specified, the 0 destination will
                                    ;                                               be used.
                                    ;                                               Option '*' can also be used to breakout. It is undecided at this point whether
                                    ;                                               providing that option will be used as well. (probably should).
                                    ;
                                    ;
                                    ; /AMPUSER/<ext>/vmx/[busy|unavail]/state:                                                              enabled|disabled
                                    ; /AMPUSER/<ext>/vmx/[busy|unavail]/repeat:                                                             n (times to repeat message)
                                    ; /AMPUSER/<ext>/vmx/[busy|unavail]/timeout:                                                    n (timeout to wait for digit)
                                    ; /AMPUSER/<ext>/vmx/[busy|unavail]/loops:                                                              n (loop returies for invalid entries)
                                    ; /AMPUSER/<ext>/vmx/[busy|unavail]/vmxopts/dovm:                                       vmoptions (if ext is dovm)
                                    ; /AMPUSER/<ext>/vmx/[busy|unavail]/vmxopts/timeout:                    vmoptions (if timeout)
                                    ; /AMPUSER/<ext>/vmx/[busy|unavail]/vmxopts/loops:                              vmoptions (if loops)
                                    ; /AMPUSER/<ext>/vmx/[busy|unavail]/timedest/ext:                                       extension (if timeout)
                                    ; /AMPUSER/<ext>/vmx/[busy|unavail]/timedest/context:                   context (if timeout)
                                    ; /AMPUSER/<ext>/vmx/[busy|unavail]/timedest/pri:                                       priority (if timeout)
                                    ; /AMPUSER/<ext>/vmx/[busy|unavail]/loopdest/ext:                                       extension (if too many failures)
                                    ; /AMPUSER/<ext>/vmx/[busy|unavail]/loopdest/context:                   context (if too many failures)
                                    ; /AMPUSER/<ext>/vmx/[busy|unavail]/loopdest/pri:                                       priority (if too many failures)
                                    ; /AMPUSER/<ext>/vmx/[busy|unavail]/[0-9*]/ext:                                         extension (dovm for vm access)
                                    ; /AMPUSER/<ext>/vmx/[busy|unavail]/[0-9*]/context:                             context
                                    ; /AMPUSER/<ext>/vmx/[busy|unavail]/[0-9*]/pri:                                         priority
                                    ;------------------------------------------------------------------------
            */
            // ARG1 - extension
            // ARG2 - DIRECTDIAL/BUSY
            // ARG3 - RETURN makes macro return, otherwise hangup
            //
            $ext->add('macro-vm', 's', '', new ext_macro('user-callerid', 'SKIPTTL'));
            $ext->add('macro-vm', 's', '', new ext_setvar("VMGAIN", '${IF($["foo${VM_GAIN}"!="foo"]?"g(${VM_GAIN})":"")}'));
            // If BLKVM_OVERRIDE is set, then someone told us to block calls from going to
            // voicemail. This variable is reset by the answering channel so subsequent
            // transfers will properly function.
            //
            $ext->add('macro-vm', 's', '', new ext_gotoif('$["foo${DB(${BLKVM_OVERRIDE})}" != "fooTRUE"]', 'vmx,1'));
            // we didn't branch so block this from voicemail
            //
            $ext->add('macro-vm', 's', '', new ext_NoOp('CAME FROM: ${NODEST} - Blocking VM cause of key: ${DB(BLKVM_OVERRIDE)}'));
            $ext->add('macro-vm', 's', '', new ext_hangup(''));
            // If vmx not enabled for the current mode,then jump to normal voicemail behavior
            // also - if not message (no-msg) is requested, straight to voicemail
            //
            $ext->add('macro-vm', 'vmx', '', new ext_setvar("MEXTEN", '${ARG1}'));
            $ext->add('macro-vm', 'vmx', '', new ext_setvar("MMODE", '${ARG2}'));
            $ext->add('macro-vm', 'vmx', '', new ext_setvar("RETVM", '${ARG3}'));
            $ext->add('macro-vm', 'vmx', '', new ext_setvar("MODE", '${IF($["${MMODE}"="BUSY"]?busy:unavail)}'));
            // If this use has individual option set for playing standardized message, then override the global option
            //
            $ext->add('macro-vm', 'vmx', '', new ext_gotoif('$["${DB_EXISTS(AMPUSER/${MEXTEN}/vmx/${MODE}/vmxopts/timeout)}" = "0"]', 'chknomsg'));
            $ext->add('macro-vm', 'vmx', '', new ext_setvar("VM_OPTS", '${DB_RESULT}'));
            $ext->add('macro-vm', 'vmx', 'chknomsg', new ext_gotoif('$["${MMODE}"="NOMESSAGE"]', 's-${MMODE},1'));
            $ext->add('macro-vm', 'vmx', '', new ext_gotoif('$["${MMODE}" != "DIRECTDIAL"]', 'notdirect'));
            $ext->add('macro-vm', 'vmx', '', new ext_setvar("MODE", '${IF($["${REGEX("[b]" ${VM_DDTYPE})}" = "1"]?busy:${MODE})}'));
            $ext->add('macro-vm', 'vmx', 'notdirect', new ext_NoOp('Checking if ext ${MEXTEN} is enabled: ${DB(AMPUSER/${MEXTEN}/vmx/${MODE}/state)}'));
            $ext->add('macro-vm', 'vmx', '', new ext_gotoif('$["${DB(AMPUSER/${MEXTEN}/vmx/${MODE}/state)}" != "enabled"]', 's-${MMODE},1'));
            // If the required voicemail file does not exist, then abort and go to normal voicemail behavior
            //
            // If 1.4 or above, use the STAT function to check for the file. Prior to 1.4, use the AGI script since the System() command tried
            // in the past had errors.
            //
            $ext->add('macro-vm', 'vmx', '', new ext_macro('get-vmcontext', '${MEXTEN}'));
            //$ext->add('macro-vm', 'vmx', '', new ext_trysystem('/bin/ls ${ASTSPOOLDIR}/voicemail/${VMCONTEXT}/${MEXTEN}/${MODE}.[wW][aA][vV]'));
            if ($ast_ge_14) {
                $ext->add('macro-vm', 'vmx', '', new ext_gotoif('$[(${STAT(f,${ASTSPOOLDIR}/voicemail/${VMCONTEXT}/${MEXTEN}/temp.wav)} = 1) || (${STAT(f,${ASTSPOOLDIR}/voicemail/${VMCONTEXT}/${MEXTEN}/temp.WAV)} = 1)]', 'tmpgreet'));
                $ext->add('macro-vm', 'vmx', '', new ext_gotoif('$[(${STAT(f,${ASTSPOOLDIR}/voicemail/${VMCONTEXT}/${MEXTEN}/${MODE}.wav)} = 0) && (${STAT(f,${ASTSPOOLDIR}/voicemail/${VMCONTEXT}/${MEXTEN}/${MODE}.WAV)} = 0)]', 'nofile'));
            } else {
                $ext->add('macro-vm', 'vmx', '', new ext_agi('checksound.agi,${ASTSPOOLDIR}/voicemail/${VMCONTEXT}/${MEXTEN}/temp'));
                $ext->add('macro-vm', 'vmx', '', new ext_gotoif('$["${SYSTEMSTATUS}" = "SUCCESS"]', 'tmpgreet'));
                $ext->add('macro-vm', 'vmx', '', new ext_agi('checksound.agi,${ASTSPOOLDIR}/voicemail/${VMCONTEXT}/${MEXTEN}/${MODE}'));
                $ext->add('macro-vm', 'vmx', '', new ext_gotoif('$["${SYSTEMSTATUS}" != "SUCCESS"]', 'nofile'));
            }
            $ext->add('macro-vm', 'vmx', '', new ext_setvar("LOOPCOUNT", '0'));
            $ext->add('macro-vm', 'vmx', '', new ext_gotoif('$["${DB_EXISTS(AMPUSER/${MEXTEN}/vmx/${MODE}/repeat)}" = "0"]', 'vmxtime'));
            $ext->add('macro-vm', 'vmx', '', new ext_setvar("VMX_REPEAT", '${DB_RESULT}'));
            $ext->add('macro-vm', 'vmx', 'vmxtime', new ext_gotoif('$["${DB_EXISTS(AMPUSER/${MEXTEN}/vmx/${MODE}/timeout)}" = "0"]', 'vmxloops'));
            $ext->add('macro-vm', 'vmx', '', new ext_setvar("VMX_TIMEOUT", '${DB_RESULT}'));
            $ext->add('macro-vm', 'vmx', 'vmxloops', new ext_gotoif('$["${DB_EXISTS(AMPUSER/${MEXTEN}/vmx/${MODE}/loops)}" = "0"]', 'vmxanswer'));
            $ext->add('macro-vm', 'vmx', '', new ext_setvar("VMX_LOOPS", '${DB_RESULT}'));
            $ext->add('macro-vm', 'vmx', 'vmxanswer', new ext_answer(''));
            // Now play the users voicemail recording as the basis for their ivr, the Read command will repeat as needed and if it timesout
            // then we go to the timeout. Otherwise handle invalid options by looping until the limit until a valid option is played.
            //
            $ext->add('macro-vm', 'vmx', 'loopstart', new ext_read('ACTION', '${ASTSPOOLDIR}/voicemail/${VMCONTEXT}/${MEXTEN}/${MODE}', 1, 'skip', '${VMX_REPEAT}', '${VMX_TIMEOUT}'));
            $ext->add('macro-vm', 'vmx', '', new ext_gotoif('$["${EXISTS(${ACTION})}" = "1"]', 'checkopt'));
            // If we are here we timed out, go to the required destination
            //
            $ext->add('macro-vm', 'vmx', 'noopt', new ext_NoOp('Timeout: going to timeout dest'));
            $ext->add('macro-vm', 'vmx', '', new ext_setvar("VMX_OPTS", '${VMX_OPTS_TIMEOUT}'));
            $ext->add('macro-vm', 'vmx', '', new ext_gotoif('$["${DB_EXISTS(AMPUSER/${MEXTEN}/vmx/${MODE}/vmxopts/timeout)}" = "0"]', 'chktime'));
            $ext->add('macro-vm', 'vmx', '', new ext_setvar("VMX_OPTS", '${DB_RESULT}'));
            $ext->add('macro-vm', 'vmx', 'chktime', new ext_gotoif('$["${DB_EXISTS(AMPUSER/${MEXTEN}/vmx/${MODE}/timedest/ext)}" = "0"]', 'dotime'));
            $ext->add('macro-vm', 'vmx', '', new ext_setvar("VMX_TIMEDEST_EXT", '${DB_RESULT}'));
            $ext->add('macro-vm', 'vmx', '', new ext_gotoif('$["${DB_EXISTS(AMPUSER/${MEXTEN}/vmx/${MODE}/timedest/context)}" = "0"]', 'timepri'));
            $ext->add('macro-vm', 'vmx', '', new ext_setvar("VMX_TIMEDEST_CONTEXT", '${DB_RESULT}'));
            $ext->add('macro-vm', 'vmx', 'timepri', new ext_gotoif('$["${DB_EXISTS(AMPUSER/${MEXTEN}/vmx/${MODE}/timedest/pri)}" = "0"]', 'dotime'));
            $ext->add('macro-vm', 'vmx', '', new ext_setvar("VMX_TIMEDEST_PRI", '${DB_RESULT}'));
            $ext->add('macro-vm', 'vmx', 'dotime', new ext_goto('${VMX_TIMEDEST_PRI}', '${VMX_TIMEDEST_EXT}', '${VMX_TIMEDEST_CONTEXT}'));
            // We got an option, check if the option is defined, or one of the system defaults
            //
            $ext->add('macro-vm', 'vmx', 'checkopt', new ext_gotoif('$["${DB_EXISTS(AMPUSER/${MEXTEN}/vmx/${MODE}/${ACTION}/ext)}" = "1"]', 'doopt'));
            $ext->add('macro-vm', 'vmx', '', new ext_gotoif('$["${ACTION}" = "0"]', 'o,1'));
            $ext->add('macro-vm', 'vmx', '', new ext_gotoif('$["${ACTION}" = "*"]', 'adef,1'));
            // Got invalid option loop until the max
            //
            $ext->add('macro-vm', 'vmx', '', new ext_setvar("LOOPCOUNT", '$[${LOOPCOUNT} + 1]'));
            $ext->add('macro-vm', 'vmx', '', new ext_gotoif('$[${LOOPCOUNT} > ${VMX_LOOPS}]', 'toomany'));
            $ext->add('macro-vm', 'vmx', '', new ext_playback('pm-invalid-option&please-try-again'));
            $ext->add('macro-vm', 'vmx', '', new ext_goto('loopstart'));
            // tomany: to many invalid options, go to the specified destination
            //
            $ext->add('macro-vm', 'vmx', 'toomany', new ext_NoOp('Too Many invalid entries, got to invalid dest'));
            $ext->add('macro-vm', 'vmx', '', new ext_setvar("VMX_OPTS", '${VMX_OPTS_LOOPS}'));
            $ext->add('macro-vm', 'vmx', '', new ext_gotoif('$["${DB_EXISTS(AMPUSER/${MEXTEN}/vmx/${MODE}/vmxopts/loops)}" = "0"]', 'chkloop'));
            $ext->add('macro-vm', 'vmx', '', new ext_setvar("VMX_OPTS", '${DB_RESULT}'));
            $ext->add('macro-vm', 'vmx', 'chkloop', new ext_gotoif('$["${DB_EXISTS(AMPUSER/${MEXTEN}/vmx/${MODE}/loopdest/ext)}" = "0"]', 'doloop'));
            $ext->add('macro-vm', 'vmx', '', new ext_setvar("VMX_LOOPDEST_EXT", '${DB_RESULT}'));
            $ext->add('macro-vm', 'vmx', '', new ext_gotoif('$["${DB_EXISTS(AMPUSER/${MEXTEN}/vmx/${MODE}/loopdest/context)}" = "0"]', 'looppri'));
            $ext->add('macro-vm', 'vmx', '', new ext_setvar("VMX_LOOPDEST_CONTEXT", '${DB_RESULT}'));
            $ext->add('macro-vm', 'vmx', 'looppri', new ext_gotoif('$["${DB_EXISTS(AMPUSER/${MEXTEN}/vmx/${MODE}/loopdest/pri)}" = "0"]', 'doloop'));
            $ext->add('macro-vm', 'vmx', '', new ext_setvar("VMX_LOOPDEST_PRI", '${DB_RESULT}'));
            $ext->add('macro-vm', 'vmx', 'doloop', new ext_goto('${VMX_LOOPDEST_PRI}', '${VMX_LOOPDEST_EXT}', '${VMX_LOOPDEST_CONTEXT}'));
            // doopt: execute the valid option that was chosen
            //
            $ext->add('macro-vm', 'vmx', 'doopt', new ext_NoOp('Got a valid option: ${DB_RESULT}'));
            $ext->add('macro-vm', 'vmx', '', new ext_setvar("VMX_EXT", '${DB_RESULT}'));
            // Special case, if this option was to go to voicemail, set options and go
            //
            $ext->add('macro-vm', 'vmx', '', new ext_gotoif('$["${VMX_EXT}" != "dovm"]', 'getdest'));
            $ext->add('macro-vm', 'vmx', 'vmxopts', new ext_setvar("VMX_OPTS", '${VMX_OPTS_DOVM}'));
            $ext->add('macro-vm', 'vmx', '', new ext_gotoif('$["${DB_EXISTS(AMPUSER/${MEXTEN}/vmx/${MODE}/vmxopts/dovm)}" = "0"]', 'vmxdovm'));
            $ext->add('macro-vm', 'vmx', 'vmxopts', new ext_setvar("VMX_OPTS", '${DB_RESULT}'));
            $ext->add('macro-vm', 'vmx', 'vmxdovm', new ext_goto('1', 'dovm'));
            // General case, setup the goto destination and go there (no error checking, its up to the GUI's to assure
            // reasonable values
            //
            $ext->add('macro-vm', 'vmx', 'getdest', new ext_gotoif('$["${DB_EXISTS(AMPUSER/${MEXTEN}/vmx/${MODE}/${ACTION}/context)}" = "0"]', 'vmxpri'));
            $ext->add('macro-vm', 'vmx', '', new ext_setvar("VMX_CONTEXT", '${DB_RESULT}'));
            $ext->add('macro-vm', 'vmx', 'vmxpri', new ext_gotoif('$["${DB_EXISTS(AMPUSER/${MEXTEN}/vmx/${MODE}/${ACTION}/pri)}" = "0"]', 'vmxgoto'));
            $ext->add('macro-vm', 'vmx', '', new ext_setvar("VMX_PRI", '${DB_RESULT}'));
            $ext->add('macro-vm', 'vmx', 'vmxgoto', new ext_goto('${VMX_PRI}', '${VMX_EXT}', '${VMX_CONTEXT}'));
            // If the required voicemail file is not present, then revert to normal voicemail
            // behavior treating as if it was not set
            //
            $ext->add('macro-vm', 'vmx', 'nofile', new ext_NoOp('File for mode: ${MODE} does not exist, SYSTEMSTATUS: ${SYSTEMSTATUS}, going to normal voicemail'));
            $ext->add('macro-vm', 'vmx', '', new ext_goto('1', 's-${MMODE}'));
            $ext->add('macro-vm', 'vmx', 'tmpgreet', new ext_NoOp('Temporary Greeting Detected, going to normal voicemail'));
            $ext->add('macro-vm', 'vmx', '', new ext_goto('1', 's-${MMODE}'));
            // Drop into voicemail either as a direct destination (in which case VMX_OPTS might be set to something) or
            // if the user timed out or broke out of the loop then VMX_OPTS is always cleared such that an Allison
            // message is played and the caller know's what is going on.
            //
            $ext->add('macro-vm', 'dovm', '', new ext_NoOp('VMX Timeout - go to voicemail'));
            $ext->add('macro-vm', 'dovm', '', new ext_vm('${MEXTEN}@${VMCONTEXT},${VMX_OPTS}${VMGAIN}'));
            $ext->add('macro-vm', 'dovm', '', new ext_goto('1', 'exit-${VMSTATUS}'));
            $ext->add('macro-vm', 's-BUSY', '', new ext_NoOp('BUSY voicemail'));
            $ext->add('macro-vm', 's-BUSY', '', new ext_macro('get-vmcontext', '${MEXTEN}'));
            $ext->add('macro-vm', 's-BUSY', '', new ext_vm('${MEXTEN}@${VMCONTEXT},${VM_OPTS}b${VMGAIN}'));
            $ext->add('macro-vm', 's-BUSY', '', new ext_goto('1', 'exit-${VMSTATUS}'));
            $ext->add('macro-vm', 's-NOMESSAGE', '', new ext_NoOp('NOMESSAGE (beeb only) voicemail'));
            $ext->add('macro-vm', 's-NOMESSAGE', '', new ext_macro('get-vmcontext', '${MEXTEN}'));
            $ext->add('macro-vm', 's-NOMESSAGE', '', new ext_vm('${MEXTEN}@${VMCONTEXT},s${VM_OPTS}${VMGAIN}'));
            $ext->add('macro-vm', 's-NOMESSAGE', '', new ext_goto('1', 'exit-${VMSTATUS}'));
            $ext->add('macro-vm', 's-DIRECTDIAL', '', new ext_NoOp('DIRECTDIAL voicemail'));
            $ext->add('macro-vm', 's-DIRECTDIAL', '', new ext_macro('get-vmcontext', '${MEXTEN}'));
            $ext->add('macro-vm', 's-DIRECTDIAL', '', new ext_vm('${MEXTEN}@${VMCONTEXT},${VM_OPTS}${VM_DDTYPE}${VMGAIN}'));
            $ext->add('macro-vm', 's-DIRECTDIAL', '', new ext_goto('1', 'exit-${VMSTATUS}'));
            $ext->add('macro-vm', '_s-.', '', new ext_macro('get-vmcontext', '${MEXTEN}'));
            $ext->add('macro-vm', '_s-.', '', new ext_vm('${MEXTEN}@${VMCONTEXT},${VM_OPTS}u${VMGAIN}'));
            $ext->add('macro-vm', '_s-.', '', new ext_goto('1', 'exit-${VMSTATUS}'));
            // If the user has a 0 option defined, use that for operator zero-out from within voicemail
            // as well to keep it consistant with the menu structure
            //
            $ext->add('macro-vm', 'o', '', new ext_playback('one-moment-please'));
            $ext->add('macro-vm', 'o', '', new ext_gotoif('$["${DB_EXISTS(AMPUSER/${MEXTEN}/vmx/${MODE}/0/ext)}" = "0"]', 'doopdef'));
            $ext->add('macro-vm', 'o', '', new ext_setvar("VMX_OPDEST_EXT", '${DB_RESULT}'));
            $ext->add('macro-vm', 'o', '', new ext_gotoif('$["${DB_EXISTS(AMPUSER/${MEXTEN}/vmx/${MODE}/0/context)}" = "1"]', 'opcontext'));
            $ext->add('macro-vm', 'o', '', new ext_setvar("DB_RESULT", '${VMX_CONTEXT}'));
            $ext->add('macro-vm', 'o', 'opcontext', new ext_setvar("VMX_OPDEST_CONTEXT", '${DB_RESULT}'));
            $ext->add('macro-vm', 'o', '', new ext_gotoif('$["${DB_EXISTS(AMPUSER/${MEXTEN}/vmx/${MODE}/0/pri)}" = "1"]', 'oppri'));
            $ext->add('macro-vm', 'o', '', new ext_setvar("DB_RESULT", '${VMX_PRI}'));
            $ext->add('macro-vm', 'o', 'oppri', new ext_setvar("VMX_OPDEST_PRI", '${DB_RESULT}'));
            $ext->add('macro-vm', 'o', '', new ext_goto('${VMX_OPDEST_PRI}', '${VMX_OPDEST_EXT}', '${VMX_OPDEST_CONTEXT}'));
            $ext->add('macro-vm', 'o', 'doopdef', new ext_gotoif('$["x${OPERATOR_XTN}"="x"]', 'nooper', 'from-internal,${OPERATOR_XTN},1'));
            $ext->add('macro-vm', 'o', 'nooper', new ext_gotoif('$["x${FROM_DID}"="x"]', 'nodid'));
            $ext->add('macro-vm', 'o', '', new ext_dial('Local/${FROM_DID}@from-pstn', ''));
            $ext->add('macro-vm', 'o', '', new ext_macro('hangup'));
            $ext->add('macro-vm', 'o', 'nodid', new ext_dial('Local/s@from-pstn', ''));
            $ext->add('macro-vm', 'o', '', new ext_macro('hangup'));
            // If the user has a * option defined, use that for the * out from within voicemail
            // as well to keep it consistant with the menu structure
            //
            $ext->add('macro-vm', 'a', '', new ext_macro('get-vmcontext', '${MEXTEN}'));
            $ext->add('macro-vm', 'a', '', new ext_gotoif('$["${DB_EXISTS(AMPUSER/${MEXTEN}/vmx/${MODE}/*/ext)}" = "0"]', 'adef,1'));
            $ext->add('macro-vm', 'a', '', new ext_setvar("VMX_ADEST_EXT", '${DB_RESULT}'));
            $ext->add('macro-vm', 'a', '', new ext_gotoif('$["${DB_EXISTS(AMPUSER/${MEXTEN}/vmx/${MODE}/*/context)}" = "1"]', 'acontext'));
            $ext->add('macro-vm', 'a', '', new ext_setvar("DB_RESULT", '${VMX_CONTEXT}'));
            $ext->add('macro-vm', 'a', 'acontext', new ext_setvar("VMX_ADEST_CONTEXT", '${DB_RESULT}'));
            $ext->add('macro-vm', 'a', '', new ext_gotoif('$["${DB_EXISTS(AMPUSER/${MEXTEN}/vmx/${MODE}/*/pri)}" = "1"]', 'apri'));
            $ext->add('macro-vm', 'a', '', new ext_setvar("DB_RESULT", '${VMX_PRI}'));
            $ext->add('macro-vm', 'a', 'apri', new ext_setvar("VMX_ADEST_PRI", '${DB_RESULT}'));
            $ext->add('macro-vm', 'a', '', new ext_goto('${VMX_ADEST_PRI}', '${VMX_ADEST_EXT}', '${VMX_ADEST_CONTEXT}'));
            $ext->add('macro-vm', 'adef', '', new ext_vmmain('${MEXTEN}@${VMCONTEXT}'));
            $ext->add('macro-vm', 'adef', '', new ext_gotoif('$["${RETVM}" = "RETURN"]', 'exit-RETURN,1'));
            $ext->add('macro-vm', 'adef', '', new ext_hangup(''));
            $ext->add('macro-vm', 'exit-FAILED', '', new ext_playback('im-sorry&an-error-has-occured'));
            $ext->add('macro-vm', 'exit-FAILED', '', new ext_gotoif('$["${RETVM}" = "RETURN"]', 'exit-RETURN,1'));
            $ext->add('macro-vm', 'exit-FAILED', '', new ext_hangup(''));
            $ext->add('macro-vm', 'exit-SUCCESS', '', new ext_gotoif('$["${RETVM}" = "RETURN"]', 'exit-RETURN,1'));
            $ext->add('macro-vm', 'exit-SUCCESS', '', new ext_playback('goodbye'));
            $ext->add('macro-vm', 'exit-SUCCESS', '', new ext_hangup(''));
            $ext->add('macro-vm', 'exit-USEREXIT', '', new ext_gotoif('$["${RETVM}" = "RETURN"]', 'exit-RETURN,1'));
            $ext->add('macro-vm', 'exit-USEREXIT', '', new ext_playback('goodbye'));
            $ext->add('macro-vm', 'exit-USEREXIT', '', new ext_hangup(''));
            $ext->add('macro-vm', 'exit-RETURN', '', new ext_noop('Returning From Voicemail because macro'));
            $ext->add('macro-vm', 't', '', new ext_hangup(''));
            /* end macro-vm  */
            $mcontext = 'macro-exten-vm';
            $exten = 's';
            $ext->add($mcontext, $exten, '', new ext_macro('user-callerid'));
            $ext->add($mcontext, $exten, '', new ext_set("RingGroupMethod", 'none'));
            $ext->add($mcontext, $exten, '', new ext_set("VMBOX", '${ARG1}'));
            $ext->add($mcontext, $exten, '', new ext_set("__EXTTOCALL", '${ARG2}'));
            $ext->add($mcontext, $exten, '', new ext_set("CFUEXT", '${DB(CFU/${EXTTOCALL})}'));
            $ext->add($mcontext, $exten, '', new ext_set("CFBEXT", '${DB(CFB/${EXTTOCALL})}'));
            $ext->add($mcontext, $exten, '', new ext_set("RT", '${IF($[$["${VMBOX}"!="novm"] | $["${CFUEXT}"!=""]]?${RINGTIMER}:"")}'));
            $ext->add($mcontext, $exten, 'checkrecord', new ext_macro('record-enable', '${EXTTOCALL},IN'));
            if ($has_extension_state) {
                $ext->add($mcontext, $exten, 'macrodial', new ext_macro('dial-one', '${RT},${DIAL_OPTIONS},${EXTTOCALL}'));
            } else {
                $ext->add($mcontext, $exten, 'macrodial', new ext_macro('dial', '${RT},${DIAL_OPTIONS},${EXTTOCALL}'));
            }
            $ext->add($mcontext, $exten, '', new ext_gotoif('$["${VMBOX}"!="novm" & "${SCREEN}"!="" & "${DIALSTATUS}"="NOANSWER"]', 'exit'));
            $ext->add($mcontext, $exten, '', new ext_set("SV_DIALSTATUS", '${DIALSTATUS}'));
            $ext->add($mcontext, $exten, 'calldocfu', new ext_gosubif('$[("${SV_DIALSTATUS}"="NOANSWER"|"${SV_DIALSTATUS}"="CHANUNAVAIL") & "${CFUEXT}"!="" & "${SCREEN}"=""]', 'docfu,1'));
            $ext->add($mcontext, $exten, 'calldocfb', new ext_gosubif('$["${SV_DIALSTATUS}"="BUSY" & "${CFBEXT}"!=""]', 'docfb,1'));
            $ext->add($mcontext, $exten, '', new ext_set("DIALSTATUS", '${SV_DIALSTATUS}'));
            $ext->add($mcontext, $exten, '', new ext_noop('Voicemail is \'${VMBOX}\''));
            $ext->add($mcontext, $exten, '', new ext_gotoif('$["${VMBOX}"="novm"]', 's-${DIALSTATUS},1'));
            $ext->add($mcontext, $exten, '', new ext_noop('Sending to Voicemail box ${EXTTOCALL}'));
            $ext->add($mcontext, $exten, '', new ext_macro('vm', '${VMBOX},${DIALSTATUS},${IVR_RETVM}'));
            $exten = 'docfu';
            $ext->add($mcontext, $exten, 'docfu', new ext_set("RTCFU", '${IF($["${VMBOX}"!="novm"]?${RINGTIMER}:"")}'));
            $ext->add($mcontext, $exten, '', new ext_dial('Local/${CFUEXT}@from-internal/n', '${RTCFU},${DIAL_OPTIONS}'));
            $ext->add($mcontext, $exten, '', new ext_return(''));
            $exten = 'docfb';
            $ext->add($mcontext, $exten, 'docfb', new ext_set("RTCFB", '${IF($["${VMBOX}"!="novm"]?${RINGTIMER}:"")}'));
            $ext->add($mcontext, $exten, '', new ext_dial('Local/${CFBEXT}@from-internal/n', '${RTCFB},${DIAL_OPTIONS}'));
            $ext->add($mcontext, $exten, '', new ext_return(''));
            $exten = 's-BUSY';
            $ext->add($mcontext, $exten, '', new ext_noop('Extension is reporting BUSY and not passing to Voicemail'));
            $ext->add($mcontext, $exten, '', new ext_gotoif('$["${IVR_RETVM}"="RETURN" & "${IVR_CONTEXT}"!=""]', 'exit,1'));
            $ext->add($mcontext, $exten, '', new ext_playtones('busy'));
            $ext->add($mcontext, $exten, '', new ext_busy(20));
            $exten = '_s-!';
            $ext->add($mcontext, $exten, '', new ext_noop('IVR_RETVM: ${IVR_RETVM} IVR_CONTEXT: ${IVR_CONTEXT}'));
            $ext->add($mcontext, $exten, '', new ext_gotoif('$["${IVR_RETVM}"="RETURN" & "${IVR_CONTEXT}"!=""]', 'exit,1'));
            $ext->add($mcontext, $exten, '', new ext_playtones('congestion'));
            $ext->add($mcontext, $exten, '', new ext_congestion('10'));
            $exten = 'exit';
            $ext->add($mcontext, $exten, '', new ext_playback('beep&line-busy-transfer-menu&silence/1'));
            $ext->add($mcontext, $exten, '', new ext_macroexit());
            /* macro-exten-vm  */
            /*
            ;------------------------------------------------------------------------
            ; [macro-simple-dial]
            ;------------------------------------------------------------------------
            ; This macro was derived from macro-exten-vm, which is what is normally used to
            ; ring an extension. It has been simplified and designed to never go to voicemail
            ; and always return regardless of the DIALSTATUS for any incomplete call.
            ;
            ; It's current primary purpose is to allow findmefollow ring an extension prior
            ; to trying the follow-me ringgroup that is provided.
            ;
            ; Ring an extension, if the extension is busy or there is no answer, return
            ; ARGS: $EXTENSION, $RINGTIME
            ;------------------------------------------------------------------------
            */
            $mcontext = 'macro-simple-dial';
            $exten = 's';
            $ext->add($mcontext, $exten, '', new ext_set("__EXTTOCALL", '${ARG1}'));
            $ext->add($mcontext, $exten, '', new ext_set("RT", '${ARG2}'));
            $ext->add($mcontext, $exten, '', new ext_set("CFUEXT", '${DB(CFU/${EXTTOCALL})}'));
            $ext->add($mcontext, $exten, '', new ext_set("CFBEXT", '${DB(CFB/${EXTTOCALL})}'));
            $ext->add($mcontext, $exten, '', new ext_set("CWI_TMP", '${CWIGNORE}'));
            if ($has_extension_state) {
                $ext->add($mcontext, $exten, 'macrodial', new ext_macro('dial-one', '${RT},${DIAL_OPTIONS},${EXTTOCALL}'));
            } else {
                $ext->add($mcontext, $exten, 'macrodial', new ext_macro('dial', '${RT},${DIAL_OPTIONS},${EXTTOCALL}'));
            }
            $ext->add($mcontext, $exten, '', new ext_set("__CWIGNORE", '${CWI_TMP}'));
            $ext->add($mcontext, $exten, '', new ext_set("PR_DIALSTATUS", '${DIALSTATUS}'));
            $ext->add($mcontext, $exten, 'calldocfu', new ext_gosubif('$["${PR_DIALSTATUS}"="NOANSWER" & "${CFUEXT}"!=""]', 'docfu,1'));
            $ext->add($mcontext, $exten, 'calldocfb', new ext_gosubif('$["${PR_DIALSTATUS}"="BUSY" & "${CFBEXT}"!=""]', 'docfb,1'));
            $ext->add($mcontext, $exten, '', new ext_set("DIALSTATUS", '${PR_DIALSTATUS}'));
            $ext->add($mcontext, $exten, '', new ext_goto('1', 's-${DIALSTATUS}'));
            /*
            ; Try the Call Forward on No Answer / Unavailable number.
            ; We want to try CFU if set, but we want the same ring timer as was set to our call (or do we want the
            ; system ringtimer? - probably not). Then if no answer there (assuming it doesn't drop into their vm or
            ; something we return, which will have the net effect of returning to the followme setup.)
            ;
            ; want to avoid going to other follow-me settings here. So check if the CFUEXT is a user and if it is
            ; then direct it straight to ext-local (to avoid getting intercepted by findmefollow) otherwise send it
            ; to from-internal since it may be an outside line.
            ;
            */
            $exten = 'docfu';
            $ext->add($mcontext, $exten, '', new ext_gotoif('$["${DB(AMPUSER/${CFUEXT}/device)}" = "" ]', 'chlocal'));
            $ext->add($mcontext, $exten, '', new ext_dial('Local/${CFUEXT}@ext-local', '${RT},${DIAL_OPTIONS}'));
            $ext->add($mcontext, $exten, '', new ext_return(''));
            $ext->add($mcontext, $exten, 'chlocal', new ext_dial('Local/${CFUEXT}@from-internal/n', '${RT},${DIAL_OPTIONS}'));
            $ext->add($mcontext, $exten, '', new ext_return(''));
            $exten = 'docfb';
            $ext->add($mcontext, $exten, '', new ext_gotoif('$["${DB(AMPUSER/${CFBEXT}/device)}" = "" ]', 'chlocal'));
            $ext->add($mcontext, $exten, '', new ext_dial('Local/${CFBEXT}@ext-local', '${RT},${DIAL_OPTIONS}'));
            $ext->add($mcontext, $exten, '', new ext_return(''));
            $ext->add($mcontext, $exten, 'chlocal', new ext_dial('Local/${CFBEXT}@from-internal/n', '${RT},${DIAL_OPTIONS}'));
            $ext->add($mcontext, $exten, '', new ext_return(''));
            /*
            ; In all cases of no connection, come here and simply return, since the calling dialplan will
            ; decide what to do next
            */
            $exten = '_s-.';
            $ext->add($mcontext, $exten, '', new ext_noop('Extension is reporting ${EXTEN}'));
            /* macro-simple-dial */
            $mcontext = 'macro-hangupcall';
            $exten = 's';
            /*
            ; Cleanup any remaining RG flag
            */
            $ext->add($mcontext, $exten, 'start', new ext_gotoif('$["${USE_CONFIRMATION}"="" | "${RINGGROUP_INDEX}"="" | "${CHANNEL}"!="${UNIQCHAN}"]', 'skiprg'));
            $ext->add($mcontext, $exten, '', new ext_noop('Cleaning Up Confirmation Flag: RG/${RINGGROUP_INDEX}/${CHANNEL}'));
            $ext->add($mcontext, $exten, 'delrgi', new ext_dbdel('RG/${RINGGROUP_INDEX}/${CHANNEL}'));
            /*
            ; Cleanup any remaining BLKVM flag
            */
            $ext->add($mcontext, $exten, 'skiprg', new ext_gotoif('$["${BLKVM_BASE}"="" | "BLKVM/${BLKVM_BASE}/${CHANNEL}"!="${BLKVM_OVERRIDE}"]', 'skipblkvm'));
            $ext->add($mcontext, $exten, '', new ext_noop('Cleaning Up Block VM Flag: ${BLKVM_OVERRIDE}'));
            $ext->add($mcontext, $exten, 'delblkvm', new ext_dbdel('${BLKVM_OVERRIDE}'));
            /*
            ; Cleanup any remaining FollowMe DND flags
            */
            $ext->add($mcontext, $exten, 'skipblkvm', new ext_gotoif('$["${FMGRP}"="" | "${FMUNIQUE}"="" | "${CHANNEL}"!="${FMUNIQUE}"]', 'theend'));
            $ext->add($mcontext, $exten, 'delfmrgp', new ext_dbdel('FM/DND/${FMGRP}/${CHANNEL}'));
            $ext->add($mcontext, $exten, 'theend', new ext_hangup());
            /* macro-hangupcall */
            /*
            ; macro-dial-one
            ;
            TODO: This is still experimental and has not yet been fully tested. Feedback in using/testing it is welcome and we will be reponsive in
                  fixing it but it should be considered as alpha quality until then and is not used anywhere in the dialplan unless forced with the
                  un-documented USEMACRODIALONE = true flag.
            */
            if ($has_extension_state) {
                $mcontext = 'macro-dial-one';
                $exten = 's';
                $ext->add($mcontext, $exten, '', new ext_set('DEXTEN', '${ARG3}'));
                $ext->add($mcontext, $exten, '', new ext_set('DIALSTATUS_CW', ''));
                $ext->add($mcontext, $exten, '', new ext_gosubif('$["${FROM_DID}"!="" & "${SCREEN}"="" & "${DB(AMPUSER/${DEXTEN}/screen)}"!=""]', 'screen,1'));
                $ext->add($mcontext, $exten, '', new ext_gosubif('$["${DB(CF/${DEXTEN})}"!=""]', 'cf,1'));
                $ext->add($mcontext, $exten, '', new ext_gotoif('$["${DEXTEN:-1}"="#" | "${DB(DND/${DEXTEN})}"=""]', 'skip1'));
                $ext->add($mcontext, $exten, '', new ext_set('DEXTEN', ''));
                $ext->add($mcontext, $exten, '', new ext_set('DIALSTATUS', 'BUSY'));
                $ext->add($mcontext, $exten, 'skip1', new ext_gotoif('$["${DEXTEN}"=""]', 'nodial'));
                $ext->add($mcontext, $exten, '', new ext_gotoif('$["${DEXTEN:-1}"="#"]', 'continue'));
                $ext->add($mcontext, $exten, '', new ext_set('EXTHASCW', '${IF($["${CWIGNORE}"!=""]?"":${DB(CW/${DEXTEN})})}'));
                $ext->add($mcontext, $exten, '', new ext_gotoif('$["${EXTHASCW}"="" | "${DB(CFB/${DEXTEN})}"!="" | "${DB(CFU/${DEXTEN})}"!=""]', 'next1', 'cwinusebusy'));
                $ext->add($mcontext, $exten, 'next1', new ext_gotoif('$["${DB(CFU/${DEXTEN})}"!="" & ("${EXTENSION_STATE(${DEXTEN})}"="UNAVAILABLE" | "${EXTENSION_STATE(${DEXTEN})}"="UNKNOWN")]', 'docfu', 'skip3'));
                $ext->add($mcontext, $exten, 'docfu', new ext_set('DEXTEN', ''));
                $ext->add($mcontext, $exten, '', new ext_set('DIALSTATUS', 'NOANSWER'));
                $ext->add($mcontext, $exten, '', new ext_goto('nodial'));
                $ext->add($mcontext, $exten, 'skip3', new ext_gotoif('$["${EXTHASCW}"="" | "${DB(CFB/${DEXTEN})}"!=""]', 'next2', 'continue'));
                $ext->add($mcontext, $exten, 'next2', new ext_gotoif('$["${EXTENSION_STATE(${DEXTEN})}"="NOT_INUSE" | "${EXTENSION_STATE(${DEXTEN})}"="UNAVAILABLE" | "${EXTENSION_STATE(${DEXTEN})}"="UNKNOWN"]', 'continue'));
                $ext->add($mcontext, $exten, '', new ext_execif('$["${DB(CFB/${DEXTEN})}"!="" & "${CFIGNORE}"=""]', 'Set', 'DIALSTATUS=BUSY'));
                $ext->add($mcontext, $exten, '', new ext_gotoif('$["${EXTHASCW}"!="" | "${DEXTEN:-1}"="#"]', 'cwinusebusy'));
                $ext->add($mcontext, $exten, '', new ext_set('DEXTEN', ''));
                $ext->add($mcontext, $exten, '', new ext_set('DIALSTATUS', 'BUSY'));
                $ext->add($mcontext, $exten, '', new ext_goto('nodial'));
                $ext->add($mcontext, $exten, 'cwinusebusy', new ext_gotoif('$["${EXTHASCW}"!="" & "${CWINUSEBUSY}"!=""]', 'next3', 'continue'));
                $ext->add($mcontext, $exten, 'next3', new ext_execif('$["${EXTENSION_STATE(${DEXTEN})}"!="UNAVAILABLE" & "${EXTENSION_STATE(${DEXTEN})}"!="NOT_INUSE" & "${EXTENSION_STATE(${DEXTEN})}"!="UNKNOWN"]', 'Set', 'DIALSTATUS_CW=BUSY'));
                $ext->add($mcontext, $exten, 'continue', new ext_gotoif('$["${DEXTEN}"=""]', 'nodial'));
                $ext->add($mcontext, $exten, '', new ext_gosubif('$["${DEXTEN:-1}"!="#"]', 'dstring,1', 'dlocal,1'));
                $ext->add($mcontext, $exten, '', new ext_gotoif('$[${LEN(${DSTRING})}=0]', 'nodial'));
                $ext->add($mcontext, $exten, '', new ext_gotoif('$["${DEXTEN:-1}"="#"]', 'skiptrace'));
                $ext->add($mcontext, $exten, '', new ext_gosubif('$[${REGEX("^[\\+]?[0-9]+$" ${CALLERID(number)})} = 1]', 'ctset,1', 'ctclear,1'));
                //TODO: do we need to check for anything beyond auto-blkvm in this call path?
                $ext->add($mcontext, $exten, 'skiptrace', new ext_set('D_OPTIONS', '${IF($["${NODEST}"!="" & ${REGEX("(M[(]auto-blkvm[)])" ${ARG2})} != 1]?${ARG2}M(auto-blkvm):${ARG2})}'));
                $ext->add($mcontext, $exten, '', new ext_execif('$["${ALERT_INFO}"!=""]', 'SIPAddHeader', 'Alert-Info: ${ALERT_INFO}'));
                //TODO: Do I need to  re-propagage anything from ${SIPADDHEADER} ?
                $ext->add($mcontext, $exten, '', new ext_execif('$["${SIPADDHEADER}"!=""]', 'SIPAddHeader', '${SIPADDHEADER}'));
                if ($ast_ge_14) {
                    $ext->add($mcontext, $exten, '', new ext_execif('$["${MOHCLASS}"!=""]', 'Set', 'CHANNEL(musicclass)=${MEETME_MUSIC}'));
                } else {
                    $ext->add($mcontext, $exten, '', new ext_execif('$["${MOHCLASS}"!=""]', 'SetMusicOnHold', '${MOHCLASS}'));
                }
                $ext->add($mcontext, $exten, '', new ext_gosubif('$["${QUEUEWAIT}"!=""]', 'qwait,1'));
                $ext->add($mcontext, $exten, '', new ext_set('__CWIGNORE', '${CWIGNORE}'));
                $ext->add($mcontext, $exten, '', new ext_set('__KEEPCID', 'TRUE'));
                $ext->add($mcontext, $exten, '', new ext_dial('${DSTRING}', '${ARG1},${D_OPTIONS}'));
                $ext->add($mcontext, $exten, '', new ext_execif('$["${DIALSTATUS_CW}"!=""]', 'Set', 'DIALSTATUS=${DIALSTATUS_CW}'));
                $ext->add($mcontext, $exten, '', new ext_gosubif('$["${SCREEN}"!=""|"${DIALSTATUS}"="ANSWER"]', 's-${DIALSTATUS},1'));
                $ext->add($mcontext, $exten, '', new ext_macroexit());
                $ext->add($mcontext, $exten, 'nodial', new ext_execif('$["${DIALSTATUS}" = ""]', 'Set', 'DIALSTATUS=NOANSWER'));
                $ext->add($mcontext, $exten, '', new ext_noop('Returned from dial-one with nothing to call and DIALSTATUS: ${DIALSTATUS}'));
                $ext->add($mcontext, $exten, '', new ext_macroexit());
                $exten = 'h';
                $ext->add($mcontext, $exten, '', new ext_macro('hangupcall'));
                $exten = 'screen';
                $ext->add($mcontext, $exten, '', new ext_gotoif('$["${DB(AMPUSER/${DEXTEN}/screen)}"!="nomemory" | "${CALLERID(number)}"=""]', 'memory'));
                $ext->add($mcontext, $exten, '', new ext_execif('$[${REGEX("^[0-9a-zA-Z ]+$" ${CALLERID(number)})} = 1]', 'System', 'rm -f ${ASTVARLIBDIR}/sounds/priv-callerintros/${CALLERID(number)}.*'));
                $ext->add($mcontext, $exten, 'memory', new ext_set('__SCREEN', '${DB(AMPUSER/${DEXTEN}/screen)}'));
                $ext->add($mcontext, $exten, '', new ext_set('__SCREEN_EXTEN', '${DEXTEN}'));
                $ext->add($mcontext, $exten, '', new ext_set('ARG2', '${ARG2}p'));
                $ext->add($mcontext, $exten, '', new ext_return(''));
                $exten = 'cf';
                $ext->add($mcontext, $exten, '', new ext_set('CFAMPUSER', '${IF($["${AMPUSER}"=""]?${CALLERID(number)}:${AMPUSER})}'));
                $ext->add($mcontext, $exten, '', new ext_execif('$["${DB(CF/${DEXTEN})}"="${CFAMPUSER}" | "${DB(CF/${DEXTEN})}"="${REALCALLERIDNUM}" | "${CUT(CUT(BLINDTRANSFER,-,1),/,1)}" = "${DB(CF/${DEXTEN})}" | "${DEXTEN}"="${DB(CF/${DEXTEN})}"]', 'Return'));
                $ext->add($mcontext, $exten, '', new ext_set('DEXTEN', '${IF($["${CFIGNORE}"=""]?"${DB(CF/${DEXTEN})}#":"")}'));
                $ext->add($mcontext, $exten, '', new ext_execif('$["${DEXTEN}"!=""]', 'Return'));
                $ext->add($mcontext, $exten, '', new ext_set('DIALSTATUS', 'NOANSWER'));
                $ext->add($mcontext, $exten, '', new ext_return(''));
                $exten = 'qwait';
                $ext->add($mcontext, $exten, '', new ext_execif('$["${SAVEDCIDNAME}" = ""]', 'Set', '__SAVEDCIDNAME=${CALLERID(name)}'));
                $ext->add($mcontext, $exten, '', new ext_set('ELAPSED', '${MATH($[${EPOCH}+30-${QUEUEWAIT}]/60,int)}'));
                $ext->add($mcontext, $exten, '', new ext_set('CALLERID(name)', 'M${ELAPSED}:${SAVEDCIDNAME}'));
                $ext->add($mcontext, $exten, '', new ext_return(''));
                $exten = 'ctset';
                $ext->add($mcontext, $exten, '', new ext_set('DB(CALLTRACE/${DEXTEN})', '${CALLERID(number)}'));
                $ext->add($mcontext, $exten, '', new ext_return(''));
                $exten = 'ctclear';
                $ext->add($mcontext, $exten, '', new ext_dbdel('CALLTRACE/${DEXTEN}'));
                $ext->add($mcontext, $exten, '', new ext_return(''));
                $exten = 'dstring';
                $ext->add($mcontext, $exten, '', new ext_set('DSTRING', ''));
                $ext->add($mcontext, $exten, '', new ext_set('DEVICES', '${DB(AMPUSER/${DEXTEN}/device)}'));
                $ext->add($mcontext, $exten, '', new ext_execif('$["${DEVICES}"=""]', 'Return'));
                $ext->add($mcontext, $exten, '', new ext_execif('$["${DEVICES:0:1}"="&"]', 'Set', 'DEVICES=${DEVICES:1}'));
                $ext->add($mcontext, $exten, '', new ext_set('LOOPCNT', '${FIELDQTY(DEVICES,&)}'));
                $ext->add($mcontext, $exten, '', new ext_set('ITER', '1'));
                $ext->add($mcontext, $exten, 'begin', new ext_set('THISDIAL', '${DB(DEVICE/${CUT(DEVICES,&,${ITER})}/dial)}'));
                if ($chan_dahdi) {
                    $ext->add($mcontext, $exten, '', new ext_gosubif('$["${ASTCHANDAHDI}" != ""]', 'zap2dahdi,1'));
                }
                $ext->add($mcontext, $exten, '', new ext_set('DSTRING', '${DSTRING}${THISDIAL}&'));
                $ext->add($mcontext, $exten, '', new ext_set('ITER', '$[${ITER}+1]'));
                $ext->add($mcontext, $exten, '', new ext_gotoif('$[${ITER}<=${LOOPCNT}]', 'begin'));
                $ext->add($mcontext, $exten, '', new ext_set('DSTRING', '${DSTRING:0:$[${LEN(${DSTRING})}-1]}'));
                $ext->add($mcontext, $exten, '', new ext_return(''));
                $exten = 'dlocal';
                $ext->add($mcontext, $exten, '', new ext_set('DSTRING', 'Local/${DEXTEN:0:${MATH(${LEN(${DEXTEN})}-1,int)}}@from-internal/n'));
                $ext->add($mcontext, $exten, '', new ext_return(''));
                if ($chan_dahdi) {
                    $exten = 'zap2dahdi';
                    $ext->add($mcontext, $exten, '', new ext_execif('$["${THISDIAL}" = ""]', 'Return'));
                    $ext->add($mcontext, $exten, '', new ext_set('NEWDIAL', ''));
                    $ext->add($mcontext, $exten, '', new ext_set('LOOPCNT2', '${FIELDQTY(THISDIAL,&)}'));
                    $ext->add($mcontext, $exten, '', new ext_set('ITER2', '1'));
                    $ext->add($mcontext, $exten, 'begin2', new ext_set('THISPART2', '${CUT(THISDIAL,&,${ITER2})}'));
                    $ext->add($mcontext, $exten, '', new ext_execif('$["${THISPART2:0:3}" = "ZAP"]', 'Set', 'THISPART2=DAHDI${THISPART2:3}'));
                    $ext->add($mcontext, $exten, '', new ext_set('NEWDIAL', '${NEWDIAL}${THISPART2}&'));
                    $ext->add($mcontext, $exten, '', new ext_set('ITER2', '$[${ITER2} + 1]'));
                    $ext->add($mcontext, $exten, '', new ext_gotoif('$[${ITER2} <= ${LOOPCNT2}]', 'begin2'));
                    $ext->add($mcontext, $exten, '', new ext_set('THISDIAL', '${NEWDIAL:0:$[${LEN(${NEWDIAL})}-1]}'));
                    $ext->add($mcontext, $exten, '', new ext_return(''));
                }
                $exten = 's-NOANSWER';
                $ext->add($mcontext, $exten, '', new ext_macro('vm', '${SCREEN_EXTEN},BUSY,${IVR_RETVM}'));
                $ext->add($mcontext, $exten, '', new ext_execif('$["${IVR_RETVM}"!="RETURN" | "${IVR_CONTEXT}"=""]', 'Hangup'));
                $ext->add($mcontext, $exten, '', new ext_return(''));
                /*
                 * There are reported bugs in Asterisk Blind Trasfers that result in Dial() returning and continuing
                 * execution with a status of ANSWER. So we hangup at this point
                 */
                $exten = 's-ANSWER';
                $ext->add($context, $exten, '', new ext_noop('Call successfully answered - Hanging up now'));
                $ext->add($context, $exten, '', new ext_macro('hangupcall'));
                $exten = 's-TORTURE';
                $ext->add($mcontext, $exten, '', new ext_goto('1', 'musiconhold', 'app-blackhole'));
                $ext->add($mcontext, $exten, '', new ext_macro('hangupcall'));
                $exten = 's-DONTCALL';
                $ext->add($mcontext, $exten, '', new ext_answer(''));
                $ext->add($mcontext, $exten, '', new ext_wait('1'));
                $ext->add($mcontext, $exten, '', new ext_zapateller(''));
                $ext->add($mcontext, $exten, '', new ext_playback('ss-noservice'));
                $ext->add($mcontext, $exten, '', new ext_macro('hangupcall'));
                /* macro-dial-one */
            }
            break;
    }
}
예제 #2
0
        }
        break;
    case 'delete':
        core_zapchandids_delete($channel);
        needreload();
        redirect_standard();
        break;
}
?>
 
</div>

<div class="rnav"><ul>
<?php 
echo '<li><a href="config.php?display=' . $display . '&type=' . $type . '">' . _('Add Channel') . '</a></li>';
foreach (core_zapchandids_list() as $row) {
    echo '<li><a href="config.php?display=' . $display . '&type=' . $type . '&extdisplay=' . $row['channel'] . '" class="">' . $row['channel'] . ': ' . $row['description'] . '</a></li>';
}
?>
</ul></div>

<div class="content">

<?php 
if ($extdisplay != '') {
    // load
    $row = core_zapchandids_get($extdisplay);
    $description = $row['description'];
    $channel = $row['channel'];
    $did = $row['did'];
    echo "<h2>" . _("Edit Zap Channel: ") . $channel . "</h2>";