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

AsteriskManager::Originate()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 37
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 18
c 0
b 0
f 0
dl 0
loc 37
rs 9.6666
cc 4
nc 6
nop 12

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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
}