All pastes #526223 Raw Edit

Someone

public text v1 · immutable
#526223 ·published 2007-05-31 18:36 UTC
rendered paste body
#!/usr/bin/env php
<?php 

// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//

$config = parse_amportal_conf( "/etc/amportal.conf" );

require_once "phpagi.php";
require_once "phpagi-asmanager.php";

$debug = 4;

$ext			= array();		// Hash that will contain our list of extensions to call
$ext_hunt	= array();		// Hash that will contain our list of extensions to call used by huntgroup
$cidnum		= "";		// Caller ID Number for this call
$cidname	= "";		// Caller ID Name for this call
$timer		= "";		// Call timer for Dial command
$dialopts	= "";		// options for dialing
$rc		= "";		// Catch return code
$priority	= "";		// Next priority 
$rgmethod	= "";		// If Ring Group what ringing method was chosen
$dsarray	= array();	// This will hold all the dial strings, used to check for duplicate extensions

$AGI = new AGI();
debug("Starting New Dialparties.agi", 1);

$astman = new AGI_AsteriskManager( );	
if (!$astman->connect("127.0.0.1", $config["AMPMGRUSER"] , $config["AMPMGRPASS"])) {
	exit (1);
}
	

$priority = get_var( $AGI, "priority" ) + 1;
debug( "priority is $priority", 1 );

// Caller ID info is stored in $request in AGI class, passed from Asterisk
$cidnum = $AGI->request['agi_callerid'];
$cidname = $AGI->request['agi_calleridname'];
debug("Caller ID name is '$cidname' number is '$cidnum'", 1);

// From this point forward, Set KEEPCID in the channel so subsequent calls, CF, etc. retain the incoming
// CID that get sent down channel local.
$AGI->set_variable('__KEEPCID','TRUE');

$timer		= get_var( $AGI, "ARG1" );
$dialopts	= get_var( $AGI, "ARG2" );
$rgmethod	= get_var( $AGI, "RingGroupMethod" );

$pr_dialstatus	= get_var( $AGI, "PR_DIALSTATUS" );

$nodest	= get_var( $AGI, "NODEST" );
if (empty($nodest))	$nodest	= '';

$use_confirmation	= get_var( $AGI, "USE_CONFIRMATION" );
$ringgroup_index	= get_var( $AGI, "RINGGROUP_INDEX" );

if (empty($timer))	$timer		= 0;
if (empty($dialopts))	$dialopts	= "";
if (empty($rgmethod))	$rgmethod	= "none";

if (empty($use_confirmation))	$use_confirmation = "FALSE";

debug("Methodology of ring is  '$rgmethod'", 1);
debug("USE_CONFIRMATION:  '$use_confirmation'", 4);
debug("RINGGROUP_INDEX:   '$ringgroup_index'", 4);

// reset the ringgroup method to its fundamental algorithm and pull out if
// master mode.

if ($rgmethod == "ringall-prim") {
	$rgmethod	= "ringall";
	$mastermode	= 1;
} elseif ($rgmethod == "hunt-prim") {
	$rgmethod	= "hunt";
	$mastermode	= 1;
} elseif ($rgmethod == "memoryhunt-prim") {
	$rgmethod	= "memoryhunt";
	$mastermode	= 1;
} else {
	$mastermode	= 0;
	$pr_dialstatus = ""; // not relevant if not mastermode, clear it so dnd doesn't propagate, and other
}


// call confirmation only works with ringall and ringall-prim. The javascripts in ringgroups
// and follow-me should enforce this. If that has been overridden then force ringall.
// Keep this code after the matermode check above, since they will at least get mastermode
// if they set a -prim mode in one of the others
//
if ( ($use_confirmation != "FALSE") && ($rgmethod != "ringall") ) {
	debug("Unsupported RingMethod reset to: '$rgmethod'", 1);
	$rgmethod	= "ringall";
}

// Start with Arg Count set to 3 as two args are used
$arg_cnt = 3;
while($arg = get_var($AGI,"ARG". $arg_cnt))
{
	if ($arg == '-')
	{  // not sure why, dialparties will get stuck in a loop if noresponse
		debug("get_variable got a \"noresponse\"!  Exiting",3);
		exit($arg_cnt);
	}
	
	$extarray = split( '-', $arg );
	foreach ( $extarray as $k )
	{
		$ext[] = $k;
		debug("Added extension $k to extension map", 3);
	}
	
	$arg_cnt++;
}

// IF THE FIRST EXTENSION IS CALL FORWARD ENABLED (put in logic) then we don't do master mode
// which means we reset the flag here after detecting that and just say we are not in master
// mode and all is well. That means the loop below needs to be modified to detect the first
// extension and do this if the case.

// Check for call forwarding first
// If call forward is enabled, we use chan_local
// Hacky. We should be using an associative array, shouldn't we?
$count = 0;
foreach( $ext as $k)
{
	$cf  = $AGI->database_get('CF',$k);
	$cf  = $cf['data'];
	if (strlen($cf))
	{
		// append a hash sign so we can send out on chan_local below.
		$ext[$count] = $cf.'#';  
		debug("Extension $k has call forward set to $cf", 1);

		// if this is the primary extension and CF enabled, then cancel mastermode
		// whether it is or not, no need to check.
		//
		if ($count == 0)
		{
			$mastermode = 0;
			$pr_dialstatus = ""; // not relevant if not mastermode, clear it so dnd doesn't propagate, and other
			debug("Primary ext is CF so disabling mastermode if it was set", 4);
		}
	}
	else
		debug("Extension $k cf is disabled", 3);
	$count++;
}

// IF DND AND we process it as a DND (no CF enabled) then we need to some how flag that ALL THE REST
// should now be ignored and not processed if in master mode (and this primary). Do this by setting some
// sort of flag that says master mode DND so skip everything else below (set them all to "").
//

// Hacky.
$count = 0;
$dndprimary = 0;
// Now check for DND
foreach ( $ext as $k )
{	//if ( !preg_match($k, "/\#/", $matches) )
	if ( (strpos($k,"#")==0) )
	{
		// no point in doing if cf is enabled
		$dnd = $AGI->database_get('DND',$k);
		$dnd = $dnd['data'];
		if (strlen($dnd) || $pr_dialstatus == "BUSY") 
		{
			debug("Extension $k has do not disturb enabled, or followme pre-ring returned busy", 1);
			unset($ext[$count]);
			$AGI->set_variable('DIALSTATUS','BUSY');						
			// if this is primary set dndprimary and figure out if needed below
			//
			if ($count == 0 && $mastermode)
			{
				$dndprimary = 1;
				debug("Primary extension is DND, so if mastermode, all should be dnd", 4);
			}
		} 
		else
			debug("Extension $k do not disturb is disabled", 3);
	}
	$count++;
}

// Main calling loop
$skipremaining = 0; // used to allow primary to ring but skip the rest

$ds = '';
foreach ( $ext as $k )
{
	// mastermode description:
	//
	// if mastermode is set then the first extension will be examined and mastermode will be reset so that the others
	// are left alone. If the remaining extensions are not to be tried, skpremaining will be set to 1 which will
	// result in dndprimary being set to 1 thus diabling the remaining list.
	//
	// if cf unconditional was already detected on the primary, then mastermode will have been reset at this point
	// since that will negate the mastermode concpet.
	//
	// if dnd was set on the primary then dndprimary will already be set resulting in a completly blanked out list
	// since dnd on the primary means don't bother me on any.

	// Don't bother checking these if we will be blanking the extnum anyhow
	if ($skipremaining == 1)
	{
		$dndprimary = 1;
	}
	if ($dndprimary == 0)
	{
		// TODO what are these comments...? do we need to remove them...?
		$extnum    = $k;
		$exthascw  = $AGI->database_get('CW', $extnum);// ? 1 : 0;
		$exthascw  = $exthascw['data']? 1:0;
		$extcfb    = $AGI->database_get('CFB', $extnum);//? 1 : 0;
		$extcfb    = $extcfb['data'];
		$exthascfb = (strlen($extcfb) > 0) ? 1 : 0;
		$extcfu    = $AGI->database_get('CFU', $extnum);// ? 1 : 0;
		$extcfu    = $extcfu['data'];
 		$exthascfu = (strlen($extcfu) > 0) ? 1 : 0;
	
		// Dump details in level 4
		debug("extnum: $extnum",4);
		debug("exthascw: $exthascw",4);
		debug("exthascfb: $exthascfb",4);
		debug("extcfb: $extcfb",4);
		debug("exthascfu: $exthascfu",4);
		debug("extcfu: $extcfu",4);

		// check if mastermode and then reset here. If mastermode, this will be the first extension so
		// the state is checked and a decision is made as to what to do. We have gotten all the cf variables
		// above. If CF unconditional was set, we never get here because we alread reset mastermode. If DND
		// were set then we never get here becasue didprimary was set
		if ($mastermode == 1)
		{
			$mastermode = 0;
			$extstate = is_ext_avail($extnum);
			debug("Extension $extnum has ExtensionState: $extstate",1);

			if ( ($exthascw == 1) && ($extstate == 1) )
			{
				// process this one extension but the remaining should be skipped since there is cw and
				// the extension is occupied. This will try this extension but not the others.
				$skipremaining = 1;
				debug("In mastermode with cw enabled so $extnum will be tried and others skipped",4);
			}
			elseif ( ($exthascw == 0) && ($extstate == 1) )
			{
				// no cw, ext is busy. So if cfb is set, it will forward there and if not, it will be
				// ignored as normal behavior. In either case, we skip the remaining numbers.
				$skipremaining = 1;
				debug("In mastermode with cw disabled so $extnum will be processed in case cfb set",4);
			}
			// All other cases should act like normal. Unavailable, not busy, ringing, etc.
			// should not be effected
		}
	} // end if ($dndprimary == 0)
	else // $dndprimary == 1 so clear the extension
	{
		// clear the current extension if dndprimary has been set. This will only be the case if in mastermode so no need to check
		// that. Use this to skip remaining extensions also if just ringing the primary.
		$extnum = '';
	}

	// if CF is not in use and $dndprimary is not set otherwise $extnum has been cleared and nothing to do
	//
	if ( (strpos($k,"#")==0) && $dndprimary == 0)
	{
		// CW is not in use or CFB is in use on this extension, then we need to check!
		if ( ($exthascw == 0) || ($exthascfb == 1) || ($exthascfu == 1) )
		{
			// get ExtensionState: 0-idle; 1-busy; 4-unavail; 8-ringing <--- these are unconfirmed
			$extstate = is_ext_avail($extnum);
			debug("Extension $extnum has ExtensionState: $extstate",1);
	
			if ( ($exthascfu == 1) && ($extstate == 4) ) // Ext has CFU and is Unavailable
			{
				// If part of a ring group, then just do what CF does, otherwise needs to
				// drop back to dialplan with NOANSWER
				if ($rgmethod != '' && $rgmethod != 'none')
				{
					debug("Extension $extnum has call forward on no answer set and is unavailable and is part of a Ring Group forwarding to '$extcfu'",1);
					$extnum = $extcfu . '#';   # same method as the normal cf, i.e. send to Local
				}
				else
				{
					debug("Extension $extnum has call forward on no answer set and is unavailable",1);
					$extnum = '';
					$AGI->set_variable('DIALSTATUS','NOANSWER');
				}
			}
			elseif ( ($exthascw == 0) || ($exthascfb == 1) )
			{
				debug("Checking CW and CFB status for extension $extnum",3);
			
				if ($extstate > 0 && $extstate != 4)
				{ // extension in use
					debug("Extension $extnum is not available to be called", 1);
					
					if ($exthascfb == 1) // extension in use
					{	// CFB is in use
						debug("Extension $extnum has call forward on busy set to $extcfb",1);
						$extnum = $extcfb . '#';   # same method as the normal cf, i.e. send to Local
					}
					elseif ($exthascw == 0) 
					{	// CW not in use
						debug("Extension $extnum has call waiting disabled",1);
						$extnum = '';
						$AGI->set_variable('DIALSTATUS','BUSY');						
					} 
					else 
					{
						debug("Extension $extnum has call waiting enabled",1);
					}
				}
			}
			elseif ($extstate < 0)
			{	// -1 means couldn't read status usually due to missing HINT
				debug("ExtensionState for $extnum could not be read...assuming ok",3);
			} 
			else
			{
				debug("Extension $extnum is available",1);
			}
		}
		elseif ( ($exthascw == 1) && ($rgmethod == 'firstnotonphone') )
		{	// get ExtensionState: 0-idle; 1-busy; 4-unavail; 8-ringing <--- these are unconfirmed
			$extstate = is_ext_avail($extnum);
			debug("Extension $extnum has ExtensionState: $extstate",1);
			if ($extstate == 1)
			{	// CW in use - but blocked for hunt
				debug("Extension $extnum has call waiting enabled but blocked for hunt",1);
				$extnum = '';
				$AGI->set_variable('DIALSTATUS','BUSY');	
			}
		}
	}
	
	if ($extnum != '')
	{	// Still got an extension to be called?
		// check if we already have a dial string for this extension
		// if so, ignore it as it's pointless ringing it twice !
		$realext = str_replace("#", "", $extnum);
		if ( isset($dsarray[$realext]) )
		{
			debug("Extension '$realext' already in the dialstring, ignoring duplicate",1);
		}
		else
		{
			$dsarray[$realext] = 1;  // could be dial string i suppose but currently only using for duplicate check
			$extds = get_dial_string( $AGI, $extnum, $use_confirmation, $ringgroup_index);
			if (strlen($extds)) $ds .= $extds . '&';
		
			// Update Caller ID for calltrace application
			if ((strpos($k,"#")==0) && (($rgmethod != "hunt") && ($rgmethod != "memoryhunt") && ($rgmethod != "firstavailable") && ($rgmethod != "firstnotonphone")) )
			{
				if ( isset($cidnum) && is_numeric($cidnum) )
				{
					$rc = $AGI->database_put('CALLTRACE', $k, $cidnum);
					if ($rc['result'] == 1) 
					{
						debug("dbset CALLTRACE/$k to $cidnum", 3);
					}
					else
					{
						debug("Failed to DbSet CALLTRACE/$k to $cidnum ({$rc['result']})", 1);
					}
				}
				else
				{
					// We don't care about retval, this key may not exist
					$AGI->database_del('CALLTRACE', $k);
					debug("DbDel CALLTRACE/$k - Caller ID is not defined", 3);
				}
			}
			else
			{
				$ext_hunt[$k]=$extds; // Need to have the extension HASH set with technology for hunt group ring 
			}
		}
	}
} // end foreach ( $ext as $k )

$dshunt		= '';
$loops		= 0;
$myhuntmember	= "";

if (($rgmethod == "hunt") || ($rgmethod == "memoryhunt") || ($rgmethod == "firstavailable") || ($rgmethod == "firstnotonphone"))
{
	if ($cidnum)
		$AGI->set_variable('CALLTRACE_HUNT',$cidnum);
		
	foreach ($extarray as $k )
	{
		// we loop through the original array to get the extensions in order of importance
		if ($ext_hunt[$k])
		{
			//If the original array is included in the extension hash then set variables
			$myhuntmember="HuntMember"."$loops";
			if (($rgmethod == "hunt") || ($rgmethod == "firstavailable") || ($rgmethod == "firstnotonphone"))
				$AGI->set_variable($myhuntmember,$ext_hunt[$k]);
			elseif ($rgmethod == "memoryhunt") 
			{
				if ($loops==0) 
					$dshunt =$ext_hunt[$k];
				else
					$dshunt .='&'.$ext_hunt[$k];
				$AGI->set_variable($myhuntmember,$dshunt);
			}
			$loops += 1;
		}
	}
}

$ds = chop($ds," &");

if ($nodest != '' && $use_confirmation == 'FALSE') {
	if (strpos($dialopts,"M(auto-blkvm)") > 0  || strpos($dialopts,"M(auto-blkvm)") === 0 ||
	    strpos($dialopts,"M(auto-confirm") > 0 || strpos($dialopts,"M(auto-confirm") === 0 ||
	    strpos($dialopts,"M(confirm") > 0      || strpos($dialopts,"M(confirm") === 0) {
		debug("NODEST: $nodest blkvm enabled macro already in dialopts: $dialopts",4);
	} else {
		$dialopts .= "M(auto-blkvm)";
		debug("NODEST: $nodest adding M(auto-blkvm) to dialopts: $dialopts",4);
	}
}

if (!strlen($ds))
{
	$AGI->noop('');
} else {
	if (($rgmethod == "hunt") || ($rgmethod == "memoryhunt") || ($rgmethod == "firstavailable") || ($rgmethod == "firstnotonphone"))
	{
		$ds = '|';
		if ($timer)
			$ds .= $timer;
		$ds .= '|' . $dialopts; // pound to transfer, provide ringing
		$AGI->set_variable('ds',$ds);
		$AGI->set_variable("HuntMembers",$loops);
		$AGI->set_priority(20); // dial command is at priority 20 where dialplan handles calling a ringgroup with strategy of "hunt" or "MemoryHunt"
	}
	else
	{
		$ds .= '|';
		if ($timer)
		{
			$ds .= $timer;
			if (trim($use_confirmation) != "FALSE")
				$AGI->set_variable('__RT',$timer);
		}
		$ds .= '|' . $dialopts; // pound to transfer, provide ringing
		if (trim($use_confirmation) != "FALSE")
		{
			$AGI->set_variable('__RG_IDX',$ringgroup_index);
			if ( isset($cidnum) && is_numeric($cidnum) ) {
				$AGI->set_variable('__CALLCONFIRMCID',$cidnum);
			} else {
				$AGI->set_variable('__CALLCONFIRMCID',"999");
			}
		}
		$AGI->set_variable('ds',$ds);
		$AGI->set_priority(10); // dial command is at priority 10
	}
}

$astman->disconnect();

// EOF dialparties.agi
exit( 0 );

// helper functions
function get_var( $agi, $value)
{
	$r = $agi->get_variable( $value );
	
	if ($r['result'] == 1)
	{
		$result = $r['data'];
		return $result;
	}
	else
		return '';
}

function get_dial_string( $agi, $extnum, $use_confirmation, $ringgroup_index )
{
	$dialstring = '';
	
	if (strpos($extnum,'#') != 0)
	{
		// "#" used to identify external numbers in forwards and callgourps
		// If using call confirmation, need to put the # back into the new dialstring
		// we then place all external calls (denoted with a # at the end) through
		// the [grps] extension for the RINGGROUP_INDEX that was called. This
		// triggers the call confirmation macro along with the required messages
		// that were set.
		//
		$extnum = str_replace("#", "", $extnum);
		if (trim($use_confirmation) == "FALSE")
			$dialstring = 'Local/'.$extnum.'@from-internal/n';
		else
			$dialstring = 'Local/RG-'.$ringgroup_index.'-'.$extnum.'#@from-internal';
		debug("Built External dialstring component for $extnum: $dialstring", 4);
	}
	else
	{
		$device_str = sprintf("%s/device", $extnum);
		$device = $agi->database_get('AMPUSER',$device_str);
		$device = $device['data'];
		
		// a user can be logged into multipe devices, append the dial string for each		
		$device_array = split( '&', $device );
		foreach ($device_array as $adevice) 
		{
			if (trim($use_confirmation) == "FALSE")
			{
				$dds = $agi->database_get('DEVICE',$adevice.'/dial');
				$dialstring .= $dds['data'];
				$dialstring .= '&';
			} else {
				$dialstring .= 'Local/LC-'.$adevice.'@from-internal&';
			}
		}
		$dialstring = trim($dialstring," &");
	}
	
	return $dialstring;
}

function debug($string, $level=3)
{
	global $AGI;
	$AGI->verbose($string, $level);
}

function mycallback( $rc )
{
	debug("User hung up. (rc=" . $rc . ")", 1);
	exit ($rc);
}

function is_ext_avail( $extnum )
{
	global $config;
	global $astman;
	
	$status = $astman->ExtensionState( $extnum, 'from-internal' );
		
	$status = $status['Status'];
	debug("ExtensionState: $status", 4);
	return $status;
}

function parse_amportal_conf($filename) 
{
	$file = file($filename);
	$matches = array();
	$matchpattern = '/^\s*([a-zA-Z0-9]+)\s*=\s*(.*)\s*([;#].*)?/';
	foreach ($file as $line)
	{
		if (preg_match($matchpattern, $line, $matches)) 
		{
			$conf[ $matches[1] ] = $matches[2];
		}
	}
	return $conf;
}

?>