phpAGI 2

phpAGI is a PHP class for the Asterisk Gateway Interface. The package is available for use and distribution under the terms of the GNU Public License.

phpAGI was originally written by Matthew Asham with contributions by Florian Overkamp.

Dependencies

phpAGI requires PHP 4.3.0 or better with sockets enabled. I doubt that it works in PHP 5 without complaining, but should work (not tested).

phpAGI is known to work on FreeBSD and Linux. I expect it should work well anywhere PHP with sockets works.

Download

Examples

sip_show_peer.php
dtmf.php
ping.php
input.php

Configuration

phpAGI supports an ini style configuration file, and run time configuration.

By default the class reads in the contents of /etc/asterisk/phpagi.conf into $this->config. The format of the ini file is as follows:

[examplesection]
foo=bar
bar=dew
dew=pale ale

This is read into $this->config as:

$this->config['examplesection']['foo']='bar';
$this->config['examplesection']['bar]='dew';
$this->config['examplesection']['dew']='pale ale';

Run time configuration is also supported. Pass an array of variables and values as the second parameter to the AGI constructor, these fields are stored in $this->config['phpagi'].

ie:

$myconfig=array('error_handler'=>true);
$agi = new AGI('/etc/asterisk/phpagi.conf', $myconfig);

Asterisk Manager API

Security

Validation:

If asterisk is running as root, the manager interface may allow the execution of arbitrary shell commands as root. If the user can update any configuration file that can execute arbitrary command (like the dialplan), the system may be compromised.

Also, look out for command injection. Consider the following example:

	$as->Events($_POST['events_status']);

We expect either 'on' or 'off', but the attacker uses:

	"\r\n\r\nAction: Command\r\nCommand: database put forward 54321 19005551212";

Validation is a *must* for all user data.

Username and Secret:

Storing the username and secret in the config file will isolate them from your code.

Isolation of username and secret in the config file does not mean that the script cannot simple read the config file. The config file must be readable by the script.

Creating a new instance of the class

The class can be created standalone of phpagi.php, or through phpagi.

Standalone:

require "phpagi-asmanager.php";
$as = new AGI_AsteriskManager();

From phpAGI:

require "phpagi.php"; $agi = new AGI(); $as = $agi->new_AsteriskManager();

Notes:

Configuration

phpagi-asmanager uses the same configuration file as phpagi.conf (usually /etc/asterisk/phpagi.conf). All configuration information specific to phpagi-asmanager is contained in the [asmanager] section of the .conf file.

Usage:

Connecting

	$res = $as->connect("localhost", "username", "password");
	if($res == FALSE) {
		echo "Connection failed.\n";
	}
	elseif($res == TRUE){
		echo "Connection established.\n";
	}

A port can also be specified for the hostname. eg:

	$res = $as->connect("my.asterisk.server:1234", "username", "port");

If the no parameters are specified, the defaults from the config will be used.

Disconnecting

	$as->disconnect();

Sending Requests

	$as->send_request($eventname, $arrayofparameterstopass);

send_request() calls wait_request and returns an array of returned data from the manager. If something went wrong, it returns false.

wait_request() shouldn't need to be called from a script directly unless you are implementing merely an event listener.

wait_request() will also detect events and dispatch any registered event handlers for the event.

Example:

	$res = $as->send_request('EventName',
                                 array('Channel'=>'Zap/1/16045551212',
                                                  'SomeParameter'=>'data'));
	echo "Dump of returned data:\n";
	foreach($res as $var=>$val)
	  echo "$var = $val\n";

$res['Response'] will generally be 'Success' on success and 'Error' on failure. But this is not always true. If $res['Response'] == 'Follows', a multi-line response will be stored in $res['data'].

Events

The class uses event callbacks to process events received from the manager.

The event callback prototype looks like:

	function dump_event($ecode, $data, $server, $port)
	{
	  echo "received event '$ecode' from $server:$port\n";
	  print_r($data);
	}

To register an event call back:

	$as->add_event_handler('eventname', 'eventfunction');

eg:

	$as->add_event_handler('registry', 'dump_event');

The special eventname "*" can also be registered. any eventname not specifically registered will be handled by the "*" handler. If no "*" handler is defined, the event will be silently ignored.

Fast AGI

Fast AGI is implemented using xinetd.

First, in /etc/services, at this line:

fastagi         4573/tcp                        # Asterisk AGI

Second, create /etc/xnetd.d/fastagi with:

fastagi.xinetd

Make sure you set the path to the phpagi-fastagi.php script. Set the user and group to a non-root user if none of your scripts need root access. You might consider using posix_setuid and friends to reduce privileges. Change the bind address to your outbound IP address or to 0.0.0.0 to allow anyone to connect. Be sure to read up about xinetd and take advantage of security features it provides. Fast AGI doesn't provide authentification. It's up to you to keep unwanted visitors from extracting information from your AGI implementation.

Third, write your code.

Take special notice of how fastagi.php works:

  1. $fastagi is initialized as a new AGI.
  2. The script determines which script was requested.
  3. The script is called using reqire_once($fastagi->request['agi_request']).
  4. Your script takes over. You must not create a new AGI, but instead use the $fastagi instance that has already been created.

In your dialplan:

exten => 5551212, 1, Agi(agi://127.0.0.1/myscript.php)

TODO