From 673f6d45e0bcc91e93a05f878935114015ab0c3e Mon Sep 17 00:00:00 2001
From: steve-lad <72376554+steve-lad@users.noreply.github.com>
Date: Fri, 12 Mar 2021 14:03:50 +0100
Subject: [PATCH] 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
---
Sccp_manager.inc/aminterface/Event.class.php | 114 +++---
.../aminterface/Message.class.php | 9 +-
.../aminterface/Response.class.php | 208 ++++------
.../aminterface/aminterface.class.php | 354 +++++++-----------
Sccp_manager.inc/srvinterface.class.php | 37 +-
assets/js/sccp_manager.js | 11 +-
install.php | 18 +-
views/server.info.php | 4 +
8 files changed, 324 insertions(+), 431 deletions(-)
diff --git a/Sccp_manager.inc/aminterface/Event.class.php b/Sccp_manager.inc/aminterface/Event.class.php
index 640ffd5..a08c50f 100644
--- a/Sccp_manager.inc/aminterface/Event.class.php
+++ b/Sccp_manager.inc/aminterface/Event.class.php
@@ -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;
+ }
+}
diff --git a/Sccp_manager.inc/aminterface/Message.class.php b/Sccp_manager.inc/aminterface/Message.class.php
index b109512..d7193c4 100644
--- a/Sccp_manager.inc/aminterface/Message.class.php
+++ b/Sccp_manager.inc/aminterface/Message.class.php
@@ -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");
}
}
diff --git a/Sccp_manager.inc/aminterface/Response.class.php b/Sccp_manager.inc/aminterface/Response.class.php
index 1a00df0..f7e30ed 100644
--- a/Sccp_manager.inc/aminterface/Response.class.php
+++ b/Sccp_manager.inc/aminterface/Response.class.php
@@ -1,12 +1,10 @@
_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('
---- r --
');
-// 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('
---- r --
');
-// print_r($rawContent);
-// print_r('
---- re --
');
$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()
{
diff --git a/Sccp_manager.inc/aminterface/aminterface.class.php b/Sccp_manager.inc/aminterface/aminterface.class.php
index 428717f..008bf30 100644
--- a/Sccp_manager.inc/aminterface/aminterface.class.php
+++ b/Sccp_manager.inc/aminterface/aminterface.class.php
@@ -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('
--- E2 Response Type 2 ----------
');
- 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('
--- EProcecc ----------
');
}
private function _msgToDebug($level, $msg)
@@ -287,74 +272,32 @@ class aminterface
print_r('
');
}
- private function _msgToResponse($msg)
+ private function _responseObjFromMsg($message)
{
- // print_r("
------------hmsg----------
");
- // print_r($this->_lastActionClass);
-// print_r($this->_lastRequestedResponseHandler);
-// print_r("
------------emsg----------
");
-// print_r($msg);
- $response = $this->_msgFromRaw($msg, $this->_lastActionClass, $this->_lastRequestedResponseHandler);
-// print_r("
------------rmsg----------
");
- // print_r($response);
-// print_r("
------------ermsg----------
");
-
- $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("
------------dispatch----------
");
@@ -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;
diff --git a/Sccp_manager.inc/srvinterface.class.php b/Sccp_manager.inc/srvinterface.class.php
index 0321aa2..84302ee 100644
--- a/Sccp_manager.inc/srvinterface.class.php
+++ b/Sccp_manager.inc/srvinterface.class.php
@@ -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 {
diff --git a/assets/js/sccp_manager.js b/assets/js/sccp_manager.js
index b8502ff..c1afed0 100644
--- a/assets/js/sccp_manager.js
+++ b/assets/js/sccp_manager.js
@@ -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)
}
}
}
-
diff --git a/install.php b/install.php
index b35772f..72e3f1d 100644
--- a/install.php
+++ b/install.php
@@ -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)) {
@@ -378,6 +379,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 = '';
@@ -448,13 +450,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("