Tighten Ami controls

Check that have compatible version of chan-sccp for AMI and disable AMI  if not found
Check returned values
Add new classes for response to reduce testing
This commit is contained in:
steve-lad 2021-03-12 14:03:50 +01:00
parent 2b07718c30
commit d5290f18cf
8 changed files with 324 additions and 431 deletions

View file

@ -26,8 +26,7 @@ abstract class Event extends IncomingMessage
{
parent::__construct($rawContent);
$this->_events = array();
$this->_eventsCount = 0;
// $this->_completed = !$this->isList();
$this->_completed = false;
}
}
@ -35,8 +34,6 @@ class UnknownEvent extends Event
{
public function __construct($rawContent = '')
{
// print_r($rawContent);
// die();
}
}
@ -60,28 +57,13 @@ class TableEnd_Event extends Event
class SCCPSoftKeySetEntry_Event extends Event
{
// This is a list of tables, each table is an entry
protected $_data;
public function __construct($rawContent)
{
parent::__construct($rawContent);
return null;
}
}
class SCCPShowSoftKeySetsComplete_Event extends Event
{
public function getListItems()
{
return intval($this->getKey('ListItems'));
}
}
class ExtensionStatus_Event extends Event
{
// this is a list of tables, each table is an entry
public function getPrivilege()
{
return $this->getKey('Privilege');
@ -110,27 +92,12 @@ class ExtensionStatus_Event extends Event
class SCCPDeviceEntry_Event extends Event
{
}
class SCCPShowDeviceComplete_Event extends Event
{
public function getListItems()
{
return intval($this->getKey('ListItems'));
}
public function __construct($rawContent)
{
parent::__construct($rawContent);
$this->_completed = $this->getKey('EventList');
// return null;
}
// This is a list of tables, each table is an entry
}
class SCCPShowDevice_Event extends Event
{
// This is a list of tables
public function getCapabilities()
{
$ret = array();
@ -153,15 +120,6 @@ class SCCPShowDevice_Event extends Event
return $ret;
}
}
class SCCPShowDevicesComplete_Event extends Event
{
public function getListItems()
{
return intval($this->getKey('ListItems'));
}
}
class SCCPDeviceButtonEntry_Event extends Event
{
}
@ -182,7 +140,67 @@ class SCCPDeviceStatisticsEntry_Event extends Event
class SCCPDeviceSpeeddialEntry_Event extends Event
{
}
class ExtensionStateListComplete_Event extends Event
abstract class ClosingEvent extends Event
{
public function __construct($message) {
parent::__construct($message);
$this->_completed = true;
}
public function getListItems() {
return intval($this->getKey('ListItems'));
}
}
class ResponseComplete_Event extends ClosingEvent
{
// dummy event to avoid unnecessary testing
public function listCorrectlyReceived($_message, $_eventCount){
return true;
}
}
class SCCPShowDeviceComplete_Event extends ClosingEvent
{
public function listCorrectlyReceived($_message, $_eventCount){
// Have end of list event. Check with number of lines received and send true if match.
// Remove 9 for the start and end events, and then 4.
if ($this->getKey('listitems') === substr_count( $_message, "\n") -13) {
return true;
}
return false;
}
}
class SCCPShowDevicesComplete_Event extends ClosingEvent
{
public function listCorrectlyReceived($_message, $_eventCount) {
// Have end of list event. Check with number of events received and send true if match.
// Remove 9 for the lines in the list start and end, and the 2 blank lines.
if ($this->getKey('listitems') === substr_count( $_message, "\n") -11) {
return true;
}
return false;
}
}
class ExtensionStateListComplete_Event extends ClosingEvent
{
public function listCorrectlyReceived($_message, $_eventCount){
// Have end of list event. Check with number of events received and send true if match.
// Remove 1 as the closing event is included in the count.
if ($this->getKey('listitems') === $_eventCount -1) {
return true;
}
return false;
}
}
class SCCPShowSoftKeySetsComplete_Event extends ClosingEvent
{
public function listCorrectlyReceived($_message, $_eventCount){
// Have the end of list event. Check the number of lines received and
// return true if match. Remove 8 for the complete event.
if ($this->getKey('listitems') === substr_count( $_message, "\n") -11) {
return true;
}
return false;
}
}

View file

@ -176,7 +176,7 @@ abstract class Message
return (double) $value;
}
default:
throw new PAMIException("Don't know how to convert: '" . $value . "'\n");
throw new AMIException("Don't know how to convert: '" . $value . "'\n");
break;
}
}
@ -242,6 +242,11 @@ abstract class IncomingMessage extends Message
return $this->rawContent;
}
public function isComplete()
{
return $this->_completed;
}
public function __sleep()
{
$ret = parent::__sleep();
@ -418,6 +423,6 @@ class SCCPConfigMetaDataAction extends ActionMessage
if ($segment != false) {
$this->setKey('Segment', $segment);
}
$this->setResponseHandler("SCCPGeneric");
$this->setResponseHandler("SCCPJSON");
}
}

View file

@ -1,12 +1,10 @@
<?php
/**
/*
*
* Core Comsnd Interface
* Response class definitions
*
* 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;
@ -26,12 +24,7 @@ abstract class Response extends IncomingMessage
parent::__construct($rawContent);
$this->_events = array();
$this->_eventsCount = 0;
$this->_completed = !$this->isList();
}
public function isComplete()
{
return $this->_completed;
$this->_completed = $this->isSuccess();
}
public function __sleep()
@ -42,20 +35,19 @@ abstract class Response extends IncomingMessage
return $ret;
}
public function addEvent($event)
{
$this->_events[] = $event;
if (stristr($event->getEventList(), 'complete') !== false
|| stristr($event->getName(), 'complete') !== false
|| stristr($event->getName(), 'DBGetResponse') !== false
) {
$this->_completed = true;
}
}
public function getEvents()
{
return $this->_events;
}
public function getClosingEvent() {
return $this->_events['ClosingEvent'];
}
public function removeClosingEvent() {
unset($this->_events['ClosingEvent']);
}
public function getCountOfEvents() {
return count($this->_events);
}
public function isSuccess()
{
@ -65,10 +57,9 @@ abstract class Response extends IncomingMessage
public function isList()
{
return
stristr($this->getKey('EventList'), 'start') !== false
|| stristr($this->getMessage(), 'follow') !== false
;
if ($this->getKey('EventList') === 'start' ) {
return true;
}
}
public function getMessage()
@ -94,36 +85,37 @@ abstract class Response extends IncomingMessage
}
}
}
class GenericResponse extends Response
{
}
//****************************************************************************
// There are two types of Response messages returned by AMI
// Self contained responses which include any data requested;
// List Responses which contain the data in event messages that follow
// the response message.Response and Event
// Following are the self contained Response classes.
//****************************************************************************
class Generic_Response extends Response
{
public function __construct($rawContent)
{
parent::__construct($rawContent);
// print_r('<br>---- r --<br>');
// print_r($rawContent);
// add dummy closing event
$this->_events['ClosingEvent'] = new ResponseComplete_Event($rawContent);
}
}
class Login_Response extends Response
class Login_Response extends Generic_Response
{
public function __construct($rawContent)
{
parent::__construct($rawContent);
return $this->isSuccess();
}
}
class Command_Response extends Response
class Command_Response extends Generic_Response
{
private $_temptable;
public function __construct($rawContent)
{
// print_r('<br>---- r --<br>');
// print_r($rawContent);
// print_r('<br>---- re --<br>');
$this->_temptable = array();
parent::__construct($rawContent);
$lines = explode(Message::EOL, $rawContent);
@ -150,26 +142,35 @@ class Command_Response extends Response
}
}
}
/* Not required $_temptable cannot be empty as has at least an actionID - see also getResult
if (!empty($this->_temptable)) {
$this->setKey('output', 'array');
}
*/
$this->_completed = $this->isSuccess();
// return $this->isSuccess();
}
public function getResult()
{
/* Below test no longer valid as key no longer set
if (stristr($this->getKey('output'), 'array') !== false) {
$result = $this->_temptable;
} else {
$result = $this->getMessage();
}
*/ return $this->_temptable;
return $this->_temptable;
}
}
class SCCPJSON_Response extends Generic_Response
{
public function __construct($rawContent)
{
parent::__construct($rawContent);
$this->getVariable($rawContent, array("DataType" => "DataType:", "JSONRAW" => "JSON:"));
if (null !== $this->getKey('JSONRAW')) {
$this->setKey('Response', 'Success');
}
}
public function getResult()
{
if (($json = json_decode($this->getKey('JSON'), true)) != false) {
return $json;
}
}
}
//***************************************************************************//
// Following are the Response classes where the data is contained in a series.
// of event messages.
class SCCPGeneric_Response extends Response
{
protected $_tables;
@ -178,18 +179,15 @@ class SCCPGeneric_Response extends Response
public function __construct($rawContent)
{
parent::__construct($rawContent);
$_fields = array("EventList" => "EventList:", "Message" => "Message:");
// Confirm that there is a list following. This overrides any setting
// made in one of the parent constructs.
$this->_completed = !$this->isList();
}
public function addEvent($event)
{
if ($event->getEventList() === 'start') {
// Have started a list of events; this may include tables
// Nothing to do with this event, only need to handle
// the events that follow
return;
}
// Start of list is handled by the isList function in the Constructor
// which also defines the list end event
if ( empty($thisSetEventEntryType)) {
// This is empty as soon as we have received a TableStart.
@ -201,8 +199,9 @@ class SCCPGeneric_Response extends Response
$thisSetEventEntryType = 'undefinedAsThisIsNotASet';
}
}
$unknownevent = "FreePBX\\modules\\Sccp_manager\\aminterface\\UnknownEvent";
if ($event instanceof $unknownevent) {
// Unknown events will cause an exception.
// All event classes must be defined within Event.class.
if (get_class($event) === 'FreePBX\modules\Sccp_manager\aminterface\UnknownEvent') {
$this->_events[] = $event;
return;
}
@ -230,18 +229,20 @@ class SCCPGeneric_Response extends Response
// If counts do not match return false and table will not be
//loaded
if ($event->getKey('TableEntries') != count($this->_tables[$event->getTableName()]['Entries'])) {
return $this->_completed = false;
return false;
}
break;
//case $eventListEndEvent;
case $this->getKey('eventListEndEvent');
// Have the list end event. The correct number of entries is verified in the event constructor
$this->_events['ClosingEvent'] = $event;
$this->eventListEndEvent = null;
//return $this->_completed = true;
break;
default:
// add regular list event
$this->_events[] = $event;
}
if ($event->getEventList() === 'Complete') {
// Received a complete eventList.
return $this->_completed = true;
}
}
protected function ConvertTableData(String $_tablename, Array $_fkey, Array $_fields)
@ -305,19 +306,6 @@ class SCCPGeneric_Response extends Response
return $result;
}
/* public function hasTable()
{
if (is_array($this->_tables)) {
return true;
}
return false;
}
public function getTableNames()
{
return (is_array($this->_tables)) ? array_keys($this->_tables) : null;
}
*/
public function Table2Array( String $tablename )
{
$result =array();
@ -330,79 +318,28 @@ class SCCPGeneric_Response extends Response
return $result;
}
/* public function Events2Array()
{
$result =array();
foreach ($this->_events as $trow) {
// $tmp_result = $trow->getKeys();
// if (is_array($tmp_result)) {
$result = array_merge($result, $trow->getKeys());
//} else {
// $result [] = $tmp_result;
// }
}
return $result;
}
public function getTable($tablename)
{
if (is_array($this->_tables) && array_key_exists($tablename, $this->_tables)) {
return $this->_tables[$tablename];
}
throw new PAMIException("No such table.");
}
public function getJSON()
{
if (strlen($this->getKey('JSON')) > 0) {
if (($json = json_decode($this->getKey('JSON'), true)) != false) {
return $json;
}
}
throw new AMIException("No JSON Key found to return.");
}
*/
public function getResult()
{
if ($this->getKey('JSON') !== null && !empty($this->getKey('JSON'))) {
if (($json = json_decode($this->getKey('JSON'), true)) != false) {
return $json;
}
} else {
return $this->getMessage();
}
}
}
class SCCPJSON_Response extends Response
{
public function __construct($rawContent)
{
parent::__construct($rawContent);
$this->getVariable($rawContent, array("DataType" => "DataType:", "JSONRAW" => "JSON:"));
if (null !== $this->getKey('JSONRAW')) {
$this->setKey('Response', 'Success');
}
return $this->isSuccess();
}
}
class SCCPShowSoftkeySets_Response extends SCCPGeneric_Response
{
public function __construct($rawContent)
{
parent::__construct($rawContent);
$this->setKey('eventlistendevent', 'SCCPShowSoftKeySetsComplete');
}
public function getResult()
{
// $_fields = array('description'=>'description','label'=>'label','lblid'=>'lblid');
return $this->ConvertTableData(
'SoftKeySets',
array('set','mode'),
array('description'=>'description','label'=>'label','lblid'=>'lblid')
);
// return $result;
}
}
@ -411,18 +348,16 @@ class SCCPShowDevices_Response extends SCCPGeneric_Response
public function __construct($rawContent)
{
parent::__construct($rawContent);
$this->setKey('eventlistendevent', 'SCCPShowDevicesComplete');
}
public function getResult()
{
// $_fields = array('mac'=>'mac','address'=>'address','descr'=>'descr','regstate'=>'status',
// 'token'=>'token','act'=>'act', 'lines'=>'lines','nat'=>'nat','regtime'=>'regtime');
return $this->ConvertTableData(
'Devices',
array('mac'),
array('mac'=>'name','address'=>'address','descr'=>'descr','regstate'=>'status',
'token'=>'token','act'=>'act', 'lines'=>'lines','nat'=>'nat','regtime'=>'regtime')
);
// return $result;
}
}
@ -431,6 +366,7 @@ class SCCPShowDevice_Response extends SCCPGeneric_Response
public function __construct($rawContent)
{
parent::__construct($rawContent);
$this->setKey('eventlistendevent', 'SCCPShowDeviceComplete');
}
public function getResult()
{
@ -440,6 +376,7 @@ class SCCPShowDevice_Response extends SCCPGeneric_Response
foreach ($this->_events as $trow) {
$result = array_merge($result, $trow->getKeys());
}
// Now handle label changes so that keys from AMI correspond to db keys in _tables
$result['Buttons'] = $this->ConvertTableData(
'Buttons',
array('id'),
@ -479,6 +416,7 @@ class ExtensionStateList_Response extends SCCPGeneric_Response
public function __construct($rawContent)
{
parent::__construct($rawContent);
$this->setKey('eventlistendevent', 'ExtensionStateListComplete');
}
public function getResult()
{

View file

@ -6,7 +6,6 @@
*
* 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;
@ -17,17 +16,15 @@ class aminterface
var $_error;
var $_config;
var $_test;
var $_countE;
private $_connect_state;
// var $ProcessingMessage;
private $_lastActionClass;
private $_lastActionId;
private $_lastRequestedResponseHandler;
private $_ProcessingMessage;
private $_DumpMessage;
private $_eventFactory;
private $_responseFactory;
private $debug_level = 1;
private $_incomingRawMessage;
private $eventListEndEvent;
public function load_subspace($parent_class = null)
{
@ -56,10 +53,10 @@ class aminterface
$this->_error = array();
$this->_config = array('host' => 'localhost', 'user' => '', 'pass' => '', 'port' => '5038', 'tsoket' => 'tcp://', 'timeout' => 30, 'enabled' => true);
$this->_eventListeners = array();
// $this->_eventFactory = new EventFactoryImpl(\Logger::getLogger('EventFactory'));
// $this->_responseFactory = new ResponseFactoryImpl(\Logger::getLogger('ResponseFactory'));
$this->_incomingQueue = array();
$this->_incomingMsgObjectList = array();
$this->_lastActionId = false;
$this->_incomingRawMessage = array();
$this->eventListEndEvent = '';
$fld_conf = array('user' => 'AMPMGRUSER', 'pass' => 'AMPMGRPASS');
if (isset($amp_conf['AMPMGRUSER'])) {
@ -86,7 +83,7 @@ class aminterface
public function info()
{
$Ver = '13.0.4';
if ($this->_config['enabled']) {
if ($this->_config['enabled']){
return array('Version' => $Ver,
'about' => 'AMI data ver: ' . $Ver, 'test' => get_declared_classes());
} else {
@ -95,9 +92,8 @@ class aminterface
}
}
/**
* Opens a tcp connection to ami.
*
/*
* Opens a socket connection to ami.
*/
public function open()
{
@ -134,7 +130,7 @@ class aminterface
return true;
}
/**
/*
* Closes the connection to ami.
*/
public function close()
@ -143,80 +139,86 @@ class aminterface
$this->_ProcessingMessage = '';
@stream_socket_shutdown($this->_socket, STREAM_SHUT_RDWR);
}
/*
* Send action message to ami, and wait for Response
*/
public function send($message)
{
$_incomingRawMessage = array();
$messageToSend = $message->serialize();
$length = strlen($messageToSend);
$this->_countE = 0;
$this->_DumpMessage = '';
$this->_lastActionId = $message->getActionId();
$this->_lastActionId = $message->getActionID();
$this->_lastRequestedResponseHandler = $message->getResponseHandler();
$this->_lastActionClass = $message;
$this->_incomingRawMessage[$this->_lastActionId] = '';
$this->eventListIsCompleted = array();
if (@fwrite($this->_socket, $messageToSend) < $length) {
$this->_errorException('Could not send message');
return false;
}
$time_connect = microtime_float();
$this->_msgToDebug(90, 'Time: '. ($time_connect));
while (1) {
// Have sent a message and now have to wait for and read the reply
// The below infinite loop waits for $this->completed to be true.
// The loop calls readBuffer, which calls GetMessages, which calls Process
// This loop then continues until we have _thisComplete as an object variable
$this->eventListIsCompleted[$this->_lastActionId] = false;
while (true) {
stream_set_timeout($this->_socket, 1);
// stream_set_timeout($this->_socket, (isset($this->socket_param['timeout']) ? $this->socket_param['timeout'] : 1));
$this->process();
$time_co = microtime_float();
$this->_msgToDebug(90, 'Time: '. ($time_co-$time_connect));
$this->readBuffer();
$info = stream_get_meta_data($this->_socket);
if ($info['timed_out'] == false) {
$response = $this->getRelated($message);
if ($response != false) {
$this->_lastActionId = false;
$this->_msgToDebug(98, '---- Dump MSG -------');
$this->_msgToDebug(98, $this->_DumpMessage);
if ($info['timed_out'] == true) {
$this->_errorException("Read waittime: " . ($this->socket_param['timeout']) . " exceeded (timeout).\n");
return false;
}
if ($this->eventListIsCompleted[$this->_lastActionId]) {
$response = $this->_incomingMsgObjectList[$this->_lastActionId];
// need to test that the list was successfully completed here
$allReceived = $response->getClosingEvent()
->listCorrectlyReceived($this->_incomingRawMessage[$this->_lastActionId],
$response->getCountOfEvents());
// now tidy up removing any temp variables or objects
$response->removeClosingEvent();
unset($_incomingRawMessage[$this->_lastActionId]);
unset($this->_incomingMsgObjectList[$this->_lastActionId]);
unset($this->_lastActionId);
if ($allReceived) {
return $response;
}
} else {
break;
// Something is missing from the events list received via AMI, or
// the control parameter at the end of the list has changed.
// This will cause an exception as returning a boolean instead of a Response
// Maybe should handle better, but
// need to break out of the loop as nothing more coming.
try {
throw new \invalidArgumentException("Counts do not match on returned AMI Result");
} catch ( \invalidArgumentException $e) {
echo substr(strrchr(get_class($response), '\\'), 1), " ", $e->getMessage(), "\n";
}
return $response;
}
}
$this->_errorException("Read waittime: " . ($this->socket_param['timeout']) . " exceeded (timeout).\n");
}
protected function getRelated($message)
protected function readBuffer ()
{
$ret = false;
$id = 0;
$id = $message->getActionID('ActionID');
if (isset($this->_incomingQueue[$id])) {
$response = $this->_incomingQueue[$id];
if ($response->isComplete()) {
unset($this->_incomingQueue[$id]);
$ret = $response;
}
$read = @fread($this->_socket, 65535);
// AMI never returns EOF
if ($read === false ) {
$this->_errorException('Error reading');
}
return $ret;
}
private function _messageToEvent($msg)
{
return $this->_eventFromRaw($msg);
// Do not return empty Messages
while ($read == "" ) {
$read = @fread($this->_socket, 65535);
}
// Add read to the rest of buffer from previous read
$this->_ProcessingMessage .= $read;
$this->getMessages();
}
protected function getMessages()
{
$msgs = array();
// Read something.
$read = @fread($this->_socket, 65535);
if ($read === false || @feof($this->_socket)) {
$this->_errorException('Error reading');
}
if ($read == "") {
usleep(100);
} else {
$this->_msgToDebug(98, '--- Not Empy AMI MSG --- ');
}
$this->_ProcessingMessage .= $read;
$this->_DumpMessage .= $read;
// Extract any complete messages and leave remainder for next read
while (($marker = strpos($this->_ProcessingMessage, aminterface\Message::EOM))) {
$msg = substr($this->_ProcessingMessage, 0, $marker);
$this->_ProcessingMessage = substr(
@ -225,56 +227,39 @@ class aminterface
);
$msgs[] = $msg;
}
return $msgs;
$this->process($msgs);
}
public function process()
public function process(array $msgs)
{
$msgs = $this->getMessages();
$this->_msgToDebug(90, $msgs);
$this->_countE++;
if ($this->_countE > 10000) {
$this->_msgToDebug(9, '--- Procecc Die, Dump --- ');
$this->_msgToDebug(9, $this->_DumpMessage);
$this->_msgToDebug(9, '--- END Procecc Die, Dump --- ');
die();
}
foreach ($msgs as $aMsg) {
$resPos = strpos($aMsg, 'Response:');
$evePos = strpos($aMsg, 'Event:');
// 2 types of message; Response or Event. Response only incudes data
// for JSON response and Command response. All other responses expect
// data in an event list - these events need to be attached to the response.
$resPos = strpos($aMsg, 'Response: '); // Have a Response message. This may not be 0.
$evePos = strpos($aMsg, 'Event: '); // Have an Event Message. This should always be 0.
// Add the incoming message to a string that can be checked
// against the completed message event when it is received.
$this->_incomingRawMessage[$this->_lastActionId] .= "\r\n\r\n" . $aMsg;
if (($resPos !== false) && (($resPos < $evePos) || $evePos === false)) {
$response = $this->_msgToResponse($aMsg); // resp Ok
$this->_incomingQueue[$this->_lastActionId] = $response;
} elseif ($evePos !== false) {
$event = $this->_messageToEvent($aMsg); // Event Ok
$this->_msgToDebug(99, '--- Response Type 2 --- ');
$this->_msgToDebug(99, $aMsg);
$this->_msgToDebug(99, '--- Event Response Type 2 --- ');
$this->_msgToDebug(99, $event);
if ($event != null) {
$response = $this->findResponse($event);
// print_r($response);
// print_r('<br>--- E2 Response Type 2 ----------<br>');
if ($response === false || $response->isComplete()) {
$this->dispatch($event); // не работает
} else {
$response->addEvent($event);
}
}
$response = $this->_responseObjFromMsg($aMsg); // resp Ok
$this->eventListEndEvent = $response->getKey('eventlistendevent');
$this->_incomingMsgObjectList[$this->_lastActionId] = $response;
$this->eventListIsCompleted[$this->_lastActionId] = $response->isComplete();
} elseif ($evePos === 0) { // Event must be at the start of the msg.
$event = $this->_eventObjFromMsg($aMsg); // Event Ok
$this->eventListIsCompleted[$this->_lastActionId] = $event->isComplete();
$this->_incomingMsgObjectList[$this->_lastActionId]->addEvent($event);
} else {
// broken ami most probably through changes in chan_sccp_b.
//sending a response with events without Event and ActionId
// AMI is sending a message which is neither a response nor an event.
$this->_msgToDebug(1, 'resp broken ami');
$bMsg = 'Event: ResponseEvent' . "\r\n";
$bMsg .= 'ActionId: ' . $this->_lastActionId . "\r\n" . $aMsg;
$event = $this->_messageToEvent($bMsg);
$response = $this->findResponse($event);
$response->addEvent($event);
$event = $this->_eventObjFromMsg($bMsg);
$this->_incomingMsgObjectList[$this->_lastActionId]->addEvent($event);
}
}
// print_r('<br>--- EProcecc ----------<br>');
}
private function _msgToDebug($level, $msg)
@ -287,74 +272,32 @@ class aminterface
print_r('<br>');
}
private function _msgToResponse($msg)
private function _responseObjFromMsg($message)
{
// print_r("<br>------------hmsg----------<br>");
// print_r($this->_lastActionClass);
// print_r($this->_lastRequestedResponseHandler);
// print_r("<br>------------emsg----------<br>");
// print_r($msg);
$response = $this->_msgFromRaw($msg, $this->_lastActionClass, $this->_lastRequestedResponseHandler);
// print_r("<br>------------rmsg----------<br>");
// print_r($response);
// print_r("<br>------------ermsg----------<br>");
$actionId = $response->getActionId();
if ($actionId === null) {
$actionId = $this->_lastActionId;
$response->setActionId($this->_lastActionId);
}
return $response;
}
/*
*
*
*/
public function _msgFromRaw($message, $requestingaction = false, $responseHandler = false)
{
$_className = false;
$responseclass = '\\FreePBX\\modules\\Sccp_manager\\aminterface\\Generic_Response';
if ($responseHandler != false) {
$_className = '\\FreePBX\\modules\\Sccp_manager\\aminterface\\' . $responseHandler . '_Response';
} elseif ($requestingaction != false) {
$_className = '\\FreePBX\\modules\\Sccp_manager\\' . substr(get_class($requestingaction), 20, -6) . '_Response';
$responseClass = '\\FreePBX\\modules\\Sccp_manager\\aminterface\\Generic_Response';
if ($this->_lastRequestedResponseHandler != false) {
$_className = '\\FreePBX\\modules\\Sccp_manager\\aminterface\\' . $this->_lastRequestedResponseHandler . '_Response';
}
if ($_className) {
if (class_exists($_className, true)) {
$responseclass = $_className;
$responseClass = $_className;
} elseif ($responseHandler != false) {
$this->_errorException('Response Class ' . $_className . ' requested via responseHandler, could not be found');
}
}
return new $responseclass($message);
}
protected function _errorException($msg)
{
$this->_error[] = $msg;
}
/*
* Replace or dublicate to AMI interface
*/
public function _eventFromRaw($message)
{
$eventStart = strpos($message, 'Event: ') + 7;
if ($eventStart > strlen($message)) {
return new aminterface\UnknownEvent($message);
$response = new $responseClass($message);
$actionId = $response->getActionID();
if ($actionId === null) {
$response->setActionId($this->_lastActionId);
}
$eventEnd = strpos($message, aminterface\Message::EOL, $eventStart);
if ($eventEnd === false) {
$eventEnd = strlen($message);
}
$name = substr($message, $eventStart, $eventEnd - $eventStart);
return $response;
}
public function _eventObjFromMsg($message)
{
$eventType = explode(aminterface\Message::EOL,$message,2);
$name = trim(explode(':',$eventType[0],2)[1]);
$className = '\\FreePBX\\modules\\Sccp_manager\\aminterface\\' . $name . '_Event';
if (class_exists($className, true) === false) {
$className = '\\FreePBX\\modules\\Sccp_manager\\aminterface\\UnknownEvent';
@ -362,40 +305,6 @@ class aminterface
return new $className($message);
}
public function _respnceFromRaw($message, $requestingaction = false, $responseHandler = false)
{
$responseclass = '\\FreePBX\\modules\\Sccp_manager\\aminterface\\Response';
$_className = false;
if ($responseHandler != false) {
$_className = '\\FreePBX\\modules\\Sccp_manager\\aminterface\\' . $responseHandler . '_Response';
} elseif ($requestingaction != false) {
$_className = '\\FreePBX\\modules\\Sccp_manager\\aminterface\\' . substr(get_class($requestingaction), 20, -6) . '_Response';
}
if ($_className) {
if (class_exists($_className, true)) {
$responseclass = $_className;
} elseif ($responseHandler != false) {
throw new AMIException('Response Class ' . $_className . ' requested via responseHandler, could not be found');
}
}
// if ($this->_logger->isDebugEnabled()) $this->_logger->debug('Created: ' . $responseclass . "\n");
print_r($responseclass);
die();
return new $responseclass($message);
}
// protected function findResponse(IncomingMessage $message) {
protected function findResponse($message)
{
$actionId = $message->getActionId();
if (isset($this->_incomingQueue[$actionId])) {
return $this->_incomingQueue[$actionId];
}
return false;
}
protected function dispatch($message)
{
print_r("<br>------------dispatch----------<br>");
@ -451,8 +360,7 @@ class aminterface
$result = array();
if ($this->_connect_state) {
$_action = new \FreePBX\modules\Sccp_manager\aminterface\ExtensionStateListAction();
$_response = $this->send($_action);
$_res = $_response->getResult();
$_res = $this->send($_action)->getResult();
foreach ($_res as $key => $value) {
foreach ($value as $key2 => $value2) {
$result[$key.'@'.$key2] = $key.'@'.$key2;
@ -467,8 +375,7 @@ class aminterface
$result = array();
if ($this->_connect_state) {
$_action = new \FreePBX\modules\Sccp_manager\aminterface\SCCPShowSoftkeySetsAction();
$_response = $this->send($_action);
$_res = $_response->getResult();
$_res = $this->send($_action)->getResult();
foreach ($_res as $key => $value) {
$result[$key] = $key;
}
@ -480,9 +387,8 @@ class aminterface
$result = array();
if ($this->_connect_state) {
$_action = new \FreePBX\modules\Sccp_manager\aminterface\SCCPShowDevicesAction();
$_response = $this->send($_action);
$result = $_response->getResult();
foreach ($result as $key => $value) {
$_res = $this->send($_action)->getResult();
foreach ($_res as $key => $value) {
$result[$key]['name'] = $key;
}
}
@ -493,8 +399,7 @@ class aminterface
$result = array();
if ($this->_connect_state) {
$_action = new \FreePBX\modules\Sccp_manager\aminterface\SCCPShowDeviceAction($devicename);
$_response = $this->send($_action);
$result = $_response->getResult();
$result = $this->send($_action)->getResult();
$result['MAC_Address'] = $result['macaddress'];
}
return $result;
@ -510,7 +415,6 @@ class aminterface
$_response = $this->send($_action);
$result['data'] = 'Device :'.$devicename.' Result: '.$_response->getMessage();
$result['Response']=$_response->getKey('Response');
// $result = $_response->getResult();
}
return $result;
}
@ -533,55 +437,55 @@ class aminterface
$metadata = $this->send($_action)->getResult();
}
//return $result;
if ($metadata && array_key_exists("Version", $metadata)) {
$result["Version"] = $metadata["Version"];
$version_parts = explode(".", $metadata["Version"]);
$result["vCode"] = 0;
if ($version_parts[0] == "4") {
if (isset($metadata['Version'])) {
$result['Version'] = $metadata['Version'];
$version_parts = explode('.', $metadata['Version']);
$result['vCode'] = 0;
if ($version_parts[0] === 4) {
switch ($version_parts[1]) {
case "1":
$result["vCode"] = 410;
case 1:
$result['vCode'] = 410;
break;
case "2":
$result["vCode"] = 420;
case 2:
$result['vCode'] = 420;
break;
case 3. . .5:
if($version_parts[2] == "3"){
$result["vCode"] = 433;
if($version_parts[2] == 3){
$result['vCode'] = 433;
} else {
$result["vCode"] = 430;
$result['vCode'] = 430;
}
break;
default:
$result["vCode"] = 400;
$result['vCode'] = 400;
break;
}
}
/* Revision got replaced by RevisionHash in 10404 (using the hash does not work) */
if (array_key_exists("Revision", $metadata)) {
if (base_convert($metadata["Revision"], 16, 10) == base_convert('702487a', 16, 10)) {
$result["vCode"] = 431;
$result['vCode'] = 431;
}
if (base_convert($metadata["Revision"], 16, 10) >= "10403") {
$result["vCode"] = 431;
$result['vCode'] = 431;
}
}
if (array_key_exists("RevisionHash", $metadata)) {
$result["RevisionHash"] = $metadata["RevisionHash"];
$result['RevisionHash'] = $metadata["RevisionHash"];
} else {
$result["RevisionHash"] = '';
$result['RevisionHash'] = '';
}
if (array_key_exists("RevisionNum", $metadata)) {
$result["RevisionNum"] = $metadata["RevisionNum"];
if ($metadata["RevisionNum"] >= "10403") { // new method, RevisionNum is incremental
$result["vCode"] = 432;
if (isset($metadata['RevisionNum'])) {
$result['RevisionNum'] = $metadata['RevisionNum'];
if ($metadata['RevisionNum'] >= 10403) { // new method, RevisionNum is incremental
$result['vCode'] = 432;
}
if ($metadata["RevisionNum"] >= "10491") { // new method, RevisionNum is incremental
$result["vCode"] = 433;
if ($metadata['RevisionNum'] >= 10491) { // new method, RevisionNum is incremental
$result['vCode'] = 433;
}
}
if (array_key_exists("ConfigureEnabled", $metadata)) {
$result["futures"] = implode(';', $metadata["ConfigureEnabled"]);
if (isset($metadata['ConfigureEnabled'])) {
$result['futures'] = implode(';', $metadata['ConfigureEnabled']);
}
}
return $result;

View file

@ -14,7 +14,8 @@ class srvinterface {
var $error;
var $_info;
var $ami_mode;
var $ami_mode = false;
var $useAmiInterface = true;
public function __construct($parent_class = null) {
$this->paren_class = $parent_class;
@ -51,9 +52,13 @@ class srvinterface {
}
}
if ($this->aminterface->status()) {
$this->aminterface->open();
// Ami is not hard disabled in Amiinterface __construct 54.
if ($this->aminterface->open()) {
// Can open a connection. Now check compatibility with chan-sccp.
// will return true if compatible.
$this->ami_mode = $this->get_compatible_sccp(true)[1];
}
}
$this->ami_mode = $this->aminterface->status();
}
public function info() {
@ -198,24 +203,35 @@ class srvinterface {
}
}
public function get_compatible_sccp() {
public function get_compatible_sccp($revNumComp=false) {
// only called with args from installer to get revision and compatibility
$res = $this->getSCCPVersion();
if (empty($res)) {
return 0;
}
switch ($res["vCode"]) {
case 0:
return 0;
$retval = 0;
break;
case 433:
return 433;
$retval = 433;
break;
case 432:
$retval = 430;
break;
case 431:
return 431;
$retval = 431;
break;
default:
return 430;
$retval = 430;
}
if ($res['RevisionNum'] < 11063) {
$this->useAmiInterface = false;
}
if ($revNumComp) {
return array($retval, $this->useAmiInterface);
}
return $retval;
}
public function getSCCPVersion() {
@ -227,7 +243,6 @@ class srvinterface {
}
public function sccp_list_keysets() {
if ($this->ami_mode) {
return $this->aminterface->sccp_list_keysets();
} else {

View file

@ -241,7 +241,7 @@ $(document).ready(function () {
}
e.preventDefault();
});
// Form.buttons - Form.adddevice
$('.futuretype').change(function (e) {
var kid = $(this).data('id');
@ -256,7 +256,7 @@ $(document).ready(function () {
}
}
});
});
$('.buttontype').change(function (e) {
@ -627,7 +627,7 @@ $(document).ready(function () {
i++;
});
}
if (datas === '') {
if (confirm(conf_msg)) {
datas = 'name[0]=all';
@ -1011,7 +1011,7 @@ function bs_page_reload()
}
function bs_alert(data, status, reload)
{
if (document.getElementById('hwalert') === null) {
if (Array.isArray(data)) {
data.forEach(function (entry) {
@ -1027,7 +1027,7 @@ function bs_alert(data, status, reload)
if (status === true) {
modal.find('.modal-title').text('Operation result');
} else {
modal.find('.modal-title').text('Erroe operation ');
modal.find('.modal-title').text('Error operation ');
}
} else {
modal.find('.modal-title').text('Operation result');
@ -1075,4 +1075,3 @@ function sleep(milliseconds)
}
}
}

View file

@ -18,6 +18,7 @@ global $version;
global $srvinterface;
global $mobile_hw;
$mobile_hw = '0';
global $useAmiInterface;
$class = "\\FreePBX\\Modules\\Sccp_manager\\srvinterface";
if (!class_exists($class, false)) {
@ -375,6 +376,7 @@ $table_req = array('sccpdevice', 'sccpline');
$ss = FreePBX::create()->Sccp_manager;
$astman = FreePBX::create()->astman;
$sccp_compatible = 0;
$chanSCCPWarning = true;
//$db_config = $db_config_v0;
$db_config = '';
@ -445,13 +447,13 @@ function CheckAsteriskVersion()
function CheckChanSCCPCompatible()
{
global $chanSCCPWarning;
global $srvinterface, $astman;
if (!$astman) {
ie_freepbx('No asterisk manager connection provided!. Installation Failed');
}
$sccp_compatible = $srvinterface->get_compatible_sccp();
outn("<li>" . _("Sccp model Compatible code : ") . $sccp_compatible . "</li>");
return $sccp_compatible;
// calling with true returns array with compatibility and RevisionNumber
return $srvinterface->get_compatible_sccp(true);
}
function InstallDB_Buttons()
@ -964,7 +966,11 @@ function Setup_RealTime()
CheckSCCPManagerDBTables($table_req);
#CheckPermissions();
CheckAsteriskVersion();
$sccp_compatible = CheckChanSCCPCompatible();
$sccp_version = array();
$sccp_version = CheckChanSCCPCompatible();
$sccp_compatible = $sccp_version[0];
$chanSCCPWarning = $sccp_version[1] ^= 1;
outn("<li>" . _("Sccp model Compatible code : ") . $resultReturned[0] . "</li>");
if ($sccp_compatible == 0) {
// die_freepbx('Chan Sccp not Found. Install it before continuing');
outn("<br>");
@ -995,6 +1001,10 @@ if (!$sccp_db_ver) {
InstallDB_createButtonConfigTrigger();
InstallDB_CreateSccpDeviceConfigView($sccp_compatible);
InstallDB_updateDBVer($sccp_compatible);
if ($chanSCCPWarning) {
outn("<br>");
outn("<font color='red'>Warning: Upgrade chan_sccp_b to use full ami functionality</font>");
}
if (!$sccp_db_ver) {
Setup_RealTime();
outn("<br>");

View file

@ -36,6 +36,10 @@ $info['aminterface'] = $this->aminterface->info();
$info['XML'] = $this->xmlinterface->info();
$info['sccp_class'] = $driver['sccp'];
$info['Core_sccp'] = array('Version' => $core['Version'], 'about' => 'Sccp ver.' . $core['Version'] . ' r' . $core['vCode'] . ' Revision :' . $core['RevisionNum'] . ' Hash :' . $core['RevisionHash']);
if (!$this->srvinterface->useAmiInterface) {
$info['aminterface']['about'] .= ' -- Disabled';
$info['Core_sccp'] = array('Version' => $core['Version'], 'about' => 'Sccp ver.' . $core['Version'] . ' r' . $core['vCode'] . ' Revision :' . $core['RevisionNum'] . ' Hash :' . $core['RevisionHash'] . ' ----Warning: Upgrade chan_sccp to use full ami functionality');
}
$info['Asterisk'] = array('Version' => FreePBX::Config()->get('ASTVERSION'), 'about' => 'Asterisk.');