Passed
Push — develop ( 71706d...37e66e )
by Портнов
04:36
created

AsteriskManager::ExtensionState()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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