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.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. */
  28.  
  29. if (!class_exists('AGI_AsteriskManager'))
  30. {
  31.     require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'phpagi-asmanager.php');
  32. }
  33.  
  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');
  38.  
  39. define('AST_DIGIT_ANY', '0123456789#*');
  40.  
  41. define('AGIRES_OK', 200);
  42.  
  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);
  53.  
  54. define('AUDIO_FILENO', 3); // STDERR_FILENO + 1
  55.  
  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;
  93.  
  94.     /**
  95.     * Config variables
  96.     *
  97.     * @var array 
  98.     * @access public
  99.     */
  100.     var $config;
  101.  
  102.     /**
  103.     * Asterisk Manager
  104.     *
  105.     * @var AGI_AsteriskManager 
  106.     * @access public
  107.     */
  108.     var $asmanager;
  109.  
  110.     /**
  111.     * Input Stream
  112.     *
  113.     * @access private
  114.     */
  115.     var $in = NULL;
  116.  
  117.     /**
  118.     * Output Stream
  119.     *
  120.     * @access private
  121.     */
  122.     var $out = NULL;
  123.  
  124.     /**
  125.     * Audio Stream
  126.     *
  127.     * @access public
  128.     */
  129.     var $audio = NULL;
  130.  
  131.  
  132.     /**
  133.     * Application option delimiter
  134.     * 
  135.     * @access public
  136.     */
  137.     public $option_delim = ",";
  138.     
  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);
  152.  
  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;
  156.  
  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;
  162.  
  163.         // festival TTS config
  164.         if(!isset($this->config['festival']['text2wave'])) $this->config['festival']['text2wave'] = $this->which('text2wave');
  165.  
  166.         // swift TTS config
  167.         if(!isset($this->config['cepstral']['swift'])) $this->config['cepstral']['swift'] = $this->which('swift');
  168.  
  169.         ob_implicit_flush(true);
  170.  
  171.         // open stdin & stdout
  172.         $this->in = defined('STDIN') ? STDIN : fopen('php://stdin', 'r');
  173.         $this->out = defined('STDOUT') ? STDOUT : fopen('php://stdout', 'w');
  174.  
  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.         }
  183.  
  184.         // make sure temp folder exists
  185.         $this->make_folder($this->config['phpagi']['tempdir']);
  186.  
  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.         }
  194.  
  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');
  207.  
  208.           if($this->audio) stream_set_blocking($this->audio, 0);
  209.         }
  210.  
  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.     }
  216.  
  217.     // *********************************************************************************************************
  218.     // **                             COMMANDS                                                                                            **
  219.     // *********************************************************************************************************
  220.  
  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.     }
  235.  
  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.     }
  263.  
  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.     }
  276.  
  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.     }
  291.  
  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.     }
  304.  
  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.     }
  318.  
  319.  
  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.     }
  336.  
  337.  
  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.     }
  354.  
  355.  
  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.     }
  370.  
  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.     }
  412.  
  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");
  427.  
  428.         if($getvalue==FALSE)
  429.           return($res);
  430.  
  431.         return($res['data']);
  432.     }
  433.  
  434.  
  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.       }
  453.       
  454.       $res=$this->evaluate('GET VARIABLE FULL '.$req);
  455.       
  456.       if($getvalue==FALSE)
  457.         return($res);
  458.       
  459.       return($res['data']);
  460.       
  461.     }
  462.  
  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.     }
  481.  
  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.     }
  492.  
  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.     }
  506.  
  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.     }
  530.  
  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.     }
  544.  
  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.     }
  558.  
  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.     }
  572.  
  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.     }
  587.  
  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.     }
  602.  
  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.     }
  617.  
  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.     }
  632.  
  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.     }
  649.  
  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.     }
  667.  
  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.     }
  685.  
  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.     }
  699.  
  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.     }
  714.  
  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.     }
  733.  
  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.     }
  752.  
  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.     }
  764.  
  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.     }
  789.  
  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.     }
  802.  
  803.  
  804.     // *********************************************************************************************************
  805.     // **                             APPLICATIONS                                                                                        **
  806.     // *********************************************************************************************************
  807.  
  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.     }
  825.  
  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.     }
  837.  
  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.     }
  848.  
  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.     }
  862.  
  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.     }
  883.  
  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.     }
  899.  
  900.  
  901.     // *********************************************************************************************************
  902.     // **                             FAST PASSING                                                                                        **
  903.     // *********************************************************************************************************
  904.  
  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.     }
  933.  
  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.     }
  962.  
  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.     }
  991.  
  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.     }
  1020.  
  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.     }
  1052.  
  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.     }
  1081.  
  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.     }
  1110.  
  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.     }
  1138.  
  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.     }
  1200.  
  1201.     // *********************************************************************************************************
  1202.     // **                             DERIVED                                                                                             **
  1203.     // *********************************************************************************************************
  1204.  
  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);
  1228.  
  1229.             if($ret['code'] != AGIRES_OK || $ret['result'] == -1)
  1230.             {
  1231.                 $choice = -1;
  1232.                 break;
  1233.             }
  1234.  
  1235.             if($ret['result'] != 0)
  1236.             {
  1237.                 $choice = chr($ret['result']);
  1238.                 break;
  1239.             }
  1240.           }
  1241.  
  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.     }
  1253.  
  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.     }
  1267.  
  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'];
  1283.  
  1284.         $ret = array('name'=>'', 'protocol'=>'', 'username'=>'', 'host'=>'', 'port'=>'');
  1285.         $callerid = trim($callerid);
  1286.  
  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.         }
  1294.  
  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.         }
  1304.  
  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.         }
  1314.  
  1315.         return $ret;
  1316.     }
  1317.  
  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;
  1335.  
  1336.         $hash = md5($text);
  1337.         $fname = $this->config['phpagi']['tempdir'] . DIRECTORY_SEPARATOR;
  1338.         $fname .= 'text2wav_' . $hash;
  1339.  
  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.           }
  1350.  
  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.         }
  1358.  
  1359.         // stream it
  1360.         $ret = $this->stream_file($fname, $escape_digits);
  1361.  
  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);
  1367.  
  1368.         return $ret;
  1369.     }
  1370.  
  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']}";
  1386.  
  1387.         $text = trim($text);
  1388.         if($text == '') return true;
  1389.  
  1390.         $hash = md5($text);
  1391.         $fname = $this->config['phpagi']['tempdir'] . DIRECTORY_SEPARATOR;
  1392.         $fname .= 'swift_' . $hash;
  1393.  
  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.           }
  1404.  
  1405.           shell_exec("{$this->config['cepstral']['swift']} -p audio/channels=1,audio/sampling-rate=$frequency $voice -o $fname.wav -f $fname.txt");
  1406.         }
  1407.  
  1408.         // stream it
  1409.         $ret = $this->stream_file($fname, $escape_digits);
  1410.  
  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);
  1416.  
  1417.         return $ret;
  1418.     }
  1419.  
  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.     }
  1492.  
  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.     }
  1546.  
  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.     }
  1557.  
  1558.  
  1559.     // *********************************************************************************************************
  1560.     // **                             PRIVATE                                                                                             **
  1561.     // *********************************************************************************************************
  1562.  
  1563.  
  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'=>'');
  1574.  
  1575.         // write command
  1576.         if(!@fwrite($this->out, trim($command) . "\n")) return $broken;
  1577.         fflush($this->out);
  1578.  
  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);
  1589.  
  1590.         if($count >= 5)
  1591.         {
  1592.     //          $this->conlog("evaluate error on read for $command");
  1593.           return $broken;
  1594.         }
  1595.  
  1596.         // parse result
  1597.         $ret['code'] = substr($str, 0, 3);
  1598.         $str = trim(substr($str, 3));
  1599.  
  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.         }
  1617.  
  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.         }
  1651.  
  1652.         // log some errors
  1653.         if($ret['result'] < 0)
  1654.           $this->conlog("$command returned {$ret['result']}");
  1655.  
  1656.         return $ret;
  1657.     }
  1658.  
  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;
  1670.  
  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.     }
  1681.  
  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;
  1694.  
  1695.         foreach(explode(':', $chpath) as $path)
  1696.           if(is_executable("$path/$cmd"))
  1697.             return "$path/$cmd";
  1698.  
  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.     }
  1704.  
  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.     }    
  1729.  
  1730. }
  1731.  
  1732.  
  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 @
  1745.  
  1746.     @syslog(LOG_WARNING, $file . '[' . $line . ']: ' . $message);
  1747.  
  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.         }
  1766.  
  1767.         // build message
  1768.         $basefile = basename($file);
  1769.         $subject = "$basefile/$line/$level: $message";
  1770.         $message = "$level: $message in $file on line $line\n\n";
  1771.  
  1772.         if(function_exists('mysql_errno') && strpos(' '.strtolower($message), 'mysql'))
  1773.           $message .= 'MySQL error ' . mysql_errno() . ": " . mysql_error() . "\n\n";
  1774.  
  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.         }
  1786.  
  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);
  1791.  
  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.         }
  1800.  
  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;
  1816.  
  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.   }
  1824.  
  1825.   $phpagi_error_handler_email = NULL;

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