Revision
15082
Author
gled
Date
2009-10-05 07:40:50 -0500 (Mon, 05 Oct 2009)

Log Message

needed for tests

Added Paths

Diff

Added: freeswitch/trunk/contrib/gled/php/check.php (0 => 15082)


--- freeswitch/trunk/contrib/gled/php/check.php	                        (rev 0)
+++ freeswitch/trunk/contrib/gled/php/check.php	2009-10-05 12:40:50 UTC (rev 15082)
@@ -0,0 +1,18 @@
+#!/usr/bin/php -q
+<?php
+require_once('phpagi-2.14/phpagi.php');
+
+$agi = new AGI();
+logthis("New call incoming");
+
+$agi->answer();
+
+logthis("Answered");
+
+$res = $agi->stream_file('/telephony/freeswitch/sounds/en/us/callie/ivr/8000/ivr-you_have_dialed_an_invalid_extension.wav',"*");
+
+logthis("Stream_file res:".print_r($res,true));
+
+$agi->hangup();
+
+?>
Property changes on: freeswitch/trunk/contrib/gled/php/check.php
___________________________________________________________________
Name: svn:executable
   + *

Added: freeswitch/trunk/contrib/gled/php/phpagi-2.14/COPYING (0 => 15082)


--- freeswitch/trunk/contrib/gled/php/phpagi-2.14/COPYING	                        (rev 0)
+++ freeswitch/trunk/contrib/gled/php/phpagi-2.14/COPYING	2009-10-05 12:40:50 UTC (rev 15082)
@@ -0,0 +1,458 @@
+		  GNU LESSER GENERAL PUBLIC LICENSE
+		       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+		  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+			    NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+		     END OF TERMS AND CONDITIONS

Added: freeswitch/trunk/contrib/gled/php/phpagi-2.14/README (0 => 15082)


--- freeswitch/trunk/contrib/gled/php/phpagi-2.14/README	                        (rev 0)
+++ freeswitch/trunk/contrib/gled/php/phpagi-2.14/README	2009-10-05 12:40:50 UTC (rev 15082)
@@ -0,0 +1,52 @@
+PHPAGI README
+ $Id: README,v 1.4 2005/05/19 13:19:37 pinhole Exp $
+---------------------------------------
+
+Welcome to PHPAGI. 
+
+phpagi is a set of PHP classes for use in developing applications with
+the Asterisk Gateway Interface, and is licensed under the GNU Lesser
+General Public License (see COPYING for terms).
+
+This release (version 2) of the phpagi classes is a significant overhaul
+from the old version 1 library.  API functions have been renamed and 
+restructured.
+
+Version 1 of phpagi is no longer supported, but will continue to be
+available for historical purposes.  We strongly encourage you to migrate
+to this new version.
+
+If you have developed software based around phpagi, we'd like to hear from 
+you!  Drop us a note, and indicate whether you'd like us to list your 
+application on our website.
+
+FILES
+-----
+ phpagi.php           - The main phpagi class.
+ phpagi-asmanager.php - The Asterisk Manager class.
+ phpagi-fastagi.php   - FastAGI class.
+ phpagi_1.php         - Compatability class for 1.12 release
+
+ docs/                - README files for the classes.
+ api-docs/            - API Documentation (html)
+
+DOCS
+----
+ README.phpagi           - The main phpagi README
+ README.phpagi-asmanager - The phpagi asterisk manager README
+ README.phpagi-fastagi   - phpagi fastagi README
+ 
+ CHANGELOG               - Change Log.
+
+ phpagi.conf             - An example configuration file for phpagi.
+ fastagi.xinetd          - xinetd.conf sample configuration for fastagi
+
+
+SUPPORT
+-------
+
+Support for phpagi is available from the project website. 
+
+ * http://phpagi.sourceforge.net
+ * http://sourceforge.net/projects/phpagi/
+

Added: freeswitch/trunk/contrib/gled/php/phpagi-2.14/docs/CHANGELOG (0 => 15082)


--- freeswitch/trunk/contrib/gled/php/phpagi-2.14/docs/CHANGELOG	                        (rev 0)
+++ freeswitch/trunk/contrib/gled/php/phpagi-2.14/docs/CHANGELOG	2009-10-05 12:40:50 UTC (rev 15082)
@@ -0,0 +1,159 @@
+----------
+CHANGES
+----------
+
+May 25, 2005 - David Eder
+	* Added fastpass support.
+
+May 18, 2005 - David Eder
+	* Added phpagi_1.php as a wrapper class for 1.12 compatibility.
+
+March 25, 2005 - David Eder
+	* Changed the way text2wave and swift are executed for better compatibility.
+	* Added caching to text2wav and swift.
+
+March 12, 2005 - David Eder
+	* fixed autohangup in phpagi-asmanager.php, renamed to set_autohangup.
+	* Added more documentation to phpagi-asmanager.php.
+	* Added weather example.
+
+March 4, 2005 - David Eder
+	* FastAGI via xinetd
+
+February 17, 2005 - David Eder
+	* Fixed bugs with error handler.
+
+February 16, 2005 - David Eder
+	* Added Cepstral swift TTS patch from C. Arbusti of Soluzioni Vocali ( http://soluzionivocali.it ).
+	* Extended swift TTS functionality.
+	* Moved tempdir from [festival] to [phpagi] so that it can be shared with other extensions.
+
+February 2, 2005 - David Eder
+	* More documentation.
+	* Reworked and reintegrated AGI_AsteriskManager class.
+
+January 21, 2005 - David Eder
+	* Added exec_dial($type, $identifier, $timeout=NULL, $options=NULL, $url=NULL).
+        * Added exec_goto($a, $b=NULL, $c=NULL).
+	* Fixed bugs in evaluate, better support of multiline and  closed input and output.
+	* Fixed bugs in config initialization.
+
+January 19, 2005 - David Eder
+        * SUMMARY:
+	*   Massive restructuring!
+        *   Updated to use more PHP internal functions.
+        *   Updated function arguments to represent their AGI function's arguments.
+        *   Functions now return a consistent result array.
+        *   Added phpdoc documentation.
+        *   Removed functions that can be done with internal PHP or AGI functions.
+        *   Enhanced error handler.
+	*
+	* Goals of changes:
+	*
+	* It is important for a language API to not wander too far from the general
+	* API. Functions that are named differently have been updated.  Function
+	* arguments that do not match the AGI API have been updated. Underscores
+	* have been substituted for spaces to make them compatible with PHP function
+	* names.  Optional arguments in AGI should remain optional if possible. Return
+	* values should be consistent.  Functions need to be better documented.
+	*
+	* Result:
+	*
+	* The return from most functions is now
+	*   array('code'=>$code, 'result'=>$result, 'data'=>$data)
+	* ['data'] still needs some work.
+	*
+	* removed class variables:
+	*   $response -  It was no longer used with the new return structure.
+	*
+	* removed functions:
+	*   agi_is_error($retarr) - It was no longer used with the new return
+	*     structure.  Each function has it's own result that must be evalutated
+	*     by the programmer, as the return values are often specific to the function.
+	*   agi_readresult($str=FALSE) - It was no longer used with the new return structure.
+	*   agi_response_code() - It was no longer used with the new return structure.
+	*   agi_response_result() - It was no longer used with the new return structure.
+	*   agi_response_data() - It was no longer used with the new return structure.
+	*   agi_response_var($var) - It was no longer used with the new return structure.
+	*   agi_response_is_error() - see agi_is_error
+	*   agi_read() - It was deprecated
+	*   con_print_r($arr,$label='',$lvl=0) - Use print_r($arr, true) in conjunction with conlog
+	*   agi_getdtmf($len,$timeout,$terminator=FALSE,$prompt=FALSE) - use get_data
+	*   agi_dtmf2text($len,$timeout,$terminator=FALSE,$prompt=FALSE) - use text_input
+	*   arr2str($arr) - use PHP function join
+	*   config_load($file) - use PHP function parse_ini_file
+	*   enum_lookup($telnumber,$rDNS="e164.org") - use exec_enumlookup
+	*   enum_txtlookup($telnumber,$rDNS="e164.org") - use exec_enumlookup
+	*
+	* added functions:
+	*   answer()
+	*   autohangup($time=0)
+	*   exec($application, $options)
+	*   get_data($filename, $timeout=NULL, $max_digits=NULL)
+	*   receive_char($timeout=-1)
+	*   say_phonetic($text, $escape_digits='')
+	*   set_context($context)
+	*   set_extension($extension)
+	*   set_priority($priority)
+	*   tdd_mode($setting)
+	*   wait_for_digit($timeout=-1)
+	*   database_deltree($family, $keytree='')
+	*   noop()
+	*   set_music($enabled=true, $class='')
+	*   exec_absolutetimeout($seconds=0)
+	*   exec_agi($command, $args)
+	*   exec_enumlookup($exten)
+	*   text_input($mode='NUMERIC')
+	*   say_punctuation($text, $escape_digits='', $frequency=8000)
+	*   which($cmd, $checkpath=NULL)
+	*   make_folder($folder, $perms=0755)
+	* updated functions:
+	*   agi_exec($str) -> evaluate($command) - exec is an AGI function
+	*   agi_verbose($str,$vbl=1) -> verbose($message, $level=1) - consistency with AGI
+	*   db_get($family,$key) -> database_get($family, $key) - consistency with AGI
+	*   db_put($family,$key,$val) -> database_put($family, $key, $value) - consistency with AGI
+	*   db_del($family,$key) -> database_del($family, $key) - consistency with AGI
+	*   get_var($var) -> get_variable($variable) - consistency with AGI
+	*   set_var($var,$val) -> set_variable($variable, $value) - consistency with AGI
+	*   agi_hangup() -> hangup($channel='') - consistency with AGI
+	*   agi_channel_status($channel) -> channel_status($channel='') - consistency with AGI
+	*   agi_recordfile($file,$format,$timeout=5000,$prompt=FALSE) -> record_file($file, $format, $escape_digits='', $timeout=-1, $beep=false, $silence=NULL) - consistency with AGI
+	*   agi_play($file) -> stream_file($filename, $escape_digits='', $offset=0) - consistency with AGI
+	*   agi_goto($con,$ext='s',$pri=1) -> goto($context, $extension='s', $priority=1) - consistency with AGI
+	*   agi_saydigits($digits) -> say_digits($digits, $escape_digits='') - consistency with AGI
+	*   agi_saynumber($number) -> say_number($number, $escape_digits='') - consistency with AGI
+	*   agi_saytime($time="") -> say_time($time=NULL, $escape_digits='') - consistency with AGI
+	*   agi_setlanguage($language="en") -> exec_setlanguage($language='en') - consistency with AGI
+	*   text2wav($text) -> text2wav($text, $escape_digits='', $frequency=8000) - consistency with AGI
+	*   phpagi_error_handler($errno, $errstr, $errfile, $errline) -> phpagi_error_handler($level, $message, $file, $line, $context) - I needed better error handling
+
+August 29, 2004 -
+	* Fixed db_get now returns a value
+	* Fixed db_put now stores a value
+	* Added enum_lookup, requires external "dig" utility.
+	* Tweak con_print_r now dumps arrays, displays variable type.
+	* Added enum_txtlookup, requires external "dig" utility".
+	* Added parse_callerid, thanks to http://www.sbuehl.com/projects/asterisk/asterisk-howto-3.html
+
+August 26, 2004 - 
+	* Added agi_verbose
+	* Added debug config-option to shut conlog up
+	* Added agi_saydigits, agi_saynumber, agi_saytime, agi_setlanguage
+
+April 8, 2004 - v1.5 internal
+    * Added agi_goto - David Croft
+    * Added this changelog (maintain it eh?) - Matthew Asham
+    * Added quote fixing to conlog - David Croft
+    * Added new parameter to AGI constructor, $configopt array - Matthew Asham
+    - Added phpagi_error_handler - David Croft
+    
+ 
+    
+March 20, 2004 - v1.5 
+	* Fixed buffering bugs - David Croft
+	* added demo app. - Matthew Asham
+	* misc stuff. - Matthew Asham
+
+November 2003 
+	* Initial version
+	

Added: freeswitch/trunk/contrib/gled/php/phpagi-2.14/docs/CVS/Entries (0 => 15082)


--- freeswitch/trunk/contrib/gled/php/phpagi-2.14/docs/CVS/Entries	                        (rev 0)
+++ freeswitch/trunk/contrib/gled/php/phpagi-2.14/docs/CVS/Entries	2009-10-05 12:40:50 UTC (rev 15082)
@@ -0,0 +1,7 @@
+/CHANGELOG/1.4/Wed May 25 18:43:57 2005//
+/README.phpagi/1.1/Mon Mar 14 19:38:20 2005//
+/README.phpagi-asmanager/1.1/Mon Mar 14 19:38:20 2005//
+/README.phpagi-fastagi/1.1/Mon Mar 14 19:38:20 2005//
+/fastagi.xinetd/1.1/Mon Mar 14 19:38:20 2005//
+/phpagi.example.conf/1.1/Mon Mar 14 19:38:20 2005//
+D

Added: freeswitch/trunk/contrib/gled/php/phpagi-2.14/docs/CVS/Repository (0 => 15082)


--- freeswitch/trunk/contrib/gled/php/phpagi-2.14/docs/CVS/Repository	                        (rev 0)
+++ freeswitch/trunk/contrib/gled/php/phpagi-2.14/docs/CVS/Repository	2009-10-05 12:40:50 UTC (rev 15082)
@@ -0,0 +1 @@
+phpagi/docs

Added: freeswitch/trunk/contrib/gled/php/phpagi-2.14/docs/CVS/Root (0 => 15082)


--- freeswitch/trunk/contrib/gled/php/phpagi-2.14/docs/CVS/Root	                        (rev 0)
+++ freeswitch/trunk/contrib/gled/php/phpagi-2.14/docs/CVS/Root	2009-10-05 12:40:50 UTC (rev 15082)
@@ -0,0 +1 @@
+:ext:masham@cvs.sourceforge.net:/cvsroot/phpagi

Added: freeswitch/trunk/contrib/gled/php/phpagi-2.14/docs/README.phpagi (0 => 15082)


--- freeswitch/trunk/contrib/gled/php/phpagi-2.14/docs/README.phpagi	                        (rev 0)
+++ freeswitch/trunk/contrib/gled/php/phpagi-2.14/docs/README.phpagi	2009-10-05 12:40:50 UTC (rev 15082)
@@ -0,0 +1,94 @@
+phpagi:  an AGI class written in PHP
+
+  Matthew Asham <matthewa@bcwireless.net>
+  http://phpagi.sourceforge.net
+
+Contributions by:
+  Florian Overkamp <florian@speakup.nl>
+  David Eder <david@eder.us>
+
+OVERVIEW
+--------
+
+Include it:
+
+require 'phpagi.php';
+
+Construct it:
+
+$agi = new AGI();
+
+This creates a new AGI object with all the agi vars read in, you're now ready
+to rock and roll.
+
+Answer the line.
+
+$agi->answer();
+
+Play a file.
+
+$agi->stream_file('somefile.gsm');
+
+Record a file.
+
+$agi->record_file($file, $format, $escape_digits='', $timeout=-1, $beep=false, $silence=NULL)
+
+There's more, read the class or refer to the PHPAGI website for more information.  
+
+                     This README is seriousley lacking.
+
+CONFIGURATION
+-------------
+
+phpagi supports an ini style configuration file, and run time configuration.
+
+By default the class reads in the contents of /etc/asterisk/phpagi.conf into
+$this->config.  The format of the ini file is as follows:
+
+[examplesection]
+foo=bar
+bar=dew
+dew=pale ale
+
+This is read into $this->config as:
+
+$this->config['examplesection']['foo']='bar';
+$this->config['examplesection']['bar]='dew';
+$this->config['examplesection']['dew']='pale ale';
+
+
+Run time configuration is also supported.  Pass an array of variables and values as the
+second parameter to the AGI constructor, these fields are stored in $this->config['phpagi'].
+
+ie:
+
+$myconfig=array(
+	"error_handler"="true"
+);
+
+	$agi=new AGI("/etc/asterisk/phpagi.conf",$myconfig);
+
+
+The following run-time configuration options are used by the phpagi class to change behaviour.  They 
+are all contained in the $this->config['phpagi'] array.
+
+ * error_handler - set to "true" (string) to enable php debugging
+ 
+	
+EXAMPLE APPLICATION
+-------------------
+
+ping.php is an example phpagi application.  it asks for an IP address to be
+entered, and reads back the results of a "ping" summary.
+
+to use it, you'll need festival installed.  see  http://www.voip-info.org/tiki-index.php?page=Asterisk+festival+installation 
+for all the groovy bits to get festival working with asterisk, then configure
+phpagi to use it too:
+
+in /etc/asterisk/phpagi.conf:
+
+[festival]
+text2wave=/usr/src/festival/bin/text2wave
+tempdir=/var/lib/asterisk/sounds/tmp/
+
+

Added: freeswitch/trunk/contrib/gled/php/phpagi-2.14/docs/README.phpagi-asmanager (0 => 15082)


--- freeswitch/trunk/contrib/gled/php/phpagi-2.14/docs/README.phpagi-asmanager	                        (rev 0)
+++ freeswitch/trunk/contrib/gled/php/phpagi-2.14/docs/README.phpagi-asmanager	2009-10-05 12:40:50 UTC (rev 15082)
@@ -0,0 +1,218 @@
+phpagi-asmanager:  an Asterisk Manager class written in PHP
+
+  Matthew Asham <matthewa@bcwireless.net>
+  http://phpagi.sourceforge.net
+------------------------------------------------------------------------------
+
+------------------------------------------------------------------------------
+SECURITY
+------------------------------------------------------------------------------
+
+Validation:
+
+******If asterisk is running as root, the manager interface may allow the
+execution of arbitrary shell commands as root. If the user can update any
+configuration file that can execute arbitrary command (like the dialplan),
+the system may be compromised.
+
+Also, look out for command injection.  Consider the following example:
+
+	$as->Events($_POST['events_status']);
+
+We expect either 'on' or 'off', but the attacker uses:
+
+	"\r\n\r\nAction: Command\r\nCommand: database put forward 54321 19005551212";
+
+
+Validation is a *must* for all user data.
+
+
+Username and Secret:
+
+Storing the username and secret in the config file will isolate them from your
+code.
+
+Isolation of username and secret in the config file does not mean that the
+script cannot simple read the config file.  The config file must be readable
+by the script.
+
+
+CREATING A NEW INSTANCE OF THE CLASS
+------------------------------------------------------------------------------
+
+The class can be created standalone of phpagi.php, or through phpagi.
+
+STANDALONE:
+
+require "phpagi-asmanager.php";
+
+$as = new AGI_AsteriskManager();
+
+FROM PHPAGI:
+
+require "phpagi.php";
+
+$agi = new AGI();
+$as = $agi->new_AsteriskManager();
+
+Notes:
+
+* If the class is created using $agi->new_AsteriskManager(),
+  AGI_AsteriskManager will use the parent phpagi for logging to the Asterisk 
+  console.  
+
+* phpagi.php will include phpagi-asmanager.php by itself.  
+  * If phpagi-asmanager.php is included _before_ phpagi.php, phpagi.php will 
+    not attempt to re-include it.
+  * If phpagi.php tries to include phpagi-asmanager.php but is unable to do 
+    so, an error will be echoed to the asterisk console and the script will 
+    continue running normally.  in this case the return value of 
+    new_AsteriskManager() will be FALSE.
+
+------------------------------------------------------------------------------
+CONFIGURATION
+------------------------------------------------------------------------------
+
+phpagi-asmanager uses the same configuration file as phpagi.conf (usually
+/etc/asterisk/phpagi.conf). All configuration information specific to
+phpagi-asmanager is contained in the [asmanager] section of the .conf file.
+
+supported directives:
+
+[asmanager]
+# server to connect to
+server=localhost
+
+# default manager port
+port=5038
+
+#username for login
+username=me_and_only_me
+
+#password for login
+secret=i_am_not_telling
+
+
+
+------------------------------------------------------------------------------
+CONNECTING
+------------------------------------------------------------------------------
+
+	$res = $as->connect("localhost", "username", "password");
+	if($res == FALSE) {
+		echo "Connection failed.\n";
+	}
+	elseif($res == TRUE){
+		echo "Connection established.\n";
+	}
+
+A port can also be specified for the hostname.  eg:
+
+	$res = $as->connect("my.asterisk.server:1234", "username", "port");
+
+If the no parameters are specified, the defaults from the config will be used.
+
+
+------------------------------------------------------------------------------
+DISCONNECTING
+------------------------------------------------------------------------------
+
+  $as->disconnect();
+
+------------------------------------------------------------------------------
+SENDING REQUESTS
+------------------------------------------------------------------------------
+
+	$as->send_request($eventname, $arrayofparameterstopass);
+
+send_request() calls wait_request and returns an array of returned data from
+the manager.  If something went wrong, it returns false.
+
+wait_request() shouldn't need to be called from a script directly unless you 
+are implementing merely an event listener.
+
+wait_request() will also detect events and dispatch any registered event
+handlers for the event.
+
+examples:
+
+	$res = $as->send_request('EventName',
+                                 array('Channel'=>'Zap/1/16045551212',
+                                                  'SomeParameter'=>'data'));
+	echo "Dump of returned data:\n";
+	foreach($res as $var=>$val)
+	  echo "$var = $val\n";
+
+
+$res['Response'] will generally be 'Success' on success and 'Error' on
+failure. But this is not always true.  If $res['Response'] == 'Follows', a
+multi-line response will be stored in $res['data'].
+
+Several manager commands have been aliased for convenience. See below.
+
+------------------------------------------------------------------------------
+EVENTS
+------------------------------------------------------------------------------
+
+TODO: non-blocking socket i/o. 
+
+The class uses event callbacks to process events received from the manager.
+
+The event callback prototype looks like:
+
+	function dump_event($ecode, $data, $server, $port)
+	{
+	  echo "received event '$ecode' from $server:$port\n";
+	  print_r($data);
+	}
+
+To register an event call back:
+
+	$as->add_event_handler('eventname', 'eventfunction');
+
+eg:
+
+	$as->add_event_handler('registry', 'dump_event');
+
+
+The special eventname "*" can also be registered.  any eventname not
+specifically registered will be handled by the "*" handler.  If no "*" handler
+is defined, the event will be silently ignored.
+
+
+------------------------------------------------------------------------------
+PRECANNED FUNCTIONS
+------------------------------------------------------------------------------
+
+The following Manager functions have been aliased for convenience:
+
+AbsoluteTimeout
+ChangeMonitor
+Command
+Events
+ExtensionState
+GetVar
+Hangup
+IAXPeers
+ListCommands
+Logoff
+MailboxCount
+MailboxStatus
+Monitor
+Originate
+ParkedCalls
+Ping
+Queues
+QueueStatus
+Redirect
+SetCDRUserField
+SetVar
+SIPpeers
+Status
+StopMontor
+ZapDialOffhook
+ZapDNDoff
+ZapDNDon
+ZapHangup
+ZapTransfer
+

Added: freeswitch/trunk/contrib/gled/php/phpagi-2.14/docs/README.phpagi-fastagi (0 => 15082)


--- freeswitch/trunk/contrib/gled/php/phpagi-2.14/docs/README.phpagi-fastagi	                        (rev 0)
+++ freeswitch/trunk/contrib/gled/php/phpagi-2.14/docs/README.phpagi-fastagi	2009-10-05 12:40:50 UTC (rev 15082)
@@ -0,0 +1,46 @@
+First, in /etc/services, at this line:
+
+fastagi         4573/tcp                        # Asterisk AGI
+
+
+
+Second, create /etc/xnetd.d/fastagi with:
+
+# default: off
+# description: fastagi is a remote AGI interface
+service fastagi
+{
+        socket_type  = stream
+        user         = root
+        group        = nobody
+        server       = <path to phpagi-fastagi.php>
+        wait         = no
+        protocol     = tcp
+        bind         = 127.0.0.1
+        disable      = no
+}
+
+Make sure you set the path to the phpagi-fastagi.php script.  Set the user
+and group to a non-root user if none of your scripts need root access. You
+might consider using posix_setuid and friends to reduce privileges. Change
+the bind address to your outbound IP address or to 0.0.0.0 to allow anyone
+to connect. Be sure to read up about xinetd and take advantage of security
+features it provides.  Fast AGI doesn't provide authentification.  It's up
+to you to keep unwanted visitors from extracting information from your AGI
+implementation.
+
+
+Third, write your code.
+
+Take special notice of how fastagi.php works:
+
+	1. $fastagi is initialized as a new AGI.
+	2. The script determines which script was requested.
+	3. The script is called using
+	   reqire_once($fastagi->request['agi_request']).
+	4. Your script takes over.  You must not create a new AGI, but
+	   insead use the $fastagi instance that has already been created.
+
+In your dialplan:
+
+exten => 5551212, 1, Agi(agi://127.0.0.1/myscript.php)

Added: freeswitch/trunk/contrib/gled/php/phpagi-2.14/docs/fastagi.xinetd (0 => 15082)


--- freeswitch/trunk/contrib/gled/php/phpagi-2.14/docs/fastagi.xinetd	                        (rev 0)
+++ freeswitch/trunk/contrib/gled/php/phpagi-2.14/docs/fastagi.xinetd	2009-10-05 12:40:50 UTC (rev 15082)
@@ -0,0 +1,14 @@
+# default: off
+# description: fastagi is a remote AGI interface
+service fastagi
+{
+        socket_type  = stream
+        user         = root
+        group        = nobody
+        server       = /var/lib/asterisk/agi-bin/phpagi/phpagi-fastagi.php
+        wait         = no
+        protocol     = tcp
+        bind         = 127.0.0.1
+        disable      = no
+}
+

Added: freeswitch/trunk/contrib/gled/php/phpagi-2.14/docs/phpagi.example.conf (0 => 15082)


--- freeswitch/trunk/contrib/gled/php/phpagi-2.14/docs/phpagi.example.conf	                        (rev 0)
+++ freeswitch/trunk/contrib/gled/php/phpagi-2.14/docs/phpagi.example.conf	2009-10-05 12:40:50 UTC (rev 15082)
@@ -0,0 +1,26 @@
+; example phpagi.conf
+
+[phpagi]
+debug=true				; enable debuging
+error_handler=true			; use internal error handler
+admin=errors@mydomain.com		; mail errors to
+hostname=sip.mydomain.com		; host name of this server
+tempdir=/var/spool/asterisk/tmp/	; temporary directory for storing temporary output
+
+[asmanager]
+server=localhost			; server to connect to
+port=5038				; default manager port
+username=me_and_only_me			; username for login
+secret=i_am_not_telling			; password for login
+
+[fastagi]
+setuid=true				; drop privileges to owner of script
+basedir=/var/lib/asterisk/agi-bin/	; path to script folder
+
+[festival]				; text to speech engine
+text2wave=/usr/bin/text2wave		; path to text2wave binary
+
+[cepstral]				; alternate text to speech engine
+swift=/opt/swift/bin/swift		; path to switft binary
+voice=David				; default voice
+

Added: freeswitch/trunk/contrib/gled/php/phpagi-2.14/mkdocs.php (0 => 15082)


--- freeswitch/trunk/contrib/gled/php/phpagi-2.14/mkdocs.php	                        (rev 0)
+++ freeswitch/trunk/contrib/gled/php/phpagi-2.14/mkdocs.php	2009-10-05 12:40:50 UTC (rev 15082)
@@ -0,0 +1,7 @@
+#!/usr/local/bin/php -q
+<?php
+  $cwd = getcwd();
+  passthru("phpdoc -t \"$cwd/api-docs\" -f \"$cwd/phpagi.php,$cwd/phpagi-asmanager.php,$cwd/phpagi-fastagi.php,$cwd/phpagi_1.php\" -pp on -ti \"phpAGI\" -o HTML:frames:earthli -s on");
+?>
+
+
Property changes on: freeswitch/trunk/contrib/gled/php/phpagi-2.14/mkdocs.php
___________________________________________________________________
Name: svn:executable
   + *

Added: freeswitch/trunk/contrib/gled/php/phpagi-2.14/phpagi-asmanager.php (0 => 15082)


--- freeswitch/trunk/contrib/gled/php/phpagi-2.14/phpagi-asmanager.php	                        (rev 0)
+++ freeswitch/trunk/contrib/gled/php/phpagi-2.14/phpagi-asmanager.php	2009-10-05 12:40:50 UTC (rev 15082)
@@ -0,0 +1,818 @@
+<?php
+ /**
+  * phpagi-asmanager.php : PHP Asterisk Manager functions
+  * Website: http://phpagi.sourceforge.net
+  *
+  * $Id: phpagi-asmanager.php,v 1.10 2005/05/25 18:43:48 pinhole Exp $
+  *
+  * Copyright (c) 2004, 2005 Matthew Asham <matthewa@bcwireless.net>, David Eder <david@eder.us>
+  * All Rights Reserved.
+  *
+  * This software is released under the terms of the GNU Lesser General Public License v2.1
+  *  A copy of which is available from http://www.gnu.org/copyleft/lesser.html
+  * 
+  * We would be happy to list your phpagi based application on the phpagi
+  * website.  Drop me an Email if you'd like us to list your program.
+  *
+  * @package phpAGI
+  * @version 2.0
+  */
+
+
+ /**
+  * Written for PHP 4.3.4, should work with older PHP 4.x versions.  
+  * Please submit bug reports, patches, etc to http://sourceforge.net/projects/phpagi/
+  * Gracias. :)
+  *
+  */
+
+  if(!class_exists('AGI'))
+  {
+    require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'phpagi.php');
+  }
+
+ /**
+  * Asterisk Manager class
+  *
+  * @link http://www.voip-info.org/wiki-Asterisk+config+manager.conf
+  * @link http://www.voip-info.org/wiki-Asterisk+manager+API
+  * @example examples/sip_show_peer.php Get information about a sip peer
+  * @package phpAGI
+  */
+  class AGI_AsteriskManager
+  {
+   /**
+    * Config variables
+    *
+    * @var array
+    * @access public
+    */
+    var $config;
+
+   /**
+    * Socket
+    *
+    * @access public
+    */
+    var $socket = NULL;
+
+   /**
+    * Server we are connected to
+    *
+    * @access public
+    * @var string
+    */
+    var $server;
+
+   /**
+    * Port on the server we are connected to
+    *
+    * @access public
+    * @var integer
+    */
+    var $port;
+
+   /**
+    * Parent AGI
+    *
+    * @access private
+    * @var AGI
+    */
+    var $pagi;
+
+   /**
+    * Event Handlers
+    *
+    * @access private
+    * @var array
+    */
+    var $event_handlers;
+
+   /**
+    * Constructor
+    *
+    * @param string $config is the name of the config file to parse or a parent agi from which to read the config
+    * @param array $optconfig is an array of configuration vars and vals, stuffed into $this->config['asmanager']
+    */
+    function AGI_AsteriskManager($config=NULL, $optconfig=array())
+    {
+      // load config
+      if(!is_null($config) && file_exists($config))
+        $this->config = parse_ini_file($config, true);
+      elseif(file_exists(DEFAULT_PHPAGI_CONFIG))
+        $this->config = parse_ini_file(DEFAULT_PHPAGI_CONFIG, true);
+
+      // If optconfig is specified, stuff vals and vars into 'asmanager' config array.
+      foreach($optconfig as $var=>$val)
+        $this->config['asmanager'][$var] = $val;
+
+      // add default values to config for uninitialized values
+      if(!isset($this->config['asmanager']['server'])) $this->config['asmanager']['server'] = 'localhost';
+      if(!isset($this->config['asmanager']['port'])) $this->config['asmanager']['port'] = 5038;
+      if(!isset($this->config['asmanager']['username'])) $this->config['asmanager']['username'] = 'placecalls';
+      if(!isset($this->config['asmanager']['secret'])) $this->config['asmanager']['secret'] = 'asecret';
+    }
+
+   /**
+    * Send a request
+    *
+    * @param string $action
+    * @param array $parameters
+    * @return array of parameters
+    */
+    function send_request($action, $parameters=array())
+    {
+      $req = "Action: $action\r\n";
+      foreach($parameters as $var=>$val)
+        $req .= "$var: $val\r\n";
+      $req .= "\r\n";
+      fwrite($this->socket, $req);
+      return $this->wait_response();
+    }
+
+   /**
+    * Wait for a response
+    *
+    * If a request was just sent, this will return the response.
+    * Otherwise, it will loop forever, handling events.
+    *
+    * @param boolean $allow_timeout if the socket times out, return an empty array
+    * @return array of parameters, empty on timeout
+    */
+    function wait_response($allow_timeout=false)
+    {
+      $timeout = false;
+      do
+      {
+        $type = NULL;
+        $parameters = array();
+
+        $buffer = trim(fgets($this->socket, 4096));
+        while($buffer != '')
+        {
+          $a = strpos($buffer, ':');
+          if($a)
+          {
+            if(!count($parameters)) // first line in a response?
+            {
+              $type = strtolower(substr($buffer, 0, $a));
+              if(substr($buffer, $a + 2) == 'Follows')
+              {
+                // A follows response means there is a miltiline field that follows.
+                $parameters['data'] = '';
+                $buff = fgets($this->socket, 4096);
+                while(substr($buff, 0, 6) != '--END ')
+                {
+                  $parameters['data'] .= $buff;
+                  $buff = fgets($this->socket, 4096);
+                }
+              }
+            }
+
+            // store parameter in $parameters
+            $parameters[substr($buffer, 0, $a)] = substr($buffer, $a + 2);
+          }
+          $buffer = trim(fgets($this->socket, 4096));
+        }
+
+        // process response
+        switch($type)
+        {
+          case '': // timeout occured
+            $timeout = $allow_timeout;
+            break;
+          case 'event':
+            $this->process_event($parameters);
+            break;
+          case 'response':
+            break;
+          default:
+            $this->log('Unhandled response packet from Manager: ' . print_r($parameters, true));
+            break;
+        }
+      } while($type != 'response' && !$timeout);
+      return $parameters;
+    }
+
+   /**
+    * Connect to Asterisk
+    *
+    * @example examples/sip_show_peer.php Get information about a sip peer
+    *
+    * @param string $server
+    * @param string $username
+    * @param string $secret
+    * @return boolean true on success
+    */
+    function connect($server=NULL, $username=NULL, $secret=NULL)
+    {
+      // use config if not specified
+      if(is_null($server)) $server = $this->config['asmanager']['server'];
+      if(is_null($username)) $username = $this->config['asmanager']['username'];
+      if(is_null($secret)) $secret = $this->config['asmanager']['secret'];
+
+      // get port from server if specified
+      if(strpos($server, ':') !== false)
+      {
+        $c = explode(':', $server);
+        $this->server = $c[0];
+        $this->port = $c[1];
+      }
+      else
+      {
+        $this->server = $server;
+        $this->port = $this->config['asmanager']['port'];
+      }
+
+      // connect the socket
+      $errno = $errstr = NULL;
+      $this->socket = @fsockopen($this->server, $this->port, $errno, $errstr);
+      if($this->socket == false)
+      {
+        $this->log("Unable to connect to manager {$this->server}:{$this->port} ($errno): $errstr");
+        return false;
+      }
+
+      // read the header
+      $str = fgets($this->socket);
+      if($str == false)
+      {
+        // a problem.
+        $this->log("Asterisk Manager header not received.");
+        return false;
+      }
+      else
+      {
+        // note: don't $this->log($str) until someone looks to see why it mangles the logging
+      }
+
+      // login
+      $res = $this->send_request('login', array('Username'=>$username, 'Secret'=>$secret, 'Events' => 'off'));
+      if($res['Response'] != 'Success')
+      {
+        $this->log("Failed to login.");
+        $this->disconnect();
+        return false;
+      }
+      return true;
+    }
+
+   /**
+    * Disconnect
+    *
+    * @example examples/sip_show_peer.php Get information about a sip peer
+    */
+    function disconnect()
+    {
+      $this->logoff();
+      fclose($this->socket);
+    }
+
+   // *********************************************************************************************************
+   // **                       COMMANDS                                                                      **
+   // *********************************************************************************************************
+
+   /**
+    * Set Absolute Timeout
+    *
+    * Hangup a channel after a certain time.
+    *
+    * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+AbsoluteTimeout
+    * @param string $channel Channel name to hangup
+    * @param integer $timeout Maximum duration of the call (sec)
+    */
+    function AbsoluteTimeout($channel, $timeout)
+    {
+      return $this->send_request('AbsoluteTimeout', array('Channel'=>$channel, 'Timeout'=>$timeout));
+    }
+
+   /**
+    * Change monitoring filename of a channel
+    *
+    * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+ChangeMonitor
+    * @param string $channel the channel to record.
+    * @param string $file the new name of the file created in the monitor spool directory.
+    */
+    function ChangeMonitor($channel, $file)
+    {
+      return $this->send_request('ChangeMontior', array('Channel'=>$channel, 'File'=>$file));
+    }
+
+   /**
+    * Execute Command
+    *
+    * @example examples/sip_show_peer.php Get information about a sip peer
+    * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+Command
+    * @link http://www.voip-info.org/wiki-Asterisk+CLI
+    * @param string $command
+    * @param string $actionid message matching variable
+    */
+    function Command($command, $actionid=NULL)
+    {
+      $parameters = array('Command'=>$command);
+      if($actionid) $parameters['ActionID'] = $actionid;
+      return $this->send_request('Command', $parameters);
+    }
+
+   /**
+    * Enable/Disable sending of events to this manager
+    *
+    * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+Events
+    * @param string $eventmask is either 'on', 'off', or 'system,call,log'
+    */
+    function Events($eventmask)
+    {
+      return $this->send_request('Events', array('EventMask'=>$eventmask));
+    }
+
+   /**
+    * Check Extension Status
+    *
+    * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+ExtensionState
+    * @param string $exten Extension to check state on
+    * @param string $context Context for extension
+    * @param string $actionid message matching variable
+    */
+    function ExtensionState($exten, $context, $actionid=NULL)
+    {
+      $parameters = array('Exten'=>$exten, 'Context'=>$context);
+      if($actionid) $parameters['ActionID'] = $actionid;
+      return $this->send_request('ExtensionState', $parameters);
+    }
+
+   /**
+    * Gets a Channel Variable
+    *
+    * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+GetVar
+    * @link http://www.voip-info.org/wiki-Asterisk+variables
+    * @param string $channel Channel to read variable from
+    * @param string $variable
+    * @param string $actionid message matching variable
+    */
+    function GetVar($channel, $variable, $actionid=NULL)
+    {
+      $parameters = array('Channel'=>$channel, 'Variable'=>$variable);
+      if($actionid) $parameters['ActionID'] = $actionid;
+      return $this->send_request('GetVar', $parameters);
+    }
+
+   /**
+    * Hangup Channel
+    *
+    * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+Hangup
+    * @param string $channel The channel name to be hungup
+    */
+    function Hangup($channel)
+    {
+      return $this->send_request('Hangup', array('Channel'=>$channel));
+    }
+
+   /**
+    * List IAX Peers
+    *
+    * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+IAXpeers
+    */
+    function IAXPeers()
+    {
+      return $this->send_request('IAXPeers');
+    }
+
+   /**
+    * List available manager commands
+    *
+    * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+ListCommands
+    * @param string $actionid message matching variable
+    */
+    function ListCommands($actionid=NULL)
+    {
+      if($actionid)
+        return $this->send_request('ListCommands', array('ActionID'=>$actionid));
+      else
+        return $this->send_request('ListCommands');
+    }
+
+   /**
+    * Logoff Manager
+    *
+    * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+Logoff
+    */
+    function Logoff()
+    {
+      return $this->send_request('Logoff');
+    }
+
+   /**
+    * Check Mailbox Message Count
+    *
+    * Returns number of new and old messages.
+    *   Message: Mailbox Message Count
+    *   Mailbox: <mailboxid>
+    *   NewMessages: <count>
+    *   OldMessages: <count>
+    *
+    * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+MailboxCount
+    * @param string $mailbox Full mailbox ID <mailbox>@<vm-context>
+    * @param string $actionid message matching variable
+    */
+    function MailboxCount($mailbox, $actionid=NULL)
+    {
+      $parameters = array('Mailbox'=>$mailbox);
+      if($actionid) $parameters['ActionID'] = $actionid;
+      return $this->send_request('MailboxCount', $parameters);
+    }
+
+   /**
+    * Check Mailbox
+    *
+    * Returns number of messages.
+    *   Message: Mailbox Status
+    *   Mailbox: <mailboxid>
+    *   Waiting: <count>
+    *
+    * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+MailboxStatus
+    * @param string $mailbox Full mailbox ID <mailbox>@<vm-context>
+    * @param string $actionid message matching variable
+    */
+    function MailboxStatus($mailbox, $actionid=NULL)
+    {	
+      $parameters = array('Mailbox'=>$mailbox);
+      if($actionid) $parameters['ActionID'] = $actionid;
+      return $this->send_request('MailboxStatus', $parameters);
+    }
+
+   /**
+    * Monitor a channel
+    *
+    * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+Monitor
+    * @param string $channel
+    * @param string $file
+    * @param string $format
+    * @param boolean $mix
+    */
+    function Monitor($channel, $file=NULL, $format=NULL, $mix=NULL)
+    {
+      $parameters = array('Channel'=>$channel);
+      if($file) $parameters['File'] = $file;
+      if($format) $parameters['Format'] = $format;
+      if(!is_null($file)) $parameters['Mix'] = ($mix) ? 'true' : 'false';
+      return $this->send_request('Monitor', $parameters);
+    }
+
+   /**
+    * Originate Call
+    *
+    * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+Originate
+    * @param string $channel Channel name to call
+    * @param string $exten Extension to use (requires 'Context' and 'Priority')
+    * @param string $context Context to use (requires 'Exten' and 'Priority')
+    * @param string $priority Priority to use (requires 'Exten' and 'Context')
+    * @param string $application Application to use
+    * @param string $data Data to use (requires 'Application')
+    * @param integer $timeout How long to wait for call to be answered (in ms)
+    * @param string $callerid Caller ID to be set on the outgoing channel
+    * @param string $variable Channel variable to set (VAR1=value1|VAR2=value2)
+    * @param string $account Account code
+    * @param boolean $async true fast origination
+    * @param string $actionid message matching variable
+    */
+    function Originate($channel,
+                       $exten=NULL, $context=NULL, $priority=NULL,
+                       $application=NULL, $data=NULL,
+                       $timeout=NULL, $callerid=NULL, $variable=NULL, $account=NULL, $async=NULL, $actionid=NULL)
+    {
+      $parameters = array('Channel'=>$channel);
+
+      if($exten) $parameters['Exten'] = $exten;
+      if($context) $parameters['Context'] = $context;
+      if($priority) $parameters['Priority'] = $priority;
+
+      if($application) $parameters['Application'] = $application;
+      if($data) $parameters['Data'] = $data;
+
+      if($timeout) $parameters['Timeout'] = $timeout;
+      if($callerid) $parameters['CallerID'] = $callerid;
+      if($variable) $parameters['Variable'] = $variable;
+      if($account) $parameters['Account'] = $account;
+      if(!is_null($async)) $parameters['Async'] = ($async) ? 'true' : 'false';
+      if($actionid) $parameters['ActionID'] = $actionid;
+
+      return $this->send_request('Originate', $parameters);
+    }	
+
+   /**
+    * List parked calls
+    *
+    * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+ParkedCalls
+    * @param string $actionid message matching variable
+    */
+    function ParkedCalls($actionid=NULL)
+    {
+      if($actionid)
+        return $this->send_request('ParkedCalls', array('ActionID'=>$actionid));
+      else
+        return $this->send_request('ParkedCalls');
+    }
+
+   /**
+    * Ping
+    *
+    * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+Ping
+    */
+    function Ping()
+    {
+      return $this->send_request('Ping');
+    }
+
+   /**
+    * Queue Add
+    *
+    * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+QueueAdd
+    * @param string $queue
+    * @param string $interface
+    * @param integer $penalty
+    */
+    function QueueAdd($queue, $interface, $penalty=0)
+    {
+      $parameters = array('Queue'=>$queue, 'Interface'=>$interface);
+      if($penalty) $parameters['Penalty'] = $penalty;
+      return $this->send_request('QueueAdd', $parameters);
+    }
+
+   /**
+    * Queue Remove
+    *
+    * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+QueueRemove
+    * @param string $queue
+    * @param string $interface
+    */
+    function QueueRemove($queue, $interface)
+    {
+      return $this->send_request('QueueRemove', array('Queue'=>$queue, 'Interface'=>$interface));
+    }
+
+   /**
+    * Queues
+    *
+    * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+Queues
+    */
+    function Queues()
+    {
+      return $this->send_request('Queues');
+    }
+
+   /**
+    * Queue Status
+    *
+    * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+QueueStatus
+    * @param string $actionid message matching variable
+    */
+    function QueueStatus($actionid=NULL)
+    {
+      if($actionid)
+        return $this->send_request('QueueStatus', array('ActionID'=>$actionid));
+      else
+        return $this->send_request('QueueStatus');
+    }
+
+   /**
+    * Redirect
+    *
+    * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+Redirect
+    * @param string $channel
+    * @param string $extrachannel
+    * @param string $exten
+    * @param string $context
+    * @param string $priority
+    */
+    function Redirect($channel, $extrachannel, $exten, $context, $priority)
+    {
+      return $this->send_request('Redirect', array('Channel'=>$channel, 'ExtraChannel'=>$extrachannel, 'Exten'=>$exten,
+                                                   'Context'=>$context, 'Priority'=>$priority));
+    }
+
+   /**
+    * Set the CDR UserField
+    *
+    * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+SetCDRUserField
+    * @param string $userfield
+    * @param string $channel
+    * @param string $append
+    */
+    function SetCDRUserField($userfield, $channel, $append=NULL)
+    {
+      $parameters = array('UserField'=>$userfield, 'Channel'=>$channel);
+      if($append) $parameters['Append'] = $append;
+      return $this->send_request('SetCDRUserField', $parameters);
+    }
+
+   /**
+    * Set Channel Variable
+    *
+    * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+SetVar
+    * @param string $channel Channel to set variable for
+    * @param string $variable name
+    * @param string $value
+    */
+    function SetVar($channel, $variable, $value)
+    {
+      return $this->send_request('SetVar', array('Channel'=>$channel, 'Variable'=>$variable, 'Value'=>$value));
+    }
+
+   /**
+    * Channel Status
+    *
+    * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+Status
+    * @param string $channel
+    * @param string $actionid message matching variable
+    */
+    function Status($channel=NULL, $actionid=NULL)
+    {
+      if ($channel) {
+       $parameters = array('Channel'=>$channel);
+      }
+      else {
+       $parameters = array();
+      }
+      if($actionid) $parameters['ActionID'] = $actionid;
+      return $this->send_request('Status', $parameters);
+    }
+
+   /**
+    * Stop monitoring a channel
+    *
+    * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+StopMonitor
+    * @param string $channel
+    */
+    function StopMontor($channel)
+    {
+      return $this->send_request('StopMonitor', array('Channel'=>$channel));
+    }
+
+   /**
+    * Dial over Zap channel while offhook
+    *
+    * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+ZapDialOffhook
+    * @param string $zapchannel
+    * @param string $number
+    */
+    function ZapDialOffhook($zapchannel, $number)
+    {
+      return $this->send_request('ZapDialOffhook', array('ZapChannel'=>$zapchannel, 'Number'=>$number));
+    }
+
+   /**
+    * Toggle Zap channel Do Not Disturb status OFF
+    *
+    * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+ZapDNDoff
+    * @param string $zapchannel
+    */
+    function ZapDNDoff($zapchannel)
+    {
+      return $this->send_request('ZapDNDoff', array('ZapChannel'=>$zapchannel));
+    }
+
+   /**
+    * Toggle Zap channel Do Not Disturb status ON
+    *
+    * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+ZapDNDon
+    * @param string $zapchannel
+    */
+    function ZapDNDon($zapchannel)
+    {
+      return $this->send_request('ZapDNDon', array('ZapChannel'=>$zapchannel));
+    }
+
+   /**
+    * Hangup Zap Channel
+    *
+    * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+ZapHangup
+    * @param string $zapchannel
+    */
+    function ZapHangup($zapchannel)
+    {
+      return $this->send_request('ZapHangup', array('ZapChannel'=>$zapchannel));
+    }
+
+   /**
+    * Transfer Zap Channel
+    *
+    * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+ZapTransfer
+    * @param string $zapchannel
+    */
+    function ZapTransfer($zapchannel)
+    {
+      return $this->send_request('ZapTransfer', array('ZapChannel'=>$zapchannel));
+    }
+
+   /**
+    * Zap Show Channels
+    *
+    * @link http://www.voip-info.org/wiki-Asterisk+Manager+API+Action+ZapShowChannels
+    * @param string $actionid message matching variable
+    */
+    function ZapShowChannels($actionid=NULL)
+    {
+      if($actionid)
+        return $this->send_request('ZapShowChannels', array('ActionID'=>$actionid));
+      else
+        return $this->send_request('ZapShowChannels');
+    }
+
+   // *********************************************************************************************************
+   // **                       MISC                                                                          **
+   // *********************************************************************************************************
+
+   /*
+    * Log a message
+    *
+    * @param string $message
+    * @param integer $level from 1 to 4
+    */
+    function log($message, $level=1)
+    {
+      if($this->pagi != false)
+        $this->pagi->conlog($message, $level);
+      else
+        error_log(date('r') . ' - ' . $message);
+    }
+
+   /**
+    * Add event handler
+    *
+    * Known Events include ( http://www.voip-info.org/wiki-asterisk+manager+events )
+    *   Link - Fired when two voice channels are linked together and voice data exchange commences.
+    *   Unlink - Fired when a link between two voice channels is discontinued, for example, just before call completion.
+    *   Newexten -
+    *   Hangup -
+    *   Newchannel -
+    *   Newstate -
+    *   Reload - Fired when the "RELOAD" console command is executed.
+    *   Shutdown -
+    *   ExtensionStatus -
+    *   Rename -
+    *   Newcallerid -
+    *   Alarm -
+    *   AlarmClear -
+    *   Agentcallbacklogoff -
+    *   Agentcallbacklogin -
+    *   Agentlogoff -
+    *   MeetmeJoin -
+    *   MessageWaiting -
+    *   join -
+    *   leave -
+    *   AgentCalled -
+    *   ParkedCall - Fired after ParkedCalls
+    *   Cdr -
+    *   ParkedCallsComplete -
+    *   QueueParams -
+    *   QueueMember -
+    *   QueueStatusEnd -
+    *   Status -
+    *   StatusComplete -
+    *   ZapShowChannels - Fired after ZapShowChannels
+    *   ZapShowChannelsComplete -
+    *
+    * @param string $event type or * for default handler
+    * @param string $callback function
+    * @return boolean sucess
+    */
+    function add_event_handler($event, $callback)
+    {
+      $event = strtolower($event);
+      if(isset($this->event_handlers[$event]))
+      {
+        $this->log("$event handler is already defined, not over-writing.");
+        return false;
+      }
+      $this->event_handlers[$event] = $callback;
+      return true;
+    }
+
+   /**
+    * Process event
+    *
+    * @access private
+    * @param array $parameters
+    * @return mixed result of event handler or false if no handler was found
+    */
+    function process_event($parameters)
+    {
+      $ret = false;
+      $e = strtolower($parameters['Event']);
+      // $this->log("Got event.. $e");		
+
+      $handler = '';
+      if(isset($this->event_handlers[$e])) $handler = $this->event_handlers[$e];
+      elseif(isset($this->event_handlers['*'])) $handler = $this->event_handlers['*'];
+
+      if(function_exists($handler))
+      {
+        // $this->log("Execute handler $handler");
+        $ret = $handler($e, $parameters, $this->server, $this->port);
+      }
+      else
+        $this->log("No event handler for event '$e'");
+      return $ret;
+    }
+  }
+?>

Added: freeswitch/trunk/contrib/gled/php/phpagi-2.14/phpagi-fastagi.php (0 => 15082)


--- freeswitch/trunk/contrib/gled/php/phpagi-2.14/phpagi-fastagi.php	                        (rev 0)
+++ freeswitch/trunk/contrib/gled/php/phpagi-2.14/phpagi-fastagi.php	2009-10-05 12:40:50 UTC (rev 15082)
@@ -0,0 +1,79 @@
+#!/usr/local/bin/php -q
+<?php
+ /**
+  * phpagi-fastagi.php : PHP FastAGI bootstrap
+  * Website: http://phpagi.sourceforge.net
+  *
+  * $Id: phpagi-fastagi.php,v 1.2 2005/05/25 18:43:48 pinhole Exp $
+  *
+  * Copyright (c) 2004, 2005 Matthew Asham <matthewa@bcwireless.net>, David Eder <david@eder.us>
+  * All Rights Reserved.
+  *
+  * This software is released under the terms of the GNU Lesser General Public License v2.1
+  * A copy of which is available from http://www.gnu.org/copyleft/lesser.html
+  *
+  * We would be happy to list your phpagi based application on the phpagi
+  * website.  Drop me an Email if you'd like us to list your program.
+  *
+  * @package phpAGI
+  * @version 2.0
+  * @example docs/fastagi.xinetd Example xinetd config file
+  */
+
+ /**
+  * Written for PHP 4.3.4, should work with older PHP 4.x versions.
+  * Please submit bug reports, patches, etc to http://sourceforge.net/projects/phpagi/
+  * Gracias. :)
+  *
+  */
+
+  require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'phpagi.php');
+
+  $fastagi = new AGI();
+
+  $fastagi->verbose(print_r($fastagi, true));
+
+  if(!isset($fastagi->config['fastagi']['basedir']))
+    $fastagi->config['fastagi']['basedir'] = dirname(__FILE__);
+
+  // perform some security checks
+
+  $script = $fastagi->config['fastagi']['basedir'] . DIRECTORY_SEPARATOR . $fastagi->request['agi_network_script'];
+
+  // in the same directory (or subdirectory)
+  $mydir = dirname($fastagi->config['fastagi']['basedir']) . DIRECTORY_SEPARATOR;
+  $dir = dirname($script) . DIRECTORY_SEPARATOR;
+  if(substr($dir, 0, strlen($mydir)) != $mydir)
+  {
+    $fastagi->conlog("$script is not allowed to execute.");
+    exit;
+  }
+
+  // make sure it exists
+  if(!file_exists($script))
+  {
+    $fastagi->conlog("$script does not exist.");
+    exit;
+  }
+
+  // drop privileges
+  if(isset($fastagi->config['fastagi']['setuid']) && $fastagi->config['fastagi']['setuid'])
+  {
+    $owner = fileowner($script);
+    $group = filegroup($script);
+    if(!posix_setgid($group) || !posix_setegid($group) || !posix_setuid($owner) || !posix_seteuid($owner))
+    {
+      $fastagi->conlog("failed to lower privileges.");
+      exit;      
+    }
+  }
+
+  // make sure script is still readable
+  if(!is_readable($script))
+  {
+    $fastagi->conlog("$script is not readable.");
+    exit;
+  }
+
+  require_once($script);
+?>
Property changes on: freeswitch/trunk/contrib/gled/php/phpagi-2.14/phpagi-fastagi.php
___________________________________________________________________
Name: svn:executable
   + *

Added: freeswitch/trunk/contrib/gled/php/phpagi-2.14/phpagi.php (0 => 15082)


--- freeswitch/trunk/contrib/gled/php/phpagi-2.14/phpagi.php	                        (rev 0)
+++ freeswitch/trunk/contrib/gled/php/phpagi-2.14/phpagi.php	2009-10-05 12:40:50 UTC (rev 15082)
@@ -0,0 +1,1753 @@
+<?php
+
+ /**
+  * phpagi.php : PHP AGI Functions for Asterisk
+  * Website: http://phpagi.sourceforge.net/
+  *
+  * $Id: phpagi.php,v 2.14 2005/05/25 20:30:46 pinhole Exp $
+  *
+  * Copyright (c) 2003, 2004, 2005 Matthew Asham <matthewa@bcwireless.net>, David Eder <david@eder.us>
+  * All Rights Reserved.
+  *
+  * This software is released under the terms of the GNU Lesser General Public License v2.1
+  * A copy of which is available from http://www.gnu.org/copyleft/lesser.html
+  *
+  * We would be happy to list your phpagi based application on the phpagi
+  * website.  Drop me an Email if you'd like us to list your program.
+  *
+  *
+  * Written for PHP 4.3.4, should work with older PHP 4.x versions.
+  *
+  * Please submit bug reports, patches, etc to http://sourceforge.net/projects/phpagi/
+  * Gracias. :)
+  *
+  *
+  * @package phpAGI
+  * @version 2.0
+  */
+
+/*
+ PATCH
+*/
+
+ function logthis($str) {
+        file_put_contents('/telephony/freeswitch/log/ivr.log',date("Y-m-d H:i:s").": $str\n",FILE_APPEND);
+ }
+
+ /**
+  */
+
+  if(!class_exists('AGI_AsteriskManager'))
+  {
+    require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'phpagi-asmanager.php');
+  }
+
+  define('AST_CONFIG_DIR', '/pstn/confs/asterisk');
+  define('AST_SPOOL_DIR', '/pstn/system/var/spool/asterisk/');
+  define('AST_TMP_DIR', AST_SPOOL_DIR . '/tmp/');
+  define('DEFAULT_PHPAGI_CONFIG', AST_CONFIG_DIR . '/phpagi.conf');
+
+  define('AST_DIGIT_ANY', '0123456789#*');
+
+  define('AGIRES_OK', 200);
+
+  define('AST_STATE_DOWN', 0);
+  define('AST_STATE_RESERVED', 1);
+  define('AST_STATE_OFFHOOK', 2);
+  define('AST_STATE_DIALING', 3);
+  define('AST_STATE_RING', 4);
+  define('AST_STATE_RINGING', 5);
+  define('AST_STATE_UP', 6);
+  define('AST_STATE_BUSY', 7);
+  define('AST_STATE_DIALING_OFFHOOK', 8);
+  define('AST_STATE_PRERING', 9);
+
+  define('AUDIO_FILENO', 3); // STDERR_FILENO + 1
+
+ /**
+  * AGI class
+  *
+  * @package phpAGI
+  * @link http://www.voip-info.org/wiki-Asterisk+agi
+  * @example examples/dtmf.php Get DTMF tones from the user and say the digits
+  * @example examples/input.php Get text input from the user and say it back
+  * @example examples/ping.php Ping an IP address
+  */
+  class AGI
+  {
+   /**
+    * Request variables read in on initialization.
+    *
+    * Often contains any/all of the following:
+    *   agi_request - name of agi script
+    *   agi_channel - current channel
+    *   agi_language - current language
+    *   agi_type - channel type (SIP, ZAP, IAX, ...)
+    *   agi_uniqueid - unique id based on unix time
+    *   agi_callerid - callerID string
+    *   agi_dnid - dialed number id
+    *   agi_rdnis - referring DNIS number
+    *   agi_context - current context
+    *   agi_extension - extension dialed
+    *   agi_priority - current priority
+    *   agi_enhanced - value is 1.0 if started as an EAGI script
+    *   agi_accountcode - set by SetAccount in the dialplan
+    *   agi_network - value is yes if this is a fastagi
+    *   agi_network_script - name of the script to execute
+    *
+    * NOTE: program arguments are still in $_SERVER['argv'].
+    *
+    * @var array
+    * @access public
+    */
+    var $request;
+
+   /**
+    * Config variables
+    *
+    * @var array
+    * @access public
+    */
+    var $config;
+
+   /**
+    * Asterisk Manager
+    *
+    * @var AGI_AsteriskManager
+    * @access public
+    */
+    var $asmanager;
+
+   /**
+    * Input Stream
+    *
+    * @access private
+    */
+    var $in = NULL;
+
+   /**
+    * Output Stream
+    *
+    * @access private
+    */
+    var $out = NULL;
+
+   /**
+    * Audio Stream
+    *
+    * @access public
+    */
+    var $audio = NULL;
+
+   /**
+    * Constructor
+    *
+    * @param string $config is the name of the config file to parse
+    * @param array $optconfig is an array of configuration vars and vals, stuffed into $this->config['phpagi']
+    */
+    function AGI($config=NULL, $optconfig=array())
+    {
+      // load config
+      if(!is_null($config) && file_exists($config))
+        $this->config = parse_ini_file($config, true);
+      elseif(file_exists(DEFAULT_PHPAGI_CONFIG))
+        $this->config = parse_ini_file(DEFAULT_PHPAGI_CONFIG, true);
+
+      // If optconfig is specified, stuff vals and vars into 'phpagi' config array.
+      foreach($optconfig as $var=>$val)
+        $this->config['phpagi'][$var] = $val;
+
+      // add default values to config for uninitialized values
+      if(!isset($this->config['phpagi']['error_handler'])) $this->config['phpagi']['error_handler'] = true;
+      if(!isset($this->config['phpagi']['debug'])) $this->config['phpagi']['debug'] = false;
+      if(!isset($this->config['phpagi']['admin'])) $this->config['phpagi']['admin'] = NULL;
+      if(!isset($this->config['phpagi']['tempdir'])) $this->config['phpagi']['tempdir'] = AST_TMP_DIR;
+
+      // festival TTS config
+      if(!isset($this->config['festival']['text2wave'])) $this->config['festival']['text2wave'] = $this->which('text2wave');
+
+      // swift TTS config
+      if(!isset($this->config['cepstral']['swift'])) $this->config['cepstral']['swift'] = $this->which('swift');
+
+      ob_implicit_flush(true);
+
+      // open stdin & stdout
+      $this->in = defined('STDIN') ? STDIN : fopen('php://stdin', 'r');
+      $this->out = defined('STDOUT') ? STDOUT : fopen('php://stdout', 'w');
+
+      // initialize error handler
+      if($this->config['phpagi']['error_handler'] == true)
+      {
+        set_error_handler('phpagi_error_handler');
+        global $phpagi_error_handler_email;
+        $phpagi_error_handler_email = $this->config['phpagi']['admin'];
+        error_reporting(E_ALL);
+      }
+
+      // make sure temp folder exists
+      $this->make_folder($this->config['phpagi']['tempdir']);
+
+      // read the request
+      $str = fgets($this->in);
+      while($str != "\n")
+      {
+        $this->request[substr($str, 0, strpos($str, ':'))] = trim(substr($str, strpos($str, ':') + 1));
+        $str = fgets($this->in);
+      }
+
+      // open audio if eagi detected
+      if($this->request['agi_enhanced'] == '1.0')
+      {
+        if(file_exists('/proc/' . getmypid() . '/fd/3'))
+          $this->audio = fopen('/proc/' . getmypid() . '/fd/3', 'r');
+        elseif(file_exists('/dev/fd/3'))
+        {
+          // may need to mount fdescfs
+          $this->audio = fopen('/dev/fd/3', 'r');
+        }
+        else
+          $this->conlog('Unable to open audio stream');
+
+        if($this->audio) stream_set_blocking($this->audio, 0);
+      }
+
+//       $this->conlog('AGI Request:');
+//       $this->conlog(print_r($this->request, true));
+//       $this->conlog('PHPAGI internal configuration:');
+//       $this->conlog(print_r($this->config, true));
+    }
+
+   // *********************************************************************************************************
+   // **                       COMMANDS                                                                      **
+   // *********************************************************************************************************
+
+   /**
+    * Answer channel if not already in answer state.
+    *
+    * @link http://www.voip-info.org/wiki-answer
+    * @example examples/dtmf.php Get DTMF tones from the user and say the digits
+    * @example examples/input.php Get text input from the user and say it back
+    * @example examples/ping.php Ping an IP address
+    *
+    * @return array, see evaluate for return information.  ['result'] is 0 on success, -1 on failure.
+    */
+    function answer()
+    {
+      return $this->evaluate('ANSWER');
+    }
+
+   /**
+    * Get the status of the specified channel. If no channel name is specified, return the status of the current channel.
+    *
+    * @link http://www.voip-info.org/wiki-channel+status
+    * @param string $channel
+    * @return array, see evaluate for return information. ['data'] contains description.
+    */
+    function channel_status($channel='')
+    {
+      $ret = $this->evaluate("CHANNEL STATUS $channel");
+      switch($ret['result'])
+      {
+        case -1: $ret['data'] = trim("There is no channel that matches $channel"); break;
+        case AST_STATE_DOWN: $ret['data'] = 'Channel is down and available'; break;
+        case AST_STATE_RESERVED: $ret['data'] = 'Channel is down, but reserved'; break;
+        case AST_STATE_OFFHOOK: $ret['data'] = 'Channel is off hook'; break;
+        case AST_STATE_DIALING: $ret['data'] = 'Digits (or equivalent) have been dialed'; break;
+        case AST_STATE_RING: $ret['data'] = 'Line is ringing'; break;
+        case AST_STATE_RINGING: $ret['data'] = 'Remote end is ringing'; break;
+        case AST_STATE_UP: $ret['data'] = 'Line is up'; break;
+        case AST_STATE_BUSY: $ret['data'] = 'Line is busy'; break;
+        case AST_STATE_DIALING_OFFHOOK: $ret['data'] = 'Digits (or equivalent) have been dialed while offhook'; break;
+        case AST_STATE_PRERING: $ret['data'] = 'Channel has detected an incoming call and is waiting for ring'; break;
+        default: $ret['data'] = "Unknown ({$ret['result']})"; break;
+      }
+      return $ret;
+    }
+
+   /**
+    * Deletes an entry in the Asterisk database for a given family and key.
+    *
+    * @link http://www.voip-info.org/wiki-database+del
+    * @param string $family
+    * @param string $key
+    * @return array, see evaluate for return information. ['result'] is 1 on sucess, 0 otherwise.
+    */
+    function database_del($family, $key)
+    {
+      return $this->evaluate("DATABASE DEL \"$family\" \"$key\"");
+    }
+
+   /**
+    * Deletes a family or specific keytree within a family in the Asterisk database.
+    *
+    * @link http://www.voip-info.org/wiki-database+deltree
+    * @param string $family
+    * @param string $keytree
+    * @return array, see evaluate for return information. ['result'] is 1 on sucess, 0 otherwise.
+    */
+    function database_deltree($family, $keytree='')
+    {
+      $cmd = "DATABASE DELTREE \"$family\"";
+      if($keytree != '') $cmd .= " \"$keytree\"";
+      return $this->evaluate($cmd);
+    }
+
+   /**
+    * Retrieves an entry in the Asterisk database for a given family and key.
+    *
+    * @link http://www.voip-info.org/wiki-database+get
+    * @param string $family
+    * @param string $key
+    * @return array, see evaluate for return information. ['result'] is 1 on sucess, 0 failure. ['data'] holds the value
+    */
+    function database_get($family, $key)
+    {
+      return $this->evaluate("DATABASE GET \"$family\ \"$key\"");
+    }
+
+   /**
+    * Adds or updates an entry in the Asterisk database for a given family, key, and value.
+    *
+    * @param string $family
+    * @param string $key
+    * @param string $value
+    * @return array, see evaluate for return information. ['result'] is 1 on sucess, 0 otherwise
+    */
+    function database_put($family, $key, $value)
+    {
+      $value = str_replace("\n", '\n', addslashes($value));
+      return $this->evaluate("DATABASE PUT \"$family\" \"$key\" \"$value\"");
+    }
+
+   /**
+    * Executes the specified Asterisk application with given options.
+    *
+    * @link http://www.voip-info.org/wiki-exec
+    * @link http://www.voip-info.org/wiki-Asterisk+-+documentation+of+application+commands
+    * @param string $application
+    * @param mixed $options
+    * @return array, see evaluate for return information. ['result'] is whatever the application returns, or -2 on failure to find application
+    */
+    function exec($application, $options='')
+    {
+      if(is_array($options)) $options = join('|', $options);
+      return $this->evaluate("EXEC $application $options");
+    }
+
+   /**
+    * Plays the given file and receives DTMF data.
+    *
+    * This is similar to STREAM FILE, but this command can accept and return many DTMF digits,
+    * while STREAM FILE returns immediately after the first DTMF digit is detected.
+    *
+    * Asterisk looks for the file to play in /var/lib/asterisk/sounds by default.
+    *
+    * If the user doesn't press any keys when the message plays, there is $timeout milliseconds
+    * of silence then the command ends.
+    *
+    * The user has the opportunity to press a key at any time during the message or the
+    * post-message silence. If the user presses a key while the message is playing, the
+    * message stops playing. When the first key is pressed a timer starts counting for
+    * $timeout milliseconds. Every time the user presses another key the timer is restarted.
+    * The command ends when the counter goes to zero or the maximum number of digits is entered,
+    * whichever happens first.
+    *
+    * If you don't specify a time out then a default timeout of 2000 is used following a pressed
+    * digit. If no digits are pressed then 6 seconds of silence follow the message.
+    *
+    * If you don't specify $max_digits then the user can enter as many digits as they want.
+    *
+    * Pressing the # key has the same effect as the timer running out: the command ends and
+    * any previously keyed digits are returned. A side effect of this is that there is no
+    * way to read a # key using this command.
+    *
+    * @example examples/ping.php Ping an IP address
+    *
+    * @link http://www.voip-info.org/wiki-get+data
+    * @param string $filename file to play. Do not include file extension.
+    * @param integer $timeout milliseconds
+    * @param integer $max_digits
+    * @return array, see evaluate for return information. ['result'] holds the digits and ['data'] holds the timeout if present.
+    *
+    * This differs from other commands with return DTMF as numbers representing ASCII characters.
+    */
+    function get_data($filename, $timeout=NULL, $max_digits=NULL)
+    {
+      return $this->evaluate(rtrim("GET DATA $filename $timeout $max_digits"));
+    }
+
+   /**
+    * Fetch the value of a variable.
+    *
+    * Does not work with global variables. Does not work with some variables that are generated by modules.
+    *
+    * @link http://www.voip-info.org/wiki-get+variable
+    * @link http://www.voip-info.org/wiki-Asterisk+variables
+    * @param string $variable name
+    * @return array, see evaluate for return information. ['result'] is 0 if variable hasn't been set, 1 if it has. ['data'] holds the value.
+    */
+    function get_variable($variable)
+    {
+      return $this->evaluate("GET VARIABLE $variable");
+    }
+
+   /**
+    * Hangup the specified channel. If no channel name is given, hang up the current channel.
+    *
+    * With power comes responsibility. Hanging up channels other than your own isn't something
+    * that is done routinely. If you are not sure why you are doing so, then don't.
+    *
+    * @link http://www.voip-info.org/wiki-hangup
+    * @example examples/dtmf.php Get DTMF tones from the user and say the digits
+    * @example examples/input.php Get text input from the user and say it back
+    * @example examples/ping.php Ping an IP address
+    *
+    * @param string $channel
+    * @return array, see evaluate for return information. ['result'] is 1 on success, -1 on failure.
+    */
+    function hangup($channel='')
+    {
+      return $this->evaluate("HANGUP $channel");
+    }
+
+   /**
+    * Does nothing.
+    *
+    * @link http://www.voip-info.org/wiki-noop
+    * @return array, see evaluate for return information.
+    */
+    function noop()
+    {
+      return $this->evaluate('NOOP');
+    }
+
+   /**
+    * Receive a character of text from a connected channel. Waits up to $timeout milliseconds for
+    * a character to arrive, or infinitely if $timeout is zero.
+    *
+    * @link http://www.voip-info.org/wiki-receive+char
+    * @param integer $timeout milliseconds
+    * @return array, see evaluate for return information. ['result'] is 0 on timeout or not supported, -1 on failure. Otherwise
+    * it is the decimal value of the DTMF tone. Use chr() to convert to ASCII.
+    */
+    function receive_char($timeout=-1)
+    {
+      return $this->evaluate("RECEIVE CHAR $timeout");
+    }
+
+   /**
+    * Record sound to a file until an acceptable DTMF digit is received or a specified amount of
+    * time has passed. Optionally the file BEEP is played before recording begins.
+    *
+    * @link http://www.voip-info.org/wiki-record+file
+    * @param string $file to record, without extension, often created in /var/lib/asterisk/sounds
+    * @param string $format of the file. GSM and WAV are commonly used formats. MP3 is read-only and thus cannot be used.
+    * @param string $escape_digits
+    * @param integer $timeout is the maximum record time in milliseconds, or -1 for no timeout.
+    * @param integer $offset to seek to without exceeding the end of the file.
+    * @param boolean $beep
+    * @param integer $silence number of seconds of silence allowed before the function returns despite the
+    * lack of dtmf digits or reaching timeout.
+    * @return array, see evaluate for return information. ['result'] is -1 on error, 0 on hangup, otherwise a decimal value of the
+    * DTMF tone. Use chr() to convert to ASCII.
+    */
+    function record_file($file, $format, $escape_digits='', $timeout=-1, $offset=NULL, $beep=false, $silence=NULL)
+    {
+      $cmd = trim("RECORD FILE $file $format \"$escape_digits\" $timeout $offset");
+      if($beep) $cmd .= ' BEEP';
+      if(!is_null($silence)) $cmd .= " s=$silence";
+      return $this->evaluate($cmd);
+    }
+
+   /**
+    * Say the given digit string, returning early if any of the given DTMF escape digits are received on the channel.
+    *
+    * @link http://www.voip-info.org/wiki-say+digits
+    * @param integer $digits
+    * @param string $escape_digits
+    * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no
+    * digit received, otherwise a decimal value of the DTMF tone.  Use chr() to convert to ASCII.
+    */
+    function say_digits($digits, $escape_digits='')
+    {
+      return $this->evaluate("SAY DIGITS $digits \"$escape_digits\"");
+    }
+
+   /**
+    * Say the given number, returning early if any of the given DTMF escape digits are received on the channel.
+    *
+    * @link http://www.voip-info.org/wiki-say+number
+    * @param integer $number
+    * @param string $escape_digits
+    * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no
+    * digit received, otherwise a decimal value of the DTMF tone.  Use chr() to convert to ASCII.
+    */
+    function say_number($number, $escape_digits='')
+    {
+      return $this->evaluate("SAY NUMBER $number \"$escape_digits\"");
+    }
+
+   /**
+    * Say the given character string, returning early if any of the given DTMF escape digits are received on the channel.
+    *
+    * @link http://www.voip-info.org/wiki-say+phonetic
+    * @param string $text
+    * @param string $escape_digits
+    * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no
+    * digit received, otherwise a decimal value of the DTMF tone.  Use chr() to convert to ASCII.
+    */
+    function say_phonetic($text, $escape_digits='')
+    {
+      return $this->evaluate("SAY PHONETIC $text \"$escape_digits\"");
+    }
+
+   /**
+    * Say a given time, returning early if any of the given DTMF escape digits are received on the channel.
+    *
+    * @link http://www.voip-info.org/wiki-say+time
+    * @param integer $time number of seconds elapsed since 00:00:00 on January 1, 1970, Coordinated Universal Time (UTC).
+    * @param string $escape_digits
+    * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no
+    * digit received, otherwise a decimal value of the DTMF tone.  Use chr() to convert to ASCII.
+    */
+    function say_time($time=NULL, $escape_digits='')
+    {
+      if(is_null($time)) $time = time();
+      return $this->evaluate("SAY TIME $time \"$escape_digits\"");
+    }
+
+   /**
+    * Send the specified image on a channel.
+    *
+    * Most channels do not support the transmission of images.
+    *
+    * @link http://www.voip-info.org/wiki-send+image
+    * @param string $image without extension, often in /var/lib/asterisk/images
+    * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if the image is sent or
+    * channel does not support image transmission.
+    */
+    function send_image($image)
+    {
+      return $this->evaluate("SEND IMAGE $image");
+    }
+
+   /**
+    * Send the given text to the connected channel.
+    *
+    * Most channels do not support transmission of text.
+    *
+    * @link http://www.voip-info.org/wiki-send+text
+    * @param $text
+    * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if the text is sent or
+    * channel does not support text transmission.
+    */
+    function send_text($text)
+    {
+      return $this->evaluate("SEND TEXT \"$text\"");
+    }
+
+   /**
+    * Cause the channel to automatically hangup at $time seconds in the future.
+    * If $time is 0 then the autohangup feature is disabled on this channel.
+    *
+    * If the channel is hungup prior to $time seconds, this setting has no effect.
+    *
+    * @link http://www.voip-info.org/wiki-set+autohangup
+    * @param integer $time until automatic hangup
+    * @return array, see evaluate for return information.
+    */
+    function set_autohangup($time=0)
+    {
+      return $this->evaluate("SET AUTOHANGUP $time");
+    }
+
+   /**
+    * Changes the caller ID of the current channel.
+    *
+    * @link http://www.voip-info.org/wiki-set+callerid
+    * @param string $cid example: "John Smith"<1234567>
+    * This command will let you take liberties with the <caller ID specification> but the format shown in the example above works
+    * well: the name enclosed in double quotes followed immediately by the number inside angle brackets. If there is no name then
+    * you can omit it. If the name contains no spaces you can omit the double quotes around it. The number must follow the name
+    * immediately; don't put a space between them. The angle brackets around the number are necessary; if you omit them the
+    * number will be considered to be part of the name.
+    * @return array, see evaluate for return information.
+    */
+    function set_callerid($cid)
+    {
+      return $this->evaluate("SET CALLERID $cid");
+    }
+
+   /**
+    * Sets the context for continuation upon exiting the application.
+    *
+    * Setting the context does NOT automatically reset the extension and the priority; if you want to start at the top of the new
+    * context you should set extension and priority yourself.
+    *
+    * If you specify a non-existent context you receive no error indication (['result'] is still 0) but you do get a
+    * warning message on the Asterisk console.
+    *
+    * @link http://www.voip-info.org/wiki-set+context
+    * @param string $context
+    * @return array, see evaluate for return information.
+    */
+    function set_context($context)
+    {
+      return $this->evaluate("SET CONTEXT $context");
+    }
+
+   /**
+    * Set the extension to be used for continuation upon exiting the application.
+    *
+    * Setting the extension does NOT automatically reset the priority. If you want to start with the first priority of the
+    * extension you should set the priority yourself.
+    *
+    * If you specify a non-existent extension you receive no error indication (['result'] is still 0) but you do
+    * get a warning message on the Asterisk console.
+    *
+    * @link http://www.voip-info.org/wiki-set+extension
+    * @param string $extension
+    * @return array, see evaluate for return information.
+    */
+    function set_extension($extension)
+    {
+      return $this->evaluate("SET EXTENSION $extension");
+    }
+
+   /**
+    * Enable/Disable Music on hold generator.
+    *
+    * @link http://www.voip-info.org/wiki-set+music
+    * @param boolean $enabled
+    * @param string $class
+    * @return array, see evaluate for return information.
+    */
+    function set_music($enabled=true, $class='')
+    {
+      $enabled = ($enabled) ? 'ON' : 'OFF';
+      return $this->evaluate("SET MUSIC $enabled $class");
+    }
+
+   /**
+    * Set the priority to be used for continuation upon exiting the application.
+    *
+    * If you specify a non-existent priority you receive no error indication (['result'] is still 0)
+    * and no warning is issued on the Asterisk console.
+    *
+    * @link http://www.voip-info.org/wiki-set+priority
+    * @param integer $priority
+    * @return array, see evaluate for return information.
+    */
+    function set_priority($priority)
+    {
+      return $this->evaluate("SET PRIORITY $priority");
+    }
+
+   /**
+    * Sets a variable to the specified value. The variables so created can later be used by later using ${<variablename>}
+    * in the dialplan.
+    *
+    * These variables live in the channel Asterisk creates when you pickup a phone and as such they are both local and temporary.
+    * Variables created in one channel can not be accessed by another channel. When you hang up the phone, the channel is deleted
+    * and any variables in that channel are deleted as well.
+    *
+    * @link http://www.voip-info.org/wiki-set+variable
+    * @param string $variable is case sensitive
+    * @param string $value
+    * @return array, see evaluate for return information.
+    */
+    function set_variable($variable, $value)
+    {
+      $value = str_replace("\n", '\n', addslashes($value));
+      return $this->evaluate("SET VARIABLE $variable \"$value\"");
+    }
+
+   /**
+    * Play the given audio file, allowing playback to be interrupted by a DTMF digit. This command is similar to the GET DATA
+    * command but this command returns after the first DTMF digit has been pressed while GET DATA can accumulated any number of
+    * digits before returning.
+    *
+    * @example examples/ping.php Ping an IP address
+    *
+    * @link http://www.voip-info.org/wiki-stream+file
+    * @param string $filename without extension, often in /var/lib/asterisk/sounds
+    * @param string $escape_digits
+    * @param integer $offset
+    * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no
+    * digit received, otherwise a decimal value of the DTMF tone.  Use chr() to convert to ASCII.
+    */
+    function stream_file($filename, $escape_digits='', $offset=0)
+    {
+      return $this->evaluate("STREAM FILE $filename \"$escape_digits\" $offset");
+    }
+
+   /**
+    * Enable or disable TDD transmission/reception on the current channel.
+    *
+    * @link http://www.voip-info.org/wiki-tdd+mode
+    * @param string $setting can be on, off or mate
+    * @return array, see evaluate for return information. ['result'] is 1 on sucess, 0 if the channel is not TDD capable.
+    */
+    function tdd_mode($setting)
+    {
+      return $this->evaluate("TDD MODE $setting");
+    }
+
+   /**
+    * Sends $message to the Asterisk console via the 'verbose' message system.
+    *
+    * If the Asterisk verbosity level is $level or greater, send $message to the console.
+    *
+    * The Asterisk verbosity system works as follows. The Asterisk user gets to set the desired verbosity at startup time or later
+    * using the console 'set verbose' command. Messages are displayed on the console if their verbose level is less than or equal
+    * to desired verbosity set by the user. More important messages should have a low verbose level; less important messages
+    * should have a high verbose level.
+    *
+    * @link http://www.voip-info.org/wiki-verbose
+    * @param string $message
+    * @param integer $level from 1 to 4
+    * @return array, see evaluate for return information.
+    */
+    function verbose($message, $level=1)
+    {
+      foreach(explode("\n", str_replace("\r\n", "\n", print_r($message, true))) as $msg)
+      {
+        @syslog(LOG_WARNING, $msg);
+        $ret = $this->evaluate("VERBOSE \"$msg\" $level");
+      }
+      return $ret;
+    }
+
+   /**
+    * Waits up to $timeout milliseconds for channel to receive a DTMF digit.
+    *
+    * @link http://www.voip-info.org/wiki-wait+for+digit
+    * @param integer $timeout in millisecons. Use -1 for the timeout value if you want the call to wait indefinitely.
+    * @return array, see evaluate for return information. ['result'] is 0 if wait completes with no
+    * digit received, otherwise a decimal value of the DTMF tone.  Use chr() to convert to ASCII.
+    */
+    function wait_for_digit($timeout=-1)
+    {
+      return $this->evaluate("WAIT FOR DIGIT $timeout");
+    }
+
+
+   // *********************************************************************************************************
+   // **                       APPLICATIONS                                                                  **
+   // *********************************************************************************************************
+
+   /**
+    * Set absolute maximum time of call.
+    *
+    * Note that the timeout is set from the current time forward, not counting the number of seconds the call has already been up.
+    * Each time you call AbsoluteTimeout(), all previous absolute timeouts are cancelled.
+    * Will return the call to the T extension so that you can playback an explanatory note to the calling party (the called party
+    * will not hear that)
+    *
+    * @link http://www.voip-info.org/wiki-Asterisk+-+documentation+of+application+commands
+    * @link http://www.dynx.net/ASTERISK/AGI/ccard/agi-ccard.agi
+    * @param $seconds allowed, 0 disables timeout
+    * @return array, see evaluate for return information.
+    */
+    function exec_absolutetimeout($seconds=0)
+    {
+      return $this->exec('AbsoluteTimeout', $seconds);
+    }
+
+   /**
+    * Executes an AGI compliant application.
+    *
+    * @param string $command
+    * @return array, see evaluate for return information. ['result'] is -1 on hangup or if application requested hangup, or 0 on non-hangup exit.
+    * @param string $args
+    */
+    function exec_agi($command, $args)
+    {
+      return $this->exec("AGI $command", $args);
+    }
+
+   /**
+    * Set Language.
+    *
+    * @param string $language code
+    * @return array, see evaluate for return information.
+    */
+    function exec_setlanguage($language='en')
+    {
+      return $this->exec('SetLanguage', $language);
+    }
+
+   /**
+    * Do ENUM Lookup.
+    *
+    * Note: to retrieve the result, use
+    *   get_variable('ENUM');
+    *
+    * @param $exten
+    * @return array, see evaluate for return information.
+    */
+    function exec_enumlookup($exten)
+    {
+      return $this->exec('EnumLookup', $exten);
+    }
+
+   /**
+    * Dial.
+    *
+    * Dial takes input from ${VXML_URL} to send XML Url to Cisco 7960
+    * Dial takes input from ${ALERT_INFO} to set ring cadence for Cisco phones
+    * Dial returns ${CAUSECODE}: If the dial failed, this is the errormessage.
+    * Dial returns ${DIALSTATUS}: Text code returning status of last dial attempt.
+    *
+    * @link http://www.voip-info.org/wiki-Asterisk+cmd+Dial
+    * @param string $type
+    * @param string $identifier
+    * @param integer $timeout
+    * @param string $options
+    * @param string $url
+    * @return array, see evaluate for return information.
+    */
+    function exec_dial($type, $identifier, $timeout=NULL, $options=NULL, $url=NULL)
+    {
+      return $this->exec('Dial', trim("$type/$identifier|$timeout|$options|$url", '|'));
+    }
+
+   /**
+    * Goto.
+    *
+    * This function takes three arguments: context,extension, and priority, but the leading arguments
+    * are optional, not the trailing arguments.  Thuse goto($z) sets the priority to $z.
+    *
+    * @param string $a
+    * @param string $b;
+    * @param string $c;
+    * @return array, see evaluate for return information.
+    */
+    function exec_goto($a, $b=NULL, $c=NULL)
+    {
+      return $this->exec('Goto', trim("$a|$b|$c", '|'));
+    }
+
+
+   // *********************************************************************************************************
+   // **                       FAST PASSING                                                                  **
+   // *********************************************************************************************************
+
+   /**
+    * Say the given digit string, returning early if any of the given DTMF escape digits are received on the channel.
+    * Return early if $buffer is adequate for request.
+    *
+    * @link http://www.voip-info.org/wiki-say+digits
+    * @param string $buffer
+    * @param integer $digits
+    * @param string $escape_digits
+    * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no
+    * digit received, otherwise a decimal value of the DTMF tone.  Use chr() to convert to ASCII.
+    */
+   function fastpass_say_digits(&$buffer, $digits, $escape_digits='')
+   {
+     $proceed = false;
+     if($escape_digits != '' && $buffer != '')
+     {
+       if(!strpos(chr(255) . $escape_digits, $buffer{strlen($buffer)-1}))
+         $proceed = true;
+     }
+     if($buffer == '' || $proceed)
+     {
+       $res = $this->say_digits($digits, $escape_digits);
+       if($res['code'] == AGIRES_OK && $res['result'] > 0)
+         $buffer .= chr($res['result']);
+       return $res;
+     }
+     return array('code'=>AGIRES_OK, 'result'=>ord($buffer{strlen($buffer)-1}));
+   }
+
+   /**
+    * Say the given number, returning early if any of the given DTMF escape digits are received on the channel.
+    * Return early if $buffer is adequate for request.
+    *
+    * @link http://www.voip-info.org/wiki-say+number
+    * @param string $buffer
+    * @param integer $number
+    * @param string $escape_digits
+    * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no
+    * digit received, otherwise a decimal value of the DTMF tone.  Use chr() to convert to ASCII.
+    */
+   function fastpass_say_number(&$buffer, $number, $escape_digits='')
+   {
+     $proceed = false;
+     if($escape_digits != '' && $buffer != '')
+     {
+       if(!strpos(chr(255) . $escape_digits, $buffer{strlen($buffer)-1}))
+         $proceed = true;
+     }
+     if($buffer == '' || $proceed)
+     {
+       $res = $this->say_number($number, $escape_digits);
+       if($res['code'] == AGIRES_OK && $res['result'] > 0)
+         $buffer .= chr($res['result']);
+       return $res;
+     }
+     return array('code'=>AGIRES_OK, 'result'=>ord($buffer{strlen($buffer)-1}));
+   }
+
+   /**
+    * Say the given character string, returning early if any of the given DTMF escape digits are received on the channel.
+    * Return early if $buffer is adequate for request.
+    *
+    * @link http://www.voip-info.org/wiki-say+phonetic
+    * @param string $buffer
+    * @param string $text
+    * @param string $escape_digits
+    * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no
+    * digit received, otherwise a decimal value of the DTMF tone.  Use chr() to convert to ASCII.
+    */
+   function fastpass_say_phonetic(&$buffer, $text, $escape_digits='')
+   {
+     $proceed = false;
+     if($escape_digits != '' && $buffer != '')
+     {
+       if(!strpos(chr(255) . $escape_digits, $buffer{strlen($buffer)-1}))
+         $proceed = true;
+     }
+     if($buffer == '' || $proceed)
+     {
+       $res = $this->say_phonetic($text, $escape_digits);
+       if($res['code'] == AGIRES_OK && $res['result'] > 0)
+         $buffer .= chr($res['result']);
+       return $res;
+     }
+     return array('code'=>AGIRES_OK, 'result'=>ord($buffer{strlen($buffer)-1}));
+   }
+
+   /**
+    * Say a given time, returning early if any of the given DTMF escape digits are received on the channel.
+    * Return early if $buffer is adequate for request.
+    *
+    * @link http://www.voip-info.org/wiki-say+time
+    * @param string $buffer
+    * @param integer $time number of seconds elapsed since 00:00:00 on January 1, 1970, Coordinated Universal Time (UTC).
+    * @param string $escape_digits
+    * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no
+    * digit received, otherwise a decimal value of the DTMF tone.  Use chr() to convert to ASCII.
+    */
+   function fastpass_say_time(&$buffer, $time=NULL, $escape_digits='')
+   {
+     $proceed = false;
+     if($escape_digits != '' && $buffer != '')
+     {
+       if(!strpos(chr(255) . $escape_digits, $buffer{strlen($buffer)-1}))
+         $proceed = true;
+     }
+     if($buffer == '' || $proceed)
+     {
+       $res = $this->say_time($time, $escape_digits);
+       if($res['code'] == AGIRES_OK && $res['result'] > 0)
+         $buffer .= chr($res['result']);
+       return $res;
+     }
+     return array('code'=>AGIRES_OK, 'result'=>ord($buffer{strlen($buffer)-1}));
+   }
+
+   /**
+    * Play the given audio file, allowing playback to be interrupted by a DTMF digit. This command is similar to the GET DATA
+    * command but this command returns after the first DTMF digit has been pressed while GET DATA can accumulated any number of
+    * digits before returning.
+    * Return early if $buffer is adequate for request.
+    *
+    * @link http://www.voip-info.org/wiki-stream+file
+    * @param string $buffer
+    * @param string $filename without extension, often in /var/lib/asterisk/sounds
+    * @param string $escape_digits
+    * @param integer $offset
+    * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no
+    * digit received, otherwise a decimal value of the DTMF tone.  Use chr() to convert to ASCII.
+    */
+   function fastpass_stream_file(&$buffer, $filename, $escape_digits='', $offset=0)
+   {
+     $proceed = false;
+     if($escape_digits != '' && $buffer != '')
+     {
+       if(!strpos(chr(255) . $escape_digits, $buffer{strlen($buffer)-1}))
+         $proceed = true;
+     }
+     if($buffer == '' || $proceed)
+     {
+       $res = $this->stream_file($filename, $escape_digits, $offset);
+       if($res['code'] == AGIRES_OK && $res['result'] > 0)
+         $buffer .= chr($res['result']);
+       return $res;
+     }
+     return array('code'=>AGIRES_OK, 'result'=>ord($buffer{strlen($buffer)-1}), 'endpos'=>0);
+   }
+
+   /**
+    * Use festival to read text.
+    * Return early if $buffer is adequate for request.
+    *
+    * @link http://www.cstr.ed.ac.uk/projects/festival/
+    * @param string $buffer
+    * @param string $text
+    * @param string $escape_digits
+    * @param integer $frequency
+    * @return array, see evaluate for return information.
+    */
+   function fastpass_text2wav(&$buffer, $text, $escape_digits='', $frequency=8000)
+   {
+     $proceed = false;
+     if($escape_digits != '' && $buffer != '')
+     {
+       if(!strpos(chr(255) . $escape_digits, $buffer{strlen($buffer)-1}))
+         $proceed = true;
+     }
+     if($buffer == '' || $proceed)
+     {
+       $res = $this->text2wav($text, $escape_digits, $frequency);
+       if($res['code'] == AGIRES_OK && $res['result'] > 0)
+         $buffer .= chr($res['result']);
+       return $res;
+     }
+     return array('code'=>AGIRES_OK, 'result'=>ord($buffer{strlen($buffer)-1}), 'endpos'=>0);
+   }
+
+   /**
+    * Use Cepstral Swift to read text.
+    * Return early if $buffer is adequate for request.
+    *
+    * @link http://www.cepstral.com/
+    * @param string $buffer
+    * @param string $text
+    * @param string $escape_digits
+    * @param integer $frequency
+    * @return array, see evaluate for return information.
+    */
+   function fastpass_swift(&$buffer, $text, $escape_digits='', $frequency=8000, $voice=NULL)
+   {
+     $proceed = false;
+     if($escape_digits != '' && $buffer != '')
+     {
+       if(!strpos(chr(255) . $escape_digits, $buffer{strlen($buffer)-1}))
+         $proceed = true;
+     }
+     if($buffer == '' || $proceed)
+     {
+       $res = $this->swift($text, $escape_digits, $frequency, $voice);
+       if($res['code'] == AGIRES_OK && $res['result'] > 0)
+         $buffer .= chr($res['result']);
+       return $res;
+     }
+     return array('code'=>AGIRES_OK, 'result'=>ord($buffer{strlen($buffer)-1}), 'endpos'=>0);
+   }
+
+   /**
+    * Say Puncutation in a string.
+    * Return early if $buffer is adequate for request.
+    *
+    * @param string $buffer
+    * @param string $text
+    * @param string $escape_digits
+    * @param integer $frequency
+    * @return array, see evaluate for return information.
+    */
+   function fastpass_say_punctuation(&$buffer, $text, $escape_digits='', $frequency=8000)
+   {
+     $proceed = false;
+     if($escape_digits != '' && $buffer != '')
+     {
+       if(!strpos(chr(255) . $escape_digits, $buffer{strlen($buffer)-1}))
+         $proceed = true;
+     }
+     if($buffer == '' || $proceed)
+     {
+       $res = $this->say_punctuation($text, $escape_digits, $frequency);
+       if($res['code'] == AGIRES_OK && $res['result'] > 0)
+         $buffer .= chr($res['result']);
+       return $res;
+     }
+     return array('code'=>AGIRES_OK, 'result'=>ord($buffer{strlen($buffer)-1}));
+   }
+
+   /**
+    * Plays the given file and receives DTMF data.
+    * Return early if $buffer is adequate for request.
+    *
+    * This is similar to STREAM FILE, but this command can accept and return many DTMF digits,
+    * while STREAM FILE returns immediately after the first DTMF digit is detected.
+    *
+    * Asterisk looks for the file to play in /var/lib/asterisk/sounds by default.
+    *
+    * If the user doesn't press any keys when the message plays, there is $timeout milliseconds
+    * of silence then the command ends.
+    *
+    * The user has the opportunity to press a key at any time during the message or the
+    * post-message silence. If the user presses a key while the message is playing, the
+    * message stops playing. When the first key is pressed a timer starts counting for
+    * $timeout milliseconds. Every time the user presses another key the timer is restarted.
+    * The command ends when the counter goes to zero or the maximum number of digits is entered,
+    * whichever happens first.
+    *
+    * If you don't specify a time out then a default timeout of 2000 is used following a pressed
+    * digit. If no digits are pressed then 6 seconds of silence follow the message.
+    *
+    * If you don't specify $max_digits then the user can enter as many digits as they want.
+    *
+    * Pressing the # key has the same effect as the timer running out: the command ends and
+    * any previously keyed digits are returned. A side effect of this is that there is no
+    * way to read a # key using this command.
+    *
+    * @link http://www.voip-info.org/wiki-get+data
+    * @param string $buffer
+    * @param string $filename file to play. Do not include file extension.
+    * @param integer $timeout milliseconds
+    * @param integer $max_digits
+    * @return array, see evaluate for return information. ['result'] holds the digits and ['data'] holds the timeout if present.
+    *
+    * This differs from other commands with return DTMF as numbers representing ASCII characters.
+    */
+   function fastpass_get_data(&$buffer, $filename, $timeout=NULL, $max_digits=NULL)
+   {
+     if(is_null($max_digits) || strlen($buffer) < $max_digits)
+     {
+       if($buffer == '')
+       {
+         $res = $this->get_data($filename, $timeout, $max_digits);
+         if($res['code'] == AGIRES_OK)
+           $buffer .= $res['result'];
+         return $res;
+       }
+       else
+       {
+         while(is_null($max_digits) || strlen($buffer) < $max_digits)
+         {
+           $res = $this->wait_for_digit();
+           if($res['code'] != AGIRES_OK) return $res;
+           if($res['result'] == ord('#')) break;
+           $buffer .= chr($res['result']);
+         }
+       }
+     }
+     return array('code'=>AGIRES_OK, 'result'=>$buffer);
+   }
+
+   // *********************************************************************************************************
+   // **                       DERIVED                                                                       **
+   // *********************************************************************************************************
+
+   /**
+    * Menu.
+    *
+    * This function presents the user with a menu and reads the response
+    *
+    * @param array $choices has the following structure:
+    *   array('1'=>'*Press 1 for this', // festival reads if prompt starts with *
+    *         '2'=>'some-gsm-without-extension',
+    *         '*'=>'*Press star for help');
+    * @return mixed key pressed on sucess, -1 on failure
+    */
+    function menu($choices, $timeout=2000)
+    {
+      $keys = join('', array_keys($choices));
+      $choice = NULL;
+      while(is_null($choice))
+      {
+        foreach($choices as $prompt)
+        {
+          if($prompt{0} == '*')
+            $ret = $this->text2wav(substr($prompt, 1), $keys);
+          else
+            $ret = $this->stream_file($prompt, $keys);
+
+          if($ret['code'] != AGIRES_OK || $ret['result'] == -1)
+          {
+            $choice = -1;
+            break;
+          }
+
+          if($ret['result'] != 0)
+          {
+            $choice = chr($ret['result']);
+            break;
+          }
+        }
+
+        if(is_null($choice))
+        {
+          $ret = $this->get_data('beep', $timeout, 1);
+          if($ret['code'] != AGIRES_OK || $ret['result'] == -1)
+            $choice = -1;
+          elseif($ret['result'] != '' && strpos(' '.$keys, $ret['result']))
+            $choice = $ret['result'];
+        }
+      }
+      return $choice;
+    }
+
+   /**
+    * Goto - Set context, extension and priority.
+    *
+    * @param string $context
+    * @param string $extension
+    * @param string $priority
+    */
+    function goto($context, $extension='s', $priority=1)
+    {
+      $this->set_context($context);
+      $this->set_extension($extension);
+      $this->set_priority($priority);
+    }
+
+   /**
+    * Parse caller id.
+    *
+    * @example examples/dtmf.php Get DTMF tones from the user and say the digits
+    * @example examples/input.php Get text input from the user and say it back
+    *
+    * "name" <proto:user@server:port>
+    *
+    * @param string $callerid
+    * @return array('Name'=>$name, 'Number'=>$number)
+    */
+    function parse_callerid($callerid=NULL)
+    {
+      if(is_null($callerid))
+        $callerid = $this->request['agi_callerid'];
+
+      $ret = array('name'=>'', 'protocol'=>'', 'username'=>'', 'host'=>'', 'port'=>'');
+      $callerid = trim($callerid);
+
+      if($callerid{0} == '"' || $callerid{0} == "'")
+      {
+        $d = $callerid{0};
+        $callerid = explode($d, substr($callerid, 1));
+        $ret['name'] = array_shift($callerid);
+        $callerid = join($d, $callerid);
+      }
+
+      $callerid = explode('@', trim($callerid, '<> '));
+      $username  = explode(':', array_shift($callerid));
+      if(count($username) == 1)
+        $ret['username'] = $username[0];
+      else
+      {
+        $ret['protocol'] = array_shift($username);
+        $ret['username'] = join(':', $username);
+      }
+
+      $callerid = join('@', $callerid);
+      $host = explode(':', $callerid);
+      if(count($host) == 1)
+        $ret['host'] =  $host[0];
+      else
+      {
+        $ret['host'] = array_shift($host);
+        $ret['port'] = join(':', $host);
+      }
+
+      return $ret;
+    }
+
+   /**
+    * Use festival to read text.
+    *
+    * @example examples/dtmf.php Get DTMF tones from the user and say the digits
+    * @example examples/input.php Get text input from the user and say it back
+    * @example examples/ping.php Ping an IP address
+    *
+    * @link http://www.cstr.ed.ac.uk/projects/festival/
+    * @param string $text
+    * @param string $escape_digits
+    * @param integer $frequency
+    * @return array, see evaluate for return information.
+    */
+    function text2wav($text, $escape_digits='', $frequency=8000)
+    {
+      $text = trim($text);
+      if($text == '') return true;
+
+      $hash = md5($text);
+      $fname = $this->config['phpagi']['tempdir'] . DIRECTORY_SEPARATOR;
+      $fname .= 'text2wav_' . $hash;
+
+      // create wave file
+      if(!file_exists("$fname.wav"))
+      {
+        // write text file
+        if(!file_exists("$fname.txt"))
+        {
+          $fp = fopen("$fname.txt", 'w');
+          fputs($fp, $text);
+          fclose($fp);
+        }
+
+        shell_exec("{$this->config['festival']['text2wave']} -F $frequency -o $fname.wav $fname.txt");
+      }
+      else
+      {
+        touch("$fname.txt");
+        touch("$fname.wav");
+      }
+
+      // stream it
+      $ret = $this->stream_file($fname, $escape_digits);
+
+      // clean up old files
+      $delete = time() - 2592000; // 1 month
+      foreach(glob($this->config['phpagi']['tempdir'] . DIRECTORY_SEPARATOR . 'text2wav_*') as $file)
+        if(filemtime($file) < $delete)
+          unlink($file);
+
+      return $ret;
+    }
+
+   /**
+    * Use Cepstral Swift to read text.
+    *
+    * @link http://www.cepstral.com/
+    * @param string $text
+    * @param string $escape_digits
+    * @param integer $frequency
+    * @return array, see evaluate for return information.
+    */
+    function swift($text, $escape_digits='', $frequency=8000, $voice=NULL)
+    {
+      if(!is_null($voice))
+        $voice = "-n $voice";
+      elseif(isset($this->config['cepstral']['voice']))
+        $voice = "-n {$this->config['cepstral']['voice']}";
+
+      $text = trim($text);
+      if($text == '') return true;
+
+      $hash = md5($text);
+      $fname = $this->config['phpagi']['tempdir'] . DIRECTORY_SEPARATOR;
+      $fname .= 'swift_' . $hash;
+
+      // create wave file
+      if(!file_exists("$fname.wav"))
+      {
+        // write text file
+        if(!file_exists("$fname.txt"))
+        {
+          $fp = fopen("$fname.txt", 'w');
+          fputs($fp, $text);
+          fclose($fp);
+        }
+
+        shell_exec("{$this->config['cepstral']['swift']} -p audio/channels=1,audio/sampling-rate=$frequency $voice -o $fname.wav -f $fname.txt");
+      }
+
+      // stream it
+      $ret = $this->stream_file($fname, $escape_digits);
+
+      // clean up old files
+      $delete = time() - 2592000; // 1 month
+      foreach(glob($this->config['phpagi']['tempdir'] . DIRECTORY_SEPARATOR . 'swift_*') as $file)
+        if(filemtime($file) < $delete)
+          unlink($file);
+
+      return $ret;
+    }
+
+   /**
+    * Text Input.
+    *
+    * Based on ideas found at http://www.voip-info.org/wiki-Asterisk+cmd+DTMFToText
+    *
+    * Example:
+    *              UC   H     LC   i      ,     SP   h     o      w    SP   a    r      e     SP   y      o      u     ?
+    *   $string = '*8'.'44*'.'*5'.'444*'.'00*'.'0*'.'44*'.'666*'.'9*'.'0*'.'2*'.'777*'.'33*'.'0*'.'999*'.'666*'.'88*'.'0000*';
+    *
+    * @link http://www.voip-info.org/wiki-Asterisk+cmd+DTMFToText
+    * @example examples/input.php Get text input from the user and say it back
+    *
+    * @return string
+    */
+    function text_input($mode='NUMERIC')
+    {
+      $alpha = array( 'k0'=>' ', 'k00'=>',', 'k000'=>'.', 'k0000'=>'?', 'k00000'=>'0',
+                      'k1'=>'!', 'k11'=>':', 'k111'=>';', 'k1111'=>'#', 'k11111'=>'1',
+                      'k2'=>'A', 'k22'=>'B', 'k222'=>'C', 'k2222'=>'2',
+                      'k3'=>'D', 'k33'=>'E', 'k333'=>'F', 'k3333'=>'3',
+                      'k4'=>'G', 'k44'=>'H', 'k444'=>'I', 'k4444'=>'4',
+                      'k5'=>'J', 'k55'=>'K', 'k555'=>'L', 'k5555'=>'5',
+                      'k6'=>'M', 'k66'=>'N', 'k666'=>'O', 'k6666'=>'6',
+                      'k7'=>'P', 'k77'=>'Q', 'k777'=>'R', 'k7777'=>'S', 'k77777'=>'7',
+                      'k8'=>'T', 'k88'=>'U', 'k888'=>'V', 'k8888'=>'8',
+                      'k9'=>'W', 'k99'=>'X', 'k999'=>'Y', 'k9999'=>'Z', 'k99999'=>'9');
+      $symbol = array('k0'=>'=',
+                      'k1'=>'<', 'k11'=>'(', 'k111'=>'[', 'k1111'=>'{', 'k11111'=>'1',
+                      'k2'=>'@', 'k22'=>'$', 'k222'=>'&', 'k2222'=>'%', 'k22222'=>'2',
+                      'k3'=>'>', 'k33'=>')', 'k333'=>']', 'k3333'=>'}', 'k33333'=>'3',
+                      'k4'=>'+', 'k44'=>'-', 'k444'=>'*', 'k4444'=>'/', 'k44444'=>'4',
+                      'k5'=>"'", 'k55'=>'`', 'k555'=>'5',
+                      'k6'=>'"', 'k66'=>'6',
+                      'k7'=>'^', 'k77'=>'7',
+                      'k8'=>"\\",'k88'=>'|', 'k888'=>'8',
+                      'k9'=>'_', 'k99'=>'~', 'k999'=>'9');
+      $text = '';
+      do
+      {
+        $command = false;
+        $result = $this->get_data('beep');
+        foreach(explode('*', $result['result']) as $code)
+        {
+          if($command)
+          {
+            switch($code{0})
+            {
+              case '2': $text = substr($text, 0, strlen($text) - 1); break; // backspace
+              case '5': $mode = 'LOWERCASE'; break;
+              case '6': $mode = 'NUMERIC'; break;
+              case '7': $mode = 'SYMBOL'; break;
+              case '8': $mode = 'UPPERCASE'; break;
+              case '9': $text = explode(' ', $text); unset($text[count($text)-1]); $text = join(' ', $text); break; // backspace a word
+            }
+            $code = substr($code, 1);
+            $command = false;
+          }
+          if($code == '')
+            $command = true;
+          elseif($mode == 'NUMERIC')
+            $text .= $code;
+          elseif($mode == 'UPPERCASE' && isset($alpha['k'.$code]))
+            $text .= $alpha['k'.$code];
+          elseif($mode == 'LOWERCASE' && isset($alpha['k'.$code]))
+            $text .= strtolower($alpha['k'.$code]);
+          elseif($mode == 'SYMBOL' && isset($symbol['k'.$code]))
+            $text .= $symbol['k'.$code];
+        }
+        $this->say_punctuation($text);
+      } while(substr($result['result'], -2) == '**');
+      return $text;
+    }
+
+   /**
+    * Say Puncutation in a string.
+    *
+    * @param string $text
+    * @param string $escape_digits
+    * @param integer $frequency
+    * @return array, see evaluate for return information.
+    */
+    function say_punctuation($text, $escape_digits='', $frequency=8000)
+    {
+      for($i = 0; $i < strlen($text); $i++)
+      {
+        switch($text{$i})
+        {
+          case ' ': $ret .= 'SPACE ';
+          case ',': $ret .= 'COMMA '; break;
+          case '.': $ret .= 'PERIOD '; break;
+          case '?': $ret .= 'QUESTION MARK '; break;
+          case '!': $ret .= 'EXPLANATION POINT '; break;
+          case ':': $ret .= 'COLON '; break;
+          case ';': $ret .= 'SEMICOLON '; break;
+          case '#': $ret .= 'POUND '; break;
+          case '=': $ret .= 'EQUALS '; break;
+          case '<': $ret .= 'LESS THAN '; break;
+          case '(': $ret .= 'LEFT PARENTHESIS '; break;
+          case '[': $ret .= 'LEFT BRACKET '; break;
+          case '{': $ret .= 'LEFT BRACE '; break;
+          case '@': $ret .= 'AT '; break;
+          case '$': $ret .= 'DOLLAR SIGN '; break;
+          case '&': $ret .= 'AMPERSAND '; break;
+          case '%': $ret .= 'PERCENT '; break;
+          case '>': $ret .= 'GREATER THAN '; break;
+          case ')': $ret .= 'RIGHT PARENTHESIS '; break;
+          case ']': $ret .= 'RIGHT BRACKET '; break;
+          case '}': $ret .= 'RIGHT BRACE '; break;
+          case '+': $ret .= 'PLUS '; break;
+          case '-': $ret .= 'MINUS '; break;
+          case '*': $ret .= 'ASTERISK '; break;
+          case '/': $ret .= 'SLASH '; break;
+          case "'": $ret .= 'SINGLE QUOTE '; break;
+          case '`': $ret .= 'BACK TICK '; break;
+          case '"': $ret .= 'QUOTE '; break;
+          case '^': $ret .= 'CAROT '; break;
+          case "\\": $ret .= 'BACK SLASH '; break;
+          case '|': $ret .= 'BAR '; break;
+          case '_': $ret .= 'UNDERSCORE '; break;
+          case '~': $ret .= 'TILDE '; break;
+          default: $ret .= $text{$i} . ' '; break;
+        }
+      }
+      return $this->text2wav($ret, $escape_digits, $frequency);
+    }
+
+   /**
+    * Create a new AGI_AsteriskManager.
+    */
+    function &new_AsteriskManager()
+    {
+      $this->asm = new AGI_AsteriskManager(NULL, $this->config);
+      $this->asm->pagi =& $this;
+      $this->config =& $this->asm->config;
+      return $this->asm;
+    }
+
+
+   // *********************************************************************************************************
+   // **                       PRIVATE                                                                       **
+   // *********************************************************************************************************
+
+
+   /**
+    * Evaluate an AGI command.
+    *
+    * @access private
+    * @param string $command
+    * @return array ('code'=>$code, 'result'=>$result, 'data'=>$data)
+    */
+    function evaluate($command)
+    {
+      $broken = array('code'=>500, 'result'=>-1, 'data'=>'');
+
+      // write command
+      if(!@fwrite($this->out, trim($command) . "\n")) return $broken;
+      fflush($this->out);
+
+      // Read result.  Occasionally, a command return a string followed by an extra new line.
+      // When this happens, our script will ignore the new line, but it will still be in the
+      // buffer.  So, if we get a blank line, it is probably the result of a previous
+      // command.  We read until we get a valid result or asterisk hangs up.  One offending
+      // command is SEND TEXT.
+      $count = 0;
+      do
+      {
+        $str = trim(fgets($this->in, 4096));
+      } while($str == '' && $count++ < 5);
+    
+/*
+ PATCH
+*/  
+      logthis("AGI EVALUATE '$command' returned: '$str' in $count reads");
+
+      if($count >= 5)
+      {
+//        $this->conlog("evaluate error on read for $command");
+        return $broken;
+      }
+
+      // parse result
+      $ret['code'] = substr($str, 0, 3);
+      $str = trim(substr($str, 3));
+
+      if($str{0} == '-') // we have a multiline response!
+      {
+        $count = 0;
+        $str = substr($str, 1) . "\n";
+        $line = fgets($this->in, 4096);
+        while(substr($line, 0, 3) != $ret['code'] && $count < 5)
+        {
+          $str .= $line;
+          $line = fgets($this->in, 4096);
+          $count = (trim($line) == '') ? $count + 1 : 0;
+        }
+        if($count >= 5)
+        {
+//          $this->conlog("evaluate error on multiline read for $command");
+          return $broken;
+        }
+      }
+
+      $ret['result'] = NULL;
+      $ret['data'] = '';
+      if($ret['code'] != AGIRES_OK) // some sort of error
+      {
+        $ret['data'] = $str;
+        $this->conlog(print_r($ret, true));
+      }
+      else // normal AGIRES_OK response
+      {
+        $parse = explode(' ', trim($str));
+        $in_token = false;
+        foreach($parse as $token)
+        {
+          if($in_token) // we previously hit a token starting with ')' but not ending in ')'
+          {
+            $ret['data'] .= ' ' . trim($token, '() ');
+            if($token{strlen($token)-1} == ')') $in_token = false;
+          }
+          elseif($token{0} == '(')
+          {
+            if($token{strlen($token)-1} != ')') $in_token = true;
+            $ret['data'] .= ' ' . trim($token, '() ');
+          }
+          elseif(strpos($token, '='))
+          {
+            $token = explode('=', $token);
+            $ret[$token[0]] = $token[1];
+          }
+          elseif($token != '')
+            $ret['data'] .= ' ' . $token;
+        }
+        $ret['data'] = trim($ret['data']);
+      }
+
+      // log some errors
+      if($ret['result'] < 0)
+        $this->conlog("$command returned {$ret['result']}");
+
+      return $ret;
+    }
+
+   /**
+    * Log to console if debug mode.
+    *
+    * @example examples/ping.php Ping an IP address
+    *
+    * @param string $str
+    * @param integer $vbl verbose level
+    */
+    function conlog($str, $vbl=1)
+    {
+      static $busy = false;
+
+      if($this->config['phpagi']['debug'] != false)
+      {
+        if(!$busy) // no conlogs inside conlog!!!
+        {
+          $busy = true;
+          $this->verbose($str, $vbl);
+          $busy = false;
+        }
+      }
+    }
+
+   /**
+    * Find an execuable in the path.
+    *
+    * @access private
+    * @param string $cmd command to find
+    * @param string $checkpath path to check
+    * @return string the path to the command
+    */
+    function which($cmd, $checkpath=NULL)
+    {
+      global $_ENV;
+      $chpath = is_null($checkpath) ? $_ENV['PATH'] : $checkpath;
+
+      foreach(explode(':', $chpath) as $path)
+        if(is_executable("$path/$cmd"))
+          return "$path/$cmd";
+
+      if(is_null($checkpath))
+        return $this->which($cmd, '/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:'.
+                                  '/usr/X11R6/bin:/usr/local/apache/bin:/usr/local/mysql/bin');
+      return false;
+    }
+
+   /**
+    * Make a folder recursively.
+    *
+    * @access private
+    * @param string $folder
+    * @param integer $perms
+    */
+    function make_folder($folder, $perms=0755)
+    {
+      $f = explode(DIRECTORY_SEPARATOR, $folder);
+      $base = '';
+      for($i = 0; $i < count($f); $i++)
+      {
+        $base .= $f[$i];
+        if($f[$i] != '' && !file_exists($base))
+          mkdir($base, $perms);
+        $base .= DIRECTORY_SEPARATOR;
+      }
+    }
+  }
+
+/**
+ * error handler for phpagi.
+ *
+ * @param integer $level PHP error level
+ * @param string $message error message
+ * @param string $file path to file
+ * @param integer $line line number of error
+ * @param array $context variables in the current scope
+ */
+  function phpagi_error_handler($level, $message, $file, $line, $context)
+  {
+    if(ini_get('error_reporting') == 0) return; // this happens with an @
+
+    @syslog(LOG_WARNING, $file . '[' . $line . ']: ' . $message);
+
+    global $phpagi_error_handler_email;
+    if(function_exists('mail') && !is_null($phpagi_error_handler_email)) // generate email debugging information
+    {
+      // decode error level
+      switch($level)
+      {
+        case E_WARNING:
+        case E_USER_WARNING:
+          $level = "Warning";
+          break;
+        case E_NOTICE:
+        case E_USER_NOTICE:
+          $level = "Notice";
+          break;
+        case E_USER_ERROR:
+          $level = "Error";
+          break;
+      }
+
+      // build message
+      $basefile = basename($file);
+      $subject = "$basefile/$line/$level: $message";
+      $message = "$level: $message in $file on line $line\n\n";
+
+      if(function_exists('mysql_errno') && strpos(' '.strtolower($message), 'mysql'))
+        $message .= 'MySQL error ' . mysql_errno() . ": " . mysql_error() . "\n\n";
+
+      // figure out who we are
+      if(function_exists('socket_create'))
+      {
+        $addr = NULL;
+        $port = 80;
+        $socket = @socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
+        @socket_connect($socket, '64.0.0.0', $port);
+        @socket_getsockname($socket, $addr, $port);
+        @socket_close($socket);
+        $message .= "\n\nIP Address: $addr\n";
+      }
+
+      // include variables
+      $message .= "\n\nContext:\n" . print_r($context, true);
+      $message .= "\n\nGLOBALS:\n" . print_r($GLOBALS, true);
+      $message .= "\n\nBacktrace:\n" . print_r(debug_backtrace(), true);
+
+      // include code fragment
+      if(file_exists($file))
+      {
+        $message .= "\n\n$file:\n";
+        $code = @file($file);
+        for($i = max(0, $line - 10); $i < min($line + 10, count($code)); $i++)
+          $message .= ($i + 1)."\t$code[$i]";
+      }
+
+      // make sure message is fully readable (convert unprintable chars to hex representation)
+      $ret = '';
+      for($i = 0; $i < strlen($message); $i++)
+      {
+        $c = ord($message{$i});
+        if($c == 10 || $c == 13 || $c == 9)
+          $ret .= $message{$i};
+        elseif($c < 16)
+          $ret .= '\x0' . dechex($c);
+        elseif($c < 32 || $c > 127)
+          $ret .= '\x' . dechex($c);
+        else
+          $ret .= $message{$i};
+      }
+      $message = $ret;
+
+      // send the mail if less than 5 errors
+      static $mailcount = 0;
+      if($mailcount < 5)
+        @mail($phpagi_error_handler_email, $subject, $message);
+      $mailcount++;
+    }
+  }
+  $phpagi_error_handler_email = NULL;
+?>