1
|
|
|
<?php |
2
|
|
|
namespace PHPDaemon\Clients\Asterisk; |
3
|
|
|
|
4
|
|
|
use PHPDaemon\Core\CallbackWrapper; |
5
|
|
|
use PHPDaemon\Core\Daemon; |
6
|
|
|
use PHPDaemon\Network\ClientConnection; |
7
|
|
|
use PHPDaemon\Structures\StackCallbacks; |
8
|
|
|
|
9
|
|
|
/** |
10
|
|
|
* Asterisk Call Manager Connection |
11
|
|
|
*/ |
12
|
|
|
class Connection extends ClientConnection |
13
|
|
|
{ |
14
|
|
|
/** |
15
|
|
|
* @TODO DESCR |
16
|
|
|
*/ |
17
|
|
|
const CONN_STATE_START = 0; |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* @TODO DESCR |
21
|
|
|
*/ |
22
|
|
|
const CONN_STATE_GOT_INITIAL_PACKET = 0.1; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* @TODO DESCR |
26
|
|
|
*/ |
27
|
|
|
const CONN_STATE_AUTH = 1; |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* @TODO DESCR |
31
|
|
|
*/ |
32
|
|
|
const CONN_STATE_LOGIN_PACKET_SENT = 1.1; |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* @TODO DESCR |
36
|
|
|
*/ |
37
|
|
|
const CONN_STATE_CHALLENGE_PACKET_SENT = 1.2; |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* @TODO DESCR |
41
|
|
|
*/ |
42
|
|
|
const CONN_STATE_LOGIN_PACKET_SENT_AFTER_CHALLENGE = 1.3; |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* @TODO DESCR |
46
|
|
|
*/ |
47
|
|
|
const CONN_STATE_HANDSHAKED_OK = 2.1; |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* @TODO DESCR |
51
|
|
|
*/ |
52
|
|
|
const CONN_STATE_HANDSHAKED_ERROR = 2.2; |
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* @TODO DESCR |
56
|
|
|
*/ |
57
|
|
|
const INPUT_STATE_START = 0; |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* @TODO DESCR |
61
|
|
|
*/ |
62
|
|
|
const INPUT_STATE_END_OF_PACKET = 1; |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* @TODO DESCR |
66
|
|
|
*/ |
67
|
|
|
const INPUT_STATE_PROCESSING = 2; |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* @var string EOL |
71
|
|
|
*/ |
72
|
|
|
public $EOL = "\r\n"; |
73
|
|
|
|
74
|
|
|
/** |
75
|
|
|
* @var string The username to access the interface |
76
|
|
|
*/ |
77
|
|
|
public $username; |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* @var string The password defined in manager interface of server |
81
|
|
|
*/ |
82
|
|
|
public $secret; |
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* @var float Connection's state |
86
|
|
|
*/ |
87
|
|
|
public $state = self::CONN_STATE_START; |
88
|
|
|
|
89
|
|
|
/** |
90
|
|
|
* @var integer Input state |
91
|
|
|
*/ |
92
|
|
|
public $instate = self::INPUT_STATE_START; |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* @var array Received packets |
96
|
|
|
*/ |
97
|
|
|
public $packets = []; |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* @var integer For composite response on action |
101
|
|
|
*/ |
102
|
|
|
public $cnt = 0; |
103
|
|
|
|
104
|
|
|
/** |
105
|
|
|
* @var array Stack of callbacks called when response received |
106
|
|
|
*/ |
107
|
|
|
public $callbacks = []; |
108
|
|
|
|
109
|
|
|
/** |
110
|
|
|
* Assertions for callbacks. |
111
|
|
|
* Assertion: if more events may follow as response this is a main part or full |
112
|
|
|
* an action complete event indicating that all data has been sent |
113
|
|
|
* @var array |
114
|
|
|
*/ |
115
|
|
|
public $assertions = []; |
116
|
|
|
|
117
|
|
|
/** |
118
|
|
|
* @var callable Callback. Called when received response on challenge action |
119
|
|
|
*/ |
120
|
|
|
public $onChallenge; |
121
|
|
|
|
122
|
|
|
/** |
123
|
|
|
* Execute the given callback when/if the connection is handshaked |
124
|
|
|
* @param callable Callback |
125
|
|
|
* @return void |
126
|
|
|
*/ |
127
|
|
View Code Duplication |
public function onConnected($cb) |
|
|
|
|
128
|
|
|
{ |
129
|
|
|
if ($this->state === self::CONN_STATE_HANDSHAKED_ERROR) { |
130
|
|
|
$cb($this); |
131
|
|
|
} elseif ($this->state === self::CONN_STATE_HANDSHAKED_OK) { |
132
|
|
|
$cb($this); |
133
|
|
|
} else { |
134
|
|
|
if (!$this->onConnected) { |
135
|
|
|
$this->onConnected = new StackCallbacks(); |
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
$this->onConnected->push($cb); |
139
|
|
|
} |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
/** |
143
|
|
|
* Called when the connection is handshaked (at low-level), and peer is ready to recv. data |
144
|
|
|
* @return void |
145
|
|
|
*/ |
146
|
|
|
public function onReady() |
147
|
|
|
{ |
148
|
|
|
if ($this->url === null) { |
149
|
|
|
return; |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
if ($this->connected && !$this->busy) { |
153
|
|
|
$this->pool->servConnFree[$this->url]->attach($this); |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
$url = parse_url($this->url); |
157
|
|
|
|
158
|
|
|
$this->username = $url['user']; |
159
|
|
|
$this->secret = $url['pass']; |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
/** |
163
|
|
|
* Called when the worker is going to shutdown |
164
|
|
|
* @return boolean Ready to shutdown? |
165
|
|
|
*/ |
166
|
|
|
public function gracefulShutdown() |
167
|
|
|
{ |
168
|
|
|
if ($this->finished) { |
169
|
|
|
return !$this->writing; |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
$this->logoff(); |
173
|
|
|
|
174
|
|
|
$this->finish(); |
175
|
|
|
|
176
|
|
|
return false; |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
/** |
180
|
|
|
* Called when session finishes |
181
|
|
|
* @return void |
182
|
|
|
*/ |
183
|
|
|
public function onFinish() |
184
|
|
|
{ |
185
|
|
|
$this->state = self::CONN_STATE_START; |
|
|
|
|
186
|
|
|
|
187
|
|
|
parent::onFinish(); |
188
|
|
|
|
189
|
|
|
$this->event('disconnect'); |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
/** |
193
|
|
|
* Called when new data received |
194
|
|
|
* @return void |
195
|
|
|
*/ |
196
|
|
|
public function onRead() |
197
|
|
|
{ |
198
|
|
|
if ($this->state === self::CONN_STATE_START) { |
199
|
|
|
if (($ver = $this->readline()) === null) { |
200
|
|
|
return; |
201
|
|
|
} |
202
|
|
|
$this->pool->setAmiVersion($this->addr, $ver); |
203
|
|
|
$this->state = self::CONN_STATE_GOT_INITIAL_PACKET; |
204
|
|
|
$this->auth(); |
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
while (($line = $this->readline()) !== null) { |
208
|
|
|
//Daemon::log('>>> '.$line); |
209
|
|
|
if ($line === '') { |
210
|
|
|
$this->instate = self::INPUT_STATE_END_OF_PACKET; |
211
|
|
|
$packet =& $this->packets[$this->cnt]; |
212
|
|
|
++$this->cnt; |
213
|
|
|
} else { |
214
|
|
|
$this->instate = self::INPUT_STATE_PROCESSING; |
215
|
|
|
list($header, $value) = Pool::extract($line); |
216
|
|
|
$this->packets[$this->cnt][$header] = $value; |
217
|
|
|
} |
218
|
|
|
|
219
|
|
|
if ((int)$this->state === self::CONN_STATE_AUTH) { |
220
|
|
|
if ($this->instate === self::INPUT_STATE_END_OF_PACKET) { |
221
|
|
|
if ($packet['response'] === 'success') { |
222
|
|
|
if ($this->state === self::CONN_STATE_CHALLENGE_PACKET_SENT) { |
223
|
|
|
if (is_callable($this->onChallenge)) { |
224
|
|
|
$func = $this->onChallenge; |
225
|
|
|
$func($this, $packet['challenge']); |
|
|
|
|
226
|
|
|
} |
227
|
|
View Code Duplication |
} else { |
|
|
|
|
228
|
|
|
if ($packet['message'] === 'authentication accepted') { |
229
|
|
|
$this->state = self::CONN_STATE_HANDSHAKED_OK; |
230
|
|
|
|
231
|
|
|
Daemon::$process->log( |
232
|
|
|
__METHOD__ . ': Authentication ok. Connected to ' . |
233
|
|
|
parse_url($this->addr, PHP_URL_HOST) |
234
|
|
|
); |
235
|
|
|
|
236
|
|
|
if ($this->onConnected) { |
237
|
|
|
$this->connected = true; |
238
|
|
|
$this->onConnected->executeAll($this); |
239
|
|
|
$this->onConnected = null; |
240
|
|
|
} |
241
|
|
|
|
242
|
|
|
$this->event('connected'); |
243
|
|
|
} |
244
|
|
|
} |
245
|
|
View Code Duplication |
} else { |
|
|
|
|
246
|
|
|
$this->state = self::CONN_STATE_HANDSHAKED_ERROR; |
247
|
|
|
|
248
|
|
|
Daemon::$process->log( |
249
|
|
|
__METHOD__ . ': Authentication failed. Connection to ' . |
250
|
|
|
parse_url($this->addr, PHP_URL_HOST) . ' failed.' |
251
|
|
|
); |
252
|
|
|
|
253
|
|
|
if ($this->onConnected) { |
254
|
|
|
$this->connected = false; |
255
|
|
|
$this->onConnected->executeAll($this); |
256
|
|
|
$this->onConnected = null; |
257
|
|
|
} |
258
|
|
|
|
259
|
|
|
$this->finish(); |
260
|
|
|
} |
261
|
|
|
|
262
|
|
|
$this->packets = []; |
263
|
|
|
} |
264
|
|
|
} elseif ($this->state === self::CONN_STATE_HANDSHAKED_OK) { |
265
|
|
|
if ($this->instate === self::INPUT_STATE_END_OF_PACKET) { |
266
|
|
|
// Event |
267
|
|
|
if (isset($packet['event']) && !isset($packet['actionid'])) { |
268
|
|
|
$this->event('event_' . $packet['event'], $packet); |
269
|
|
|
$this->event('event', $packet); |
270
|
|
|
} // Response |
271
|
|
|
elseif (isset($packet['actionid'])) { |
272
|
|
|
$action_id =& $packet['actionid']; |
273
|
|
|
|
274
|
|
|
if (isset($this->callbacks[$action_id])) { |
275
|
|
|
if (isset($this->assertions[$action_id])) { |
276
|
|
|
$this->packets[$action_id][] = $packet; |
277
|
|
|
|
278
|
|
|
$assertations = count( |
279
|
|
|
array_uintersect_uassoc( |
280
|
|
|
$this->assertions[$action_id], |
281
|
|
|
$packet, |
282
|
|
|
'strcasecmp', |
283
|
|
|
'strcasecmp' |
284
|
|
|
) |
285
|
|
|
); |
286
|
|
|
if ($assertations === count($this->assertions[$action_id])) { |
287
|
|
|
if (is_callable($this->callbacks[$action_id])) { |
288
|
|
|
$this->callbacks[$action_id]($this, $this->packets[$action_id]); |
289
|
|
|
unset($this->callbacks[$action_id]); |
290
|
|
|
} |
291
|
|
|
|
292
|
|
|
unset($this->assertions[$action_id]); |
293
|
|
|
unset($this->packets[$action_id]); |
294
|
|
|
} |
295
|
|
|
} else { |
296
|
|
|
if (is_callable($this->callbacks[$action_id])) { |
297
|
|
|
$this->callbacks[$action_id]($this, $packet); |
298
|
|
|
unset($this->callbacks[$action_id]); |
299
|
|
|
} |
300
|
|
|
} |
301
|
|
|
} |
302
|
|
|
} |
303
|
|
|
|
304
|
|
|
unset($packet); |
305
|
|
|
unset($this->packets[$this->cnt - 1]); |
306
|
|
|
} |
307
|
|
|
} |
308
|
|
|
} |
309
|
|
|
} |
310
|
|
|
|
311
|
|
|
/** |
312
|
|
|
* Send authentication packet |
313
|
|
|
* @return void |
314
|
|
|
*/ |
315
|
|
|
protected function auth() |
316
|
|
|
{ |
317
|
|
|
if ($this->state !== self::CONN_STATE_GOT_INITIAL_PACKET) { |
318
|
|
|
return; |
319
|
|
|
} |
320
|
|
|
|
321
|
|
|
if ($this->pool->config->authtype->value === 'md5') { |
322
|
|
|
$this->challenge(function ($conn, $challenge) { |
323
|
|
|
$packet = "Action: Login\r\n" |
324
|
|
|
. "AuthType: MD5\r\n" |
325
|
|
|
. "Username: " . $this->username . "\r\n" |
326
|
|
|
. "Key: " . md5($challenge . $this->secret) . "\r\n" |
327
|
|
|
. "Events: on\r\n" |
328
|
|
|
. "\r\n"; |
329
|
|
|
$this->state = self::CONN_STATE_LOGIN_PACKET_SENT_AFTER_CHALLENGE; |
330
|
|
|
$conn->write($packet); |
331
|
|
|
}); |
332
|
|
|
} else { |
333
|
|
|
$this->login(); |
334
|
|
|
} |
335
|
|
|
} |
336
|
|
|
|
337
|
|
|
/** |
338
|
|
|
* Action: Login |
339
|
|
|
* Synopsis: Login Manager |
340
|
|
|
* Privilege: <none> |
341
|
|
|
* |
342
|
|
|
* @return void |
343
|
|
|
*/ |
344
|
|
|
protected function login() |
345
|
|
|
{ |
346
|
|
|
$this->state = self::CONN_STATE_LOGIN_PACKET_SENT; |
347
|
|
|
$this->write( |
348
|
|
|
"Action: login\r\n" |
349
|
|
|
. "Username: " . $this->username . "\r\n" |
350
|
|
|
. "Secret: " . $this->secret . "\r\n" |
351
|
|
|
. "Events: on\r\n" |
352
|
|
|
. "\r\n" |
353
|
|
|
); |
354
|
|
|
} |
355
|
|
|
|
356
|
|
|
/** |
357
|
|
|
* Action: Challenge |
358
|
|
|
* Synopsis: Generate Challenge for MD5 Auth |
359
|
|
|
* Privilege: <none> |
360
|
|
|
* |
361
|
|
|
* @param callable $cb |
362
|
|
|
* @return void |
363
|
|
|
*/ |
364
|
|
|
protected function challenge($cb) |
365
|
|
|
{ |
366
|
|
|
$this->onChallenge = $cb; |
367
|
|
|
$this->state = self::CONN_STATE_CHALLENGE_PACKET_SENT; |
368
|
|
|
$this->write( |
369
|
|
|
"Action: Challenge\r\n" |
370
|
|
|
. "AuthType: MD5\r\n" |
371
|
|
|
. "\r\n" |
372
|
|
|
); |
373
|
|
|
} |
374
|
|
|
|
375
|
|
|
/** |
376
|
|
|
* Action: SIPpeers |
377
|
|
|
* Synopsis: List SIP peers (text format) |
378
|
|
|
* Privilege: system,reporting,all |
379
|
|
|
* Description: Lists SIP peers in text format with details on current status. |
380
|
|
|
* Peerlist will follow as separate events, followed by a final event called |
381
|
|
|
* PeerlistComplete. |
382
|
|
|
* Variables: |
383
|
|
|
* ActionID: <id> Action ID for this transaction. Will be returned. |
384
|
|
|
* |
385
|
|
|
* @param callable $cb Callback called when response received |
386
|
|
|
* @callback $cb ( Connection $conn, array $packet ) |
387
|
|
|
* @return void |
388
|
|
|
*/ |
389
|
|
|
public function getSipPeers($cb) |
390
|
|
|
{ |
391
|
|
|
$this->command("Action: SIPpeers\r\n", $cb, ['event' => 'peerlistcomplete']); |
392
|
|
|
} |
393
|
|
|
|
394
|
|
|
/** |
395
|
|
|
* Action: IAXpeerlist |
396
|
|
|
* Synopsis: List IAX Peers |
397
|
|
|
* Privilege: system,reporting,all |
398
|
|
|
* |
399
|
|
|
* @param callable $cb Callback called when response received |
400
|
|
|
* @callback $cb ( Connection $conn, array $packet ) |
401
|
|
|
* @return void |
402
|
|
|
*/ |
403
|
|
|
public function getIaxPeers($cb) |
404
|
|
|
{ |
405
|
|
|
$this->command("Action: IAXpeerlist\r\n", $cb, ['event' => 'peerlistcomplete']); |
406
|
|
|
} |
407
|
|
|
|
408
|
|
|
/** |
409
|
|
|
* Action: GetConfig |
410
|
|
|
* Synopsis: Retrieve configuration |
411
|
|
|
* Privilege: system,config,all |
412
|
|
|
* Description: A 'GetConfig' action will dump the contents of a configuration |
413
|
|
|
* file by category and contents or optionally by specified category only. |
414
|
|
|
* Variables: (Names marked with * are required) |
415
|
|
|
* *Filename: Configuration filename (e.g. foo.conf) |
416
|
|
|
* Category: Category in configuration file |
417
|
|
|
* |
418
|
|
|
* @param string $filename Filename |
419
|
|
|
* @param callable $cb Callback called when response received |
420
|
|
|
* @callback $cb ( Connection $conn, array $packet ) |
421
|
|
|
* @return void |
422
|
|
|
*/ |
423
|
|
|
public function getConfig($filename, $cb) |
424
|
|
|
{ |
425
|
|
|
$this->command("Action: GetConfig\r\nFilename: " . trim($filename) . "\r\n", $cb); |
426
|
|
|
} |
427
|
|
|
|
428
|
|
|
/** |
429
|
|
|
* Action: GetConfigJSON |
430
|
|
|
* Synopsis: Retrieve configuration |
431
|
|
|
* Privilege: system,config,all |
432
|
|
|
* Description: A 'GetConfigJSON' action will dump the contents of a configuration |
433
|
|
|
* file by category and contents in JSON format. This only makes sense to be used |
434
|
|
|
* using rawman over the HTTP interface. |
435
|
|
|
* Variables: |
436
|
|
|
* Filename: Configuration filename (e.g. foo.conf) |
437
|
|
|
* |
438
|
|
|
* @param string $filename Filename |
439
|
|
|
* @param callable $cb Callback called when response received |
440
|
|
|
* @callback $cb ( Connection $conn, array $packet ) |
441
|
|
|
* @return void |
442
|
|
|
*/ |
443
|
|
|
public function getConfigJSON($filename, $cb) |
444
|
|
|
{ |
445
|
|
|
$this->command("Action: GetConfigJSON\r\nFilename: " . trim($filename) . "\r\n", $cb); |
446
|
|
|
} |
447
|
|
|
|
448
|
|
|
/** |
449
|
|
|
* Action: Setvar |
450
|
|
|
* Synopsis: Set Channel Variable |
451
|
|
|
* Privilege: call,all |
452
|
|
|
* Description: Set a global or local channel variable. |
453
|
|
|
* Variables: (Names marked with * are required) |
454
|
|
|
* Channel: Channel to set variable for |
455
|
|
|
* *Variable: Variable name |
456
|
|
|
* *Value: Value |
457
|
|
|
* |
458
|
|
|
* @param string $channel |
459
|
|
|
* @param string $variable |
460
|
|
|
* @param string $value |
461
|
|
|
* @param callable $cb |
462
|
|
|
* @callback $cb ( Connection $conn, array $packet ) |
463
|
|
|
* @return void |
464
|
|
|
*/ |
465
|
|
|
public function setVar($channel, $variable, $value, $cb) |
466
|
|
|
{ |
467
|
|
|
$cmd = "Action: SetVar\r\n"; |
468
|
|
|
|
469
|
|
|
if ($channel) { |
470
|
|
|
$cmd .= "Channel: " . trim($channel) . "\r\n"; |
471
|
|
|
} |
472
|
|
|
|
473
|
|
|
if (isset($variable, $value)) { |
474
|
|
|
$cmd .= "Variable: " . trim($variable) . "\r\n" |
475
|
|
|
. "Value: " . trim($value) . "\r\n"; |
476
|
|
|
|
477
|
|
|
$this->command($cmd, $cb); |
478
|
|
|
} |
479
|
|
|
} |
480
|
|
|
|
481
|
|
|
/** |
482
|
|
|
* Action: CoreShowChannels |
483
|
|
|
* Synopsis: List currently active channels |
484
|
|
|
* Privilege: system,reporting,all |
485
|
|
|
* Description: List currently defined channels and some information |
486
|
|
|
* about them. |
487
|
|
|
* Variables: |
488
|
|
|
* ActionID: Optional Action id for message matching. |
489
|
|
|
* |
490
|
|
|
* @param callable $cb |
491
|
|
|
* @callback $cb ( Connection $conn, array $packet ) |
492
|
|
|
* @return void |
493
|
|
|
*/ |
494
|
|
|
public function coreShowChannels($cb) |
495
|
|
|
{ |
496
|
|
|
$this->command( |
497
|
|
|
"Action: CoreShowChannels\r\n", |
498
|
|
|
$cb, |
499
|
|
|
['event' => 'coreshowchannelscomplete', 'eventlist' => 'complete'] |
500
|
|
|
); |
501
|
|
|
} |
502
|
|
|
|
503
|
|
|
/** |
504
|
|
|
* Action: Status |
505
|
|
|
* Synopsis: Lists channel status |
506
|
|
|
* Privilege: system,call,reporting,all |
507
|
|
|
* Description: Lists channel status along with requested channel vars. |
508
|
|
|
* Variables: (Names marked with * are required) |
509
|
|
|
*Channel: Name of the channel to query for status |
510
|
|
|
* Variables: Comma ',' separated list of variables to include |
511
|
|
|
* ActionID: Optional ID for this transaction |
512
|
|
|
* Will return the status information of each channel along with the |
513
|
|
|
* value for the specified channel variables. |
514
|
|
|
* |
515
|
|
|
* @param callable $cb |
516
|
|
|
* @param string $channel |
|
|
|
|
517
|
|
|
* @callback $cb ( Connection $conn, array $packet ) |
518
|
|
|
* @return void |
519
|
|
|
*/ |
520
|
|
|
public function status($cb, $channel = null) |
521
|
|
|
{ |
522
|
|
|
$cmd = "Action: Status\r\n"; |
523
|
|
|
|
524
|
|
|
if ($channel !== null) { |
525
|
|
|
$cmd .= 'Channel: ' . trim($channel) . "\r\n"; |
526
|
|
|
} |
527
|
|
|
|
528
|
|
|
$this->command($cmd, $cb, ['event' => 'statuscomplete']); |
529
|
|
|
} |
530
|
|
|
|
531
|
|
|
/** |
532
|
|
|
* Action: Redirect |
533
|
|
|
* Synopsis: Redirect (transfer) a call |
534
|
|
|
* Privilege: call,all |
535
|
|
|
* Description: Redirect (transfer) a call. |
536
|
|
|
* Variables: (Names marked with * are required) |
537
|
|
|
* *Channel: Channel to redirect |
538
|
|
|
* ExtraChannel: Second call leg to transfer (optional) |
539
|
|
|
* *Exten: Extension to transfer to |
540
|
|
|
* *Context: Context to transfer to |
541
|
|
|
* *Priority: Priority to transfer to |
542
|
|
|
* ActionID: Optional Action id for message matching. |
543
|
|
|
* |
544
|
|
|
* @param array $params |
545
|
|
|
* @param callable $cb Callback called when response received |
546
|
|
|
* @callback $cb ( Connection $conn, array $packet ) |
547
|
|
|
* @return void |
548
|
|
|
*/ |
549
|
|
|
public function redirect(array $params, $cb) |
550
|
|
|
{ |
551
|
|
|
$this->command("Action: Redirect\r\n" . $this->implodeParams($params), $cb); |
552
|
|
|
} |
553
|
|
|
|
554
|
|
|
/** |
555
|
|
|
* Action: Originate |
556
|
|
|
* Synopsis: Originate a call |
557
|
|
|
* Privilege: call,all |
558
|
|
|
* Description: first the Channel is rung. Then, when that answers, the Extension is dialled within the Context |
559
|
|
|
* to initiate the other end of the call. |
560
|
|
|
* Variables: (Names marked with * are required) |
561
|
|
|
* *Channel: Channel on which to originate the call (The same as you specify in the Dial application command) |
562
|
|
|
* *Context: Context to use on connect (must use Exten & Priority with it) |
563
|
|
|
* *Exten: Extension to use on connect (must use Context & Priority with it) |
564
|
|
|
* *Priority: Priority to use on connect (must use Context & Exten with it) |
565
|
|
|
* Timeout: Timeout (in milliseconds) for the originating connection to happen(defaults to 30000 milliseconds) |
566
|
|
|
* *CallerID: CallerID to use for the call |
567
|
|
|
* Variable: Channels variables to set (max 32). Variables will be set for both channels (local and connected). |
568
|
|
|
* Account: Account code for the call |
569
|
|
|
* Application: Application to use on connect (use Data for parameters) |
570
|
|
|
* Data : Data if Application parameter is used |
571
|
|
|
* ActionID: Optional Action id for message matching. |
572
|
|
|
* |
573
|
|
|
* @param array $params |
574
|
|
|
* @param callable $cb Callback called when response received |
575
|
|
|
* @callback $cb ( Connection $conn, array $packet ) |
576
|
|
|
* @return void |
577
|
|
|
*/ |
578
|
|
|
public function originate(array $params, $cb) |
579
|
|
|
{ |
580
|
|
|
$params['Async'] = 1; |
581
|
|
|
|
582
|
|
|
$this->command("Action: Originate\r\n" . $this->implodeParams($params), $cb); |
583
|
|
|
} |
584
|
|
|
|
585
|
|
|
/** |
586
|
|
|
* Action: ExtensionState |
587
|
|
|
* Synopsis: Get an extension's state. |
588
|
|
|
* Description: function can be used to retrieve the state from any hinted extension. |
589
|
|
|
* Variables: (Names marked with * are required) |
590
|
|
|
* *Exten: Extension to get state |
591
|
|
|
* Context: Context for exten |
592
|
|
|
* ActionID: Optional Action id for message matching. |
593
|
|
|
* |
594
|
|
|
* @param array $params |
595
|
|
|
* @param callable $cb Callback called when response received |
596
|
|
|
* @callback $cb ( Connection $conn, array $packet ) |
597
|
|
|
* @return void |
598
|
|
|
*/ |
599
|
|
|
public function extensionState(array $params, $cb) |
600
|
|
|
{ |
601
|
|
|
$this->command("Action: ExtensionState\r\n" . $this->implodeParams($params), $cb); |
602
|
|
|
} |
603
|
|
|
|
604
|
|
|
/** |
605
|
|
|
* Action: Ping |
606
|
|
|
* Description: A 'Ping' action will ellicit a 'Pong' response. Used to keep the |
607
|
|
|
* manager connection open. |
608
|
|
|
* Variables: NONE |
609
|
|
|
* |
610
|
|
|
* @param callable $cb Callback called when response received |
611
|
|
|
* @callback $cb ( Connection $conn, array $packet ) |
612
|
|
|
* @return void |
613
|
|
|
*/ |
614
|
|
|
public function ping($cb) |
615
|
|
|
{ |
616
|
|
|
$this->command("Action: Ping\r\n", $cb); |
617
|
|
|
} |
618
|
|
|
|
619
|
|
|
/** |
620
|
|
|
* For almost any actions in Action: ListCommands |
621
|
|
|
* Privilege: depends on $action |
622
|
|
|
* |
623
|
|
|
* @param string $action Action |
624
|
|
|
* @param callable $cb Callback called when response received |
625
|
|
|
* @param array $params Parameters |
|
|
|
|
626
|
|
|
* @param array $assertion If more events may follow as response this is a main part or full an action complete event indicating that all data has been sent |
|
|
|
|
627
|
|
|
* @callback $cb ( Connection $conn, array $packet ) |
628
|
|
|
* @return void |
629
|
|
|
*/ |
630
|
|
|
public function action($action, $cb, array $params = null, array $assertion = null) |
631
|
|
|
{ |
632
|
|
|
$action = trim($action); |
633
|
|
|
|
634
|
|
|
$this->command("Action: {$action}\r\n" . ($params ? $this->implodeParams($params) : ''), $cb, $assertion); |
635
|
|
|
} |
636
|
|
|
|
637
|
|
|
/** |
638
|
|
|
* Action: Logoff |
639
|
|
|
* Synopsis: Logoff Manager |
640
|
|
|
* Privilege: <none> |
641
|
|
|
* Description: Logoff this manager session |
642
|
|
|
* Variables: NONE |
643
|
|
|
* |
644
|
|
|
* @param callable $cb Optional callback called when response received |
|
|
|
|
645
|
|
|
* @callback $cb ( Connection $conn, array $packet ) |
646
|
|
|
* @return void |
647
|
|
|
*/ |
648
|
|
|
public function logoff($cb = null) |
649
|
|
|
{ |
650
|
|
|
$this->command("Action: Logoff\r\n", $cb); |
|
|
|
|
651
|
|
|
} |
652
|
|
|
|
653
|
|
|
/** |
654
|
|
|
* Called when event occured |
655
|
|
|
* @deprecated Replaced with ->bind('event', ...) |
656
|
|
|
* @param callable $cb Callback |
657
|
|
|
* @return void |
658
|
|
|
*/ |
659
|
|
|
public function onEvent($cb) |
660
|
|
|
{ |
661
|
|
|
$this->bind('event', $cb); |
662
|
|
|
} |
663
|
|
|
|
664
|
|
|
/** |
665
|
|
|
* Sends arbitrary command |
666
|
|
|
* @param string $packet A packet for sending by the connected client to Asterisk |
667
|
|
|
* @param callable $cb Callback called when response received |
668
|
|
|
* @param array $assertion If more events may follow as response this is a main part or full an action complete event indicating that all data has been sent |
|
|
|
|
669
|
|
|
*/ |
670
|
|
|
protected function command($packet, $cb, $assertion = null) |
671
|
|
|
{ |
672
|
|
|
if ($this->finished) { |
673
|
|
|
throw new ConnectionFinished; |
674
|
|
|
} |
675
|
|
|
|
676
|
|
|
if ($this->state !== self::CONN_STATE_HANDSHAKED_OK) { |
677
|
|
|
return; |
678
|
|
|
} |
679
|
|
|
|
680
|
|
|
$actionId = Daemon::uniqid(); |
681
|
|
|
|
682
|
|
|
if (!is_callable($cb, true)) { |
683
|
|
|
$cb = false; |
684
|
|
|
} |
685
|
|
|
|
686
|
|
|
$this->callbacks[$actionId] = CallbackWrapper::wrap($cb); |
|
|
|
|
687
|
|
|
|
688
|
|
|
if ($assertion !== null) { |
689
|
|
|
$this->assertions[$actionId] = $assertion; |
690
|
|
|
} |
691
|
|
|
|
692
|
|
|
$this->write($packet); |
693
|
|
|
$this->write('ActionID: ' . $actionId . "\r\n\r\n"); |
694
|
|
|
} |
695
|
|
|
|
696
|
|
|
/** |
697
|
|
|
* Generate AMI packet string from associative array provided |
698
|
|
|
* @param array $params |
699
|
|
|
* @return string |
700
|
|
|
*/ |
701
|
|
|
protected function implodeParams(array $params) |
702
|
|
|
{ |
703
|
|
|
$s = ''; |
704
|
|
|
|
705
|
|
|
foreach ($params as $header => $value) { |
706
|
|
|
$s .= trim($header) . ": " . trim($value) . "\r\n"; |
707
|
|
|
} |
708
|
|
|
|
709
|
|
|
return $s; |
710
|
|
|
} |
711
|
|
|
} |
712
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.