root/freepbx/branches/2.5/amp_conf/agi-bin/phpagi.php

Revision 5007, 64.2 kB (checked in by p_lindheimer, 6 years ago)

more general purpose fix for sipaddheader issues

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