Source for file phpagi.php

Documentation is available at phpagi.php

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

Documentation generated on Wed, 16 Nov 2005 12:49:14 -0700 by phpDocumentor 1.3.0RC3