root/freepbx/trunk/amp_conf/agi-bin/phpagi.php

Revision 8560, 64.3 kB (checked in by p_lindheimer, 3 years ago)

Merged revisions 7771-7912,7914-8559 via svnmerge from
http://svn.freepbx.org/freepbx/branches/2.5

........

r7782 | mickecarlsson | 2009-05-30 11:32:36 -0700 (Sat, 30 May 2009) | 1 line


Adds charset and emailsubject to vm_email.inc so that the text can be localized using utf-8

........

r7808 | mickecarlsson | 2009-06-09 22:14:19 -0700 (Tue, 09 Jun 2009) | 1 line


Fixed spelling errors

........

r7813 | mickecarlsson | 2009-06-13 14:58:27 -0700 (Sat, 13 Jun 2009) | 1 line


Fixes #3375 set expiration time for the language cookie to one year

........

r7818 | mickecarlsson | 2009-06-14 08:59:00 -0700 (Sun, 14 Jun 2009) | 1 line


Fixed spelling error in Swedish translation for core

........

r7902 | mickecarlsson | 2009-07-29 09:26:59 -0700 (Wed, 29 Jul 2009) | 1 line


Closes #3699, adds Ukranian language to ARI. Thank you Oleh

........

r7912 | p_lindheimer | 2009-08-02 18:44:47 -0700 (Sun, 02 Aug 2009) | 1 line


allow freepbx_debug to print objects also

........

r7914 | p_lindheimer | 2009-08-02 18:48:28 -0700 (Sun, 02 Aug 2009) | 3 lines


Initialized merge tracking via "svnmerge" with revisions "1-7913" from
http://svn.freepbx.org/freepbx/branches/2.6

........

r8119 | mickecarlsson | 2009-08-24 12:37:31 -0700 (Mon, 24 Aug 2009) | 1 line


Fixed spelling error

........

r8120 | mickecarlsson | 2009-08-24 12:39:12 -0700 (Mon, 24 Aug 2009) | 1 line


Closes #3756 updated French language for amp. Thank you medialsace

........

r8161 | mickecarlsson | 2009-08-25 11:47:28 -0700 (Tue, 25 Aug 2009) | 1 line


Closes 3707 removes unnecessary debug output of static information

........

r8300 | p_lindheimer | 2009-09-05 18:55:46 -0700 (Sat, 05 Sep 2009) | 1 line


update 2.5 to jQuery 1.3.2

........

r8301 | p_lindheimer | 2009-09-05 18:56:54 -0700 (Sat, 05 Sep 2009) | 1 line


update jQuery in view

........

r8302 | p_lindheimer | 2009-09-05 18:58:21 -0700 (Sat, 05 Sep 2009) | 1 line


update libreepbx.javascript.js with update jQuery, and build tool

........

r8315 | p_lindheimer | 2009-09-05 19:36:02 -0700 (Sat, 05 Sep 2009) | 1 line


Auto checkin packed libfreepbx.javascripts.js as part of build process

........

r8316 | p_lindheimer | 2009-09-05 19:36:11 -0700 (Sat, 05 Sep 2009) | 1 line


Creating release 2.5.2

........

r8348 | p_lindheimer | 2009-09-08 15:55:32 -0700 (Tue, 08 Sep 2009) | 1 line


created 2.5.2 in wrong branch, adding it 2.5 so properly version info is returned

........

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