Passed
Push — master ( 1d0c59...b84937 )
by Nikolay
19:20 queued 13:00
created

AsteriskManager::QueueRemove()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 2
1
<?php
2
/*
3
 * Copyright © MIKO LLC - All Rights Reserved
4
 * Unauthorized copying of this file, via any medium is strictly prohibited
5
 * Proprietary and confidential
6
 * Written by Alexey Portnov, 9 2020
7
 */
8
9
namespace MikoPBX\Core\Asterisk;
10
11
use MikoPBX\Core\System\Util;
12
use mysql_xdevapi\Result;
13
14
/**
15
 * Asterisk Manager class
16
 *
17
 * @link    http://www.voip-info.org/wiki-Asterisk+config+manager.conf
18
 * @link    http://www.voip-info.org/wiki-Asterisk+manager+API
19
 * @example examples/sip_show_peer.php Get information about a sip peer
20
 * @package phpAGI
21
 */
22
class AsteriskManager
23
{
24
    /**
25
     * Config variables
26
     *
27
     * @var array
28
     * @access public
29
     */
30
    public $config;
31
32
    /** @var string  */
33
    private string $listenEvents;
34
35
    /**
36
     * Socket
37
     *
38
     * @access public
39
     */
40
    public $socket = null;
41
42
    /**
43
     * Server we are connected to
44
     *
45
     * @access public
46
     * @var string
47
     */
48
    public $server;
49
50
    /**
51
     * Port on the server we are connected to
52
     *
53
     * @access public
54
     * @var int
55
     */
56
    public $port;
57
58
    /**
59
     * Parent AGI
60
     *
61
     * @access public
62
     * @var AGI
63
     */
64
    public $pagi;
65
66
    /**
67
     * Event Handlers
68
     *
69
     * @access private
70
     * @var array
71
     */
72
    private $event_handlers;
73
74
    /**
75
     * Whether we're successfully logged in
76
     *
77
     * @access private
78
     * @var bool
79
     */
80
    private $_loggedIn = false;
81
82
    /**
83
     * Constructor
84
     *
85
     * @param ?string $config    is the name of the config file to parse or a parent agi from which to read the config
86
     * @param array  $optconfig is an array of configuration vars and vals, stuffed into $this->config['asmanager']
87
     */
88
    public function __construct($config = null, $optconfig = [])
89
    {
90
        // load config
91
        if ( !is_null($config) && file_exists($config)) {
92
            $arrData = parse_ini_file($config, true);
93
            $this->config = ($arrData === false)?[]:$arrData;
94
        }
95
96
        // If optconfig is specified, stuff vals and vars into 'asmanager' config array.
97
        foreach ($optconfig as $var => $val) {
98
            $this->config['asmanager'][$var] = $val;
99
        }
100
101
        // add default values to config for uninitialized values
102
        if ( ! isset($this->config['asmanager']['server'])) {
103
            $this->config['asmanager']['server'] = 'localhost';
104
        }
105
        if ( ! isset($this->config['asmanager']['port'])) {
106
            $this->config['asmanager']['port'] = 5038;
107
        }
108
        if ( ! isset($this->config['asmanager']['username'])) {
109
            $this->config['asmanager']['username'] = 'phpagi';
110
        }
111
        if ( ! isset($this->config['asmanager']['secret'])) {
112
            $this->config['asmanager']['secret'] = 'phpagi';
113
        }
114
    }
115
116
    /**
117
     * Проверка работы сервиса AMI.
118
     *
119
     * @param string $pingTube
120
     *
121
     * @return array|bool
122
     */
123
    public function pingAMIListner($pingTube = 'CdrConnector')
124
    {
125
        // Установим фильтр на события.
126
        $params = ['Operation' => 'Add', 'Filter' => 'Event: UserEvent'];
127
        $this->sendRequestTimeout('Filter', $params);
128
        // Отправим пинг.
129
        $req        = '';
130
        $parameters = [
131
            'Action'    => 'UserEvent',
132
            'UserEvent' => $pingTube,
133
        ];
134
        foreach ($parameters as $var => $val) {
135
            $req .= "$var: $val\r\n";
136
        }
137
        $req .= "\r\n";
138
        if ( ! is_resource($this->socket)) {
139
            return [];
140
        }
141
        $this->sendDataToSocket($req);
142
143
        // Замеряем время.
144
        $time_start = $this->microtimeFloat();
145
        $result     = false;
146
        $timeout    = false;
147
148
        // Слушаем события, ждем ответ.
149
        do {
150
            $type       = '';
151
            $parameters = [];
152
            if ( ! is_resource($this->socket)) {
153
                return false;
154
            }
155
            $buffer = $this->getStringDataFromSocket();
156
            while (!empty($buffer)) {
157
                $a = strpos($buffer, ':');
158
                if ($a) {
159
                    if ( ! count($parameters)) {
160
                        $type = strtolower(substr($buffer, 0, $a));
161
                    }
162
                    $parameters[substr($buffer, 0, $a)] = substr($buffer, $a + 2);
163
                }
164
                $buffer = $this->getStringDataFromSocket();
165
            }
166
167
            if ($type === '' && count($this->Ping()) === 0) {
168
                $timeout = true;
169
            } elseif (
170
                'event' === $type
171
                && $parameters['Event'] === 'UserEvent'
172
                && "{$pingTube}Pong" === $parameters['UserEvent']) {
173
                // Ответ получен.
174
                $result = true;
175
                break;
176
            }
177
            $time = $this->microtimeFloat() - $time_start;
178
            if ($time > 5) {
179
                // Таймаут ожидания.
180
                break;
181
            }
182
        } while ( ! $timeout);
183
184
        return $result;
185
    }
186
187
    /**
188
     * Send a request
189
     *
190
     * @param string $action
191
     * @param array  $parameters
192
     *
193
     * @return array of parameters
194
     */
195
    public function sendRequestTimeout($action, $parameters = [])
196
    {
197
        if ( ! is_resource($this->socket) && !$this->connectDefault()) {
198
            return [];
199
        }
200
        // Прописываем обязательные поля.
201
        $parameters['Action']   = $action;
202
        $parameters['ActionID'] = $parameters['ActionID'] ?? "{$action}_".getmypid();
203
        $req = "";
204
        foreach ($parameters as $var => $val) {
205
            $req .= "$var: $val\r\n";
206
        }
207
        $req .= "\r\n";
208
209
        $result = $this->sendDataToSocket($req);
210
        if(!$result) {
211
            usleep(500000);
212
            if($this->connectDefault()){
213
                $result = $this->sendDataToSocket($req);
214
            }
215
        }
216
217
        $response = [];
218
        if($result){
219
            $response = $this->waitResponse(true);
220
        }
221
        return $response;
222
    }
223
224
    private function connectDefault():bool{
225
        $this->connect(null, null, null, $this->listenEvents);
226
        return $this->loggedIn();
227
    }
228
229
    /**
230
     * Wait for a response
231
     *
232
     * If a request was just sent, this will return the response.
233
     * Otherwise, it will loop forever, handling events.
234
     *
235
     * @param bool $allow_timeout if the socket times out, return an empty array
236
     *
237
     * @return array of parameters, empty on timeout
238
     */
239
    public function waitResponse($allow_timeout = false): array
240
    {
241
        $timeout = false;
242
        do {
243
            $type       = null;
244
            $parameters = [];
245
            $response   = [];
246
247
            if(!$this->waitResponseGetInitialData($response)) {
248
                return $parameters;
249
            }
250
            $buffer = $response['data']??'';
251
            while ($buffer !== '') {
252
                $a = strpos($buffer, ':');
253
                if ($a) {
254
                    $event_text = substr($buffer, $a + 2);
255
                    $this->waitResponseGetEventType($parameters, $buffer, $a, $type);
256
                    $this->waitResponseReadFollowsPart($event_text,$parameters);
257
                    $this->waitResponseReadCompletePart($event_text, $parameters);
258
                    $parameters[substr($buffer, 0, $a)] = $event_text;
259
                }
260
                $buffer = $this->getStringDataFromSocket();
261
            }
262
            $this->waitResponseProcessResponse($type, $timeout, $allow_timeout, $parameters);
263
        } while ($type !== 'response' && ! $timeout);
264
265
        return $parameters;
266
    }
267
268
    /**
269
     * Получение первичных данных из соекта.
270
     * @param $response
271
     * @return bool
272
     */
273
    private function waitResponseGetInitialData(& $response):bool{
274
        if ( !is_resource($this->socket) && !$this->connectDefault()) {
275
            return false;
276
        }
277
        $result = true;
278
        $response = $this->getDataFromSocket();
279
        if(isset($response['error'])) {
280
            usleep(500000);
281
            if($this->connectDefault()){
282
                $response = $this->getDataFromSocket();
283
            }
284
        }
285
        if(isset($response['error'])) {
286
            $result = false;
287
        }
288
        return $result;
289
    }
290
291
    /**
292
     * Ответ получен, обработка ответа.
293
     * @param $type
294
     * @param $timeout
295
     * @param $allow_timeout
296
     * @param $parameters
297
     */
298
    private function waitResponseProcessResponse($type, & $timeout, $allow_timeout, $parameters):void{
299
        switch ($type) {
300
            case '':
301
                // Timeout occured
302
                $timeout = $allow_timeout;
303
                break;
304
            case 'event':
305
                $this->processEvent($parameters);
306
                break;
307
            case 'response':
308
                break;
309
        }
310
    }
311
312
    /**
313
     * Получаем тип ивента.
314
     * @param $parameters
315
     * @param $buffer
316
     * @param $a
317
     * @param $type
318
     */
319
    private function waitResponseGetEventType($parameters, $buffer, $a, & $type):void{
320
        if ( ! count($parameters)) {
321
            $type = strtolower(substr($buffer, 0, $a));
322
        }
323
    }
324
325
    /**
326
     * Получение мноопакетного ответа.
327
     * @param $event_text
328
     * @param $parameters
329
     */
330
    private function waitResponseReadFollowsPart($event_text, & $parameters):void{
331
        if ( ($event_text === 'Follows') && !count($parameters)) {
332
            // A follows response means there is a miltiline field that follows.
333
            $parameters['data'] = '';
334
            $buff               = $this->getStringDataFromSocket();
335
            while (strpos($buff, '--END ') !== 0) {
336
                $parameters['data'] .= $buff;
337
                $buff               = $this->getStringDataFromSocket();
338
            }
339
        }
340
    }
341
342
    /**
343
     * Индивидуальная обработка для ряда запросов.
344
     * Многопакетные ответы.
345
     * @param $event_text
346
     * @param $parameters
347
     */
348
    private function waitResponseReadCompletePart($event_text, & $parameters):void{
349
        $settings = [
350
            'Queue status will follow'          => 'QueueStatusComplete',
351
            'Channels will follow'              => 'CoreShowChannelsComplete',
352
            'Result will follow'                => 'DBGetComplete',
353
            'Parked calls will follow'          => 'ParkedCallsComplete',
354
            'Peer status list will follow'      => 'PeerlistComplete',
355
            'IAX Peer status list will follow'  => 'PeerlistComplete',
356
            'Registrations will follow'         => 'RegistrationsComplete',
357
            'Meetme user list will follow'      => 'MeetmeListComplete',
358
            'Meetme conferences will follow'    => 'MeetmeListRoomsComplete',
359
            'Following are Events for each object associated with the Endpoint' => 'EndpointDetailComplete',
360
            'Following are Events for each Outbound registration'               => 'OutboundRegistrationDetailComplete',
361
            'A listing of Endpoints follows, presented as EndpointList events'  => 'EndpointListComplete'
362
        ];
363
        $eventsAsNotArray = [ 'EndpointDetailComplete' ];
364
365
        $endString = $settings[$event_text]??false;
366
        if($endString !== false){
367
            $NotArray = !in_array($endString,$eventsAsNotArray);
368
            $this->waitResponseGetSubData($parameters, $endString, $NotArray);
369
        }
370
    }
371
372
    /**
373
     * Читает данные из сокета. Если возникает ошибка возвращает ее.
374
     * @return array
375
     */
376
    private function getDataFromSocket() {
377
        $response = [];
378
        if(!is_resource($this->socket)){
379
            $response['error'] = 'Socket not init.';
380
            return $response;
381
        }
382
        try {
383
            $resultFgets = fgets($this->socket, 4096);
384
            if($resultFgets !== false){
385
                $buffer = trim($resultFgets);
386
                $response['data']  = $buffer;
387
            }else{
388
                $response['error'] = 'Read data error.';
389
            }
390
391
        }catch (\Exception $e){
392
            $response['error'] = $e->getMessage();
393
        }
394
395
        return $response;
396
    }
397
398
    /**
399
     * Читает данные из сокета
400
     * @return string
401
     */
402
    private function getStringDataFromSocket() {
403
        $response = $this->getDataFromSocket();
404
        return $response['data'] ?? '';
405
    }
406
407
    /**
408
     * Отправляет данные в сокет.
409
     * @param $req
410
     * @return bool
411
     */
412
    private function sendDataToSocket($req) : bool{
413
        if(!is_resource($this->socket)){
414
            return false;
415
        }
416
        $result = true;
417
        try {
418
            $resultWrite = fwrite($this->socket, $req);
419
            if($resultWrite === false){
420
                $result = false;
421
            }
422
        }catch (\Exception $e){
423
            $result = false;
424
        }
425
        return $result;
426
    }
427
428
    private function waitResponseGetSubData(&$parameters, $end_string = '', $event_as_array = true): void
429
    {
430
        if ( ! is_array($parameters)) {
431
            $parameters = [];
432
        }
433
        if (empty($end_string)) {
434
            return;
435
        }
436
        $parameters['data'] = [];
437
        $m                  = [];
438
        do {
439
            $value = '';
440
            $buff  = $this->getStringDataFromSocket().$value;
441
            $a_pos = strpos($buff, ':');
442
            if ( ! $a_pos) {
443
                if (count($m) > 0) {
444
                    if ($event_as_array) {
445
                        $parameters['data'][$m['Event']][] = $m;
446
                    } else {
447
                        $parameters['data'][$m['Event']] = $m;
448
                    }
449
                }
450
                $m = [];
451
                continue;
452
            }
453
454
            $key   = trim(substr($buff, 0, $a_pos));
455
            $value = trim(substr($buff, $a_pos + 1));
456
457
            $m[$key] = $value;
458
        } while ($value !== $end_string);
459
    }
460
461
    /**
462
     * Process event
463
     *
464
     * @access private
465
     *
466
     * @param array $parameters
467
     *
468
     * @return mixed result of event handler or false if no handler was found
469
     */
470
    public function processEvent($parameters)
471
    {
472
        $ret = false;
473
        $e   = strtolower($parameters['Event']);
474
475
        $handler = '';
476
        if (isset($this->event_handlers[$e])) {
477
            $handler = $this->event_handlers[$e];
478
        } elseif (isset($this->event_handlers['*'])) {
479
            $handler = $this->event_handlers['*'];
480
        }
481
        if (is_array($handler)) {
482
            call_user_func($handler, $parameters);
483
        } elseif (function_exists($handler)) {
484
            $ret = $handler($e, $parameters, $this->server, $this->port);
485
        }
486
487
        return $ret;
488
    }
489
490
    public function microtimeFloat()
491
    {
492
        [$usec, $sec] = explode(" ", microtime());
493
494
        return ((float)$usec + (float)$sec);
495
    }
496
497
    /**
498
     * Ping
499
     *
500
     * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+Ping
501
     */
502
    public function Ping()
503
    {
504
        return $this->sendRequestTimeout('Ping');
505
    }
506
507
    /**
508
     * Wait for a user events.
509
     *
510
     * @param $allow_timeout bool
511
     *
512
     * @return array of parameters, empty on timeout
513
     */
514
    public function waitUserEvent($allow_timeout = false): array
515
    {
516
        $timeout = false;
517
        do {
518
            $type       = '';
519
            $parameters = [];
520
            $buffer = $this->getStringDataFromSocket();
521
            while ($buffer !== '') {
522
                $pos = strpos($buffer, ':');
523
                if ($pos) {
524
                    if ( ! count($parameters)) {
525
                        $type = strtolower(substr($buffer, 0, $pos));
526
                    }
527
                    $parameters[substr($buffer, 0, $pos)] = substr($buffer, $pos + 2);
528
                }
529
                $buffer = $this->getStringDataFromSocket();
530
            }
531
            if ($type === '' && count($this->Ping()) === 0) {
532
                $timeout = $allow_timeout;
533
            } elseif (stripos($type, 'event')!==false ) {
534
                $this->processEvent($parameters);
535
            }
536
        } while ( ! $timeout);
537
538
        return $parameters;
539
    }
540
541
    // *********************************************************************************************************
542
    // **                       COMMANDS                                                                      **
543
    // *********************************************************************************************************
544
545
    /**
546
     * Connect to Asterisk
547
     *
548
     * @param ?string $server
549
     * @param ?string $username
550
     * @param ?string $secret
551
     * @param string $events
552
     *
553
     * @return bool true on success
554
     * @example examples/sip_show_peer.php Get information about a sip peer
555
     *
556
     */
557
    public function connect($server = null, $username = null, $secret = null, $events = 'on')
558
    {
559
        $this->listenEvents = $events;
560
        // use config if not specified
561
        if (is_null($server)) {
562
            $server = $this->config['asmanager']['server'];
563
        }
564
        if (is_null($username)) {
565
            $username = $this->config['asmanager']['username'];
566
        }
567
        if (is_null($secret)) {
568
            $secret = $this->config['asmanager']['secret'];
569
        }
570
571
        // get port from server if specified
572
        if (strpos($server, ':') !== false) {
573
            $c            = explode(':', $server);
574
            $this->server = $c[0];
575
            $this->port   = (int)$c[1];
576
        } else {
577
            $this->server = $server;
578
            $this->port   = $this->config['asmanager']['port'];
579
        }
580
581
        // connect the socket
582
        $errno   = $errstr = null;
583
        $timeout = 2;
584
585
        $this->socket = @fsockopen($this->server, $this->port, $errno, $errstr, $timeout);
586
        if ($this->socket == false) {
587
            Util::sysLogMsg('asmanager', "Unable to connect to manager {$this->server}:{$this->port} ($errno): $errstr");
588
            return false;
589
        }
590
        // PT1C;
591
        stream_set_timeout($this->socket, 1, 0);
592
593
        // read the header
594
        $str = $this->getStringDataFromSocket();
595
        if ($str === '') {
596
            // a problem.
597
            Util::sysLogMsg('asmanager', "Asterisk Manager header not received.");
598
            return false;
599
        }
600
601
        // login
602
        $res = $this->sendRequest('login', ['Username' => $username, 'Secret' => $secret, 'Events' => $events]);
603
        if ($res['Response'] != 'Success') {
604
            $this->_loggedIn = false;
605
            Util::sysLogMsg('asmanager', "Failed to login.");
606
            $this->disconnect();
607
            return false;
608
        }
609
        $this->_loggedIn = true;
610
611
        return true;
612
    }
613
614
    /**
615
     * Send a request
616
     *
617
     * @param string $action
618
     * @param array  $parameters
619
     *
620
     * @return array of parameters
621
     */
622
    public function sendRequest($action, $parameters = [])
623
    {
624
        $req = "Action: $action\r\n";
625
        foreach ($parameters as $var => $val) {
626
            $req .= "$var: $val\r\n";
627
        }
628
        $req .= "\r\n";
629
        if ( ! is_resource($this->socket)) {
630
            return [];
631
        }
632
        $this->sendDataToSocket($req);
633
634
        return $this->waitResponse();
635
    }
636
637
    /**
638
     * Disconnect
639
     *
640
     * @example examples/sip_show_peer.php Get information about a sip peer
641
     */
642
    public function disconnect()
643
    {
644
        if ($this->_loggedIn === true) {
645
            $this->logoff();
646
        }
647
        if (is_resource($this->socket)) {
648
            fclose($this->socket);
649
        }
650
    }
651
652
    /**
653
     * Logoff Manager
654
     *
655
     * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+Logoff
656
     */
657
    private function Logoff()
658
    {
659
        return $this->sendRequestTimeout('Logoff');
660
    }
661
662
    public function loggedIn()
663
    {
664
        return $this->_loggedIn;
665
    }
666
667
    /**
668
     * Set Absolute Timeout
669
     *
670
     * Hangup a channel after a certain time.
671
     *
672
     * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+AbsoluteTimeout
673
     *
674
     * @param string $channel Channel name to hangup
675
     * @param int    $timeout Maximum duration of the call (sec)
676
     *
677
     * @return array
678
     */
679
    public function AbsoluteTimeout($channel, $timeout)
680
    {
681
        return $this->sendRequest('AbsoluteTimeout', ['Channel' => $channel, 'Timeout' => $timeout]);
682
    }
683
684
    /**
685
     * Change monitoring filename of a channel
686
     *
687
     * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+ChangeMonitor
688
     *
689
     * @param string $channel the channel to record.
690
     * @param string $file    the new name of the file created in the monitor spool directory.
691
     *
692
     * @return array
693
     */
694
    public function ChangeMonitor($channel, $file)
695
    {
696
        return $this->sendRequest('ChangeMontior', ['Channel' => $channel, 'File' => $file]);
697
    }
698
699
    /**
700
     * Execute Command
701
     *
702
     * @param string $command
703
     * @param ?string $actionid message matching variable
704
     *
705
     * @return array
706
     * @example examples/sip_show_peer.php Get information about a sip peer
707
     * @link    http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+Command
708
     * @link    http://www.voip-info.org/wiki-Asterisk+CLI
709
     */
710
    public function Command($command, $actionid = null)
711
    {
712
        $parameters = ['Command' => $command];
713
        if ($actionid) {
714
            $parameters['ActionID'] = $actionid;
715
        }
716
717
        return $this->sendRequest('Command', $parameters);
718
    }
719
720
    /**
721
     * Enable/Disable sending of events to this manager
722
     *
723
     * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+Events
724
     *
725
     * @param string $eventMask is either 'on', 'off', or 'system,call,log'
726
     *
727
     * @return array
728
     */
729
    public function Events($eventMask)
730
    {
731
        return $this->sendRequest('Events', ['EventMask' => $eventMask]);
732
    }
733
734
    /**
735
     * Check Extension Status
736
     *
737
     * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+ExtensionState
738
     *
739
     * @param string $exten    Extension to check state on
740
     * @param string $context  Context for extension
741
     * @param ?string $actionid message matching variable
742
     *
743
     * @return array
744
     */
745
    public function ExtensionState($exten, $context, $actionid = null)
746
    {
747
        $parameters = ['Exten' => $exten, 'Context' => $context];
748
        if ($actionid) {
749
            $parameters['ActionID'] = $actionid;
750
        }
751
752
        return $this->sendRequest('ExtensionState', $parameters);
753
    }
754
755
    /**
756
     * Возвращает массив активных каналов.
757
     *
758
     * @param bool $group
759
     *
760
     * @return array
761
     */
762
    public function GetChannels($group = true)
763
    {
764
        $res      = $this->sendRequestTimeout('CoreShowChannels');
765
        $channels = null;
766
        if (isset($res['data']['CoreShowChannel'])) {
767
            $channels = $res['data']['CoreShowChannel'];
768
        }
769
        $channels_id = [];
770
        if (null !== $channels) {
771
            foreach ($channels as $chan) {
772
                if ($group === true) {
773
                    if ( ! isset($chan['Linkedid'])) {
774
                        continue;
775
                    }
776
                    $channels_id[$chan['Linkedid']][] = $chan['Channel'];
777
                } else {
778
                    $channels_id[] = $chan['Channel'];
779
                }
780
            }
781
        }
782
783
        return $channels_id;
784
    }
785
786
    /**
787
     * Hangup Channel
788
     *
789
     * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+Hangup
790
     *
791
     * @param string $channel The channel name to be hungup
792
     *
793
     * @return array
794
     */
795
    public function Hangup($channel)
796
    {
797
        return $this->sendRequest('Hangup', ['Channel' => $channel]);
798
    }
799
800
    /**
801
     * List IAX Peers
802
     *
803
     * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+IAXpeers
804
     * @return array
805
     */
806
    public function IAXpeerlist()
807
    {
808
        $result   = $this->sendRequestTimeout('IAXpeerlist');
809
        $data     = (isset($result['data']) && is_array($result['data'])) ? $result['data'] : [];
810
        $arr_peer = (isset($data['PeerEntry']) && is_array($data['PeerEntry'])) ? $data['PeerEntry'] : [];
811
812
        return $arr_peer;
813
    }
814
815
    /**
816
     * Возвращает регистрации пиров.
817
     *
818
     * @return array
819
     */
820
    public function IAXregistry(): array
821
    {
822
        $result   = $this->sendRequestTimeout('IAXregistry');
823
        $data     = (isset($result['data']) && is_array($result['data'])) ? $result['data'] : [];
824
        $arr_peer = (isset($data['RegistryEntry']) && is_array($data['RegistryEntry'])) ? $data['RegistryEntry'] : [];
825
826
        return $arr_peer;
827
    }
828
829
    /**
830
     * List available manager commands
831
     *
832
     * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+ListCommands
833
     *
834
     * @param string $actionid message matching variable
835
     *
836
     * @return array
837
     */
838
    public function ListCommands($actionid = null)
839
    {
840
        if ($actionid) {
841
            return $this->sendRequest('ListCommands', ['ActionID' => $actionid]);
842
        } else {
843
            return $this->sendRequest('ListCommands');
844
        }
845
    }
846
847
    /**
848
     * Отправка event в AMI.
849
     *
850
     * @param string $name
851
     * @param array  $headers
852
     *
853
     * @return array
854
     */
855
    public function UserEvent($name, $headers)
856
    {
857
        $headers['UserEvent'] = $name;
858
859
        return $this->sendRequestTimeout('UserEvent', $headers);
860
    }
861
862
    /**
863
     * Check Mailbox Message Count
864
     *
865
     * Returns number of new and old messages.
866
     *   Message: Mailbox Message Count
867
     *   Mailbox: <mailboxid>
868
     *   NewMessages: <count>
869
     *   OldMessages: <count>
870
     *
871
     * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+MailboxCount
872
     *
873
     * @param string $mailbox  Full mailbox ID <mailbox>@<vm-context>
874
     * @param string $actionid message matching variable
875
     *
876
     * @return array
877
     */
878
    public function MailboxCount($mailbox, $actionid = null)
879
    {
880
        $parameters = ['Mailbox' => $mailbox];
881
        if ($actionid) {
882
            $parameters['ActionID'] = $actionid;
883
        }
884
885
        return $this->sendRequest('MailboxCount', $parameters);
886
    }
887
888
    /**
889
     * Check Mailbox
890
     *
891
     * Returns number of messages.
892
     *   Message: Mailbox Status
893
     *   Mailbox: <mailboxid>
894
     *   Waiting: <count>
895
     *
896
     * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+MailboxStatus
897
     *
898
     * @param string $mailbox  Full mailbox ID <mailbox>@<vm-context>
899
     * @param string $actionid message matching variable
900
     *
901
     * @return array
902
     */
903
    public function MailboxStatus($mailbox, $actionid = null)
904
    {
905
        $parameters = ['Mailbox' => $mailbox];
906
        if ($actionid) {
907
            $parameters['ActionID'] = $actionid;
908
        }
909
910
        return $this->sendRequest('MailboxStatus', $parameters);
911
    }
912
913
    /**
914
     * Monitor a channel
915
     *
916
     * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+Monitor
917
     *
918
     * @param string $channel
919
     * @param string $file
920
     * @param string $format
921
     * @param bool   $mix
922
     *
923
     * @return array
924
     */
925
    public function Monitor($channel, $file = null, $format = null, $mix = null)
926
    {
927
        $parameters = ['Channel' => $channel];
928
        if ($file) {
929
            $parameters['File'] = $file;
930
        }
931
        if ($format) {
932
            $parameters['Format'] = $format;
933
        }
934
        if ( ! is_null($file)) {
935
            $parameters['Mix'] = ($mix) ? 'true' : 'false';
936
        }
937
938
        return $this->sendRequest('Monitor', $parameters);
939
    }
940
941
    /**
942
     * MixMonitor a channel
943
     *
944
     * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+Monitor
945
     *
946
     * @param string $channel
947
     * @param string $file
948
     * @param string $options
949
     * @param string $command
950
     *
951
     * @return array
952
     */
953
    public function MixMonitor($channel, $file, $options, $command)
954
    {
955
        $parameters            = ['Channel' => $channel];
956
        $parameters['File']    = $file;
957
        $parameters['options'] = $options;
958
        $parameters['Command'] = $command;
959
960
        return $this->sendRequestTimeout('MixMonitor', $parameters);
961
    }
962
963
    /**
964
     * StopMixMonitor a channel
965
     *
966
     * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+Monitor
967
     *
968
     * @param string $channel
969
     *
970
     * @return array
971
     */
972
    public function StopMixMonitor($channel)
973
    {
974
        $parameters = ['Channel' => $channel];
975
976
        return $this->sendRequestTimeout('StopMixMonitor', $parameters);
977
    }
978
979
    /**
980
     * DBGet a channel
981
     *
982
     * @param string $Family
983
     * @param string $Key
984
     * @param string $Val
985
     *
986
     * @return array
987
     */
988
    public function DBPut($Family, $Key, $Val = '')
989
    {
990
        $parameters = ['Family' => $Family, 'Key' => $Key, 'Val' => $Val];
991
        $res_data   = $this->sendRequestTimeout('DBPut', $parameters);
992
993
        return $res_data;
994
    }
995
996
    /**
997
     * MeetmeListRooms
998
     *
999
     * @param $ActionID
1000
     *
1001
     * @return array
1002
     */
1003
    public function MeetmeListRooms($ActionID = ''): array
1004
    {
1005
        if (empty($ActionID)) {
1006
            $ActionID = Util::generateRandomString(5);
1007
        }
1008
        $parameters = [
1009
            'ActionID' => $ActionID,
1010
        ];
1011
1012
        return $this->sendRequestTimeout('MeetmeListRooms', $parameters);
1013
    }
1014
1015
    /**
1016
     * DBGet a channel
1017
     *
1018
     * @param string $Family
1019
     * @param string $Key
1020
     *
1021
     * @return array
1022
     */
1023
    public function DBGet($Family, $Key)
1024
    {
1025
        $parameters = ['Family' => $Family, 'Key' => $Key];
1026
1027
        return $this->sendRequestTimeout('DBGet', $parameters);
1028
    }
1029
1030
    /**
1031
     * Originate Call
1032
     *
1033
     * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+Originate
1034
     *
1035
     * @param string $channel     Channel name to call
1036
     * @param ?string $exten       Extension to use (requires 'Context' and 'Priority')
1037
     * @param ?string $context     Context to use (requires 'Exten' and 'Priority')
1038
     * @param ?string $priority    Priority to use (requires 'Exten' and 'Context')
1039
     * @param ?string $application Application to use
1040
     * @param ?string $data        Data to use (requires 'Application')
1041
     * @param ?int    $timeout     How long to wait for call to be answered (in ms)
1042
     * @param ?string $callerid    Caller ID to be set on the outgoing channel
1043
     * @param ?string $variable    Channel variable to set (VAR1=value1|VAR2=value2)
1044
     * @param ?string $account     Account code
1045
     * @param bool   $async       true fast origination
1046
     * @param ?string $actionid    message matching variable
1047
     *
1048
     * @return array
1049
     */
1050
    public function Originate(
1051
        $channel,
1052
        $exten = null,
1053
        $context = null,
1054
        $priority = null,
1055
        $application = null,
1056
        $data = null,
1057
        $timeout = null,
1058
        $callerid = null,
1059
        $variable = null,
1060
        $account = null,
1061
        $async = true,
1062
        $actionid = null
1063
    ) {
1064
1065
        $parameters = [
1066
            'Exten'         => $exten,
1067
            'Context'       => $context,
1068
            'Priority'      => $priority,
1069
            'Application'   => $application,
1070
            'Data'          => $data,
1071
            'Timeout'       => $timeout,
1072
            'CallerID'      => $callerid,
1073
            'Variable'      => $variable,
1074
            'Account'       => $account,
1075
            'ActionID'      => $actionid
1076
        ];
1077
        $keys = array_keys($parameters);
1078
        foreach ($keys as $key){
1079
            if(empty($parameters[$key])){
1080
                unset($parameters[$key]);
1081
            }
1082
        }
1083
        $parameters['Channel'] = $channel;
1084
        $parameters['Async']   = ($async === true) ? 'true' : 'false';
1085
1086
        return $this->sendRequest('Originate', $parameters);
1087
    }
1088
1089
    /**
1090
     * List parked calls
1091
     *
1092
     * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+ParkedCalls
1093
     *
1094
     * @param string $actionid   message matching variable
1095
     * @param string $parkinglot message matching variable
1096
     *
1097
     * @return array
1098
     */
1099
    public function ParkedCalls($parkinglot = null, $actionid = null)
1100
    {
1101
        $parameters = [];
1102
        if ($actionid) {
1103
            $parameters['ActionID'] = $actionid;
1104
        }
1105
        if ($parkinglot) {
1106
            $parameters['ParkingLot'] = $parkinglot;
1107
        }
1108
        $result = $this->sendRequestTimeout('ParkedCalls', $parameters);
1109
1110
        return $result;
1111
    }
1112
1113
    /**
1114
     * Queue Add
1115
     *
1116
     * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+QueueAdd
1117
     *
1118
     * @param string $queue
1119
     * @param string $interface
1120
     * @param int    $penalty
1121
     *
1122
     * @return array
1123
     */
1124
    public function QueueAdd($queue, $interface, $penalty = 0)
1125
    {
1126
        $parameters = ['Queue' => $queue, 'Interface' => $interface];
1127
        if ($penalty) {
1128
            $parameters['Penalty'] = $penalty;
1129
        }
1130
1131
        return $this->sendRequest('QueueAdd', $parameters);
1132
    }
1133
1134
    /**
1135
     * Queue Remove
1136
     *
1137
     * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+QueueRemove
1138
     *
1139
     * @param string $queue
1140
     * @param string $interface
1141
     *
1142
     * @return array
1143
     */
1144
    public function QueueRemove($queue, $interface)
1145
    {
1146
        return $this->sendRequest('QueueRemove', ['Queue' => $queue, 'Interface' => $interface]);
1147
    }
1148
1149
    /**
1150
     * Queues
1151
     *
1152
     * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+Queues
1153
     */
1154
    public function Queues()
1155
    {
1156
        return $this->sendRequest('Queues');
1157
    }
1158
1159
    /**
1160
     * Queue Status
1161
     *
1162
     * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+QueueStatus
1163
     *
1164
     * @param string $actionid message matching variable
1165
     *
1166
     * @return array
1167
     */
1168
    public function QueueStatus($actionid = null)
1169
    {
1170
        if ($actionid) {
1171
            return $this->sendRequest('QueueStatus', ['ActionID' => $actionid]);
1172
        } else {
1173
            return $this->sendRequest('QueueStatus');
1174
        }
1175
    }
1176
1177
    /**
1178
     * Redirect
1179
     *
1180
     * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+Redirect
1181
     *
1182
     * @param string $channel
1183
     * @param string $extrachannel
1184
     * @param string $exten
1185
     * @param string $context
1186
     * @param string $priority
1187
     *
1188
     * @return array
1189
     */
1190
    public function Redirect($channel, $extrachannel, $exten, $context, $priority)
1191
    {
1192
        return $this->sendRequest(
1193
            'Redirect',
1194
            [
1195
                'Channel'      => $channel,
1196
                'ExtraChannel' => $extrachannel,
1197
                'Exten'        => $exten,
1198
                'Context'      => $context,
1199
                'Priority'     => $priority,
1200
            ]
1201
        );
1202
    }
1203
1204
    /**
1205
     * Set the CDR UserField
1206
     *
1207
     * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+SetCDRUserField
1208
     *
1209
     * @param string $userfield
1210
     * @param string $channel
1211
     * @param string $append
1212
     *
1213
     * @return array
1214
     */
1215
    public function SetCDRUserField($userfield, $channel, $append = null)
1216
    {
1217
        $parameters = ['UserField' => $userfield, 'Channel' => $channel];
1218
        if ($append) {
1219
            $parameters['Append'] = $append;
1220
        }
1221
1222
        return $this->sendRequest('SetCDRUserField', $parameters);
1223
    }
1224
1225
    /**
1226
     * Set Channel Variable
1227
     *
1228
     * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+SetVar
1229
     *
1230
     * @param string $channel  Channel to set variable for
1231
     * @param string $variable name
1232
     * @param string $value
1233
     *
1234
     * @return array
1235
     */
1236
    public function SetVar($channel, $variable, $value)
1237
    {
1238
        $params = [
1239
            'Channel'   => $channel,
1240
            'Variable'  => $variable,
1241
            'Value'     => $value
1242
        ];
1243
        return $this->sendRequestTimeout('SetVar', $params);
1244
    }
1245
1246
    /**
1247
     * Channel Status
1248
     *
1249
     * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+Status
1250
     *
1251
     * @param string $channel
1252
     * @param null|string $actionid message matching variable
1253
     *
1254
     * @return array
1255
     */
1256
    public function Status($channel, $actionid = null)
1257
    {
1258
        $parameters = ['Channel' => $channel];
1259
        if ($actionid) {
1260
            $parameters['ActionID'] = $actionid;
1261
        }
1262
1263
        return $this->sendRequest('Status', $parameters);
1264
    }
1265
1266
    /*
1267
    * MIKO Start.
1268
    */
1269
1270
    /**
1271
     * Stop monitoring a channel
1272
     *
1273
     * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+StopMonitor
1274
     *
1275
     * @param string $channel
1276
     *
1277
     * @return array
1278
     */
1279
    public function StopMonitor($channel)
1280
    {
1281
        return $this->sendRequest('StopMonitor', ['Channel' => $channel]);
1282
    }
1283
1284
    /**
1285
     * Полученире текущих регистраций.
1286
     *
1287
     * @return array
1288
     */
1289
    public function getSipRegistry()
1290
    {
1291
        $peers  = [];
1292
        $result = $this->sendRequestTimeout('SIPshowregistry');
1293
        if ($result['data'] !== null && $result['data']['RegistryEntry'] !== null) {
1294
            foreach ($result['data']['RegistryEntry'] as $peer) {
1295
                $peers[] = [
1296
                    'id'       => $peer['Username'],
1297
                    'state'    => strtoupper($peer['State']),
1298
                    'host'     => $peer['Host'],
1299
                    'username' => $peer['Username'],
1300
                ];
1301
            }
1302
        }
1303
1304
        return $peers;
1305
    }
1306
1307
    /**
1308
     * Полученире текущих регистраций.
1309
     *
1310
     * @return array
1311
     */
1312
    public function getPjSipRegistry(): array
1313
    {
1314
        $peers  = [];
1315
        $result = $this->sendRequestTimeout('PJSIPShowRegistrationsOutbound');
1316
        if (isset($result['data']['OutboundRegistrationDetail'])) {
1317
            foreach ($result['data']['OutboundRegistrationDetail'] as $peer) {
1318
                [$sip, $host, $port] = explode(':', $peer['ServerUri']);
1319
                $peers[] = [
1320
                    'id'       => str_replace('REG-', '', $peer['ObjectName']),
1321
                    'state'    => strtoupper($peer['Status']),
1322
                    'host'     => $host,
1323
                    'username' => $peer['ContactUser'],
1324
                ];
1325
                unset($sip, $port);
1326
            }
1327
        }
1328
1329
        return $peers;
1330
    }
1331
1332
    /**
1333
     * Полученире текущих регистраций.
1334
     *
1335
     * @return array
1336
     */
1337
    public function getPjSipPeers(): array
1338
    {
1339
        $peers  = [];
1340
        $result = $this->sendRequestTimeout('PJSIPShowEndpoints');
1341
        if (isset($result['data']['EndpointList'])) {
1342
            foreach ($result['data']['EndpointList'] as $peer) {
1343
                if ($peer['ObjectName'] === 'anonymous') {
1344
                    continue;
1345
                }
1346
1347
                $state_array = [
1348
                    'Not in use' => 'OK',
1349
                    'Busy'       => 'OK',
1350
                ];
1351
                $state       = $state_array[$peer['DeviceState']] ?? 'UNKNOWN';
1352
1353
                $peers[] = [
1354
                    'id'    => $peer['ObjectName'],
1355
                    'state' => strtoupper($state),
1356
                ];
1357
            }
1358
        }
1359
1360
        return $peers;
1361
    }
1362
1363
    /**
1364
     * Получение статусов пиров.
1365
     *
1366
     * @return array
1367
     */
1368
    public function getSipPeers()
1369
    {
1370
        $peers = [];
1371
        $res   = $this->sendRequestTimeout('SIPpeers');
1372
        if (isset($res['data']) && $res['data'] != null && $res['data']['PeerEntry'] != null) {
1373
            foreach ($res['data']['PeerEntry'] as $peer) {
1374
                if ( ! is_numeric($peer['ObjectName'])) {
1375
                    continue;
1376
                }
1377
                // if ('Unmonitored' == $peer['Status']) continue;
1378
                $arr_status = explode(' ', $peer['Status']);
1379
                $peers[]    = ['id' => $peer['ObjectName'], 'state' => strtoupper($arr_status[0]),];
1380
            }
1381
        }
1382
1383
        return $peers;
1384
    }
1385
1386
    /**
1387
     * Получение статуса конкретного пира.
1388
     *
1389
     * @param $peer
1390
     *
1391
     * @return array
1392
     */
1393
    public function getSipPeer($peer)
1394
    {
1395
        $parameters           = ['Peer' => trim($peer)];
1396
        $res                  = $this->sendRequestTimeout('SIPshowpeer', $parameters);
1397
        $arr_status           = explode(' ', $res['Status']);
1398
        $res['state']         = strtoupper($arr_status[0]);
1399
        $res['time-response'] = strtoupper(str_replace(['(', ')'], '', $arr_status[1]));
1400
1401
        return $res;
1402
    }
1403
1404
    /**
1405
     * Получение статуса конкретного пира.
1406
     *
1407
     * @param $peer
1408
     *
1409
     * @return array
1410
     */
1411
    public function getPjSipPeer($peer)
1412
    {
1413
        $result     = [];
1414
        $parameters = ['Endpoint' => trim($peer)];
1415
        $res        = $this->sendRequestTimeout('PJSIPShowEndpoint', $parameters);
1416
1417
        if (isset($res['data']['ContactStatusDetail'])) {
1418
            $result = $res['data']['ContactStatusDetail'];
1419
        }
1420
        $result['state'] = isset($result['URI']) && ! empty($result['URI']) ? 'OK' : 'UNKNOWN';
1421
1422
        return $result;
1423
    }
1424
1425
1426
    /*
1427
    * MIKO End.
1428
    */
1429
1430
    // *********************************************************************************************************
1431
    // **                       MISC                                                                          **
1432
    // *********************************************************************************************************
1433
1434
    /**
1435
     * @param       $conference
1436
     * @param array $vars
1437
     *
1438
     * @return array
1439
     */
1440
    public function meetMeCollectInfo($conference, $vars = []): array
1441
    {
1442
        $result    = [];
1443
        $conf_data = $this->MeetmeList($conference);
1444
        if ( ! isset($conf_data['data']['MeetmeList'])) {
1445
            return $result;
1446
        }
1447
        foreach ($conf_data['data']['MeetmeList'] as $user_data) {
1448
            $user_data['linkedid']  = $this->GetVar($user_data['Channel'], 'CDR(linkedid)', null, false);
1449
            $user_data['meetme_id'] = $this->GetVar($user_data['Channel'], 'MEETMEUNIQUEID', null, false);
1450
1451
            foreach ($vars as $var) {
1452
                $user_data[$var] = $this->GetVar($user_data['Channel'], $var, null, false);
1453
            }
1454
1455
            $result[] = $user_data;
1456
        }
1457
1458
        return $result;
1459
    }
1460
1461
    /**
1462
     * MeetmeList
1463
     *
1464
     * @param $Conference
1465
     * @param $ActionID
1466
     *
1467
     * @return array
1468
     */
1469
    public function MeetmeList($Conference, $ActionID = ''): array
1470
    {
1471
        if (empty($ActionID)) {
1472
            $ActionID = Util::generateRandomString(5);
1473
        }
1474
        $parameters = [
1475
            'Conference' => $Conference,
1476
            'ActionID'   => $ActionID,
1477
        ];
1478
1479
        return $this->sendRequestTimeout('MeetmeList', $parameters);
1480
    }
1481
1482
    /**
1483
     * Gets a Channel Variable
1484
     *
1485
     * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+GetVar
1486
     * @link http://www.voip-info.org/wiki-Asterisk+variables
1487
     *
1488
     * @param string      $channel  Channel to read variable from
1489
     * @param string      $variable
1490
     * @param null|string $actionId message matching variable
1491
     * @param bool        $retArray
1492
     *
1493
     * @return string | array
1494
     */
1495
    public function GetVar($channel, $variable, $actionId = null, $retArray = true)
1496
    {
1497
        $parameters = ['Channel' => $channel, 'Variable' => $variable];
1498
        if ($actionId) {
1499
            $parameters['ActionID'] = $actionId;
1500
        }
1501
1502
        $data = $this->sendRequestTimeout('GetVar', $parameters);
1503
        if ($retArray !== true) {
1504
            $data = (isset($data['Value']) && $data['Value']) ? $data['Value'] : '';
1505
        }
1506
1507
        return $data;
1508
    }
1509
1510
    /**
1511
     * Add event handler
1512
     *
1513
     * Known Events include ( http://www.voip-info.org/wiki-asterisk+manager+events )
1514
     *   Link - Fired when two voice channels are linked together and voice data exchange commences.
1515
     *   Unlink - Fired when a link between two voice channels is discontinued, for example, just before call
1516
     *   completion. Newexten - Hangup - Newchannel - Newstate - Reload - Fired when the "RELOAD" console command is
1517
     *   executed. Shutdown - ExtensionStatus - Rename - Newcallerid - Alarm - AlarmClear - Agentcallbacklogoff -
1518
     *   Agentcallbacklogin - Agentlogoff - MeetmeJoin - MessageWaiting - join - leave - AgentCalled - ParkedCall -
1519
     *   Fired after ParkedCalls Cdr - ParkedCallsComplete - QueueParams - QueueMember - QueueStatusEnd - Status -
1520
     *   StatusComplete - ZapShowChannels - Fired after ZapShowChannels ZapShowChannelsComplete -
1521
     *
1522
     * @param string         $event    type or * for default handler
1523
     * @param string | array $callback function
1524
     *
1525
     * @return bool sucess
1526
     */
1527
    public function addEventHandler($event, $callback)
1528
    {
1529
        $event = strtolower($event);
1530
        if (isset($this->event_handlers[$event])) {
1531
            return false;
1532
        }
1533
        $this->event_handlers[$event] = $callback;
1534
1535
        return true;
1536
    }
1537
}