File Source for phpagi.php

# Source for file phpagi.php

Documentation is available at phpagi.php

1. <?php
3. /**
4. * phpagi.php : PHP AGI Functions for Asterisk
5. * Website: http://phpagi.sourceforge.net/
6. *
7. * $Id: phpagi.php,v 2.20 2010/09/30 02:21:00 masham Exp $
8. *
9. * Copyright (c) 2003 - 2010 Matthew Asham <matthew@ochrelabs.com>, David Eder <david@eder.us> and others
10. * All Rights Reserved.
11. *
12. * This software is released under the terms of the GNU Lesser General Public License v2.1
13. * A copy of which is available from http://www.gnu.org/copyleft/lesser.html
14. *
15. * We would be happy to list your phpagi based application on the phpagi
16. * website. Drop me an Email if you'd like us to list your program.
17. *
18. *
19. * Written for PHP 4.3.4, should work with older PHP 4.x versions.
20. *
21. * Please submit bug reports, patches, etc to http://sourceforge.net/projects/phpagi/
22. * Gracias. :)
23. *
24. *
25. * @package phpAGI
26. * @version 2.0
27. */
29. if (!class_exists('AGI_AsteriskManager'))
30. {
31. require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'phpagi-asmanager.php');
32. }
34. define('AST_CONFIG_DIR', '/etc/asterisk/');
35. define('AST_SPOOL_DIR', '/var/spool/asterisk/');
36. define('AST_TMP_DIR', AST_SPOOL_DIR . '/tmp/');
37. define('DEFAULT_PHPAGI_CONFIG', AST_CONFIG_DIR . '/phpagi.conf');
39. define('AST_DIGIT_ANY', '0123456789#*');
41. define('AGIRES_OK', 200);
43. define('AST_STATE_DOWN', 0);
44. define('AST_STATE_RESERVED', 1);
45. define('AST_STATE_OFFHOOK', 2);
46. define('AST_STATE_DIALING', 3);
47. define('AST_STATE_RING', 4);
48. define('AST_STATE_RINGING', 5);
49. define('AST_STATE_UP', 6);
50. define('AST_STATE_BUSY', 7);
51. define('AST_STATE_DIALING_OFFHOOK', 8);
52. define('AST_STATE_PRERING', 9);
54. define('AUDIO_FILENO', 3); // STDERR_FILENO + 1
56. /**
57. * AGI class
58. *
59. * @package phpAGI
60. * @link http://www.voip-info.org/wiki-Asterisk+agi
61. * @example examples/dtmf.php Get DTMF tones from the user and say the digits
62. * @example examples/input.php Get text input from the user and say it back
63. * @example examples/ping.php Ping an IP address
64. */
65. class AGI
66. {
67. /**
68. * Request variables read in on initialization.
69. *
70. * Often contains any/all of the following:
71. * agi_request - name of agi script
72. * agi_channel - current channel
73. * agi_language - current language
74. * agi_type - channel type (SIP, ZAP, IAX, ...)
75. * agi_uniqueid - unique id based on unix time
76. * agi_callerid - callerID string
77. * agi_dnid - dialed number id
78. * agi_rdnis - referring DNIS number
79. * agi_context - current context
80. * agi_extension - extension dialed
81. * agi_priority - current priority
82. * agi_enhanced - value is 1.0 if started as an EAGI script
83. * agi_accountcode - set by SetAccount in the dialplan
84. * agi_network - value is yes if this is a fastagi
85. * agi_network_script - name of the script to execute
86. *
87. * NOTE: program arguments are still in $_SERVER['argv'].
88. *
89. * @var array
90. * @access public
91. */
92. var $request;
94. /**
95. * Config variables
96. *
97. * @var array
98. * @access public
99. */
100. var $config;
102. /**
103. * Asterisk Manager
104. *
105. * @var AGI_AsteriskManager
106. * @access public
107. */
108. var $asmanager;
110. /**
111. * Input Stream
112. *
113. * @access private
114. */
115. var $in = NULL;
117. /**
118. * Output Stream
119. *
120. * @access private
121. */
122. var $out = NULL;
124. /**
125. * Audio Stream
126. *
127. * @access public
128. */
129. var $audio = NULL;
132. /**
133. * Application option delimiter
134. *
135. * @access public
136. */
137. public $option_delim = ",";
139. /**
140. * Constructor
141. *
142. * @param string $config is the name of the config file to parse
143. * @param array $optconfig is an array of configuration vars and vals, stuffed into $this->config['phpagi']
144. */
145. function __construct($config=NULL, $optconfig=array())
146. {
147. // load config
148. if(!is_null($config) && file_exists($config))
149. $this->config = parse_ini_file($config, true);
150. elseif(file_exists(DEFAULT_PHPAGI_CONFIG))
151. $this->config = parse_ini_file(DEFAULT_PHPAGI_CONFIG, true);
153. // If optconfig is specified, stuff vals and vars into 'phpagi' config array.
154. foreach($optconfig as $var=>$val)
155. $this->config['phpagi'][$var] = $val;
157. // add default values to config for uninitialized values
158. if(!isset($this->config['phpagi']['error_handler'])) $this->config['phpagi']['error_handler'] = true;
159. if(!isset($this->config['phpagi']['debug'])) $this->config['phpagi']['debug'] = false;
160. if(!isset($this->config['phpagi']['admin'])) $this->config['phpagi']['admin'] = NULL;
161. if(!isset($this->config['phpagi']['tempdir'])) $this->config['phpagi']['tempdir'] = AST_TMP_DIR;
163. // festival TTS config
164. if(!isset($this->config['festival']['text2wave'])) $this->config['festival']['text2wave'] = $this->which('text2wave');
166. // swift TTS config
167. if(!isset($this->config['cepstral']['swift'])) $this->config['cepstral']['swift'] = $this->which('swift');
169. ob_implicit_flush(true);
171. // open stdin & stdout
172. $this->in = defined('STDIN') ? STDIN : fopen('php://stdin', 'r');
173. $this->out = defined('STDOUT') ? STDOUT : fopen('php://stdout', 'w');
175. // initialize error handler
176. if($this->config['phpagi']['error_handler'] == true)
177. {
178. set_error_handler('phpagi_error_handler');
179. global $phpagi_error_handler_email;
180. $phpagi_error_handler_email = $this->config['phpagi']['admin'];
181. error_reporting(E_ALL);
182. }
184. // make sure temp folder exists
185. $this->make_folder($this->config['phpagi']['tempdir']);
187. // read the request
188. $str = fgets($this->in);
189. while($str != "\n")
190. {
191. $this->request[substr($str, 0, strpos($str, ':'))] = trim(substr($str, strpos($str, ':') + 1));
192. $str = fgets($this->in);
193. }
195. // open audio if eagi detected
196. if($this->request['agi_enhanced'] == '1.0')
197. {
198. if(file_exists('/proc/' . getmypid() . '/fd/3'))
199. $this->audio = fopen('/proc/' . getmypid() . '/fd/3', 'r');
200. elseif(file_exists('/dev/fd/3'))
201. {
202. // may need to mount fdescfs
203. $this->audio = fopen('/dev/fd/3', 'r');
204. }
205. else
206. $this->conlog('Unable to open audio stream');
208. if($this->audio) stream_set_blocking($this->audio, 0);
209. }
211. $this->conlog('AGI Request:');
212. $this->conlog(print_r($this->request, true));
213. $this->conlog('PHPAGI internal configuration:');
214. $this->conlog(print_r($this->config, true));
215. }
217. // *********************************************************************************************************
218. // ** COMMANDS **
219. // *********************************************************************************************************
221. /**
222. * Answer channel if not already in answer state.
223. *
224. * @link http://www.voip-info.org/wiki-answer
225. * @example examples/dtmf.php Get DTMF tones from the user and say the digits
226. * @example examples/input.php Get text input from the user and say it back
227. * @example examples/ping.php Ping an IP address
228. *
229. * @return array, see evaluate for return information. ['result'] is 0 on success, -1 on failure.
230. */
231. function answer()
232. {
233. return $this->evaluate('ANSWER');
234. }
236. /**
237. * Get the status of the specified channel. If no channel name is specified, return the status of the current channel.
238. *
239. * @link http://www.voip-info.org/wiki-channel+status
240. * @param string $channel
241. * @return array, see evaluate for return information. ['data'] contains description.
242. */
243. function channel_status($channel='')
244. {
245. $ret = $this->evaluate("CHANNEL STATUS $channel");
246. switch($ret['result'])
247. {
248. case -1: $ret['data'] = trim("There is no channel that matches $channel"); break;
249. case AST_STATE_DOWN: $ret['data'] = 'Channel is down and available'; break;
250. case AST_STATE_RESERVED: $ret['data'] = 'Channel is down, but reserved'; break;
251. case AST_STATE_OFFHOOK: $ret['data'] = 'Channel is off hook'; break;
252. case AST_STATE_DIALING: $ret['data'] = 'Digits (or equivalent) have been dialed'; break;
253. case AST_STATE_RING: $ret['data'] = 'Line is ringing'; break;
254. case AST_STATE_RINGING: $ret['data'] = 'Remote end is ringing'; break;
255. case AST_STATE_UP: $ret['data'] = 'Line is up'; break;
256. case AST_STATE_BUSY: $ret['data'] = 'Line is busy'; break;
257. case AST_STATE_DIALING_OFFHOOK: $ret['data'] = 'Digits (or equivalent) have been dialed while offhook'; break;
258. case AST_STATE_PRERING: $ret['data'] = 'Channel has detected an incoming call and is waiting for ring'; break;
259. default: $ret['data'] = "Unknown ({$ret['result']})"; break;
260. }
261. return $ret;
262. }
264. /**
265. * Deletes an entry in the Asterisk database for a given family and key.
266. *
267. * @link http://www.voip-info.org/wiki-database+del
268. * @param string $family
269. * @param string $key
270. * @return array, see evaluate for return information. ['result'] is 1 on sucess, 0 otherwise.
271. */
272. function database_del($family, $key)
273. {
274. return $this->evaluate("DATABASE DEL \"$family\" \"$key\"");
275. }
277. /**
278. * Deletes a family or specific keytree within a family in the Asterisk database.
279. *
280. * @link http://www.voip-info.org/wiki-database+deltree
281. * @param string $family
282. * @param string $keytree
283. * @return array, see evaluate for return information. ['result'] is 1 on sucess, 0 otherwise.
284. */
285. function database_deltree($family, $keytree='')
286. {
287. $cmd = "DATABASE DELTREE \"$family\"";
288. if($keytree != '') $cmd .= " \"$keytree\"";
289. return $this->evaluate($cmd);
290. }
292. /**
293. * Retrieves an entry in the Asterisk database for a given family and key.
294. *
295. * @link http://www.voip-info.org/wiki-database+get
296. * @param string $family
297. * @param string $key
298. * @return array, see evaluate for return information. ['result'] is 1 on sucess, 0 failure. ['data'] holds the value
299. */
300. function database_get($family, $key)
301. {
302. return $this->evaluate("DATABASE GET \"$family\" \"$key\"");
303. }
305. /**
306. * Adds or updates an entry in the Asterisk database for a given family, key, and value.
307. *
308. * @param string $family
309. * @param string $key
310. * @param string $value
311. * @return array, see evaluate for return information. ['result'] is 1 on sucess, 0 otherwise
312. */
313. function database_put($family, $key, $value)
314. {
315. $value = str_replace("\n", '\n', addslashes($value));
316. return $this->evaluate("DATABASE PUT \"$family\" \"$key\" \"$value\"");
317. }
320. /**
321. * Sets a global variable, using Asterisk 1.6 syntax.
322. *
323. * @link http://www.voip-info.org/wiki/view/Asterisk+cmd+Set
324. *
325. * @param string $pVariable
326. * @param string|int|float$pValue
327. * @return array, see evaluate for return information. ['result'] is 1 on sucess, 0 otherwise
328. */
329. function set_global_var($pVariable, $pValue)
330. {
331. if (is_numeric($pValue))
332. return $this->evaluate("Set({$pVariable}={$pValue},g);");
333. else
334. return $this->evaluate("Set({$pVariable}=\"{$pValue}\",g);");
335. }
338. /**
339. * Sets a variable, using Asterisk 1.6 syntax.
340. *
341. * @link http://www.voip-info.org/wiki/view/Asterisk+cmd+Set
342. *
343. * @param string $pVariable
344. * @param string|int|float$pValue
345. * @return array, see evaluate for return information. ['result'] is 1 on sucess, 0 otherwise
346. */
347. function set_var($pVariable, $pValue)
348. {
349. if (is_numeric($pValue))
350. return $this->evaluate("Set({$pVariable}={$pValue});");
351. else
352. return $this->evaluate("Set({$pVariable}=\"{$pValue}\");");
353. }
356. /**
357. * Executes the specified Asterisk application with given options.
358. *
359. * @link http://www.voip-info.org/wiki-exec
360. * @link http://www.voip-info.org/wiki-Asterisk+-+documentation+of+application+commands
361. * @param string $application
362. * @param mixed $options
363. * @return array, see evaluate for return information. ['result'] is whatever the application returns, or -2 on failure to find application
364. */
365. function exec($application, $options)
366. {
367. if(is_array($options)) $options = join('|', $options);
368. return $this->evaluate("EXEC $application $options");
369. }
371. /**
372. * Plays the given file and receives DTMF data.
373. *
374. * This is similar to STREAM FILE, but this command can accept and return many DTMF digits,
375. * while STREAM FILE returns immediately after the first DTMF digit is detected.
376. *
377. * Asterisk looks for the file to play in /var/lib/asterisk/sounds by default.
378. *
379. * If the user doesn't press any keys when the message plays, there is $timeout milliseconds
380. * of silence then the command ends.
381. *
382. * The user has the opportunity to press a key at any time during the message or the
383. * post-message silence. If the user presses a key while the message is playing, the
384. * message stops playing. When the first key is pressed a timer starts counting for
385. * $timeout milliseconds. Every time the user presses another key the timer is restarted.
386. * The command ends when the counter goes to zero or the maximum number of digits is entered,
387. * whichever happens first.
388. *
389. * If you don't specify a time out then a default timeout of 2000 is used following a pressed
390. * digit. If no digits are pressed then 6 seconds of silence follow the message.
391. *
392. * If you don't specify $max_digits then the user can enter as many digits as they want.
393. *
394. * Pressing the # key has the same effect as the timer running out: the command ends and
395. * any previously keyed digits are returned. A side effect of this is that there is no
396. * way to read a # key using this command.
397. *
398. * @example examples/ping.php Ping an IP address
399. *
400. * @link http://www.voip-info.org/wiki-get+data
401. * @param string $filename file to play. Do not include file extension.
402. * @param integer $timeout milliseconds
403. * @param integer $max_digits
404. * @return array, see evaluate for return information. ['result'] holds the digits and ['data'] holds the timeout if present.
405. *
406. * This differs from other commands with return DTMF as numbers representing ASCII characters.
407. */
408. function get_data($filename, $timeout=NULL, $max_digits=NULL)
409. {
410. return $this->evaluate(rtrim("GET DATA $filename $timeout $max_digits"));
411. }
413. /**
414. * Fetch the value of a variable.
415. *
416. * Does not work with global variables. Does not work with some variables that are generated by modules.
417. *
418. * @link http://www.voip-info.org/wiki-get+variable
419. * @link http://www.voip-info.org/wiki-Asterisk+variables
420. * @param string $variable name
421. * @param boolean $getvalue return the value only
422. * @return array, see evaluate for return information. ['result'] is 0 if variable hasn't been set, 1 if it has. ['data'] holds the value. returns value if $getvalue is TRUE
423. */
424. function get_variable($variable,$getvalue=FALSE)
425. {
426. $res=$this->evaluate("GET VARIABLE $variable");
428. if($getvalue==FALSE)
429. return($res);
431. return($res['data']);
432. }
435. /**
436. * Fetch the value of a full variable.
437. *
438. *
439. * @link http://www.voip-info.org/wiki/view/get+full+variable
440. * @link http://www.voip-info.org/wiki-Asterisk+variables
441. * @param string $variable name
442. * @param string $channel channel
443. * @param boolean $getvalue return the value only
444. * @return array, see evaluate for return information. ['result'] is 0 if variable hasn't been set, 1 if it has. ['data'] holds the value. returns value if $getvalue is TRUE
445. */
446. function get_fullvariable($variable,$channel=FALSE,$getvalue=FALSE)
447. {
448. if($channel==FALSE){
449. $req = $variable;
450. } else {
451. $req = $variable.' '.$channel;
452. }
454. $res=$this->evaluate('GET VARIABLE FULL '.$req);
456. if($getvalue==FALSE)
457. return($res);
459. return($res['data']);
461. }
463. /**
464. * Hangup the specified channel. If no channel name is given, hang up the current channel.
465. *
466. * With power comes responsibility. Hanging up channels other than your own isn't something
467. * that is done routinely. If you are not sure why you are doing so, then don't.
468. *
469. * @link http://www.voip-info.org/wiki-hangup
470. * @example examples/dtmf.php Get DTMF tones from the user and say the digits
471. * @example examples/input.php Get text input from the user and say it back
472. * @example examples/ping.php Ping an IP address
473. *
474. * @param string $channel
475. * @return array, see evaluate for return information. ['result'] is 1 on success, -1 on failure.
476. */
477. function hangup($channel='')
478. {
479. return $this->evaluate("HANGUP $channel");
480. }
482. /**
483. * Does nothing.
484. *
485. * @link http://www.voip-info.org/wiki-noop
486. * @return array, see evaluate for return information.
487. */
488. function noop($string="")
489. {
490. return $this->evaluate("NOOP \"$string\"");
491. }
493. /**
494. * Receive a character of text from a connected channel. Waits up to $timeout milliseconds for
495. * a character to arrive, or infinitely if $timeout is zero.
496. *
497. * @link http://www.voip-info.org/wiki-receive+char
498. * @param integer $timeout milliseconds
499. * @return array, see evaluate for return information. ['result'] is 0 on timeout or not supported, -1 on failure. Otherwise
500. * it is the decimal value of the DTMF tone. Use chr() to convert to ASCII.
501. */
502. function receive_char($timeout=-1)
503. {
504. return $this->evaluate("RECEIVE CHAR $timeout");
505. }
507. /**
508. * Record sound to a file until an acceptable DTMF digit is received or a specified amount of
509. * time has passed. Optionally the file BEEP is played before recording begins.
510. *
511. * @link http://www.voip-info.org/wiki-record+file
512. * @param string $file to record, without extension, often created in /var/lib/asterisk/sounds
513. * @param string $format of the file. GSM and WAV are commonly used formats. MP3 is read-only and thus cannot be used.
514. * @param string $escape_digits
515. * @param integer $timeout is the maximum record time in milliseconds, or -1 for no timeout.
516. * @param integer $offset to seek to without exceeding the end of the file.
517. * @param boolean $beep
518. * @param integer $silence number of seconds of silence allowed before the function returns despite the
519. * lack of dtmf digits or reaching timeout.
520. * @return array, see evaluate for return information. ['result'] is -1 on error, 0 on hangup, otherwise a decimal value of the
521. * DTMF tone. Use chr() to convert to ASCII.
522. */
523. function record_file($file, $format, $escape_digits='', $timeout=-1, $offset=NULL, $beep=false, $silence=NULL)
524. {
525. $cmd = trim("RECORD FILE $file $format \"$escape_digits\" $timeout $offset");
526. if($beep) $cmd .= ' BEEP';
527. if(!is_null($silence)) $cmd .= " s=$silence";
528. return $this->evaluate($cmd);
529. }
531. /**
532. * Say the given digit string, returning early if any of the given DTMF escape digits are received on the channel.
533. *
534. * @link http://www.voip-info.org/wiki-say+digits
535. * @param integer $digits
536. * @param string $escape_digits
537. * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no
538. * digit received, otherwise a decimal value of the DTMF tone. Use chr() to convert to ASCII.
539. */
540. function say_digits($digits, $escape_digits='')
541. {
542. return $this->evaluate("SAY DIGITS $digits \"$escape_digits\"");
543. }
545. /**
546. * Say the given number, returning early if any of the given DTMF escape digits are received on the channel.
547. *
548. * @link http://www.voip-info.org/wiki-say+number
549. * @param integer $number
550. * @param string $escape_digits
551. * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no
552. * digit received, otherwise a decimal value of the DTMF tone. Use chr() to convert to ASCII.
553. */
554. function say_number($number, $escape_digits='')
555. {
556. return $this->evaluate("SAY NUMBER $number \"$escape_digits\"");
557. }
559. /**
560. * Say the given character string, returning early if any of the given DTMF escape digits are received on the channel.
561. *
562. * @link http://www.voip-info.org/wiki-say+phonetic
563. * @param string $text
564. * @param string $escape_digits
565. * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no
566. * digit received, otherwise a decimal value of the DTMF tone. Use chr() to convert to ASCII.
567. */
568. function say_phonetic($text, $escape_digits='')
569. {
570. return $this->evaluate("SAY PHONETIC $text \"$escape_digits\"");
571. }
573. /**
574. * Say a given time, returning early if any of the given DTMF escape digits are received on the channel.
575. *
576. * @link http://www.voip-info.org/wiki-say+time
577. * @param integer $time number of seconds elapsed since 00:00:00 on January 1, 1970, Coordinated Universal Time (UTC).
578. * @param string $escape_digits
579. * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no
580. * digit received, otherwise a decimal value of the DTMF tone. Use chr() to convert to ASCII.
581. */
582. function say_time($time=NULL, $escape_digits='')
583. {
584. if(is_null($time)) $time = time();
585. return $this->evaluate("SAY TIME $time \"$escape_digits\"");
586. }
588. /**
589. * Send the specified image on a channel.
590. *
591. * Most channels do not support the transmission of images.
592. *
593. * @link http://www.voip-info.org/wiki-send+image
594. * @param string $image without extension, often in /var/lib/asterisk/images
595. * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if the image is sent or
596. * channel does not support image transmission.
597. */
598. function send_image($image)
599. {
600. return $this->evaluate("SEND IMAGE $image");
601. }
603. /**
604. * Send the given text to the connected channel.
605. *
606. * Most channels do not support transmission of text.
607. *
608. * @link http://www.voip-info.org/wiki-send+text
609. * @param $text
610. * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if the text is sent or
611. * channel does not support text transmission.
612. */
613. function send_text($text)
614. {
615. return $this->evaluate("SEND TEXT \"$text\"");
616. }
618. /**
619. * Cause the channel to automatically hangup at $time seconds in the future.
620. * If $time is 0 then the autohangup feature is disabled on this channel.
621. *
622. * If the channel is hungup prior to $time seconds, this setting has no effect.
623. *
624. * @link http://www.voip-info.org/wiki-set+autohangup
625. * @param integer $time until automatic hangup
626. * @return array, see evaluate for return information.
627. */
628. function set_autohangup($time=0)
629. {
630. return $this->evaluate("SET AUTOHANGUP $time");
631. }
633. /**
634. * Changes the caller ID of the current channel.
635. *
636. * @link http://www.voip-info.org/wiki-set+callerid
637. * @param string $cid example: "John Smith"<1234567>
638. * This command will let you take liberties with the <caller ID specification> but the format shown in the example above works
639. * well: the name enclosed in double quotes followed immediately by the number inside angle brackets. If there is no name then
640. * you can omit it. If the name contains no spaces you can omit the double quotes around it. The number must follow the name
641. * immediately; don't put a space between them. The angle brackets around the number are necessary; if you omit them the
642. * number will be considered to be part of the name.
643. * @return array, see evaluate for return information.
644. */
645. function set_callerid($cid)
646. {
647. return $this->evaluate("SET CALLERID $cid");
648. }
650. /**
651. * Sets the context for continuation upon exiting the application.
652. *
653. * Setting the context does NOT automatically reset the extension and the priority; if you want to start at the top of the new
654. * context you should set extension and priority yourself.
655. *
656. * If you specify a non-existent context you receive no error indication (['result'] is still 0) but you do get a
657. * warning message on the Asterisk console.
658. *
659. * @link http://www.voip-info.org/wiki-set+context
660. * @param string $context
661. * @return array, see evaluate for return information.
662. */
663. function set_context($context)
664. {
665. return $this->evaluate("SET CONTEXT $context");
666. }
668. /**
669. * Set the extension to be used for continuation upon exiting the application.
670. *
671. * Setting the extension does NOT automatically reset the priority. If you want to start with the first priority of the
672. * extension you should set the priority yourself.
673. *
674. * If you specify a non-existent extension you receive no error indication (['result'] is still 0) but you do
675. * get a warning message on the Asterisk console.
676. *
677. * @link http://www.voip-info.org/wiki-set+extension
678. * @param string $extension
679. * @return array, see evaluate for return information.
680. */
681. function set_extension($extension)
682. {
683. return $this->evaluate("SET EXTENSION $extension");
684. }
686. /**
687. * Enable/Disable Music on hold generator.
688. *
689. * @link http://www.voip-info.org/wiki-set+music
690. * @param boolean $enabled
691. * @param string $class
692. * @return array, see evaluate for return information.
693. */
694. function set_music($enabled=true, $class='')
695. {
696. $enabled = ($enabled) ? 'ON' : 'OFF';
697. return $this->evaluate("SET MUSIC $enabled $class");
698. }
700. /**
701. * Set the priority to be used for continuation upon exiting the application.
702. *
703. * If you specify a non-existent priority you receive no error indication (['result'] is still 0)
704. * and no warning is issued on the Asterisk console.
705. *
706. * @link http://www.voip-info.org/wiki-set+priority
707. * @param integer $priority
708. * @return array, see evaluate for return information.
709. */
710. function set_priority($priority)
711. {
712. return $this->evaluate("SET PRIORITY $priority");
713. }
715. /**
716. * Sets a variable to the specified value. The variables so created can later be used by later using ${<variablename>}
717. * in the dialplan.
718. *
719. * These variables live in the channel Asterisk creates when you pickup a phone and as such they are both local and temporary.
720. * Variables created in one channel can not be accessed by another channel. When you hang up the phone, the channel is deleted
721. * and any variables in that channel are deleted as well.
722. *
723. * @link http://www.voip-info.org/wiki-set+variable
724. * @param string $variable is case sensitive
725. * @param string $value
726. * @return array, see evaluate for return information.
727. */
728. function set_variable($variable, $value)
729. {
730. $value = str_replace("\n", '\n', addslashes($value));
731. return $this->evaluate("SET VARIABLE $variable \"$value\"");
732. }
734. /**
735. * Play the given audio file, allowing playback to be interrupted by a DTMF digit. This command is similar to the GET DATA
736. * command but this command returns after the first DTMF digit has been pressed while GET DATA can accumulated any number of
737. * digits before returning.
738. *
739. * @example examples/ping.php Ping an IP address
740. *
741. * @link http://www.voip-info.org/wiki-stream+file
742. * @param string $filename without extension, often in /var/lib/asterisk/sounds
743. * @param string $escape_digits
744. * @param integer $offset
745. * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no
746. * digit received, otherwise a decimal value of the DTMF tone. Use chr() to convert to ASCII.
747. */
748. function stream_file($filename, $escape_digits='', $offset=0)
749. {
750. return $this->evaluate("STREAM FILE $filename \"$escape_digits\" $offset");
751. }
753. /**
754. * Enable or disable TDD transmission/reception on the current channel.
755. *
756. * @link http://www.voip-info.org/wiki-tdd+mode
757. * @param string $setting can be on, off or mate
758. * @return array, see evaluate for return information. ['result'] is 1 on sucess, 0 if the channel is not TDD capable.
759. */
760. function tdd_mode($setting)
761. {
762. return $this->evaluate("TDD MODE $setting");
763. }
765. /**
766. * Sends $message to the Asterisk console via the 'verbose' message system.
767. *
768. * If the Asterisk verbosity level is $level or greater, send $message to the console.
769. *
770. * The Asterisk verbosity system works as follows. The Asterisk user gets to set the desired verbosity at startup time or later
771. * using the console 'set verbose' command. Messages are displayed on the console if their verbose level is less than or equal
772. * to desired verbosity set by the user. More important messages should have a low verbose level; less important messages
773. * should have a high verbose level.
774. *
775. * @link http://www.voip-info.org/wiki-verbose
776. * @param string $message
777. * @param integer $level from 1 to 4
778. * @return array, see evaluate for return information.
779. */
780. function verbose($message, $level=1)
781. {
782. foreach(explode("\n", str_replace("\r\n", "\n", print_r($message, true))) as $msg)
783. {
784. @syslog(LOG_WARNING, $msg);
785. $ret = $this->evaluate("VERBOSE \"$msg\" $level");
786. }
787. return $ret;
788. }
790. /**
791. * Waits up to $timeout milliseconds for channel to receive a DTMF digit.
792. *
793. * @link http://www.voip-info.org/wiki-wait+for+digit
794. * @param integer $timeout in millisecons. Use -1 for the timeout value if you want the call to wait indefinitely.
795. * @return array, see evaluate for return information. ['result'] is 0 if wait completes with no
796. * digit received, otherwise a decimal value of the DTMF tone. Use chr() to convert to ASCII.
797. */
798. function wait_for_digit($timeout=-1)
799. {
800. return $this->evaluate("WAIT FOR DIGIT $timeout");
801. }
804. // *********************************************************************************************************
805. // ** APPLICATIONS **
806. // *********************************************************************************************************
808. /**
809. * Set absolute maximum time of call.
810. *
811. * Note that the timeout is set from the current time forward, not counting the number of seconds the call has already been up.
812. * Each time you call AbsoluteTimeout(), all previous absolute timeouts are cancelled.
813. * Will return the call to the T extension so that you can playback an explanatory note to the calling party (the called party
814. * will not hear that)
815. *
816. * @link http://www.voip-info.org/wiki-Asterisk+-+documentation+of+application+commands
817. * @link http://www.dynx.net/ASTERISK/AGI/ccard/agi-ccard.agi
818. * @param $seconds allowed, 0 disables timeout
819. * @return array, see evaluate for return information.
820. */
821. function exec_absolutetimeout($seconds=0)
822. {
823. return $this->exec('AbsoluteTimeout', $seconds);
824. }
826. /**
827. * Executes an AGI compliant application.
828. *
829. * @param string $command
830. * @return array, see evaluate for return information. ['result'] is -1 on hangup or if application requested hangup, or 0 on non-hangup exit.
831. * @param string $args
832. */
833. function exec_agi($command, $args)
834. {
835. return $this->exec("AGI $command", $args);
836. }
838. /**
839. * Set Language.
840. *
841. * @param string $language code
842. * @return array, see evaluate for return information.
843. */
844. function exec_setlanguage($language='en')
845. {
846. return $this->exec('Set', 'CHANNEL(language)='. $language);
847. }
849. /**
850. * Do ENUM Lookup.
851. *
852. * Note: to retrieve the result, use
853. * get_variable('ENUM');
854. *
855. * @param $exten
856. * @return array, see evaluate for return information.
857. */
858. function exec_enumlookup($exten)
859. {
860. return $this->exec('EnumLookup', $exten);
861. }
863. /**
864. * Dial.
865. *
866. * Dial takes input from ${VXML_URL} to send XML Url to Cisco 7960
867. * Dial takes input from ${ALERT_INFO} to set ring cadence for Cisco phones
868. * Dial returns ${CAUSECODE}: If the dial failed, this is the errormessage.
869. * Dial returns ${DIALSTATUS}: Text code returning status of last dial attempt.
870. *
871. * @link http://www.voip-info.org/wiki-Asterisk+cmd+Dial
872. * @param string $type
873. * @param string $identifier
874. * @param integer $timeout
875. * @param string $options
876. * @param string $url
877. * @return array, see evaluate for return information.
878. */
879. function exec_dial($type, $identifier, $timeout=NULL, $options=NULL, $url=NULL)
880. {
881. return $this->exec('Dial', trim("$type/$identifier".$this->option_delim.$timeout.$this->option_delim.$options.$this->option_delim.$url, $this->option_delim));
882. }
884. /**
885. * Goto.
886. *
887. * This function takes three arguments: context,extension, and priority, but the leading arguments
888. * are optional, not the trailing arguments. Thuse goto($z) sets the priority to $z.
889. *
890. * @param string $a
891. * @param string $b;
892. * @param string $c;
893. * @return array, see evaluate for return information.
894. */
895. function exec_goto($a, $b=NULL, $c=NULL)
896. {
897. return $this->exec('Goto', trim($a.$this->option_delim.$b.$this->option_delim.$c, $this->option_delim));
898. }
901. // *********************************************************************************************************
902. // ** FAST PASSING **
903. // *********************************************************************************************************
905. /**
906. * Say the given digit string, returning early if any of the given DTMF escape digits are received on the channel.
907. * Return early if $buffer is adequate for request.
908. *
909. * @link http://www.voip-info.org/wiki-say+digits
910. * @param string $buffer
911. * @param integer $digits
912. * @param string $escape_digits
913. * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no
914. * digit received, otherwise a decimal value of the DTMF tone. Use chr() to convert to ASCII.
915. */
916. function fastpass_say_digits(&$buffer, $digits, $escape_digits='')
917. {
918. $proceed = false;
919. if($escape_digits != '' && $buffer != '')
920. {
921. if(!strpos(chr(255) . $escape_digits, $buffer{strlen($buffer)-1}))
922. $proceed = true;
923. }
924. if($buffer == '' || $proceed)
925. {
926. $res = $this->say_digits($digits, $escape_digits);
927. if($res['code'] == AGIRES_OK && $res['result'] > 0)
928. $buffer .= chr($res['result']);
929. return $res;
930. }
931. return array('code'=>AGIRES_OK, 'result'=>ord($buffer{strlen($buffer)-1}));
932. }
934. /**
935. * Say the given number, returning early if any of the given DTMF escape digits are received on the channel.
936. * Return early if $buffer is adequate for request.
937. *
938. * @link http://www.voip-info.org/wiki-say+number
939. * @param string $buffer
940. * @param integer $number
941. * @param string $escape_digits
942. * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no
943. * digit received, otherwise a decimal value of the DTMF tone. Use chr() to convert to ASCII.
944. */
945. function fastpass_say_number(&$buffer, $number, $escape_digits='')
946. {
947. $proceed = false;
948. if($escape_digits != '' && $buffer != '')
949. {
950. if(!strpos(chr(255) . $escape_digits, $buffer{strlen($buffer)-1}))
951. $proceed = true;
952. }
953. if($buffer == '' || $proceed)
954. {
955. $res = $this->say_number($number, $escape_digits);
956. if($res['code'] == AGIRES_OK && $res['result'] > 0)
957. $buffer .= chr($res['result']);
958. return $res;
959. }
960. return array('code'=>AGIRES_OK, 'result'=>ord($buffer{strlen($buffer)-1}));
961. }
963. /**
964. * Say the given character string, returning early if any of the given DTMF escape digits are received on the channel.
965. * Return early if $buffer is adequate for request.
966. *
967. * @link http://www.voip-info.org/wiki-say+phonetic
968. * @param string $buffer
969. * @param string $text
970. * @param string $escape_digits
971. * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no
972. * digit received, otherwise a decimal value of the DTMF tone. Use chr() to convert to ASCII.
973. */
974. function fastpass_say_phonetic(&$buffer, $text, $escape_digits='')
975. {
976. $proceed = false;
977. if($escape_digits != '' && $buffer != '')
978. {
979. if(!strpos(chr(255) . $escape_digits, $buffer{strlen($buffer)-1}))
980. $proceed = true;
981. }
982. if($buffer == '' || $proceed)
983. {
984. $res = $this->say_phonetic($text, $escape_digits);
985. if($res['code'] == AGIRES_OK && $res['result'] > 0)
986. $buffer .= chr($res['result']);
987. return $res;
988. }
989. return array('code'=>AGIRES_OK, 'result'=>ord($buffer{strlen($buffer)-1}));
990. }
992. /**
993. * Say a given time, returning early if any of the given DTMF escape digits are received on the channel.
994. * Return early if $buffer is adequate for request.
995. *
996. * @link http://www.voip-info.org/wiki-say+time
997. * @param string $buffer
998. * @param integer $time number of seconds elapsed since 00:00:00 on January 1, 1970, Coordinated Universal Time (UTC).
999. * @param string $escape_digits
1000. * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no
1001. * digit received, otherwise a decimal value of the DTMF tone. Use chr() to convert to ASCII.
1002. */
1003. function fastpass_say_time(&$buffer, $time=NULL, $escape_digits='')
1004. {
1005. $proceed = false;
1006. if($escape_digits != '' && $buffer != '')
1007. {
1008. if(!strpos(chr(255) . $escape_digits, $buffer{strlen($buffer)-1}))
1009. $proceed = true;
1010. }
1011. if($buffer == '' || $proceed)
1012. {
1013. $res = $this->say_time($time, $escape_digits);
1014. if($res['code'] == AGIRES_OK && $res['result'] > 0)
1015. $buffer .= chr($res['result']);
1016. return $res;
1017. }
1018. return array('code'=>AGIRES_OK, 'result'=>ord($buffer{strlen($buffer)-1}));
1019. }
1021. /**
1022. * Play the given audio file, allowing playback to be interrupted by a DTMF digit. This command is similar to the GET DATA
1023. * command but this command returns after the first DTMF digit has been pressed while GET DATA can accumulated any number of
1024. * digits before returning.
1025. * Return early if $buffer is adequate for request.
1026. *
1027. * @link http://www.voip-info.org/wiki-stream+file
1028. * @param string $buffer
1029. * @param string $filename without extension, often in /var/lib/asterisk/sounds
1030. * @param string $escape_digits
1031. * @param integer $offset
1032. * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no
1033. * digit received, otherwise a decimal value of the DTMF tone. Use chr() to convert to ASCII.
1034. */
1035. function fastpass_stream_file(&$buffer, $filename, $escape_digits='', $offset=0)
1036. {
1037. $proceed = false;
1038. if($escape_digits != '' && $buffer != '')
1039. {
1040. if(!strpos(chr(255) . $escape_digits, $buffer{strlen($buffer)-1}))
1041. $proceed = true;
1042. }
1043. if($buffer == '' || $proceed)
1044. {
1045. $res = $this->stream_file($filename, $escape_digits, $offset);
1046. if($res['code'] == AGIRES_OK && $res['result'] > 0)
1047. $buffer .= chr($res['result']);
1048. return $res;
1049. }
1050. return array('code'=>AGIRES_OK, 'result'=>ord($buffer{strlen($buffer)-1}), 'endpos'=>0);
1051. }
1053. /**
1054. * Use festival to read text.
1055. * Return early if $buffer is adequate for request.
1056. *
1057. * @link http://www.cstr.ed.ac.uk/projects/festival/
1058. * @param string $buffer
1059. * @param string $text
1060. * @param string $escape_digits
1061. * @param integer $frequency
1062. * @return array, see evaluate for return information.
1063. */
1064. function fastpass_text2wav(&$buffer, $text, $escape_digits='', $frequency=8000)
1065. {
1066. $proceed = false;
1067. if($escape_digits != '' && $buffer != '')
1068. {
1069. if(!strpos(chr(255) . $escape_digits, $buffer{strlen($buffer)-1}))
1070. $proceed = true;
1071. }
1072. if($buffer == '' || $proceed)
1073. {
1074. $res = $this->text2wav($text, $escape_digits, $frequency);
1075. if($res['code'] == AGIRES_OK && $res['result'] > 0)
1076. $buffer .= chr($res['result']);
1077. return $res;
1078. }
1079. return array('code'=>AGIRES_OK, 'result'=>ord($buffer{strlen($buffer)-1}), 'endpos'=>0);
1080. }
1082. /**
1083. * Use Cepstral Swift to read text.
1084. * Return early if $buffer is adequate for request.
1085. *
1086. * @link http://www.cepstral.com/
1087. * @param string $buffer
1088. * @param string $text
1089. * @param string $escape_digits
1090. * @param integer $frequency
1091. * @return array, see evaluate for return information.
1092. */
1093. function fastpass_swift(&$buffer, $text, $escape_digits='', $frequency=8000, $voice=NULL)
1094. {
1095. $proceed = false;
1096. if($escape_digits != '' && $buffer != '')
1097. {
1098. if(!strpos(chr(255) . $escape_digits, $buffer{strlen($buffer)-1}))
1099. $proceed = true;
1100. }
1101. if($buffer == '' || $proceed)
1102. {
1103. $res = $this->swift($text, $escape_digits, $frequency, $voice);
1104. if($res['code'] == AGIRES_OK && $res['result'] > 0)
1105. $buffer .= chr($res['result']);
1106. return $res;
1107. }
1108. return array('code'=>AGIRES_OK, 'result'=>ord($buffer{strlen($buffer)-1}), 'endpos'=>0);
1109. }
1111. /**
1112. * Say Puncutation in a string.
1113. * Return early if $buffer is adequate for request.
1114. *
1115. * @param string $buffer
1116. * @param string $text
1117. * @param string $escape_digits
1118. * @param integer $frequency
1119. * @return array, see evaluate for return information.
1120. */
1121. function fastpass_say_punctuation(&$buffer, $text, $escape_digits='', $frequency=8000)
1122. {
1123. $proceed = false;
1124. if($escape_digits != '' && $buffer != '')
1125. {
1126. if(!strpos(chr(255) . $escape_digits, $buffer{strlen($buffer)-1}))
1127. $proceed = true;
1128. }
1129. if($buffer == '' || $proceed)
1130. {
1131. $res = $this->say_punctuation($text, $escape_digits, $frequency);
1132. if($res['code'] == AGIRES_OK && $res['result'] > 0)
1133. $buffer .= chr($res['result']);
1134. return $res;
1135. }
1136. return array('code'=>AGIRES_OK, 'result'=>ord($buffer{strlen($buffer)-1}));
1137. }
1139. /**
1140. * Plays the given file and receives DTMF data.
1141. * Return early if $buffer is adequate for request.
1142. *
1143. * This is similar to STREAM FILE, but this command can accept and return many DTMF digits,
1144. * while STREAM FILE returns immediately after the first DTMF digit is detected.
1145. *
1146. * Asterisk looks for the file to play in /var/lib/asterisk/sounds by default.
1147. *
1148. * If the user doesn't press any keys when the message plays, there is $timeout milliseconds
1149. * of silence then the command ends.
1150. *
1151. * The user has the opportunity to press a key at any time during the message or the
1152. * post-message silence. If the user presses a key while the message is playing, the
1153. * message stops playing. When the first key is pressed a timer starts counting for
1154. * $timeout milliseconds. Every time the user presses another key the timer is restarted.
1155. * The command ends when the counter goes to zero or the maximum number of digits is entered,
1156. * whichever happens first.
1157. *
1158. * If you don't specify a time out then a default timeout of 2000 is used following a pressed
1159. * digit. If no digits are pressed then 6 seconds of silence follow the message.
1160. *
1161. * If you don't specify $max_digits then the user can enter as many digits as they want.
1162. *
1163. * Pressing the # key has the same effect as the timer running out: the command ends and
1164. * any previously keyed digits are returned. A side effect of this is that there is no
1165. * way to read a # key using this command.
1166. *
1167. * @link http://www.voip-info.org/wiki-get+data
1168. * @param string $buffer
1169. * @param string $filename file to play. Do not include file extension.
1170. * @param integer $timeout milliseconds
1171. * @param integer $max_digits
1172. * @return array, see evaluate for return information. ['result'] holds the digits and ['data'] holds the timeout if present.
1173. *
1174. * This differs from other commands with return DTMF as numbers representing ASCII characters.
1175. */
1176. function fastpass_get_data(&$buffer, $filename, $timeout=NULL, $max_digits=NULL)
1177. {
1178. if(is_null($max_digits) || strlen($buffer) < $max_digits)
1179. {
1180. if($buffer == '')
1181. {
1182. $res = $this->get_data($filename, $timeout, $max_digits);
1183. if($res['code'] == AGIRES_OK)
1184. $buffer .= $res['result'];
1185. return $res;
1186. }
1187. else
1188. {
1189. while(is_null($max_digits) || strlen($buffer) < $max_digits)
1190. {
1191. $res = $this->wait_for_digit();
1192. if($res['code'] != AGIRES_OK) return $res;
1193. if($res['result'] == ord('#')) break;
1194. $buffer .= chr($res['result']);
1195. }
1196. }
1197. }
1198. return array('code'=>AGIRES_OK, 'result'=>$buffer);
1199. }
1201. // *********************************************************************************************************
1202. // ** DERIVED **
1203. // *********************************************************************************************************
1205. /**
1206. * Menu.
1207. *
1208. * This function presents the user with a menu and reads the response
1209. *
1210. * @param array $choices has the following structure:
1211. * array('1'=>'*Press 1 for this', // festival reads if prompt starts with *
1212. * '2'=>'some-gsm-without-extension',
1213. * '*'=>'*Press star for help');
1214. * @return mixed key pressed on sucess, -1 on failure
1215. */
1216. function menu($choices, $timeout=2000)
1217. {
1218. $keys = join('', array_keys($choices));
1219. $choice = NULL;
1220. while(is_null($choice))
1221. {
1222. foreach($choices as $prompt)
1223. {
1224. if($prompt{0} == '*')
1225. $ret = $this->text2wav(substr($prompt, 1), $keys);
1226. else
1227. $ret = $this->stream_file($prompt, $keys);
1229. if($ret['code'] != AGIRES_OK || $ret['result'] == -1)
1230. {
1231. $choice = -1;
1232. break;
1233. }
1235. if($ret['result'] != 0)
1236. {
1237. $choice = chr($ret['result']);
1238. break;
1239. }
1240. }
1242. if(is_null($choice))
1243. {
1244. $ret = $this->get_data('beep', $timeout, 1);
1245. if($ret['code'] != AGIRES_OK || $ret['result'] == -1)
1246. $choice = -1;
1247. elseif($ret['result'] != '' && strpos(' '.$keys, $ret['result']))
1248. $choice = $ret['result'];
1249. }
1250. }
1251. return $choice;
1252. }
1254. /**
1255. * setContext - Set context, extension and priority.
1256. *
1257. * @param string $context
1258. * @param string $extension
1259. * @param string $priority
1260. */
1261. function setContext($context, $extension='s', $priority=1)
1262. {
1263. $this->set_context($context);
1264. $this->set_extension($extension);
1265. $this->set_priority($priority);
1266. }
1268. /**
1269. * Parse caller id.
1270. *
1271. * @example examples/dtmf.php Get DTMF tones from the user and say the digits
1272. * @example examples/input.php Get text input from the user and say it back
1273. *
1274. * "name" <proto:user@server:port>
1275. *
1276. * @param string $callerid
1277. * @return array('Name'=>$name, 'Number'=>$number)
1278. */
1279. function parse_callerid($callerid=NULL)
1280. {
1281. if(is_null($callerid))
1282. $callerid = $this->request['agi_callerid'];
1284. $ret = array('name'=>'', 'protocol'=>'', 'username'=>'', 'host'=>'', 'port'=>'');
1285. $callerid = trim($callerid);
1287. if($callerid{0} == '"' || $callerid{0} == "'")
1288. {
1289. $d = $callerid{0};
1290. $callerid = explode($d, substr($callerid, 1));
1291. $ret['name'] = array_shift($callerid);
1292. $callerid = join($d, $callerid);
1293. }
1295. $callerid = explode('@', trim($callerid, '<> '));
1296. $username = explode(':', array_shift($callerid));
1297. if(count($username) == 1)
1298. $ret['username'] = $username[0];
1299. else
1300. {
1301. $ret['protocol'] = array_shift($username);
1302. $ret['username'] = join(':', $username);
1303. }
1305. $callerid = join('@', $callerid);
1306. $host = explode(':', $callerid);
1307. if(count($host) == 1)
1308. $ret['host'] = $host[0];
1309. else
1310. {
1311. $ret['host'] = array_shift($host);
1312. $ret['port'] = join(':', $host);
1313. }
1315. return $ret;
1316. }
1318. /**
1319. * Use festival to read text.
1320. *
1321. * @example examples/dtmf.php Get DTMF tones from the user and say the digits
1322. * @example examples/input.php Get text input from the user and say it back
1323. * @example examples/ping.php Ping an IP address
1324. *
1325. * @link http://www.cstr.ed.ac.uk/projects/festival/
1326. * @param string $text
1327. * @param string $escape_digits
1328. * @param integer $frequency
1329. * @return array, see evaluate for return information.
1330. */
1331. function text2wav($text, $escape_digits='', $frequency=8000)
1332. {
1333. $text = trim($text);
1334. if($text == '') return true;
1336. $hash = md5($text);
1337. $fname = $this->config['phpagi']['tempdir'] . DIRECTORY_SEPARATOR;
1338. $fname .= 'text2wav_' . $hash;
1340. // create wave file
1341. if(!file_exists("$fname.wav"))
1342. {
1343. // write text file
1344. if(!file_exists("$fname.txt"))
1345. {
1346. $fp = fopen("$fname.txt", 'w');
1347. fputs($fp, $text);
1348. fclose($fp);
1349. }
1351. shell_exec("{$this->config['festival']['text2wave']} -F $frequency -o $fname.wav $fname.txt");
1352. }
1353. else
1354. {
1355. touch("$fname.txt");
1356. touch("$fname.wav");
1357. }
1359. // stream it
1360. $ret = $this->stream_file($fname, $escape_digits);
1362. // clean up old files
1363. $delete = time() - 2592000; // 1 month
1364. foreach(glob($this->config['phpagi']['tempdir'] . DIRECTORY_SEPARATOR . 'text2wav_*') as $file)
1365. if(filemtime($file) < $delete)
1366. unlink($file);
1368. return $ret;
1369. }
1371. /**
1372. * Use Cepstral Swift to read text.
1373. *
1374. * @link http://www.cepstral.com/
1375. * @param string $text
1376. * @param string $escape_digits
1377. * @param integer $frequency
1378. * @return array, see evaluate for return information.
1379. */
1380. function swift($text, $escape_digits='', $frequency=8000, $voice=NULL)
1381. {
1382. if(!is_null($voice))
1383. $voice = "-n $voice";
1384. elseif(isset($this->config['cepstral']['voice']))
1385. $voice = "-n {$this->config['cepstral']['voice']}";
1387. $text = trim($text);
1388. if($text == '') return true;
1390. $hash = md5($text);
1391. $fname = $this->config['phpagi']['tempdir'] . DIRECTORY_SEPARATOR;
1392. $fname .= 'swift_' . $hash;
1394. // create wave file
1395. if(!file_exists("$fname.wav"))
1396. {
1397. // write text file
1398. if(!file_exists("$fname.txt"))
1399. {
1400. $fp = fopen("$fname.txt", 'w');
1401. fputs($fp, $text);
1402. fclose($fp);
1403. }
1405. shell_exec("{$this->config['cepstral']['swift']} -p audio/channels=1,audio/sampling-rate=$frequency $voice -o $fname.wav -f $fname.txt");
1406. }
1408. // stream it
1409. $ret = $this->stream_file($fname, $escape_digits);
1411. // clean up old files
1412. $delete = time() - 2592000; // 1 month
1413. foreach(glob($this->config['phpagi']['tempdir'] . DIRECTORY_SEPARATOR . 'swift_*') as $file)
1414. if(filemtime($file) < $delete)
1415. unlink($file);
1417. return $ret;
1418. }
1420. /**
1421. * Text Input.
1422. *
1423. * Based on ideas found at http://www.voip-info.org/wiki-Asterisk+cmd+DTMFToText
1424. *
1425. * Example:
1426. * UC H LC i , SP h o w SP a r e SP y o u ?
1427. * $string = '*8'.'44*'.'*5'.'444*'.'00*'.'0*'.'44*'.'666*'.'9*'.'0*'.'2*'.'777*'.'33*'.'0*'.'999*'.'666*'.'88*'.'0000*';
1428. *
1429. * @link http://www.voip-info.org/wiki-Asterisk+cmd+DTMFToText
1430. * @example examples/input.php Get text input from the user and say it back
1431. *
1432. * @return string
1433. */
1434. function text_input($mode='NUMERIC')
1435. {
1436. $alpha = array( 'k0'=>' ', 'k00'=>',', 'k000'=>'.', 'k0000'=>'?', 'k00000'=>'0',
1437. 'k1'=>'!', 'k11'=>':', 'k111'=>';', 'k1111'=>'#', 'k11111'=>'1',
1438. 'k2'=>'A', 'k22'=>'B', 'k222'=>'C', 'k2222'=>'2',
1439. 'k3'=>'D', 'k33'=>'E', 'k333'=>'F', 'k3333'=>'3',
1440. 'k4'=>'G', 'k44'=>'H', 'k444'=>'I', 'k4444'=>'4',
1441. 'k5'=>'J', 'k55'=>'K', 'k555'=>'L', 'k5555'=>'5',
1442. 'k6'=>'M', 'k66'=>'N', 'k666'=>'O', 'k6666'=>'6',
1443. 'k7'=>'P', 'k77'=>'Q', 'k777'=>'R', 'k7777'=>'S', 'k77777'=>'7',
1444. 'k8'=>'T', 'k88'=>'U', 'k888'=>'V', 'k8888'=>'8',
1445. 'k9'=>'W', 'k99'=>'X', 'k999'=>'Y', 'k9999'=>'Z', 'k99999'=>'9');
1446. $symbol = array('k0'=>'=',
1447. 'k1'=>'<', 'k11'=>'(', 'k111'=>'[', 'k1111'=>'{', 'k11111'=>'1',
1448. 'k2'=>'@', 'k22'=>'$', 'k222'=>'&', 'k2222'=>'%', 'k22222'=>'2',
1449. 'k3'=>'>', 'k33'=>')', 'k333'=>']', 'k3333'=>'}', 'k33333'=>'3',
1450. 'k4'=>'+', 'k44'=>'-', 'k444'=>'*', 'k4444'=>'/', 'k44444'=>'4',
1451. 'k5'=>"'", 'k55'=>'`', 'k555'=>'5',
1452. 'k6'=>'"', 'k66'=>'6',
1453. 'k7'=>'^', 'k77'=>'7',
1454. 'k8'=>"\\",'k88'=>'|', 'k888'=>'8',
1455. 'k9'=>'_', 'k99'=>'~', 'k999'=>'9');
1456. $text = '';
1457. do
1458. {
1459. $command = false;
1460. $result = $this->get_data('beep');
1461. foreach(explode('*', $result['result']) as $code)
1462. {
1463. if($command)
1464. {
1465. switch($code{0})
1466. {
1467. case '2': $text = substr($text, 0, strlen($text) - 1); break; // backspace
1468. case '5': $mode = 'LOWERCASE'; break;
1469. case '6': $mode = 'NUMERIC'; break;
1470. case '7': $mode = 'SYMBOL'; break;
1471. case '8': $mode = 'UPPERCASE'; break;
1472. case '9': $text = explode(' ', $text); unset($text[count($text)-1]); $text = join(' ', $text); break; // backspace a word
1473. }
1474. $code = substr($code, 1);
1475. $command = false;
1476. }
1477. if($code == '')
1478. $command = true;
1479. elseif($mode == 'NUMERIC')
1480. $text .= $code;
1481. elseif($mode == 'UPPERCASE' && isset($alpha['k'.$code]))
1482. $text .= $alpha['k'.$code];
1483. elseif($mode == 'LOWERCASE' && isset($alpha['k'.$code]))
1484. $text .= strtolower($alpha['k'.$code]);
1485. elseif($mode == 'SYMBOL' && isset($symbol['k'.$code]))
1486. $text .= $symbol['k'.$code];
1487. }
1488. $this->say_punctuation($text);
1489. } while(substr($result['result'], -2) == '**');
1490. return $text;
1491. }
1493. /**
1494. * Say Puncutation in a string.
1495. *
1496. * @param string $text
1497. * @param string $escape_digits
1498. * @param integer $frequency
1499. * @return array, see evaluate for return information.
1500. */
1501. function say_punctuation($text, $escape_digits='', $frequency=8000)
1502. {
1503. $ret="";
1504. for($i = 0; $i < strlen($text); $i++)
1505. {
1506. switch($text{$i})
1507. {
1508. case ' ': $ret .= 'SPACE ';
1509. case ',': $ret .= 'COMMA '; break;
1510. case '.': $ret .= 'PERIOD '; break;
1511. case '?': $ret .= 'QUESTION MARK '; break;
1512. case '!': $ret .= 'EXPLANATION POINT '; break;
1513. case ':': $ret .= 'COLON '; break;
1514. case ';': $ret .= 'SEMICOLON '; break;
1515. case '#': $ret .= 'POUND '; break;
1516. case '=': $ret .= 'EQUALS '; break;
1517. case '<': $ret .= 'LESS THAN '; break;
1518. case '(': $ret .= 'LEFT PARENTHESIS '; break;
1519. case '[': $ret .= 'LEFT BRACKET '; break;
1520. case '{': $ret .= 'LEFT BRACE '; break;
1521. case '@': $ret .= 'AT '; break;
1522. case '$': $ret .= 'DOLLAR SIGN '; break;
1523. case '&': $ret .= 'AMPERSAND '; break;
1524. case '%': $ret .= 'PERCENT '; break;
1525. case '>': $ret .= 'GREATER THAN '; break;
1526. case ')': $ret .= 'RIGHT PARENTHESIS '; break;
1527. case ']': $ret .= 'RIGHT BRACKET '; break;
1528. case '}': $ret .= 'RIGHT BRACE '; break;
1529. case '+': $ret .= 'PLUS '; break;
1530. case '-': $ret .= 'MINUS '; break;
1531. case '*': $ret .= 'ASTERISK '; break;
1532. case '/': $ret .= 'SLASH '; break;
1533. case "'": $ret .= 'SINGLE QUOTE '; break;
1534. case '`': $ret .= 'BACK TICK '; break;
1535. case '"': $ret .= 'QUOTE '; break;
1536. case '^': $ret .= 'CAROT '; break;
1537. case "\\": $ret .= 'BACK SLASH '; break;
1538. case '|': $ret .= 'BAR '; break;
1539. case '_': $ret .= 'UNDERSCORE '; break;
1540. case '~': $ret .= 'TILDE '; break;
1541. default: $ret .= $text{$i} . ' '; break;
1542. }
1543. }
1544. return $this->text2wav($ret, $escape_digits, $frequency);
1545. }
1547. /**
1548. * Create a new AGI_AsteriskManager.
1549. */
1550. function &new_AsteriskManager()
1551. {
1552. $this->asm = new AGI_AsteriskManager(NULL, $this->config);
1553. $this->asm->pagi =& $this;
1554. $this->config =& $this->asm->config;
1555. return $this->asm;
1556. }
1559. // *********************************************************************************************************
1560. // ** PRIVATE **
1561. // *********************************************************************************************************
1564. /**
1565. * Evaluate an AGI command.
1566. *
1567. * @access private
1568. * @param string $command
1569. * @return array ('code'=>$code, 'result'=>$result, 'data'=>$data)
1570. */
1571. function evaluate($command)
1572. {
1573. $broken = array('code'=>500, 'result'=>-1, 'data'=>'');
1575. // write command
1576. if(!@fwrite($this->out, trim($command) . "\n")) return $broken;
1577. fflush($this->out);
1579. // Read result. Occasionally, a command return a string followed by an extra new line.
1580. // When this happens, our script will ignore the new line, but it will still be in the
1581. // buffer. So, if we get a blank line, it is probably the result of a previous
1582. // command. We read until we get a valid result or asterisk hangs up. One offending
1583. // command is SEND TEXT.
1584. $count = 0;
1585. do
1586. {
1587. $str = trim(fgets($this->in, 4096));
1588. } while($str == '' && $count++ < 5);
1590. if($count >= 5)
1591. {
1592. // $this->conlog("evaluate error on read for $command");
1593. return $broken;
1594. }
1596. // parse result
1597. $ret['code'] = substr($str, 0, 3);
1598. $str = trim(substr($str, 3));
1600. if($str{0} == '-') // we have a multiline response!
1601. {
1602. $count = 0;
1603. $str = substr($str, 1) . "\n";
1604. $line = fgets($this->in, 4096);
1605. while(substr($line, 0, 3) != $ret['code'] && $count < 5)
1606. {
1607. $str .= $line;
1608. $line = fgets($this->in, 4096);
1609. $count = (trim($line) == '') ? $count + 1 : 0;
1610. }
1611. if($count >= 5)
1612. {
1613. // $this->conlog("evaluate error on multiline read for $command");
1614. return $broken;
1615. }
1616. }
1618. $ret['result'] = NULL;
1619. $ret['data'] = '';
1620. if($ret['code'] != AGIRES_OK) // some sort of error
1621. {
1622. $ret['data'] = $str;
1623. $this->conlog(print_r($ret, true));
1624. }
1625. else // normal AGIRES_OK response
1626. {
1627. $parse = explode(' ', trim($str));
1628. $in_token = false;
1629. foreach($parse as $token)
1630. {
1631. if($in_token) // we previously hit a token starting with ')' but not ending in ')'
1632. {
1633. $ret['data'] .= ' ' . trim($token, '() ');
1634. if($token{strlen($token)-1} == ')') $in_token = false;
1635. }
1636. elseif($token{0} == '(')
1637. {
1638. if($token{strlen($token)-1} != ')') $in_token = true;
1639. $ret['data'] .= ' ' . trim($token, '() ');
1640. }
1641. elseif(strpos($token, '='))
1642. {
1643. $token = explode('=', $token);
1644. $ret[$token[0]] = $token[1];
1645. }
1646. elseif($token != '')
1647. $ret['data'] .= ' ' . $token;
1648. }
1649. $ret['data'] = trim($ret['data']);
1650. }
1652. // log some errors
1653. if($ret['result'] < 0)
1654. $this->conlog("$command returned {$ret['result']}");
1656. return $ret;
1657. }
1659. /**
1660. * Log to console if debug mode.
1661. *
1662. * @example examples/ping.php Ping an IP address
1663. *
1664. * @param string $str
1665. * @param integer $vbl verbose level
1666. */
1667. function conlog($str, $vbl=1)
1668. {
1669. static $busy = false;
1671. if($this->config['phpagi']['debug'] != false)
1672. {
1673. if(!$busy) // no conlogs inside conlog!!!
1674. {
1675. $busy = true;
1676. $this->verbose($str, $vbl);
1677. $busy = false;
1678. }
1679. }
1680. }
1682. /**
1683. * Find an execuable in the path.
1684. *
1685. * @access private
1686. * @param string $cmd command to find
1687. * @param string $checkpath path to check
1688. * @return string the path to the command
1689. */
1690. function which($cmd, $checkpath=NULL)
1691. {
1692. global $_ENV;
1693. $chpath = is_null($checkpath) ? $_ENV['PATH'] : $checkpath;
1695. foreach(explode(':', $chpath) as $path)
1696. if(is_executable("$path/$cmd"))
1697. return "$path/$cmd";
1699. if(is_null($checkpath))
1700. return $this->which($cmd, '/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:'.
1701. '/usr/X11R6/bin:/usr/local/apache/bin:/usr/local/mysql/bin');
1702. return false;
1703. }
1705. /**
1706. * Make a folder recursively.
1707. *
1708. * @access private
1709. * @param string $folder
1710. * @param integer $perms
1711. * @return boolean
1712. */
1713. function make_folder($folder, $perms=0755)
1714. {
1715. $f = explode(DIRECTORY_SEPARATOR, $folder);
1716. $base = '';
1717. for($i = 0; $i < count($f); $i++)
1718. {
1719. $base .= $f[$i];
1720. if($f[$i] != '' && !file_exists($base)) {
1721. if(mkdir($base, $perms)==FALSE){
1722. return(FALSE);
1723. }
1724. }
1725. $base .= DIRECTORY_SEPARATOR;
1726. }
1727. return(TRUE);
1728. }
1730. }
1733. /**
1734. * error handler for phpagi.
1735. *
1736. * @param integer $level PHP error level
1737. * @param string $message error message
1738. * @param string $file path to file
1739. * @param integer $line line number of error
1740. * @param array $context variables in the current scope
1741. */
1742. function phpagi_error_handler($level, $message, $file, $line, $context)
1743. {
1744. if(ini_get('error_reporting') == 0) return; // this happens with an @
1746. @syslog(LOG_WARNING, $file . '[' . $line . ']: ' . $message);
1748. global $phpagi_error_handler_email;
1749. if(function_exists('mail') && !is_null($phpagi_error_handler_email)) // generate email debugging information
1750. {
1751. // decode error level
1752. switch($level)
1753. {
1754. case E_WARNING:
1755. case E_USER_WARNING:
1756. $level = "Warning";
1757. break;
1758. case E_NOTICE:
1759. case E_USER_NOTICE:
1760. $level = "Notice";
1761. break;
1762. case E_USER_ERROR:
1763. $level = "Error";
1764. break;
1765. }
1767. // build message
1768. $basefile = basename($file);
1769. $subject = "$basefile/$line/$level: $message";
1770. $message = "$level: $message in $file on line $line\n\n";
1772. if(function_exists('mysql_errno') && strpos(' '.strtolower($message), 'mysql'))
1773. $message .= 'MySQL error ' . mysql_errno() . ": " . mysql_error() . "\n\n";
1775. // figure out who we are
1776. if(function_exists('socket_create'))
1777. {
1778. $addr = NULL;
1779. $port = 80;
1780. $socket = @socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
1781. @socket_connect($socket, '64.0.0.0', $port);
1782. @socket_getsockname($socket, $addr, $port);
1783. @socket_close($socket);
1784. $message .= "\n\nIP Address: $addr\n";
1785. }
1787. // include variables
1788. $message .= "\n\nContext:\n" . print_r($context, true);
1789. $message .= "\n\nGLOBALS:\n" . print_r($GLOBALS, true);
1790. $message .= "\n\nBacktrace:\n" . print_r(debug_backtrace(), true);
1792. // include code fragment
1793. if(file_exists($file))
1794. {
1795. $message .= "\n\n$file:\n";
1796. $code = @file($file);
1797. for($i = max(0, $line - 10); $i < min($line + 10, count($code)); $i++)
1798. $message .= ($i + 1)."\t$code[$i]";
1799. }
1801. // make sure message is fully readable (convert unprintable chars to hex representation)
1802. $ret = '';
1803. for($i = 0; $i < strlen($message); $i++)
1804. {
1805. $c = ord($message{$i});
1806. if($c == 10 || $c == 13 || $c == 9)
1807. $ret .= $message{$i};
1808. elseif($c < 16)
1809. $ret .= '\x0' . dechex($c);
1810. elseif($c < 32 || $c > 127)
1811. $ret .= '\x' . dechex($c);
1812. else
1813. $ret .= $message{$i};
1814. }
1815. $message = $ret;
1817. // send the mail if less than 5 errors
1818. static $mailcount = 0;
1819. if($mailcount < 5)
1820. @mail($phpagi_error_handler_email, $subject, $message);
1821. $mailcount++;
1822. }
1823. }
1825. $phpagi_error_handler_email = NULL;

Documentation generated on Thu, 30 Sep 2010 02:21:59 -0700 by phpDocumentor 1.4.2