2
0
mirror of https://github.com/ACSPRI/queXS synced 2024-04-02 12:12:16 +00:00
Files
CATI_Tool/functions/functions.voip.php
azammitdcarf 3bbb234896 VoIP status is now part of VoIP monitoring so we don't query the Asterisk server often
Uses the "Register" and "Unregister" options of Asterisk to get extension status
Stores voip_status in operator table
2010-07-30 03:18:14 +00:00

618 lines
15 KiB
PHP

<?
/**
* Functions to interact with Asterisk
* Some examples taken from {@link http://www.voip-info.org/wiki/index.php?page=Asterisk+manager+Example%3A+PHP voip-info.org}
*
*
* This file is part of queXS
*
* queXS 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.
*
* queXS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with queXS; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*
* @author Adam Zammit <adam.zammit@deakin.edu.au>
* @copyright Deakin University 2007,2008
* @package queXS
* @subpackage functions
* @link http://www.deakin.edu.au/dcarf/ queXS was writen for DCARF - Deakin Computer Assisted Research Facility
* @license http://opensource.org/licenses/gpl-2.0.php The GNU General Public License (GPL) Version 2
*
*/
/**
* Configuration file
*/
include_once(dirname(__FILE__).'/../config.inc.php');
/**
* Class to interact with Asterisk
*
* @package queXS
*/
class voip {
/**
* Socket connection to Asterisk server
*/
var $socket;
/**
* Close the socket gracefully on destruct
*/
function __destruct() {
//close the socket
if ($this->socket !== false)
{
fclose($this->socket);
$this->socket = false;
}
}
/**
* Return a list of IAX extensions
* as an associative array
*
* @return array Key is extension, value is status
*
*/
function getIAXStatus()
{
$ret = $this->query("Action: IAXPeerList\r\n\r\n","PeerlistComplete");
$c = spliti("\r\n\r\n",$ret);
$chans = array();
foreach ($c as $s)
{
if(eregi("Event: PeerEntry.*ObjectName: ([0-9a-zA-Z-]+).*Status: ([/0-9a-zA-Z-]+)",$s,$regs))
{
//print T_("Channel: SIP/") . $regs[1] . " BridgedChannel " . $regs[2] . "\n";
$chan = substr($regs[1],0,4);
$chans[$chan] = $regs[2];
}
}
return $chans;
}
/**
* Return a list of active extensions and their corresponding
* channels as an associative array
*
* @return array Key is extension, value is Asterisk channel
*
*/
function getChannels()
{
$ret = $this->query("Action: Status\r\n\r\n","StatusComplete");
$c = spliti("\r\n\r\n",$ret);
$chans = array();
foreach ($c as $s)
{
if(eregi("Event: Status.*Channel: (SIP/|IAX2/[0-9a-zA-Z-]+).*BridgedChannel: (SIP/|IAX2/[/0-9a-zA-Z-]+)",$s,$regs))
{
//print T_("Channel: SIP/") . $regs[1] . " BridgedChannel " . $regs[2] . "\n";
$ccs = explode('-',$regs[1]);
$chan = $ccs[0];
$chans[$chan] = array($regs[1],$regs[2]);
}
else if(eregi("Event: Status.*Channel: (SIP/|IAX2/[0-9a-zA-Z-]+).*",$s,$regs))
{
//print T_("Channel: ") . $regs[1] . "\n";
$ccs = explode('-', $regs[1]);
$chan = $ccs[0];
$chans[$chan] = array($regs[1],false);
}
}
return $chans;
}
/**
* Return the channel (if active) given the extension
* Return false if no channel found
*
* @param string $ext Extension as in Asterisk
* @return string|bool The Asterisk channel or false if no channel exists
*
*/
function getChannel($ext,$link = false)
{
$v = $this->getChannels();
if (isset($v[$ext]))
{
if ($link)
return $v[$ext][1];
else
return $v[$ext][0];
}
else
return false;
}
/**
* Add another party to an active call (eg add in the supervisor)
*
* @param int $ext The extension of the current call
* @param int $number The phone number to add to the call
* @todo CHECK IF THE MEETING ROOM IS EMPTY before adding use meetme list
*
*/
function addParty($ext,$number)
{
if($ext)
{
$channel = $this->getChannel($ext);
$link = $this->getChannel($ext,true);
//check if the meeting room is empty
//if so:
// 1. call the supervisor to the room
$q = "Action: Originate\r\nChannel: Local/$number@from-internal\r\nPriority: 1\r\nContext: default\r\nApplication: MeetMe\r\nData: " . MEET_ME_ROOM . ",d\r\n\r\n";
$r = $this->query($q,"Meetme");
// 2. transfer the current call to the room
$r = $this->query("Action: Redirect\r\nChannel: $channel\r\nExten: " . MEET_ME_ROOM . "\r\nPriority: 1\r\n\r\n","Response");
$r = $this->query("Action: Redirect\r\nChannel: $link\r\nExten: " . MEET_ME_ROOM . "\r\nContext: from-internal-xfer\r\nPriority: 1\r\n\r\n","Response");
}
}
/**
* Dial call from the call database
*
* @param string $ext The extension to originate the call from
* @param string $number The number to dial
*
*/
function dial($ext,$number)
{
$r = $this->query("Action: Originate\r\nChannel: $ext\r\nExten: $number\r\nPriority: 1\r\nCallerid: $ext\r\n\r\n","Response");
}
/**
* Hang up the current call by the extension
*
* @param int $ext The extension to hang up for
*
*/
function hangup($ext)
{
if($ext)
{
$channel = $this->getChannel($ext);
$r = $this->query("Action: Hangup\r\nChannel: $channel\r\n\r\n","Response");
}
}
/**
* Begin recording the call to a file
*
* @param string $ext The Asterisk extension
* @param bool|string $filename False for an auto generated file name else specify file name
* @todo Handle multiple recordings
*
*/
function beginRecord($ext,$filename = false)
{
if($ext)
{
$channel = $this->getChannel($ext);
$r = $this->query("Action: Monitor\r\nChannel: $channel\r\nFile: $filename\r\nFormat: gsm\r\nMix: 1\r\n\r\n","Response");
}
}
/**
* End the recording on this extension
*
* @param string $ext The Asterisk extension
* @todo Handle multiple recordings
* @see beginRecord()
*
*/
function endRecord($ext)
{
if($ext)
{
$channel = $this->getChannel($ext);
$r = $this->query("Action: StopMonitor\r\nChannel: $channel\r\n\r\n","Response");
}
}
/**
* Return the status of an extension
*
* @param int $ext The extension
* @return bool|int false if not available, 1 for available, 2 for available and on a call
*
*/
function getExtensionStatus($ext)
{
if($ext)
{
$type = "SIP";
$exts = explode('/', $ext, 2);
if (isset($exts[0]))
$type = $exts[0];
if (isset($exts[1]))
$ext = $exts[1];
if ($type == "SIP")
{
$ret = $this->query("Action: ExtensionState\r\nContext: from-internal\r\nExten: $ext\r\nActionID: \r\n\r\n","Status:");
if(eregi("Status: ([0-9]+)",$ret,$regs))
{
if (isset($regs[1]))
{
// 0 appears to be online, 1 online and on a call
if ($regs[1] == 0)
return 1;
else if ($regs[1] == 1 || $regs[1] == 8)
return 2;
}
}
}
else if ($type == "IAX2")
{
$exts = $this->getIAXStatus();
if (isset($exts[$ext]))
{
$status = $exts[$ext];
if ($status == "OK")
return 1;
}
}
}
return false;
}
/**
* Return whether we are connected to the Asterisk server or not
*
* @return True if connected else false
*
*/
function isConnected()
{
if ($this->socket)
return true;
else
return false;
}
/**
* Connect to the Asterisk server
*
* @param string $ip The IP Address
* @param string $user Username for Asterisk manager
* @param string $pass Password for Asterisk manager
* @param bool $events If events should be enabled or not (default false)
*
* @return bool True if connected successfully, else false
*/
function connect($ip=VOIP_SERVER,$user=VOIP_ADMIN_USER,$pass=VOIP_ADMIN_PASS,$events = false)
{
$this->socket = fsockopen($ip,VOIP_PORT,$errno,$errstr,1);
if (!$this->socket)
{
//print "$errno: $errstr";
//exit();
return false;
}
stream_set_timeout($this->socket, 1);
$q = "Action: Login\r\nUsername: $user\r\nSecret: $pass\r\nEvents: ";
if ($events)
$q .= "on";
else
$q .= "off";
$q .= "\r\n\r\n";
$r = $this->query($q,"accepted");
if (strpos($r,"Response: Success"))
{
return true;
}
else
{
fclose($this->socket);
return false;
}
}
/**
* Query the Asterisk server and wait for a response or timeout
*
* @param string $query The string to send to the Asterisk manager, see {@link http://www.voip-info.org/wiki/view/Asterisk+manager+API API} for details
* @param string $waitfor A string within the return string to wait for before returning
* @return string The response string from Asterisk
*
*/
function query($query,$waitfor=false)
{
$wrets = "";
if ($this->socket === false)
return false;
fputs($this->socket, $query);
$c = 1;
do
{
$line = fgets($this->socket, 4096);
$wrets .= $line;
$info = stream_get_meta_data($this->socket);
} while ($line != "\n" && !$info['timed_out'] && (strpos($line,$waitfor) === false));
return $wrets;
}
}
/**
* Class used to watch Asterisk events and effect changes to queXS database
*
* @package queXS
* @todo automatically code a call if we know it is busy
*/
class voipWatch extends voip {
var $keepWatching = true;
function dbReconnect()
{
global $db;
//keep reconnecting to the db so it doesn't time out
$db = newADOConnection(DB_TYPE);
$db->Connect(DB_HOST, DB_USER, DB_PASS, DB_NAME);
$db->SetFetchMode(ADODB_FETCH_ASSOC);
}
/**
* Get the call_id based on the extension
*
* @param int $ext the extension
* @return int The call_id
*/
function getCallId($ext)
{
global $db;
$sql = "SELECT l.call_id
FROM operator AS o
JOIN (`case` AS c, `call_attempt` AS ca, `call` AS l) ON
( c.current_operator_id = o.operator_id
AND c.case_id = ca.case_id
AND ca.operator_id = o.operator_id
AND ca.end IS NULL
AND l.call_attempt_id = ca.call_attempt_id
AND l.outcome_id =0 )
WHERE o.extension = '$ext'";
$rs = $db->GetRow($sql);
$call_id =0;
if (!empty($rs))
$call_id =$rs['call_id'];
return $call_id;
}
function setExtensionStatus($ext, $online = true)
{
global $db;
$s = $online ? 1 : 0;
print(T_("Extension") . " $ext " . ($online ? T_("online") : T_("offline")) . "\n");
$sql = "UPDATE operator
SET voip_status = '$s'
WHERE extension = '$ext'";
$db->Execute($sql);
}
function setState($call_id,$state,$checkOutcome = false)
{
global $db;
$sql = "UPDATE `call`
SET state = '$state'
WHERE call_id = '$call_id'";
if ($checkOutcome) $sql .= " AND outcome_id = 0";
$db->Execute($sql);
}
/**
* Update the extension status for all extensions
*/
function updateAllExtensionStatus()
{
global $db;
$sql = "SELECT extension,operator_id
FROM operator
WHERE voip = 1 AND enabled = 1";
$rs = $db->GetAll($sql);
foreach($rs as $r)
{
$o = $r['operator_id'];
$e = $r['extension'];
$s = $this->getExtensionStatus($e);
if ($s == false)
$this->setExtensionStatus($e,false);
else
$this->setExtensionStatus($e,true);
}
}
/**
* Watch for Asterisk events and make changes to the queXS databse if
* appropriate
*
*
*/
function watch($process_id = false)
{
/**
* Process file
*/
if ($process_id) include_once(dirname(__FILE__).'/../functions/functions.process.php');
$line = "";
if ($this->socket === false)
return false;
//Set initial extension status
$this->updateAllExtensionStatus();
//Watch for events
do
{
if (!$this->isConnected() || $this->socket === false){
print(T_("Disconnected") . "\n");
$this->connect(VOIP_SERVER,VOIP_ADMIN_USER,VOIP_ADMIN_PASS,true);
if ($this->isConnected()) print (T_("Reconnected") . "\n");
}
$in = fgets($this->socket, 4096);
//print "IN: $in\n";
/**
* When we have reached the end of a message, process it
*
*/
if ($in == "\r\n")
{
//print "PROCESS: ";
/**
* The call is ringing
*/
if (eregi("Event: Dial.*SubEvent: Begin.*Channel: ((SIP/|IAX2/)[0-9]+)",$line,$regs))
{
$call_id = $this->getCallId($regs[1]);
if ($call_id != 0)
{
print T_("Ringing") . T_(" Extension ") . $regs[1] . "\n";
$this->setState($call_id,2);
}
}
/**
* The call has been answered
*/
else if (eregi("Event: Bridge.*Channel1: ((SIP/|IAX2/)[0-9]+)",$line,$regs))
{
$call_id = $this->getCallId($regs[1]);
if ($call_id != 0)
{
print T_("Answered") . T_(" Extension ") . $regs[1] . "\n";
$this->setState($call_id,3);
}
}
/**
* The call has been hung up
*/
else if (eregi("Event: Hangup.*Channel: ((SIP/|IAX2/)[0-9]+)",$line,$regs))
{
$call_id = $this->getCallId($regs[1]);
if ($call_id != 0)
{
print T_("Hangup") . T_(" Extension ") . $regs[1] . "\n";
$this->setState($call_id,4,true);
}
}
/**
* The status of an extension has changed to unregistered
*/
else if (eregi("Event: PeerStatus.*Peer: ((SIP/|IAX2/)[0-9]+).*PeerStatus: Unregistered",$line,$regs))
{
print T_("Unregistered") . T_(" Extension ") . $regs[1] . "\n";
$this->setExtensionStatus($regs[1],false);
}
/**
* The status of an extension has changed to registered
*/
else if (eregi("Event: PeerStatus.*Peer: ((SIP/|IAX2/)[0-9]+).*PeerStatus: Registered",$line,$regs))
{
print T_("Registered") . T_(" Extension ") . $regs[1] . "\n";
$this->setExtensionStatus($regs[1],true);
}
//print $line . "\n\n";
$line = "";
}
else
{
/**
* Append the lines to the message if we are not yet at the end of one
*/
$line .= $in;
}
@flush();
if ($process_id)
{
$this->dbReconnect();
$this->keepWatching = !is_process_killed($process_id);
}
} while ($this->keepWatching);
}
}
?>