Optimise return values from AMI to avoid reformatting Simplify validate logic Optimise treatment of ami raw message Optimise return values from getDb_model_info and clean up method name Optimize add device moving functions outside of loop Define unique names for script methods between SIP and SCCP as in same page
452 lines
13 KiB
PHP
452 lines
13 KiB
PHP
<?php
|
|
|
|
/**
|
|
*
|
|
* Core Comsnd Interface
|
|
*
|
|
* https://www.voip-info.org/asterisk-manager-example-php/
|
|
*/
|
|
/* !TODO!: Re-Indent this file. -TODO-: What do you mean? coreaccessinterface ?? */
|
|
|
|
namespace FreePBX\modules\Sccp_manager\aminterface;
|
|
|
|
class AMIException extends \Exception
|
|
{
|
|
|
|
}
|
|
|
|
abstract class Message
|
|
{
|
|
const EOL = "\r\n";
|
|
const EOM = "\r\n\r\n";
|
|
|
|
protected $lines;
|
|
protected $variables;
|
|
protected $keys;
|
|
protected $createdDate;
|
|
private $_responseHandler;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->lines = array();
|
|
$this->variables = array();
|
|
$this->keys = array();
|
|
$this->createdDate = time();
|
|
}
|
|
|
|
public function _ToDebug($level, $msg)
|
|
{
|
|
}
|
|
|
|
public function getResponseHandler()
|
|
{
|
|
if (strlen($this->_responseHandler) > 0) {
|
|
// throw new AMIException('Hier:' . $this->_responseHandler);
|
|
return (string) $this->_responseHandler;
|
|
} else {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
public function setResponseHandler($responseHandler)
|
|
{
|
|
if (0 == strlen($responseHandler)) {
|
|
return;
|
|
}
|
|
$className = '\\FreePBX\\modules\\Sccp_manager\\aminterface\\' . $responseHandler . '_Response';
|
|
if (class_exists($className, true)) {
|
|
$this->_responseHandler = $responseHandler;
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
public function setVariable($key, $value)
|
|
{
|
|
$key = strtolower($key);
|
|
$this->variables[$key] = $value;
|
|
/* print_r('<br>----Set Value -------<br>');
|
|
print_r($key);
|
|
print_r($value);
|
|
*/
|
|
}
|
|
/*
|
|
// Duplicate declaration - also declared in Response class where is used.
|
|
public function getVariable($key)
|
|
{
|
|
$key = strtolower($key);
|
|
|
|
if (!isset($this->variables[$key])) {
|
|
return null;
|
|
}
|
|
return $this->variables[$key];
|
|
}
|
|
*/
|
|
protected function setKey($key, $value)
|
|
{
|
|
$key = strtolower((string) $key);
|
|
$this->keys[$key] = (string) $value;
|
|
}
|
|
|
|
public function getKey($key)
|
|
{
|
|
$key = strtolower($key);
|
|
if (!isset($this->keys[$key])) {
|
|
return null;
|
|
}
|
|
//return (string)$this->keys[$key];
|
|
return $this->keys[$key];
|
|
}
|
|
|
|
public function getVariables()
|
|
{
|
|
return $this->variables;
|
|
}
|
|
/*
|
|
public function getActionID()
|
|
{
|
|
return $this->getKey('ActionID');
|
|
}
|
|
*/
|
|
public function getKeys()
|
|
{
|
|
return $this->keys;
|
|
}
|
|
|
|
private function serializeVariable($key, $value)
|
|
{
|
|
return "Variable: $key=$value";
|
|
}
|
|
/*
|
|
// Obsolete - integrated into single method that calls for performance
|
|
protected function setSanitizedKey($key, $value)
|
|
{
|
|
// Handle JSON here rather than in getVariable as have already broken data into array.
|
|
// Force preference here rather than test later as is faster.
|
|
$key = strtolower($key);
|
|
switch ($key) {
|
|
case 'json':
|
|
$this->keys['json'] = (string) $value;
|
|
break;
|
|
case 'response':
|
|
case 'actionid':
|
|
case 'desc':
|
|
$this->keys[$key] = (string) $value;
|
|
break;
|
|
case 'listitems';
|
|
$this->keys[$key] = $this->sanitizeInput($value,'numeric');
|
|
break;
|
|
default:
|
|
$this->keys[$key] = $this->sanitizeInput($value);
|
|
break;
|
|
}
|
|
}
|
|
*/
|
|
protected function sanitizeInput($value, $prefered_type = '')
|
|
{
|
|
if ($prefered_type === '') {
|
|
// Find numeric first as this is coersion - is_string will always match!
|
|
if (is_numeric($value)) {
|
|
$prefered_type = 'numeric';
|
|
} else {
|
|
$prefered_type = 'string';
|
|
}
|
|
}
|
|
//if ($prefered_type !== '') {
|
|
switch ($prefered_type) {
|
|
case 'string':
|
|
if (filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)) {
|
|
return (boolean) $value;
|
|
} elseif (filter_var($value, FILTER_SANITIZE_STRING, FILTER_NULL_ON_FAILURE)) {
|
|
return (string) $value;
|
|
} elseif (filter_var($value, FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_NULL_ON_FAILURE)) {
|
|
return (string) htmlspecialchars($value, ENT_QUOTES);
|
|
} elseif (empty($value)) {
|
|
// In rare cases, $value will be empty, and so treated as a string. Trap here
|
|
// to avoid performance hit on all message lines if testing earlier.
|
|
return;
|
|
} else {
|
|
throw new AMIException("Incoming String is not sanitary. Skipping: '" . $value . "'\n");
|
|
}
|
|
break;
|
|
case 'numeric':
|
|
//if (!isset($value) || $value === null || strlen($value) == 0) {
|
|
//return 0;
|
|
//}
|
|
if (filter_var($value, FILTER_VALIDATE_INT, FILTER_FLAG_ALLOW_HEX | FILTER_FLAG_ALLOW_OCTAL)) {
|
|
return intval($value, 0);
|
|
} elseif (filter_var($value, FILTER_VALIDATE_FLOAT, FILTER_FLAG_ALLOW_FRACTION | FILTER_FLAG_ALLOW_THOUSAND | FILTER_FLAG_ALLOW_SCIENTIFIC)) {
|
|
return (float) $value;
|
|
} else {
|
|
return (double) $value;
|
|
}
|
|
default:
|
|
throw new AMIException("Don't know how to convert: '" . $value . "'\n");
|
|
break;
|
|
}
|
|
//}
|
|
}
|
|
|
|
protected function finishMessage($message)
|
|
{
|
|
return $message . self::EOL . self::EOL;
|
|
}
|
|
|
|
public function serialize()
|
|
{
|
|
$result = array();
|
|
foreach ($this->getKeys() as $k => $v) {
|
|
$result[] = $k . ': ' . $v;
|
|
}
|
|
foreach ($this->getVariables() as $k => $v) {
|
|
if (is_array($v)) {
|
|
foreach ($v as $singleValue) {
|
|
$result[] = $this->serializeVariable($k, $singleValue);
|
|
}
|
|
} else {
|
|
$result[] = $this->serializeVariable($k, $v);
|
|
}
|
|
}
|
|
$mStr = $this->finishMessage(implode(self::EOL, $result));
|
|
return $mStr;
|
|
}
|
|
|
|
public function setActionID($actionID)
|
|
{
|
|
if (0 == strlen($actionID)) {
|
|
throw new AMIException('ActionID cannot be empty.');
|
|
return;
|
|
}
|
|
|
|
if (strlen($actionID) > 69) {
|
|
throw new AMIException('ActionID can be at most 69 characters long.');
|
|
return;
|
|
}
|
|
|
|
$this->setKey('ActionID', $actionID);
|
|
}
|
|
|
|
public function __sleep()
|
|
{
|
|
return array('lines', 'variables', 'keys', 'createdDate');
|
|
}
|
|
}
|
|
|
|
abstract class IncomingMessage extends Message
|
|
{
|
|
public function __construct($rawContent)
|
|
{
|
|
parent::__construct();
|
|
$this->rawContent = $rawContent;
|
|
$lines = explode(Message::EOL, $rawContent);
|
|
foreach ($lines as $line) {
|
|
$content = explode(':', $line);
|
|
$key = strtolower(array_shift($content));
|
|
// May now have an empty array, or an array with just ' ', both of which
|
|
// are rare and will be turned into an empty string below.
|
|
// Do not test for here, as most times will not be empty, and testing here
|
|
// would mean a performance hit given the number of lines handled
|
|
|
|
// Performance - integrate method here as is only called from here!
|
|
//$this->setSanitizedKey($name, trim(implode(':', $content)));
|
|
$value = trim(implode(':', $content));
|
|
// Handle JSON here rather than in old getVariable as have already broken data into array.
|
|
// Force preference here rather than test later as is faster.
|
|
switch ($key) {
|
|
case 'json':
|
|
$this->keys['json'] = (string) $value;
|
|
break;
|
|
case 'response':
|
|
case 'actionid':
|
|
case 'desc':
|
|
$this->keys[$key] = (string) $value;
|
|
break;
|
|
case 'listitems';
|
|
$this->keys[$key] = $this->sanitizeInput($value,'numeric');
|
|
break;
|
|
default:
|
|
$this->keys[$key] = $this->sanitizeInput($value);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected $rawContent;
|
|
|
|
public function getEventList()
|
|
{
|
|
return $this->getKey('EventList');
|
|
}
|
|
|
|
public function getRawContent()
|
|
{
|
|
return $this->rawContent;
|
|
}
|
|
|
|
public function isComplete()
|
|
{
|
|
return $this->_completed;
|
|
}
|
|
|
|
public function __sleep()
|
|
{
|
|
$ret = parent::__sleep();
|
|
$ret[] = 'rawContent';
|
|
return $ret;
|
|
}
|
|
}
|
|
|
|
// namespace FreePBX\modules\Sccp_manager\aminterface\Message;
|
|
class LoginAction extends ActionMessage
|
|
{
|
|
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* @param string $user AMI username.
|
|
* @param string $password AMI password.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function __construct($user, $password)
|
|
{
|
|
parent::__construct('Login');
|
|
$this->setKey('Username', $user);
|
|
$this->setKey('Secret', $password);
|
|
$this->setKey('Events', 'off'); // &----
|
|
$this->setResponseHandler('Login');
|
|
}
|
|
}
|
|
|
|
abstract class ActionMessage extends Message
|
|
{
|
|
|
|
public function __construct($what)
|
|
{
|
|
parent::__construct();
|
|
$this->setKey('Action', $what);
|
|
$this->setKey('ActionID', microtime(true));
|
|
}
|
|
}
|
|
|
|
class CommandAction extends ActionMessage
|
|
{
|
|
public function __construct($command)
|
|
{
|
|
parent::__construct('Command');
|
|
$this->setKey('Command', $command);
|
|
$this->setResponseHandler("Command");
|
|
}
|
|
}
|
|
|
|
class ReloadAction extends ActionMessage
|
|
{
|
|
|
|
public function __construct($module = false)
|
|
{
|
|
parent::__construct('Reload');
|
|
if ($module !== false) {
|
|
$this->setKey('Module', $module);
|
|
$this->setResponseHandler("Generic");
|
|
}
|
|
}
|
|
}
|
|
|
|
class ExtensionStateListAction extends ActionMessage
|
|
{
|
|
|
|
public function __construct()
|
|
{
|
|
parent::__construct('ExtensionStateList');
|
|
//$this->setKey('Segment', 'general');
|
|
//$this->setKey('ResultFormat', 'command');
|
|
$this->setResponseHandler("ExtensionStateList");
|
|
}
|
|
}
|
|
class SCCPShowGlobalsAction extends ActionMessage
|
|
{
|
|
public function __construct()
|
|
{
|
|
parent::__construct('SCCPShowGlobals');
|
|
}
|
|
}
|
|
|
|
class SCCPShowSoftkeySetsAction extends ActionMessage
|
|
{
|
|
|
|
public function __construct()
|
|
{
|
|
parent::__construct('SCCPShowSoftkeySets');
|
|
//$this->setKey('Segment', 'general');
|
|
//$this->setKey('ResultFormat', 'command');
|
|
$this->setResponseHandler("SCCPShowSoftkeySets");
|
|
}
|
|
}
|
|
|
|
class SCCPShowDeviceAction extends ActionMessage
|
|
{
|
|
|
|
public function __construct($devicename)
|
|
{
|
|
parent::__construct('SCCPShowDevice');
|
|
//$this->setKey('Segment', 'general');
|
|
//$this->setKey('ResultFormat', 'command');
|
|
$this->setKey('DeviceName', $devicename);
|
|
$this->setResponseHandler("SCCPShowDevice");
|
|
}
|
|
}
|
|
|
|
class SCCPShowDevicesAction extends ActionMessage
|
|
{
|
|
|
|
public function __construct()
|
|
{
|
|
parent::__construct('SCCPShowDevices');
|
|
//$this->setKey('Segment', 'general');
|
|
//$this->setKey('ResultFormat', 'command');
|
|
$this->setResponseHandler("SCCPShowDevices");
|
|
}
|
|
}
|
|
|
|
class SCCPTokenAckAction extends ActionMessage
|
|
{
|
|
|
|
public function __construct($DeviceName)
|
|
{
|
|
parent::__construct('SCCPTokenAck');
|
|
$this->setKey('DeviceId', $DeviceName);
|
|
$this->setResponseHandler("Generic");
|
|
}
|
|
}
|
|
|
|
class SCCPDeviceRestartAction extends ActionMessage
|
|
{
|
|
|
|
public function __construct($DeviceName, $Type = "restart")
|
|
{
|
|
parent::__construct('SCCPDeviceRestart');
|
|
$this->setResponseHandler("Generic");
|
|
//if (empty($Type)) {
|
|
//$Type = "restart";
|
|
//}
|
|
$this->setKey('DeviceName', $DeviceName);
|
|
if (in_array(strtolower($Type), array('restart', 'full', 'reset'))) {
|
|
$this->setKey('Type', $Type);
|
|
} else {
|
|
throw new Exception('Param2 has to be one of \'restart\', \'full\', \'reset\'.');
|
|
}
|
|
}
|
|
}
|
|
|
|
class SCCPConfigMetaDataAction extends ActionMessage
|
|
{
|
|
public function __construct($segment = false)
|
|
{
|
|
parent::__construct('SCCPConfigMetaData');
|
|
if ($segment != false) {
|
|
$this->setKey('Segment', $segment);
|
|
}
|
|
$this->setResponseHandler("SCCPJSON");
|
|
}
|
|
}
|