2001-09-28 13:19:43 +00:00
/*
2005-09-14 20:46:50 +00:00
* Asterisk - - An open source telephony toolkit .
2001-09-28 13:19:43 +00:00
*
2006-02-15 00:38:27 +00:00
* Copyright ( C ) 1999 - 2006 , Digium , Inc .
2001-09-28 13:19:43 +00:00
*
2004-09-22 15:21:36 +00:00
* Mark Spencer < markster @ digium . com >
2001-09-28 13:19:43 +00:00
*
2005-09-14 20:46:50 +00:00
* See http : //www.asterisk.org for more information about
* the Asterisk project . Please do not directly contact
* any of the maintainers of this project for assistance ;
* the project provides a web site , mailing lists and IRC
* channels for your use .
*
2001-09-28 13:19:43 +00:00
* This program is free software , distributed under the terms of
2005-09-14 20:46:50 +00:00
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree .
*/
2005-10-24 20:12:06 +00:00
/*! \file
2005-09-14 20:46:50 +00:00
*
2005-10-24 20:12:06 +00:00
* \ brief AGI - the Asterisk Gateway Interface
2005-12-30 21:18:06 +00:00
*
2008-06-18 00:33:31 +00:00
* \ author Mark Spencer < markster @ digium . com >
2001-09-28 13:19:43 +00:00
*/
2006-06-07 18:54:56 +00:00
# include "asterisk.h"
ASTERISK_FILE_VERSION ( __FILE__ , " $Revision$ " )
2001-09-28 13:19:43 +00:00
# include <math.h>
2003-09-08 16:48:07 +00:00
# include <signal.h>
2001-09-28 13:19:43 +00:00
# include <sys/time.h>
2006-05-25 18:31:19 +00:00
# include <sys/wait.h>
2007-06-25 13:00:49 +00:00
# include <sys/stat.h>
2007-12-25 02:57:58 +00:00
# include <pthread.h>
2005-06-06 22:12:19 +00:00
2007-11-20 23:16:15 +00:00
# include "asterisk/paths.h" /* use many ast_config_AST_*_DIR */
2007-11-17 14:11:53 +00:00
# include "asterisk/network.h"
2005-06-06 22:12:19 +00:00
# include "asterisk/file.h"
# include "asterisk/channel.h"
# include "asterisk/pbx.h"
# include "asterisk/module.h"
# include "asterisk/astdb.h"
# include "asterisk/callerid.h"
2005-04-21 06:02:45 +00:00
# include "asterisk/cli.h"
# include "asterisk/image.h"
# include "asterisk/say.h"
# include "asterisk/app.h"
# include "asterisk/dsp.h"
# include "asterisk/musiconhold.h"
# include "asterisk/utils.h"
# include "asterisk/lock.h"
2005-08-22 23:00:23 +00:00
# include "asterisk/strings.h"
2008-01-03 06:16:48 +00:00
# include "asterisk/manager.h"
2008-02-26 20:02:14 +00:00
# include "asterisk/ast_version.h"
2007-12-03 21:03:05 +00:00
# include "asterisk/speech.h"
2007-12-14 17:29:27 +00:00
# include "asterisk/manager.h"
2008-10-06 23:08:21 +00:00
# include "asterisk/features.h"
2008-11-12 00:17:43 +00:00
# include "asterisk/term.h"
# include "asterisk/xmldoc.h"
2008-11-26 21:20:50 +00:00
# define AST_API_MODULE
# include "asterisk/agi.h"
2008-11-12 00:17:43 +00:00
/*** DOCUMENTATION
< agi name = " answer " language = " en_US " >
< synopsis >
Answer channel
< / synopsis >
< syntax / >
< description >
< para > Answers channel if not already in answer state . Returns < literal > - 1 < / literal > on
channel failure , or < literal > 0 < / literal > if successful . < / para >
< / description >
< see - also >
< ref type = " agi " > hangup < / ref >
< / see - also >
< / agi >
< agi name = " channel status " language = " en_US " >
< synopsis >
Returns status of the connected channel .
< / synopsis >
< syntax >
< parameter name = " channelname " / >
< / syntax >
< description >
< para > Returns the status of the specified < replaceable > channelname < / replaceable > .
If no channel name is given then returns the status of the current channel . < / para >
< para > Return values : < / para >
< enumlist >
< enum name = " 0 " >
< para > Channel is down and available . < / para >
< / enum >
< enum name = " 1 " >
< para > Channel is down , but reserved . < / para >
< / enum >
< enum name = " 2 " >
< para > Channel is off hook . < / para >
< / enum >
< enum name = " 3 " >
< para > Digits ( or equivalent ) have been dialed . < / para >
< / enum >
< enum name = " 4 " >
< para > Line is ringing . < / para >
< / enum >
< enum name = " 5 " >
< para > Remote end is ringing . < / para >
< / enum >
< enum name = " 6 " >
< para > Line is up . < / para >
< / enum >
< enum name = " 7 " >
< para > Line is busy . < / para >
< / enum >
< / enumlist >
< / description >
< / agi >
2008-11-12 02:20:05 +00:00
< agi name = " database del " language = " en_US " >
< synopsis >
Removes database key / value
< / synopsis >
< syntax >
< parameter name = " family " required = " true " / >
< parameter name = " key " required = " true " / >
< / syntax >
< description >
< para > Deletes an entry in the Asterisk database for a given
< replaceable > family < / replaceable > and < replaceable > key < / replaceable > . < / para >
< para > Returns < literal > 1 < / literal > if successful , < literal > 0 < / literal >
otherwise . < / para >
< / description >
< / agi >
2008-11-12 13:43:46 +00:00
< agi name = " database deltree " language = " en_US " >
< synopsis >
Removes database keytree / value
< / synopsis >
< syntax >
< parameter name = " family " required = " true " / >
< parameter name = " keytree " / >
< / syntax >
< description >
< para > Deletes a < replaceable > family < / replaceable > or specific < replaceable > keytree < / replaceable >
within a < replaceable > family < / replaceable > in the Asterisk database . < / para >
< para > Returns < literal > 1 < / literal > if successful , < literal > 0 < / literal > otherwise . < / para >
< / description >
< / agi >
< agi name = " database get " language = " en_US " >
< synopsis >
Gets database value
< / synopsis >
< syntax >
< parameter name = " family " required = " true " / >
< parameter name = " key " required = " true " / >
< / syntax >
< description >
< para > Retrieves an entry in the Asterisk database for a given < replaceable > family < / replaceable >
and < replaceable > key < / replaceable > . < / para >
< para > Returns < literal > 0 < / literal > if < replaceable > key < / replaceable > is not set .
Returns < literal > 1 < / literal > if < replaceable > key < / replaceable > is set and returns the variable
in parenthesis . < / para >
< para > Example return code : 200 result = 1 ( testvariable ) < / para >
< / description >
< / agi >
2008-11-12 02:20:05 +00:00
< agi name = " database put " language = " en_US " >
< synopsis >
Adds / updates database value
< / synopsis >
< syntax >
< parameter name = " family " required = " true " / >
< parameter name = " key " required = " true " / >
< parameter name = " value " required = " true " / >
< / syntax >
< description >
< para > Adds or updates an entry in the Asterisk database for a given
< replaceable > family < / replaceable > , < replaceable > key < / replaceable > , and
< replaceable > value < / replaceable > . < / para >
< para > Returns < literal > 1 < / literal > if successful , < literal > 0 < / literal > otherwise . < / para >
< / description >
< / agi >
2008-11-12 13:43:46 +00:00
< agi name = " exec " language = " en_US " >
< synopsis >
Executes a given Application
< / synopsis >
< syntax >
< parameter name = " application " required = " true " / >
< parameter name = " options " required = " true " / >
< / syntax >
< description >
< para > Executes < replaceable > application < / replaceable > with given
< replaceable > options < / replaceable > . < / para >
< para > Returns whatever the < replaceable > application < / replaceable > returns , or
< literal > - 2 < / literal > on failure to find < replaceable > application < / replaceable > . < / para >
< / description >
< / agi >
< agi name = " get data " language = " en_US " >
< synopsis >
Prompts for DTMF on a channel
< / synopsis >
< syntax >
< parameter name = " file " required = " true " / >
< parameter name = " timeout " / >
< parameter name = " maxdigits " / >
< / syntax >
< description >
< para > Stream the given < replaceable > file < / replaceable > , and recieve DTMF data . < / para >
< para > Returns the digits received from the channel at the other end . < / para >
< / description >
< / agi >
< agi name = " get full variable " language = " en_US " >
< synopsis >
Evaluates a channel expression
< / synopsis >
< syntax >
< parameter name = " variablename " required = " true " / >
< parameter name = " channel name " / >
< / syntax >
< description >
< para > Returns < literal > 0 < / literal > if < replaceable > variablename < / replaceable > is not set
or channel does not exist . Returns < literal > 1 < / literal > if < replaceable > variablename < / replaceable >
is set and returns the variable in parenthesis . Understands complex variable names and builtin
variables , unlike GET VARIABLE . < / para >
< para > Example return code : 200 result = 1 ( testvariable ) < / para >
< / description >
< / agi >
2008-12-04 13:45:32 +00:00
< agi name = " get option " language = " en_US " >
< synopsis >
Stream file , prompt for DTMF , with timeout .
< / synopsis >
< syntax >
< parameter name = " filename " required = " true " / >
< parameter name = " escape_digits " required = " true " / >
< parameter name = " timeout " / >
< / syntax >
< description >
< para > Behaves similar to STREAM FILE but used with a timeout option . < / para >
< / description >
< see - also >
< ref type = " agi " > stream file < / ref >
< / see - also >
< / agi >
< agi name = " get variable " language = " en_US " >
< synopsis >
Gets a channel variable .
< / synopsis >
< syntax >
< parameter name = " variablename " required = " true " / >
< / syntax >
< description >
< para > Returns < literal > 0 < / literal > if < replaceable > variablename < / replaceable > is not set .
Returns < literal > 1 < / literal > if < replaceable > variablename < / replaceable > is set and returns
the variable in parentheses . < / para >
< para > Example return code : 200 result = 1 ( testvariable ) < / para >
< / description >
< / agi >
< agi name = " hangup " language = " en_US " >
< synopsis >
Hangup the current channel .
< / synopsis >
< syntax >
< parameter name = " channelname " / >
< / syntax >
< description >
< para > Hangs up the specified channel . If no channel name is given , hangs
up the current channel < / para >
< / description >
< / agi >
< agi name = " noop " language = " en_US " >
< synopsis >
Does nothing .
< / synopsis >
< syntax / >
< description >
< para > Does nothing . < / para >
< / description >
< / agi >
2008-11-12 02:20:05 +00:00
< agi name = " set music " language = " en_US " >
< synopsis >
Enable / Disable Music on hold generator
< / synopsis >
< syntax >
< parameter required = " true " >
< enumlist >
< enum >
< parameter name = " on " literal = " true " required = " true " / >
< / enum >
< enum >
< parameter name = " off " literal = " true " required = " true " / >
< / enum >
< / enumlist >
< / parameter >
< parameter name = " class " required = " true " / >
< / syntax >
< description >
< para > Enables / Disables the music on hold generator . If < replaceable > class < / replaceable >
is not specified , then the < literal > default < / literal > music on hold class will be
used . < / para >
< para > Always returns < literal > 0 < / literal > . < / para >
< / description >
< / agi >
2008-11-12 00:17:43 +00:00
* * */
2001-09-28 13:19:43 +00:00
# define MAX_ARGS 128
2008-11-12 00:17:43 +00:00
# define MAX_CMD_LEN 80
2007-09-11 15:28:46 +00:00
# define AGI_NANDFS_RETRY 3
# define AGI_BUF_LEN 2048
2001-09-28 13:19:43 +00:00
static char * app = " AGI " ;
2003-02-23 06:00:11 +00:00
static char * eapp = " EAGI " ;
2004-03-03 18:58:57 +00:00
static char * deadapp = " DeadAGI " ;
2001-09-28 13:19:43 +00:00
static char * synopsis = " Executes an AGI compliant application " ;
2004-01-30 05:37:39 +00:00
static char * esynopsis = " Executes an EAGI compliant application " ;
2004-03-03 18:58:57 +00:00
static char * deadsynopsis = " Executes AGI on a hungup channel " ;
2001-09-28 13:19:43 +00:00
static char * descrip =
2007-07-31 01:10:47 +00:00
" [E|Dead]AGI(command,args): Executes an Asterisk Gateway Interface compliant \n "
2008-08-09 01:15:38 +00:00
" program on a channel. AGI allows Asterisk to launch external programs written \n "
" in any language to control a telephony channel, play audio, read DTMF digits, \n "
" etc. by communicating with the AGI protocol on stdin and stdout. \n "
" As of 1.6.0, this channel will not stop dialplan execution on hangup inside \n "
" of this application. Dialplan execution will continue normally, even upon \n "
" hangup until the AGI application signals a desire to stop (either by exiting \n "
" or, in the case of a net script, by closing the connection). \n "
2006-05-25 19:01:26 +00:00
" A locally executed AGI script will receive SIGHUP on hangup from the channel \n "
2008-08-09 01:15:38 +00:00
" except when using DeadAGI. A fast AGI server will correspondingly receive a \n "
" HANGUP in OOB data. Both of these signals may be disabled by setting the \n "
" AGISIGHUP channel variable to \" no \" before executing the AGI application. \n "
2006-05-25 16:44:22 +00:00
" Using 'EAGI' provides enhanced AGI, with incoming audio available out of band \n "
2008-08-09 01:15:38 +00:00
" on file descriptor 3. \n \n "
2008-09-28 23:32:14 +00:00
" Use the CLI command 'agi show commnands' to list available agi commands. \n "
2006-05-25 16:44:22 +00:00
" This application sets the following channel variable upon completion: \n "
" AGISTATUS The status of the attempt to the run the AGI script \n "
2007-12-14 15:14:24 +00:00
" text string, one of SUCCESS | FAILURE | NOTFOUND | HANGUP \n " ;
2001-09-28 13:19:43 +00:00
2004-09-25 14:22:27 +00:00
static int agidebug = 0 ;
2001-09-28 13:19:43 +00:00
# define TONE_BLOCK_SIZE 200
2004-09-22 15:21:36 +00:00
/* Max time to connect to an AGI remote host */
# define MAX_AGI_CONNECT 2000
# define AGI_PORT 4573
2006-05-25 16:44:22 +00:00
enum agi_result {
2008-07-30 19:05:35 +00:00
AGI_RESULT_FAILURE = - 1 ,
2006-05-25 16:44:22 +00:00
AGI_RESULT_SUCCESS ,
2007-09-18 22:46:05 +00:00
AGI_RESULT_SUCCESS_FAST ,
2008-01-03 06:16:48 +00:00
AGI_RESULT_SUCCESS_ASYNC ,
2007-06-21 15:58:05 +00:00
AGI_RESULT_NOTFOUND ,
AGI_RESULT_HANGUP ,
2006-05-25 16:44:22 +00:00
} ;
2007-07-23 22:02:05 +00:00
static agi_command * find_command ( char * cmds [ ] , int exact ) ;
2007-07-30 19:35:33 +00:00
AST_THREADSTORAGE ( agi_buf ) ;
# define AGI_BUF_INITSIZE 256
2008-11-19 12:42:19 +00:00
int ast_agi_send ( int fd , struct ast_channel * chan , char * fmt , . . . )
2004-09-25 14:22:27 +00:00
{
int res = 0 ;
va_list ap ;
2007-07-30 19:35:33 +00:00
struct ast_str * buf ;
if ( ! ( buf = ast_str_thread_get ( & agi_buf , AGI_BUF_INITSIZE ) ) )
return - 1 ;
2004-09-25 14:22:27 +00:00
va_start ( ap , fmt ) ;
2007-07-30 19:35:33 +00:00
res = ast_str_set_va ( & buf , 0 , fmt , ap ) ;
2004-09-25 14:22:27 +00:00
va_end ( ap ) ;
2007-07-11 21:09:42 +00:00
2004-09-25 14:22:27 +00:00
if ( res = = - 1 ) {
ast_log ( LOG_ERROR , " Out of memory \n " ) ;
2007-07-30 19:18:24 +00:00
return - 1 ;
2004-09-25 14:22:27 +00:00
}
2007-07-11 21:09:42 +00:00
2007-11-07 00:00:38 +00:00
if ( agidebug ) {
if ( chan ) {
2008-12-13 08:36:35 +00:00
ast_verbose ( " <%s>AGI Tx >> %s " , chan - > name , ast_str_buffer ( buf ) ) ;
2007-11-07 00:00:38 +00:00
} else {
2008-12-13 08:36:35 +00:00
ast_verbose ( " AGI Tx >> %s " , ast_str_buffer ( buf ) ) ;
2007-11-07 00:00:38 +00:00
}
}
2007-07-30 19:35:33 +00:00
2008-12-13 08:36:35 +00:00
return ast_carefulwrite ( fd , ast_str_buffer ( buf ) , ast_str_strlen ( buf ) , 100 ) ;
2004-09-25 14:22:27 +00:00
}
2008-01-03 06:16:48 +00:00
/* linked list of AGI commands ready to be executed by Async AGI */
struct agi_cmd {
char * cmd_buffer ;
char * cmd_id ;
AST_LIST_ENTRY ( agi_cmd ) entry ;
} ;
static void free_agi_cmd ( struct agi_cmd * cmd )
{
ast_free ( cmd - > cmd_buffer ) ;
ast_free ( cmd - > cmd_id ) ;
ast_free ( cmd ) ;
}
/* AGI datastore destructor */
static void agi_destroy_commands_cb ( void * data )
{
struct agi_cmd * cmd ;
AST_LIST_HEAD ( , agi_cmd ) * chan_cmds = data ;
AST_LIST_LOCK ( chan_cmds ) ;
2008-06-18 00:33:31 +00:00
while ( ( cmd = AST_LIST_REMOVE_HEAD ( chan_cmds , entry ) ) ) {
2008-01-03 06:16:48 +00:00
free_agi_cmd ( cmd ) ;
2008-06-18 00:33:31 +00:00
}
2008-01-03 06:16:48 +00:00
AST_LIST_UNLOCK ( chan_cmds ) ;
AST_LIST_HEAD_DESTROY ( chan_cmds ) ;
ast_free ( chan_cmds ) ;
}
/* channel datastore to keep the queue of AGI commands in the channel */
static const struct ast_datastore_info agi_commands_datastore_info = {
. type = " AsyncAGI " ,
. destroy = agi_destroy_commands_cb
} ;
static const char mandescr_asyncagi [ ] =
" Description: Add an AGI command to the execute queue of the channel in Async AGI \n "
" Variables: \n "
" *Channel: Channel that is currently in Async AGI \n "
" *Command: Application to execute \n "
" CommandID: comand id. This will be sent back in CommandID header of AsyncAGI exec event notification \n "
" \n " ;
static struct agi_cmd * get_agi_cmd ( struct ast_channel * chan )
{
struct ast_datastore * store ;
struct agi_cmd * cmd ;
AST_LIST_HEAD ( , agi_cmd ) * agi_commands ;
ast_channel_lock ( chan ) ;
store = ast_channel_datastore_find ( chan , & agi_commands_datastore_info , NULL ) ;
ast_channel_unlock ( chan ) ;
if ( ! store ) {
ast_log ( LOG_ERROR , " Hu? datastore disappeared at Async AGI on Channel %s! \n " , chan - > name ) ;
return NULL ;
}
agi_commands = store - > data ;
AST_LIST_LOCK ( agi_commands ) ;
cmd = AST_LIST_REMOVE_HEAD ( agi_commands , entry ) ;
AST_LIST_UNLOCK ( agi_commands ) ;
return cmd ;
}
2008-07-09 03:39:59 +00:00
/* channel is locked when calling this one either from the CLI or manager thread */
2008-01-03 06:16:48 +00:00
static int add_agi_cmd ( struct ast_channel * chan , const char * cmd_buff , const char * cmd_id )
{
struct ast_datastore * store ;
struct agi_cmd * cmd ;
AST_LIST_HEAD ( , agi_cmd ) * agi_commands ;
store = ast_channel_datastore_find ( chan , & agi_commands_datastore_info , NULL ) ;
if ( ! store ) {
ast_log ( LOG_WARNING , " Channel %s is not at Async AGI. \n " , chan - > name ) ;
return - 1 ;
}
agi_commands = store - > data ;
cmd = ast_calloc ( 1 , sizeof ( * cmd ) ) ;
if ( ! cmd ) {
return - 1 ;
}
cmd - > cmd_buffer = ast_strdup ( cmd_buff ) ;
if ( ! cmd - > cmd_buffer ) {
ast_free ( cmd ) ;
return - 1 ;
}
cmd - > cmd_id = ast_strdup ( cmd_id ) ;
if ( ! cmd - > cmd_id ) {
ast_free ( cmd - > cmd_buffer ) ;
ast_free ( cmd ) ;
return - 1 ;
}
AST_LIST_LOCK ( agi_commands ) ;
AST_LIST_INSERT_TAIL ( agi_commands , cmd , entry ) ;
AST_LIST_UNLOCK ( agi_commands ) ;
return 0 ;
}
static int add_to_agi ( struct ast_channel * chan )
{
struct ast_datastore * datastore ;
AST_LIST_HEAD ( , agi_cmd ) * agi_cmds_list ;
/* check if already on AGI */
ast_channel_lock ( chan ) ;
datastore = ast_channel_datastore_find ( chan , & agi_commands_datastore_info , NULL ) ;
ast_channel_unlock ( chan ) ;
if ( datastore ) {
2008-06-18 00:33:31 +00:00
/* we already have an AGI datastore, let's just
2008-01-03 06:16:48 +00:00
return success */
return 0 ;
}
/* the channel has never been on Async AGI,
let ' s allocate it ' s datastore */
2008-08-05 16:56:11 +00:00
datastore = ast_datastore_alloc ( & agi_commands_datastore_info , " AGI " ) ;
2008-01-03 06:16:48 +00:00
if ( ! datastore ) {
return - 1 ;
}
agi_cmds_list = ast_calloc ( 1 , sizeof ( * agi_cmds_list ) ) ;
if ( ! agi_cmds_list ) {
ast_log ( LOG_ERROR , " Unable to allocate Async AGI commands list. \n " ) ;
2008-08-05 16:56:11 +00:00
ast_datastore_free ( datastore ) ;
2008-01-03 06:16:48 +00:00
return - 1 ;
}
datastore - > data = agi_cmds_list ;
AST_LIST_HEAD_INIT ( agi_cmds_list ) ;
ast_channel_lock ( chan ) ;
ast_channel_datastore_add ( chan , datastore ) ;
ast_channel_unlock ( chan ) ;
return 0 ;
}
/*!
2008-06-18 00:33:31 +00:00
* \ brief CLI command to add applications to execute in Async AGI
2008-01-03 06:16:48 +00:00
* \ param e
2008-06-18 00:33:31 +00:00
* \ param cmd
2008-01-03 06:16:48 +00:00
* \ param a
*
* \ retval CLI_SUCCESS on success
* \ retval NULL when init or tab completion is used
*/
static char * handle_cli_agi_add_cmd ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a )
{
struct ast_channel * chan ;
switch ( cmd ) {
case CLI_INIT :
e - > command = " agi exec " ;
e - > usage = " Usage: agi exec <channel name> <app and arguments> [id] \n "
" Add AGI command to the execute queue of the specified channel in Async AGI \n " ;
return NULL ;
case CLI_GENERATE :
if ( a - > pos = = 2 )
return ast_complete_channels ( a - > line , a - > word , a - > pos , a - > n , 2 ) ;
return NULL ;
}
if ( a - > argc < 4 )
return CLI_SHOWUSAGE ;
chan = ast_get_channel_by_name_locked ( a - > argv [ 2 ] ) ;
if ( ! chan ) {
ast_log ( LOG_WARNING , " Channel %s does not exists or cannot lock it \n " , a - > argv [ 2 ] ) ;
return CLI_FAILURE ;
}
if ( add_agi_cmd ( chan , a - > argv [ 3 ] , ( a - > argc > 4 ? a - > argv [ 4 ] : " " ) ) ) {
ast_log ( LOG_WARNING , " failed to add AGI command to queue of channel %s \n " , chan - > name ) ;
ast_channel_unlock ( chan ) ;
return CLI_FAILURE ;
}
ast_log ( LOG_DEBUG , " Added AGI command to channel %s queue \n " , chan - > name ) ;
ast_channel_unlock ( chan ) ;
return CLI_SUCCESS ;
}
/*!
* \ brief Add a new command to execute by the Async AGI application
* \ param s
* \ param m
*
* It will append the application to the specified channel ' s queue
* if the channel is not inside Async AGI application it will return an error
* \ retval 0 on success or incorrect use
2008-06-18 00:33:31 +00:00
* \ retval 1 on failure to add the command ( most likely because the channel
2008-01-03 06:16:48 +00:00
* is not in Async AGI loop )
*/
static int action_add_agi_cmd ( struct mansession * s , const struct message * m )
{
const char * channel = astman_get_header ( m , " Channel " ) ;
const char * cmdbuff = astman_get_header ( m , " Command " ) ;
const char * cmdid = astman_get_header ( m , " CommandID " ) ;
struct ast_channel * chan ;
char buf [ 256 ] ;
if ( ast_strlen_zero ( channel ) | | ast_strlen_zero ( cmdbuff ) ) {
astman_send_error ( s , m , " Both, Channel and Command are *required* " ) ;
return 0 ;
}
chan = ast_get_channel_by_name_locked ( channel ) ;
if ( ! chan ) {
snprintf ( buf , sizeof ( buf ) , " Channel %s does not exists or cannot get its lock " , channel ) ;
astman_send_error ( s , m , buf ) ;
2008-04-05 13:24:12 +00:00
return 0 ;
2008-01-03 06:16:48 +00:00
}
if ( add_agi_cmd ( chan , cmdbuff , cmdid ) ) {
snprintf ( buf , sizeof ( buf ) , " Failed to add AGI command to channel %s queue " , chan - > name ) ;
astman_send_error ( s , m , buf ) ;
ast_channel_unlock ( chan ) ;
2008-04-05 13:24:12 +00:00
return 0 ;
2008-01-03 06:16:48 +00:00
}
astman_send_ack ( s , m , " Added AGI command to queue " ) ;
ast_channel_unlock ( chan ) ;
return 0 ;
}
static int agi_handle_command ( struct ast_channel * chan , AGI * agi , char * buf , int dead ) ;
static void setup_env ( struct ast_channel * chan , char * request , int fd , int enhanced , int argc , char * argv [ ] ) ;
static enum agi_result launch_asyncagi ( struct ast_channel * chan , char * argv [ ] , int * efd )
{
/* This buffer sizes might cause truncation if the AGI command writes more data
2008-06-18 00:33:31 +00:00
than AGI_BUF_SIZE as result . But let ' s be serious , is there an AGI command
that writes a response larger than 1024 bytes ? , I don ' t think so , most of
them are just result = blah stuff . However probably if GET VARIABLE is called
and the variable has large amount of data , that could be a problem . We could
2008-01-03 06:16:48 +00:00
make this buffers dynamic , but let ' s leave that as a second step .
2008-06-18 00:33:31 +00:00
AMI_BUF_SIZE is twice AGI_BUF_SIZE just for the sake of choosing a safe
number . Some characters of AGI buf will be url encoded to be sent to manager
clients . An URL encoded character will take 3 bytes , but again , to cause
truncation more than about 70 % of the AGI buffer should be URL encoded for
that to happen . Not likely at all .
2008-01-03 06:16:48 +00:00
2008-06-18 00:33:31 +00:00
On the other hand . I wonder if read ( ) could eventually return less data than
the amount already available in the pipe ? If so , how to deal with that ?
2008-01-03 06:16:48 +00:00
So far , my tests on Linux have not had any problems .
*/
# define AGI_BUF_SIZE 1024
# define AMI_BUF_SIZE 2048
struct ast_frame * f ;
struct agi_cmd * cmd ;
int res , fds [ 2 ] ;
2008-06-18 00:33:31 +00:00
int timeout = 100 ;
2008-01-03 06:16:48 +00:00
char agi_buffer [ AGI_BUF_SIZE + 1 ] ;
char ami_buffer [ AMI_BUF_SIZE ] ;
enum agi_result returnstatus = AGI_RESULT_SUCCESS_ASYNC ;
AGI async_agi ;
if ( efd ) {
ast_log ( LOG_WARNING , " Async AGI does not support Enhanced AGI yet \n " ) ;
return AGI_RESULT_FAILURE ;
}
/* add AsyncAGI datastore to the channel */
if ( add_to_agi ( chan ) ) {
ast_log ( LOG_ERROR , " failed to start Async AGI on channel %s \n " , chan - > name ) ;
return AGI_RESULT_FAILURE ;
2008-06-18 00:33:31 +00:00
}
2008-01-03 06:16:48 +00:00
2008-06-18 00:33:31 +00:00
/* this pipe allows us to create a "fake" AGI struct to use
2008-01-03 06:16:48 +00:00
the AGI commands */
res = pipe ( fds ) ;
if ( res ) {
ast_log ( LOG_ERROR , " failed to create Async AGI pipe \n " ) ;
2008-06-18 00:33:31 +00:00
/* intentionally do not remove datastore, added with
add_to_agi ( ) , from channel . It will be removed when
2008-01-03 06:16:48 +00:00
the channel is hung up anyways */
return AGI_RESULT_FAILURE ;
}
2008-02-14 10:19:09 +00:00
2008-06-18 00:33:31 +00:00
/* handlers will get the pipe write fd and we read the AGI responses
2008-01-03 06:16:48 +00:00
from the pipe read fd */
2008-06-18 00:33:31 +00:00
async_agi . fd = fds [ 1 ] ;
2008-01-03 06:16:48 +00:00
async_agi . ctrl = fds [ 1 ] ;
async_agi . audio = - 1 ; /* no audio support */
async_agi . fast = 0 ;
2008-06-18 00:33:31 +00:00
/* notify possible manager users of a new channel ready to
receive commands */
2008-01-03 06:16:48 +00:00
setup_env ( chan , " async " , fds [ 1 ] , 0 , 0 , NULL ) ;
/* read the environment */
res = read ( fds [ 0 ] , agi_buffer , AGI_BUF_SIZE ) ;
if ( ! res ) {
ast_log ( LOG_ERROR , " failed to read from Async AGI pipe on channel %s \n " , chan - > name ) ;
returnstatus = AGI_RESULT_FAILURE ;
goto quit ;
}
agi_buffer [ res ] = ' \0 ' ;
2008-06-18 00:33:31 +00:00
/* encode it and send it thru the manager so whoever is going to take
care of AGI commands on this channel can decide which AGI commands
2008-01-03 06:16:48 +00:00
to execute based on the setup info */
ast_uri_encode ( agi_buffer , ami_buffer , AMI_BUF_SIZE , 1 ) ;
2008-11-24 21:56:22 +00:00
manager_event ( EVENT_FLAG_AGI , " AsyncAGI " , " SubEvent: Start \r \n Channel: %s \r \n Env: %s \r \n " , chan - > name , ami_buffer ) ;
2008-01-03 06:16:48 +00:00
while ( 1 ) {
/* bail out if we need to hangup */
if ( ast_check_hangup ( chan ) ) {
ast_log ( LOG_DEBUG , " ast_check_hangup returned true on chan %s \n " , chan - > name ) ;
break ;
}
2008-06-18 00:33:31 +00:00
/* retrieve a command
2008-01-03 06:16:48 +00:00
( commands are added via the manager or the cli threads ) */
cmd = get_agi_cmd ( chan ) ;
if ( cmd ) {
2008-06-18 00:33:31 +00:00
/* OK, we have a command, let's call the
2008-01-03 06:16:48 +00:00
command handler . */
res = agi_handle_command ( chan , & async_agi , cmd - > cmd_buffer , 0 ) ;
2008-12-22 14:16:54 +00:00
if ( res < 0 ) {
2008-01-03 06:16:48 +00:00
free_agi_cmd ( cmd ) ;
break ;
}
2008-06-18 00:33:31 +00:00
/* the command handler must have written to our fake
2008-01-03 06:16:48 +00:00
AGI struct fd ( the pipe ) , let ' s read the response */
res = read ( fds [ 0 ] , agi_buffer , AGI_BUF_SIZE ) ;
if ( ! res ) {
returnstatus = AGI_RESULT_FAILURE ;
ast_log ( LOG_ERROR , " failed to read from AsyncAGI pipe on channel %s \n " , chan - > name ) ;
free_agi_cmd ( cmd ) ;
break ;
}
/* we have a response, let's send the response thru the
2008-06-18 00:33:31 +00:00
manager . Include the CommandID if it was specified
2008-01-03 06:16:48 +00:00
when the command was added */
agi_buffer [ res ] = ' \0 ' ;
ast_uri_encode ( agi_buffer , ami_buffer , AMI_BUF_SIZE , 1 ) ;
if ( ast_strlen_zero ( cmd - > cmd_id ) )
2008-11-24 21:56:22 +00:00
manager_event ( EVENT_FLAG_AGI , " AsyncAGI " , " SubEvent: Exec \r \n Channel: %s \r \n Result: %s \r \n " , chan - > name , ami_buffer ) ;
2008-01-03 06:16:48 +00:00
else
2008-11-24 21:56:22 +00:00
manager_event ( EVENT_FLAG_AGI , " AsyncAGI " , " SubEvent: Exec \r \n Channel: %s \r \n CommandID: %s \r \n Result: %s \r \n " , chan - > name , cmd - > cmd_id , ami_buffer ) ;
2008-01-03 06:16:48 +00:00
free_agi_cmd ( cmd ) ;
} else {
/* no command so far, wait a bit for a frame to read */
res = ast_waitfor ( chan , timeout ) ;
if ( res < 0 ) {
ast_log ( LOG_DEBUG , " ast_waitfor returned <= 0 on chan %s \n " , chan - > name ) ;
break ;
}
if ( res = = 0 )
continue ;
f = ast_read ( chan ) ;
if ( ! f ) {
ast_log ( LOG_DEBUG , " No frame read on channel %s, going out ... \n " , chan - > name ) ;
returnstatus = AGI_RESULT_HANGUP ;
break ;
}
2008-06-18 00:33:31 +00:00
/* is there any other frame we should care about
2008-01-03 06:16:48 +00:00
besides AST_CONTROL_HANGUP ? */
if ( f - > frametype = = AST_FRAME_CONTROL & & f - > subclass = = AST_CONTROL_HANGUP ) {
ast_log ( LOG_DEBUG , " Got HANGUP frame on channel %s, going out ... \n " , chan - > name ) ;
ast_frfree ( f ) ;
break ;
}
ast_frfree ( f ) ;
}
}
quit :
2008-06-18 00:33:31 +00:00
/* notify manager users this channel cannot be
2008-01-03 06:16:48 +00:00
controlled anymore by Async AGI */
2008-11-24 21:56:22 +00:00
manager_event ( EVENT_FLAG_AGI , " AsyncAGI " , " SubEvent: End \r \n Channel: %s \r \n " , chan - > name ) ;
2008-01-03 06:16:48 +00:00
/* close the pipe */
close ( fds [ 0 ] ) ;
close ( fds [ 1 ] ) ;
2008-06-18 00:33:31 +00:00
/* intentionally don't get rid of the datastore. So commands can be
2008-01-03 06:16:48 +00:00
still in the queue in case AsyncAGI gets called again .
Datastore destructor will be called on channel destroy anyway */
return returnstatus ;
2008-06-18 00:33:31 +00:00
# undef AGI_BUF_SIZE
# undef AMI_BUF_SIZE
2008-01-03 06:16:48 +00:00
}
2005-01-08 19:35:20 +00:00
/* launch_netscript: The fastagi handler.
FastAGI defaults to port 4573 */
2006-05-25 16:44:22 +00:00
static enum agi_result launch_netscript ( char * agiurl , char * argv [ ] , int * fds , int * efd , int * opid )
2004-09-22 15:21:36 +00:00
{
2007-07-11 21:09:42 +00:00
int s , flags , res , port = AGI_PORT ;
2004-09-22 15:21:36 +00:00
struct pollfd pfds [ 1 ] ;
2007-07-11 21:09:42 +00:00
char * host , * c , * script = " " ;
2008-08-10 00:47:56 +00:00
struct sockaddr_in addr_in ;
2004-09-22 15:21:36 +00:00
struct hostent * hp ;
struct ast_hostent ahp ;
2005-01-08 19:35:20 +00:00
2006-04-06 16:17:04 +00:00
/* agiusl is "agi://host.domain[:port][/script/name]" */
2005-01-08 19:35:20 +00:00
host = ast_strdupa ( agiurl + 6 ) ; /* Remove agi:// */
2004-09-22 15:21:36 +00:00
/* Strip off any script name */
if ( ( c = strchr ( host , ' / ' ) ) ) {
* c = ' \0 ' ;
c + + ;
script = c ;
}
if ( ( c = strchr ( host , ' : ' ) ) ) {
* c = ' \0 ' ;
c + + ;
2004-09-29 03:32:58 +00:00
port = atoi ( c ) ;
2004-09-22 15:21:36 +00:00
}
if ( efd ) {
ast_log ( LOG_WARNING , " AGI URI's don't support Enhanced AGI yet \n " ) ;
return - 1 ;
}
2007-07-11 21:09:42 +00:00
if ( ! ( hp = ast_gethostbyname ( host , & ahp ) ) ) {
2004-09-22 15:21:36 +00:00
ast_log ( LOG_WARNING , " Unable to locate host '%s' \n " , host ) ;
return - 1 ;
}
2007-07-11 21:09:42 +00:00
if ( ( s = socket ( AF_INET , SOCK_STREAM , 0 ) ) < 0 ) {
2004-09-22 15:21:36 +00:00
ast_log ( LOG_WARNING , " Unable to create socket: %s \n " , strerror ( errno ) ) ;
return - 1 ;
}
2007-07-11 21:09:42 +00:00
if ( ( flags = fcntl ( s , F_GETFL ) ) < 0 ) {
2004-09-22 15:21:36 +00:00
ast_log ( LOG_WARNING , " Fcntl(F_GETFL) failed: %s \n " , strerror ( errno ) ) ;
close ( s ) ;
return - 1 ;
}
if ( fcntl ( s , F_SETFL , flags | O_NONBLOCK ) < 0 ) {
ast_log ( LOG_WARNING , " Fnctl(F_SETFL) failed: %s \n " , strerror ( errno ) ) ;
close ( s ) ;
return - 1 ;
}
2008-08-10 00:47:56 +00:00
memset ( & addr_in , 0 , sizeof ( addr_in ) ) ;
addr_in . sin_family = AF_INET ;
addr_in . sin_port = htons ( port ) ;
memcpy ( & addr_in . sin_addr , hp - > h_addr , sizeof ( addr_in . sin_addr ) ) ;
if ( connect ( s , ( struct sockaddr * ) & addr_in , sizeof ( addr_in ) ) & & ( errno ! = EINPROGRESS ) ) {
2004-09-22 15:21:36 +00:00
ast_log ( LOG_WARNING , " Connect failed with unexpected error: %s \n " , strerror ( errno ) ) ;
close ( s ) ;
2006-05-25 16:44:22 +00:00
return AGI_RESULT_FAILURE ;
2004-09-22 15:21:36 +00:00
}
2006-02-15 00:38:27 +00:00
2004-09-22 15:21:36 +00:00
pfds [ 0 ] . fd = s ;
pfds [ 0 ] . events = POLLOUT ;
2006-05-31 23:51:39 +00:00
while ( ( res = poll ( pfds , 1 , MAX_AGI_CONNECT ) ) ! = 1 ) {
2006-02-15 00:38:27 +00:00
if ( errno ! = EINTR ) {
2006-05-31 23:51:39 +00:00
if ( ! res ) {
ast_log ( LOG_WARNING , " FastAGI connection to '%s' timed out after MAX_AGI_CONNECT (%d) milliseconds. \n " ,
agiurl , MAX_AGI_CONNECT ) ;
} else
ast_log ( LOG_WARNING , " Connect to '%s' failed: %s \n " , agiurl , strerror ( errno ) ) ;
2006-02-15 00:38:27 +00:00
close ( s ) ;
2006-05-25 16:44:22 +00:00
return AGI_RESULT_FAILURE ;
2006-02-15 00:38:27 +00:00
}
2004-09-22 15:21:36 +00:00
}
2007-07-30 19:18:24 +00:00
2008-11-19 12:42:19 +00:00
if ( ast_agi_send ( s , NULL , " agi_network: yes \n " ) < 0 ) {
2006-02-15 00:38:27 +00:00
if ( errno ! = EINTR ) {
ast_log ( LOG_WARNING , " Connect to '%s' failed: %s \n " , agiurl , strerror ( errno ) ) ;
close ( s ) ;
2006-05-25 16:44:22 +00:00
return AGI_RESULT_FAILURE ;
2006-02-15 00:38:27 +00:00
}
2004-09-22 15:21:36 +00:00
}
2005-01-08 19:35:20 +00:00
/* If we have a script parameter, relay it to the fastagi server */
2006-10-17 23:06:13 +00:00
/* Script parameters take the form of: AGI(agi://my.example.com/?extension=${EXTEN}) */
2005-01-08 19:35:20 +00:00
if ( ! ast_strlen_zero ( script ) )
2008-11-19 12:42:19 +00:00
ast_agi_send ( s , NULL , " agi_network_script: %s \n " , script ) ;
2005-01-08 19:35:20 +00:00
2007-06-14 19:39:12 +00:00
ast_debug ( 4 , " Wow, connected! \n " ) ;
2004-09-22 15:21:36 +00:00
fds [ 0 ] = s ;
fds [ 1 ] = s ;
* opid = - 1 ;
2007-09-18 22:46:05 +00:00
return AGI_RESULT_SUCCESS_FAST ;
2004-09-22 15:21:36 +00:00
}
2008-01-03 06:16:48 +00:00
static enum agi_result launch_script ( struct ast_channel * chan , char * script , char * argv [ ] , int * fds , int * efd , int * opid )
2001-09-28 13:19:43 +00:00
{
char tmp [ 256 ] ;
2008-04-16 22:57:54 +00:00
int pid , toast [ 2 ] , fromast [ 2 ] , audio [ 2 ] , res ;
2007-06-22 16:19:53 +00:00
struct stat st ;
2008-07-09 03:39:59 +00:00
if ( ! strncasecmp ( script , " agi:// " , 6 ) )
2004-09-22 15:21:36 +00:00
return launch_netscript ( script , argv , fds , efd , opid ) ;
2008-01-03 06:16:48 +00:00
if ( ! strncasecmp ( script , " agi:async " , sizeof ( " agi:async " ) - 1 ) )
return launch_asyncagi ( chan , argv , efd ) ;
2008-06-18 00:33:31 +00:00
2001-09-28 13:19:43 +00:00
if ( script [ 0 ] ! = ' / ' ) {
2006-11-04 01:40:49 +00:00
snprintf ( tmp , sizeof ( tmp ) , " %s/%s " , ast_config_AST_AGI_DIR , script ) ;
2001-09-28 13:19:43 +00:00
script = tmp ;
}
2007-06-21 15:58:05 +00:00
/* Before even trying let's see if the file actually exists */
2007-06-22 16:19:53 +00:00
if ( stat ( script , & st ) ) {
2007-06-21 15:58:05 +00:00
ast_log ( LOG_WARNING , " Failed to execute '%s': File does not exist. \n " , script ) ;
return AGI_RESULT_NOTFOUND ;
}
2001-09-28 13:19:43 +00:00
if ( pipe ( toast ) ) {
ast_log ( LOG_WARNING , " Unable to create toast pipe: %s \n " , strerror ( errno ) ) ;
2006-05-25 16:44:22 +00:00
return AGI_RESULT_FAILURE ;
2001-09-28 13:19:43 +00:00
}
if ( pipe ( fromast ) ) {
ast_log ( LOG_WARNING , " unable to create fromast pipe: %s \n " , strerror ( errno ) ) ;
close ( toast [ 0 ] ) ;
close ( toast [ 1 ] ) ;
2006-05-25 16:44:22 +00:00
return AGI_RESULT_FAILURE ;
2001-09-28 13:19:43 +00:00
}
2003-02-23 06:00:11 +00:00
if ( efd ) {
if ( pipe ( audio ) ) {
ast_log ( LOG_WARNING , " unable to create audio pipe: %s \n " , strerror ( errno ) ) ;
close ( fromast [ 0 ] ) ;
close ( fromast [ 1 ] ) ;
close ( toast [ 0 ] ) ;
close ( toast [ 1 ] ) ;
2006-05-25 16:44:22 +00:00
return AGI_RESULT_FAILURE ;
2003-02-23 06:00:11 +00:00
}
res = fcntl ( audio [ 1 ] , F_GETFL ) ;
2008-06-18 00:33:31 +00:00
if ( res > - 1 )
2003-02-23 06:00:11 +00:00
res = fcntl ( audio [ 1 ] , F_SETFL , res | O_NONBLOCK ) ;
if ( res < 0 ) {
ast_log ( LOG_WARNING , " unable to set audio pipe parameters: %s \n " , strerror ( errno ) ) ;
close ( fromast [ 0 ] ) ;
close ( fromast [ 1 ] ) ;
close ( toast [ 0 ] ) ;
close ( toast [ 1 ] ) ;
close ( audio [ 0 ] ) ;
close ( audio [ 1 ] ) ;
2006-05-25 16:44:22 +00:00
return AGI_RESULT_FAILURE ;
2003-02-23 06:00:11 +00:00
}
}
2006-12-11 00:52:19 +00:00
2008-04-16 22:57:54 +00:00
if ( ( pid = ast_safe_fork ( 1 ) ) < 0 ) {
2001-09-28 13:19:43 +00:00
ast_log ( LOG_WARNING , " Failed to fork(): %s \n " , strerror ( errno ) ) ;
2006-05-25 16:44:22 +00:00
return AGI_RESULT_FAILURE ;
2001-09-28 13:19:43 +00:00
}
if ( ! pid ) {
2006-01-21 05:22:18 +00:00
/* Pass paths to AGI via environmental variables */
setenv ( " AST_CONFIG_DIR " , ast_config_AST_CONFIG_DIR , 1 ) ;
setenv ( " AST_CONFIG_FILE " , ast_config_AST_CONFIG_FILE , 1 ) ;
setenv ( " AST_MODULE_DIR " , ast_config_AST_MODULE_DIR , 1 ) ;
setenv ( " AST_SPOOL_DIR " , ast_config_AST_SPOOL_DIR , 1 ) ;
setenv ( " AST_MONITOR_DIR " , ast_config_AST_MONITOR_DIR , 1 ) ;
setenv ( " AST_VAR_DIR " , ast_config_AST_VAR_DIR , 1 ) ;
2006-04-15 22:53:53 +00:00
setenv ( " AST_DATA_DIR " , ast_config_AST_DATA_DIR , 1 ) ;
2006-01-21 05:22:18 +00:00
setenv ( " AST_LOG_DIR " , ast_config_AST_LOG_DIR , 1 ) ;
setenv ( " AST_AGI_DIR " , ast_config_AST_AGI_DIR , 1 ) ;
setenv ( " AST_KEY_DIR " , ast_config_AST_KEY_DIR , 1 ) ;
setenv ( " AST_RUN_DIR " , ast_config_AST_RUN_DIR , 1 ) ;
2006-06-12 15:30:32 +00:00
/* Don't run AGI scripts with realtime priority -- it causes audio stutter */
ast_set_priority ( 0 ) ;
2003-02-23 06:00:11 +00:00
/* Redirect stdin and out, provide enhanced audio channel if desired */
2001-09-28 13:19:43 +00:00
dup2 ( fromast [ 0 ] , STDIN_FILENO ) ;
dup2 ( toast [ 1 ] , STDOUT_FILENO ) ;
2007-07-11 21:09:42 +00:00
if ( efd )
2003-02-23 06:00:11 +00:00
dup2 ( audio [ 0 ] , STDERR_FILENO + 1 ) ;
2007-07-11 21:09:42 +00:00
else
2003-02-23 06:00:11 +00:00
close ( STDERR_FILENO + 1 ) ;
2006-12-11 00:52:19 +00:00
2001-09-28 13:19:43 +00:00
/* Close everything but stdin/out/error */
2008-04-16 22:57:54 +00:00
ast_close_fds_above_n ( STDERR_FILENO + 1 ) ;
2005-08-03 04:17:12 +00:00
2001-09-28 13:19:43 +00:00
/* Execute script */
2006-10-17 23:06:13 +00:00
/* XXX argv should be deprecated in favor of passing agi_argX paramaters */
2004-03-23 12:50:15 +00:00
execv ( script , argv ) ;
2003-02-06 22:11:43 +00:00
/* Can't use ast_log since FD's are closed */
2008-04-16 22:57:54 +00:00
ast_child_verbose ( 1 , " Failed to execute '%s': %s " , script , strerror ( errno ) ) ;
2008-04-17 12:59:04 +00:00
/* Special case to set status of AGI to failure */
fprintf ( stdout , " failure \n " ) ;
2006-02-01 20:40:45 +00:00
fflush ( stdout ) ;
2006-10-27 17:42:57 +00:00
_exit ( 1 ) ;
2001-09-28 13:19:43 +00:00
}
2007-07-26 15:49:18 +00:00
ast_verb ( 3 , " Launched AGI Script %s \n " , script ) ;
2001-09-28 13:19:43 +00:00
fds [ 0 ] = toast [ 0 ] ;
fds [ 1 ] = fromast [ 1 ] ;
2007-07-11 21:09:42 +00:00
if ( efd )
2003-02-23 06:00:11 +00:00
* efd = audio [ 1 ] ;
2001-09-28 13:19:43 +00:00
/* close what we're not using in the parent */
close ( toast [ 1 ] ) ;
close ( fromast [ 0 ] ) ;
2003-12-27 23:39:57 +00:00
2006-05-25 16:44:22 +00:00
if ( efd )
2004-01-01 00:26:11 +00:00
close ( audio [ 0 ] ) ;
2003-12-27 23:39:57 +00:00
2001-09-28 13:19:43 +00:00
* opid = pid ;
2006-05-25 16:44:22 +00:00
return AGI_RESULT_SUCCESS ;
2001-09-28 13:19:43 +00:00
}
2006-10-17 23:06:13 +00:00
static void setup_env ( struct ast_channel * chan , char * request , int fd , int enhanced , int argc , char * argv [ ] )
2001-09-28 13:19:43 +00:00
{
2006-10-17 23:06:13 +00:00
int count ;
2001-09-28 13:19:43 +00:00
/* Print initial environment, with agi_request always being the first
thing */
2008-11-19 12:42:19 +00:00
ast_agi_send ( fd , chan , " agi_request: %s \n " , request ) ;
ast_agi_send ( fd , chan , " agi_channel: %s \n " , chan - > name ) ;
ast_agi_send ( fd , chan , " agi_language: %s \n " , chan - > language ) ;
ast_agi_send ( fd , chan , " agi_type: %s \n " , chan - > tech - > type ) ;
ast_agi_send ( fd , chan , " agi_uniqueid: %s \n " , chan - > uniqueid ) ;
ast_agi_send ( fd , chan , " agi_version: %s \n " , ast_get_version ( ) ) ;
2001-09-28 13:19:43 +00:00
/* ANI/DNIS */
2008-11-19 12:42:19 +00:00
ast_agi_send ( fd , chan , " agi_callerid: %s \n " , S_OR ( chan - > cid . cid_num , " unknown " ) ) ;
ast_agi_send ( fd , chan , " agi_calleridname: %s \n " , S_OR ( chan - > cid . cid_name , " unknown " ) ) ;
ast_agi_send ( fd , chan , " agi_callingpres: %d \n " , chan - > cid . cid_pres ) ;
ast_agi_send ( fd , chan , " agi_callingani2: %d \n " , chan - > cid . cid_ani2 ) ;
ast_agi_send ( fd , chan , " agi_callington: %d \n " , chan - > cid . cid_ton ) ;
ast_agi_send ( fd , chan , " agi_callingtns: %d \n " , chan - > cid . cid_tns ) ;
ast_agi_send ( fd , chan , " agi_dnid: %s \n " , S_OR ( chan - > cid . cid_dnid , " unknown " ) ) ;
ast_agi_send ( fd , chan , " agi_rdnis: %s \n " , S_OR ( chan - > cid . cid_rdnis , " unknown " ) ) ;
2001-09-28 13:19:43 +00:00
/* Context information */
2008-11-19 12:42:19 +00:00
ast_agi_send ( fd , chan , " agi_context: %s \n " , chan - > context ) ;
ast_agi_send ( fd , chan , " agi_extension: %s \n " , chan - > exten ) ;
ast_agi_send ( fd , chan , " agi_priority: %d \n " , chan - > priority ) ;
ast_agi_send ( fd , chan , " agi_enhanced: %s \n " , enhanced ? " 1.0 " : " 0.0 " ) ;
2001-09-28 13:19:43 +00:00
2005-01-08 19:35:20 +00:00
/* User information */
2008-11-19 12:42:19 +00:00
ast_agi_send ( fd , chan , " agi_accountcode: %s \n " , chan - > accountcode ? chan - > accountcode : " " ) ;
ast_agi_send ( fd , chan , " agi_threadid: %ld \n " , ( long ) pthread_self ( ) ) ;
2007-07-23 22:02:05 +00:00
2006-10-17 23:06:13 +00:00
/* Send any parameters to the fastagi server that have been passed via the agi application */
/* Agi application paramaters take the form of: AGI(/path/to/example/script|${EXTEN}) */
for ( count = 1 ; count < argc ; count + + )
2008-11-19 12:42:19 +00:00
ast_agi_send ( fd , chan , " agi_arg_%d: %s \n " , count , argv [ count ] ) ;
2006-10-17 23:06:13 +00:00
2001-09-28 13:19:43 +00:00
/* End with empty return */
2008-11-19 12:42:19 +00:00
ast_agi_send ( fd , chan , " \n " ) ;
2001-09-28 13:19:43 +00:00
}
2003-02-23 06:00:11 +00:00
static int handle_answer ( struct ast_channel * chan , AGI * agi , int argc , char * argv [ ] )
2002-06-14 15:50:42 +00:00
{
2007-07-11 21:09:42 +00:00
int res = 0 ;
/* Answer the channel */
if ( chan - > _state ! = AST_STATE_UP )
2002-06-14 15:50:42 +00:00
res = ast_answer ( chan ) ;
2007-07-11 21:09:42 +00:00
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=%d \n " , res ) ;
2006-04-06 16:17:04 +00:00
return ( res > = 0 ) ? RESULT_SUCCESS : RESULT_FAILURE ;
2002-06-14 15:50:42 +00:00
}
2003-02-23 06:00:11 +00:00
static int handle_waitfordigit ( struct ast_channel * chan , AGI * agi , int argc , char * argv [ ] )
2001-09-28 13:19:43 +00:00
{
2007-07-11 21:09:42 +00:00
int res , to ;
2001-09-28 13:19:43 +00:00
if ( argc ! = 4 )
return RESULT_SHOWUSAGE ;
2005-04-27 01:42:33 +00:00
if ( sscanf ( argv [ 3 ] , " %d " , & to ) ! = 1 )
2001-09-28 13:19:43 +00:00
return RESULT_SHOWUSAGE ;
2003-02-23 06:00:11 +00:00
res = ast_waitfordigit_full ( chan , to , agi - > audio , agi - > ctrl ) ;
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=%d \n " , res ) ;
2006-04-06 16:17:04 +00:00
return ( res > = 0 ) ? RESULT_SUCCESS : RESULT_FAILURE ;
2001-09-28 13:19:43 +00:00
}
2003-02-23 06:00:11 +00:00
static int handle_sendtext ( struct ast_channel * chan , AGI * agi , int argc , char * argv [ ] )
2001-09-28 13:19:43 +00:00
{
int res ;
2007-07-11 21:09:42 +00:00
2001-09-28 13:19:43 +00:00
if ( argc ! = 3 )
return RESULT_SHOWUSAGE ;
2007-07-11 21:09:42 +00:00
2001-12-27 11:07:33 +00:00
/* At the moment, the parser (perhaps broken) returns with
the last argument PLUS the newline at the end of the input
buffer . This probably needs to be fixed , but I wont do that
because other stuff may break as a result . The right way
would probably be to strip off the trailing newline before
parsing , then here , add a newline at the end of the string
before sending it to ast_sendtext - - DUDE */
2001-09-28 13:19:43 +00:00
res = ast_sendtext ( chan , argv [ 2 ] ) ;
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=%d \n " , res ) ;
2006-04-06 16:17:04 +00:00
return ( res > = 0 ) ? RESULT_SUCCESS : RESULT_FAILURE ;
2001-09-28 13:19:43 +00:00
}
2003-02-23 06:00:11 +00:00
static int handle_recvchar ( struct ast_channel * chan , AGI * agi , int argc , char * argv [ ] )
2001-12-27 11:07:33 +00:00
{
int res ;
2007-07-11 21:09:42 +00:00
2001-12-27 11:07:33 +00:00
if ( argc ! = 3 )
return RESULT_SHOWUSAGE ;
2007-07-11 21:09:42 +00:00
2001-12-27 11:07:33 +00:00
res = ast_recvchar ( chan , atoi ( argv [ 2 ] ) ) ;
if ( res = = 0 ) {
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=%d (timeout) \n " , res ) ;
2001-12-27 11:07:33 +00:00
return RESULT_SUCCESS ;
2008-06-18 00:33:31 +00:00
}
2001-12-27 11:07:33 +00:00
if ( res > 0 ) {
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=%d \n " , res ) ;
2001-12-27 11:07:33 +00:00
return RESULT_SUCCESS ;
}
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=%d (hangup) \n " , res ) ;
2008-05-14 11:51:09 +00:00
return RESULT_FAILURE ;
2001-12-27 11:07:33 +00:00
}
2005-06-21 01:16:18 +00:00
static int handle_recvtext ( struct ast_channel * chan , AGI * agi , int argc , char * argv [ ] )
{
char * buf ;
2008-06-18 00:33:31 +00:00
2005-06-21 01:16:18 +00:00
if ( argc ! = 3 )
return RESULT_SHOWUSAGE ;
2007-07-11 21:09:42 +00:00
2008-05-14 11:51:09 +00:00
buf = ast_recvtext ( chan , atoi ( argv [ 2 ] ) ) ;
2005-06-21 01:16:18 +00:00
if ( buf ) {
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=1 (%s) \n " , buf ) ;
2007-06-06 21:20:11 +00:00
ast_free ( buf ) ;
2008-06-18 00:33:31 +00:00
} else {
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=-1 \n " ) ;
2005-06-21 01:16:18 +00:00
}
2005-07-11 20:40:02 +00:00
return RESULT_SUCCESS ;
2005-06-21 01:16:18 +00:00
}
2003-02-23 06:00:11 +00:00
static int handle_tddmode ( struct ast_channel * chan , AGI * agi , int argc , char * argv [ ] )
2001-12-27 11:07:33 +00:00
{
2007-07-11 21:09:42 +00:00
int res , x ;
2001-12-27 11:07:33 +00:00
if ( argc ! = 3 )
return RESULT_SHOWUSAGE ;
2007-07-11 21:09:42 +00:00
2008-05-14 11:51:09 +00:00
if ( ! strncasecmp ( argv [ 2 ] , " on " , 2 ) ) {
2008-06-18 00:33:31 +00:00
x = 1 ;
2008-05-14 11:51:09 +00:00
} else {
2005-01-08 19:35:20 +00:00
x = 0 ;
2008-05-14 11:51:09 +00:00
}
if ( ! strncasecmp ( argv [ 2 ] , " mate " , 4 ) ) {
2005-01-08 19:35:20 +00:00
x = 2 ;
2008-05-14 11:51:09 +00:00
}
if ( ! strncasecmp ( argv [ 2 ] , " tdd " , 3 ) ) {
2005-01-08 19:35:20 +00:00
x = 1 ;
2008-05-14 11:51:09 +00:00
}
2005-01-08 19:35:20 +00:00
res = ast_channel_setoption ( chan , AST_OPTION_TDD , & x , sizeof ( char ) , 0 ) ;
2008-05-14 11:51:09 +00:00
if ( res ! = RESULT_SUCCESS ) {
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=0 \n " ) ;
2008-05-14 11:51:09 +00:00
} else {
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=1 \n " ) ;
2008-05-14 11:51:09 +00:00
}
2005-06-05 21:00:33 +00:00
return RESULT_SUCCESS ;
2001-12-27 11:07:33 +00:00
}
2003-02-23 06:00:11 +00:00
static int handle_sendimage ( struct ast_channel * chan , AGI * agi , int argc , char * argv [ ] )
2001-09-28 13:19:43 +00:00
{
int res ;
2007-07-11 21:09:42 +00:00
2008-05-14 11:51:09 +00:00
if ( argc ! = 3 ) {
2001-09-28 13:19:43 +00:00
return RESULT_SHOWUSAGE ;
2008-05-14 11:51:09 +00:00
}
2007-07-11 21:09:42 +00:00
2001-09-28 13:19:43 +00:00
res = ast_send_image ( chan , argv [ 2 ] ) ;
2008-05-14 11:51:09 +00:00
if ( ! ast_check_hangup ( chan ) ) {
2001-09-28 13:19:43 +00:00
res = 0 ;
2008-05-14 11:51:09 +00:00
}
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=%d \n " , res ) ;
2006-04-06 16:17:04 +00:00
return ( res > = 0 ) ? RESULT_SUCCESS : RESULT_FAILURE ;
2001-09-28 13:19:43 +00:00
}
2005-08-22 21:59:24 +00:00
static int handle_controlstreamfile ( struct ast_channel * chan , AGI * agi , int argc , char * argv [ ] )
{
2007-07-11 21:09:42 +00:00
int res = 0 , skipms = 3000 ;
2008-08-10 00:47:56 +00:00
char * fwd = " # " , * rev = " * " , * suspend = NULL , * stop = NULL ; /* Default values */
2005-08-22 21:59:24 +00:00
2008-05-14 11:51:09 +00:00
if ( argc < 5 | | argc > 9 ) {
2005-08-22 21:59:24 +00:00
return RESULT_SHOWUSAGE ;
2008-05-14 11:51:09 +00:00
}
2005-08-22 21:59:24 +00:00
2008-05-14 11:51:09 +00:00
if ( ! ast_strlen_zero ( argv [ 4 ] ) ) {
2005-08-22 21:59:24 +00:00
stop = argv [ 4 ] ;
2008-05-14 11:51:09 +00:00
}
2008-06-18 00:33:31 +00:00
2008-05-14 11:51:09 +00:00
if ( ( argc > 5 ) & & ( sscanf ( argv [ 5 ] , " %d " , & skipms ) ! = 1 ) ) {
2005-08-22 21:59:24 +00:00
return RESULT_SHOWUSAGE ;
2008-05-14 11:51:09 +00:00
}
2005-08-22 21:59:24 +00:00
2008-05-14 11:51:09 +00:00
if ( argc > 6 & & ! ast_strlen_zero ( argv [ 6 ] ) ) {
2005-08-22 21:59:24 +00:00
fwd = argv [ 6 ] ;
2008-06-18 00:33:31 +00:00
}
2005-08-22 21:59:24 +00:00
2008-05-14 11:51:09 +00:00
if ( argc > 7 & & ! ast_strlen_zero ( argv [ 7 ] ) ) {
2005-08-22 21:59:24 +00:00
rev = argv [ 7 ] ;
2008-05-14 11:51:09 +00:00
}
2008-06-18 00:33:31 +00:00
2008-05-14 11:51:09 +00:00
if ( argc > 8 & & ! ast_strlen_zero ( argv [ 8 ] ) ) {
2008-08-10 00:47:56 +00:00
suspend = argv [ 8 ] ;
2008-06-18 00:33:31 +00:00
}
2008-08-10 00:47:56 +00:00
res = ast_control_streamfile ( chan , argv [ 3 ] , fwd , rev , stop , suspend , NULL , skipms , NULL ) ;
2008-06-18 00:33:31 +00:00
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=%d \n " , res ) ;
2005-08-22 21:59:24 +00:00
2006-04-06 16:17:04 +00:00
return ( res > = 0 ) ? RESULT_SUCCESS : RESULT_FAILURE ;
2005-08-22 21:59:24 +00:00
}
2003-02-23 06:00:11 +00:00
static int handle_streamfile ( struct ast_channel * chan , AGI * agi , int argc , char * argv [ ] )
2001-09-28 13:19:43 +00:00
{
2007-07-11 21:09:42 +00:00
int res , vres ;
struct ast_filestream * fs , * vfs ;
long sample_offset = 0 , max_length ;
2006-07-20 16:18:07 +00:00
char * edigits = " " ;
2003-02-06 22:11:43 +00:00
2006-04-06 16:17:04 +00:00
if ( argc < 4 | | argc > 5 )
2001-09-28 13:19:43 +00:00
return RESULT_SHOWUSAGE ;
2006-07-20 16:18:07 +00:00
2008-06-18 00:33:31 +00:00
if ( argv [ 3 ] )
2006-07-20 16:18:07 +00:00
edigits = argv [ 3 ] ;
2003-02-06 22:11:43 +00:00
if ( ( argc > 4 ) & & ( sscanf ( argv [ 4 ] , " %ld " , & sample_offset ) ! = 1 ) )
return RESULT_SHOWUSAGE ;
2007-07-11 21:09:42 +00:00
if ( ! ( fs = ast_openstream ( chan , argv [ 2 ] , chan - > language ) ) ) {
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=%d endpos=%ld \n " , 0 , sample_offset ) ;
2004-12-31 21:52:56 +00:00
return RESULT_SUCCESS ;
2007-09-10 13:41:54 +00:00
}
if ( ( vfs = ast_openvstream ( chan , argv [ 2 ] , chan - > language ) ) )
ast_debug ( 1 , " Ooh, found a video stream, too \n " ) ;
2008-06-18 00:33:31 +00:00
2007-07-26 15:49:18 +00:00
ast_verb ( 3 , " Playing '%s' (escape_digits=%s) (sample_offset %ld) \n " , argv [ 2 ] , edigits , sample_offset ) ;
2006-07-20 16:18:07 +00:00
2003-02-06 22:11:43 +00:00
ast_seekstream ( fs , 0 , SEEK_END ) ;
max_length = ast_tellstream ( fs ) ;
ast_seekstream ( fs , sample_offset , SEEK_SET ) ;
res = ast_applystream ( chan , fs ) ;
2006-05-26 17:43:11 +00:00
if ( vfs )
vres = ast_applystream ( chan , vfs ) ;
2007-06-22 15:03:32 +00:00
ast_playstream ( fs ) ;
2006-05-26 17:43:11 +00:00
if ( vfs )
2007-06-22 15:03:32 +00:00
ast_playstream ( vfs ) ;
2008-06-18 00:33:31 +00:00
2003-02-23 06:00:11 +00:00
res = ast_waitstream_full ( chan , argv [ 3 ] , agi - > audio , agi - > ctrl ) ;
2003-02-06 22:11:43 +00:00
/* this is to check for if ast_waitstream closed the stream, we probably are at
* the end of the stream , return that amount , else check for the amount */
2005-01-08 19:35:20 +00:00
sample_offset = ( chan - > stream ) ? ast_tellstream ( fs ) : max_length ;
2002-06-14 15:50:42 +00:00
ast_stopstream ( chan ) ;
2003-02-23 06:00:11 +00:00
if ( res = = 1 ) {
/* Stop this command, don't print a result line, as there is a new command */
return RESULT_SUCCESS ;
}
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=%d endpos=%ld \n " , res , sample_offset ) ;
2006-04-06 16:17:04 +00:00
return ( res > = 0 ) ? RESULT_SUCCESS : RESULT_FAILURE ;
2001-09-28 13:19:43 +00:00
}
2008-02-14 10:19:09 +00:00
/*! \brief get option - really similar to the handle_streamfile, but with a timeout */
2004-11-16 03:37:07 +00:00
static int handle_getoption ( struct ast_channel * chan , AGI * agi , int argc , char * argv [ ] )
{
2007-07-11 21:09:42 +00:00
int res , vres ;
struct ast_filestream * fs , * vfs ;
long sample_offset = 0 , max_length ;
2004-11-16 03:37:07 +00:00
int timeout = 0 ;
2006-07-20 16:18:07 +00:00
char * edigits = " " ;
2004-11-16 03:37:07 +00:00
if ( argc < 4 | | argc > 5 )
return RESULT_SHOWUSAGE ;
2008-06-18 00:33:31 +00:00
if ( argv [ 3 ] )
2004-11-16 03:37:07 +00:00
edigits = argv [ 3 ] ;
if ( argc = = 5 )
timeout = atoi ( argv [ 4 ] ) ;
2008-05-01 23:06:23 +00:00
else if ( chan - > pbx - > dtimeoutms ) {
2004-11-16 03:37:07 +00:00
/* by default dtimeout is set to 5sec */
2008-05-01 23:06:23 +00:00
timeout = chan - > pbx - > dtimeoutms ; /* in msec */
2004-11-16 03:37:07 +00:00
}
2007-07-11 21:09:42 +00:00
if ( ! ( fs = ast_openstream ( chan , argv [ 2 ] , chan - > language ) ) ) {
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=%d endpos=%ld \n " , 0 , sample_offset ) ;
2006-05-26 17:43:11 +00:00
ast_log ( LOG_WARNING , " Unable to open %s \n " , argv [ 2 ] ) ;
2005-03-26 05:33:48 +00:00
return RESULT_SUCCESS ;
2006-05-26 17:43:11 +00:00
}
2007-09-10 13:41:54 +00:00
if ( ( vfs = ast_openvstream ( chan , argv [ 2 ] , chan - > language ) ) )
ast_debug ( 1 , " Ooh, found a video stream, too \n " ) ;
2008-06-18 00:33:31 +00:00
2007-07-26 15:49:18 +00:00
ast_verb ( 3 , " Playing '%s' (escape_digits=%s) (timeout %d) \n " , argv [ 2 ] , edigits , timeout ) ;
2004-11-16 03:37:07 +00:00
2006-05-26 17:43:11 +00:00
ast_seekstream ( fs , 0 , SEEK_END ) ;
max_length = ast_tellstream ( fs ) ;
ast_seekstream ( fs , sample_offset , SEEK_SET ) ;
res = ast_applystream ( chan , fs ) ;
if ( vfs )
vres = ast_applystream ( chan , vfs ) ;
2007-06-22 15:03:32 +00:00
ast_playstream ( fs ) ;
2006-05-26 17:43:11 +00:00
if ( vfs )
2007-06-22 15:03:32 +00:00
ast_playstream ( vfs ) ;
2006-05-26 17:43:11 +00:00
res = ast_waitstream_full ( chan , argv [ 3 ] , agi - > audio , agi - > ctrl ) ;
/* this is to check for if ast_waitstream closed the stream, we probably are at
* the end of the stream , return that amount , else check for the amount */
sample_offset = ( chan - > stream ) ? ast_tellstream ( fs ) : max_length ;
ast_stopstream ( chan ) ;
if ( res = = 1 ) {
/* Stop this command, don't print a result line, as there is a new command */
return RESULT_SUCCESS ;
}
2004-11-16 03:37:07 +00:00
/* If the user didnt press a key, wait for digitTimeout*/
if ( res = = 0 ) {
res = ast_waitfordigit_full ( chan , timeout , agi - > audio , agi - > ctrl ) ;
/* Make sure the new result is in the escape digits of the GET OPTION */
if ( ! strchr ( edigits , res ) )
2006-05-26 17:43:11 +00:00
res = 0 ;
2004-11-16 03:37:07 +00:00
}
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=%d endpos=%ld \n " , res , sample_offset ) ;
2006-04-06 16:17:04 +00:00
return ( res > = 0 ) ? RESULT_SUCCESS : RESULT_FAILURE ;
2004-11-16 03:37:07 +00:00
}
2008-02-14 10:19:09 +00:00
/*! \brief Say number in various language syntaxes */
2006-11-04 01:40:49 +00:00
/* While waiting, we're sending a NULL. */
2003-02-23 06:00:11 +00:00
static int handle_saynumber ( struct ast_channel * chan , AGI * agi , int argc , char * argv [ ] )
2001-09-28 13:19:43 +00:00
{
2007-07-11 21:09:42 +00:00
int res , num ;
2007-05-11 16:16:50 +00:00
if ( argc < 4 | | argc > 5 )
2001-09-28 13:19:43 +00:00
return RESULT_SHOWUSAGE ;
2005-03-29 06:21:21 +00:00
if ( sscanf ( argv [ 2 ] , " %d " , & num ) ! = 1 )
2001-09-28 13:19:43 +00:00
return RESULT_SHOWUSAGE ;
2007-05-11 16:16:50 +00:00
res = ast_say_number_full ( chan , num , argv [ 3 ] , chan - > language , argc > 4 ? argv [ 4 ] : NULL , agi - > audio , agi - > ctrl ) ;
2003-02-23 06:00:11 +00:00
if ( res = = 1 )
return RESULT_SUCCESS ;
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=%d \n " , res ) ;
2006-04-06 16:17:04 +00:00
return ( res > = 0 ) ? RESULT_SUCCESS : RESULT_FAILURE ;
2001-09-28 13:19:43 +00:00
}
2003-02-23 06:00:11 +00:00
static int handle_saydigits ( struct ast_channel * chan , AGI * agi , int argc , char * argv [ ] )
2002-06-14 15:50:42 +00:00
{
2007-07-11 21:09:42 +00:00
int res , num ;
2004-05-03 00:54:16 +00:00
2002-06-14 15:50:42 +00:00
if ( argc ! = 4 )
return RESULT_SHOWUSAGE ;
2005-04-27 01:42:33 +00:00
if ( sscanf ( argv [ 2 ] , " %d " , & num ) ! = 1 )
2002-06-14 15:50:42 +00:00
return RESULT_SHOWUSAGE ;
2004-05-03 00:54:16 +00:00
2003-02-23 06:00:11 +00:00
res = ast_say_digit_str_full ( chan , argv [ 2 ] , argv [ 3 ] , chan - > language , agi - > audio , agi - > ctrl ) ;
if ( res = = 1 ) /* New command */
return RESULT_SUCCESS ;
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=%d \n " , res ) ;
2006-04-06 16:17:04 +00:00
return ( res > = 0 ) ? RESULT_SUCCESS : RESULT_FAILURE ;
2002-06-14 15:50:42 +00:00
}
2004-12-14 18:39:25 +00:00
static int handle_sayalpha ( struct ast_channel * chan , AGI * agi , int argc , char * argv [ ] )
{
int res ;
if ( argc ! = 4 )
return RESULT_SHOWUSAGE ;
res = ast_say_character_str_full ( chan , argv [ 2 ] , argv [ 3 ] , chan - > language , agi - > audio , agi - > ctrl ) ;
if ( res = = 1 ) /* New command */
return RESULT_SUCCESS ;
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=%d \n " , res ) ;
2006-04-06 16:17:04 +00:00
return ( res > = 0 ) ? RESULT_SUCCESS : RESULT_FAILURE ;
2004-12-14 18:39:25 +00:00
}
2005-03-30 07:00:49 +00:00
static int handle_saydate ( struct ast_channel * chan , AGI * agi , int argc , char * argv [ ] )
{
2007-07-11 21:09:42 +00:00
int res , num ;
2005-03-30 07:00:49 +00:00
if ( argc ! = 4 )
return RESULT_SHOWUSAGE ;
2005-04-27 01:42:33 +00:00
if ( sscanf ( argv [ 2 ] , " %d " , & num ) ! = 1 )
2005-03-30 07:00:49 +00:00
return RESULT_SHOWUSAGE ;
res = ast_say_date ( chan , num , argv [ 3 ] , chan - > language ) ;
if ( res = = 1 )
return RESULT_SUCCESS ;
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=%d \n " , res ) ;
2006-04-06 16:17:04 +00:00
return ( res > = 0 ) ? RESULT_SUCCESS : RESULT_FAILURE ;
2005-03-30 07:00:49 +00:00
}
2004-02-27 08:30:30 +00:00
static int handle_saytime ( struct ast_channel * chan , AGI * agi , int argc , char * argv [ ] )
{
2007-07-11 21:09:42 +00:00
int res , num ;
2004-02-27 08:30:30 +00:00
if ( argc ! = 4 )
return RESULT_SHOWUSAGE ;
2005-04-27 01:42:33 +00:00
if ( sscanf ( argv [ 2 ] , " %d " , & num ) ! = 1 )
2004-02-27 08:30:30 +00:00
return RESULT_SHOWUSAGE ;
res = ast_say_time ( chan , num , argv [ 3 ] , chan - > language ) ;
if ( res = = 1 )
return RESULT_SUCCESS ;
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=%d \n " , res ) ;
2006-02-15 00:24:24 +00:00
return ( res > = 0 ) ? RESULT_SUCCESS : RESULT_FAILURE ;
2004-02-27 08:30:30 +00:00
}
2005-04-11 03:23:54 +00:00
static int handle_saydatetime ( struct ast_channel * chan , AGI * agi , int argc , char * argv [ ] )
{
2007-07-11 21:09:42 +00:00
int res = 0 ;
2006-02-15 00:24:24 +00:00
time_t unixtime ;
2007-07-11 21:09:42 +00:00
char * format , * zone = NULL ;
2008-06-18 00:33:31 +00:00
2005-04-11 03:23:54 +00:00
if ( argc < 4 )
return RESULT_SHOWUSAGE ;
if ( argc > 4 ) {
format = argv [ 4 ] ;
} else {
2006-04-06 16:17:04 +00:00
/* XXX this doesn't belong here, but in the 'say' module */
2005-04-11 03:23:54 +00:00
if ( ! strcasecmp ( chan - > language , " de " ) ) {
format = " A dBY HMS " ;
} else {
2008-06-18 00:33:31 +00:00
format = " ABdY 'digits/at' IMp " ;
2005-04-11 03:23:54 +00:00
}
}
if ( argc > 5 & & ! ast_strlen_zero ( argv [ 5 ] ) )
zone = argv [ 5 ] ;
2006-02-23 17:13:57 +00:00
if ( ast_get_time_t ( argv [ 2 ] , & unixtime , 0 , NULL ) )
2005-04-11 03:23:54 +00:00
return RESULT_SHOWUSAGE ;
2006-02-15 00:24:24 +00:00
res = ast_say_date_with_format ( chan , unixtime , argv [ 3 ] , chan - > language , format , zone ) ;
2005-04-11 03:23:54 +00:00
if ( res = = 1 )
return RESULT_SUCCESS ;
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=%d \n " , res ) ;
2006-02-15 00:24:24 +00:00
return ( res > = 0 ) ? RESULT_SUCCESS : RESULT_FAILURE ;
2005-04-11 03:23:54 +00:00
}
2004-05-03 00:54:16 +00:00
static int handle_sayphonetic ( struct ast_channel * chan , AGI * agi , int argc , char * argv [ ] )
{
int res ;
if ( argc ! = 4 )
return RESULT_SHOWUSAGE ;
res = ast_say_phonetic_str_full ( chan , argv [ 2 ] , argv [ 3 ] , chan - > language , agi - > audio , agi - > ctrl ) ;
if ( res = = 1 ) /* New command */
return RESULT_SUCCESS ;
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=%d \n " , res ) ;
2006-04-06 16:17:04 +00:00
return ( res > = 0 ) ? RESULT_SUCCESS : RESULT_FAILURE ;
2004-05-03 00:54:16 +00:00
}
2003-02-23 06:00:11 +00:00
static int handle_getdata ( struct ast_channel * chan , AGI * agi , int argc , char * argv [ ] )
2001-09-28 13:19:43 +00:00
{
2007-07-11 21:09:42 +00:00
int res , max , timeout ;
2003-02-14 06:00:11 +00:00
char data [ 1024 ] ;
2001-09-28 13:19:43 +00:00
if ( argc < 3 )
return RESULT_SHOWUSAGE ;
2005-01-08 19:35:20 +00:00
if ( argc > = 4 )
2008-06-18 00:33:31 +00:00
timeout = atoi ( argv [ 3 ] ) ;
2005-01-08 19:35:20 +00:00
else
timeout = 0 ;
2008-06-18 00:33:31 +00:00
if ( argc > = 5 )
max = atoi ( argv [ 4 ] ) ;
2005-01-08 19:35:20 +00:00
else
max = 1024 ;
2003-02-23 06:00:11 +00:00
res = ast_app_getdata_full ( chan , argv [ 2 ] , data , max , timeout , agi - > audio , agi - > ctrl ) ;
if ( res = = 2 ) /* New command */
return RESULT_SUCCESS ;
else if ( res = = 1 )
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=%s (timeout) \n " , data ) ;
2004-06-04 19:01:07 +00:00
else if ( res < 0 )
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=-1 \n " ) ;
2001-09-28 13:19:43 +00:00
else
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=%s \n " , data ) ;
2005-03-28 07:08:39 +00:00
return RESULT_SUCCESS ;
2001-09-28 13:19:43 +00:00
}
2003-02-23 06:00:11 +00:00
static int handle_setcontext ( struct ast_channel * chan , AGI * agi , int argc , char * argv [ ] )
2001-09-28 13:19:43 +00:00
{
if ( argc ! = 3 )
return RESULT_SHOWUSAGE ;
2005-07-10 23:21:39 +00:00
ast_copy_string ( chan - > context , argv [ 2 ] , sizeof ( chan - > context ) ) ;
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=0 \n " ) ;
2001-09-28 13:19:43 +00:00
return RESULT_SUCCESS ;
}
2008-06-18 00:33:31 +00:00
2003-02-23 06:00:11 +00:00
static int handle_setextension ( struct ast_channel * chan , AGI * agi , int argc , char * * argv )
2001-09-28 13:19:43 +00:00
{
if ( argc ! = 3 )
return RESULT_SHOWUSAGE ;
2005-07-10 23:21:39 +00:00
ast_copy_string ( chan - > exten , argv [ 2 ] , sizeof ( chan - > exten ) ) ;
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=0 \n " ) ;
2001-09-28 13:19:43 +00:00
return RESULT_SUCCESS ;
}
2003-02-23 06:00:11 +00:00
static int handle_setpriority ( struct ast_channel * chan , AGI * agi , int argc , char * * argv )
2001-09-28 13:19:43 +00:00
{
int pri ;
2007-07-11 21:09:42 +00:00
2001-09-28 13:19:43 +00:00
if ( argc ! = 3 )
2008-06-18 00:33:31 +00:00
return RESULT_SHOWUSAGE ;
2005-05-19 02:57:45 +00:00
if ( sscanf ( argv [ 2 ] , " %d " , & pri ) ! = 1 ) {
if ( ( pri = ast_findlabel_extension ( chan , chan - > context , chan - > exten , argv [ 2 ] , chan - > cid . cid_num ) ) < 1 )
return RESULT_SHOWUSAGE ;
}
ast_explicit_goto ( chan , NULL , NULL , pri ) ;
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=0 \n " ) ;
2001-09-28 13:19:43 +00:00
return RESULT_SUCCESS ;
}
2008-06-18 00:33:31 +00:00
2003-02-23 06:00:11 +00:00
static int handle_recordfile ( struct ast_channel * chan , AGI * agi , int argc , char * argv [ ] )
2001-09-28 13:19:43 +00:00
{
struct ast_filestream * fs ;
2002-09-04 16:53:39 +00:00
struct ast_frame * f ;
2005-07-15 23:00:47 +00:00
struct timeval start ;
2003-02-06 22:11:43 +00:00
long sample_offset = 0 ;
2002-09-04 16:53:39 +00:00
int res = 0 ;
int ms ;
2001-09-28 13:19:43 +00:00
2007-07-23 22:02:05 +00:00
struct ast_dsp * sildet = NULL ; /* silence detector dsp */
int totalsilence = 0 ;
int dspsilence = 0 ;
int silence = 0 ; /* amount of silence to allow */
int gotsilence = 0 ; /* did we timeout for silence? */
2008-06-18 00:33:31 +00:00
char * silencestr = NULL ;
int rfmt = 0 ;
2003-05-01 03:52:45 +00:00
2003-02-23 06:00:11 +00:00
/* XXX EAGI FIXME XXX */
2001-09-28 13:19:43 +00:00
if ( argc < 6 )
return RESULT_SHOWUSAGE ;
2005-04-27 01:42:33 +00:00
if ( sscanf ( argv [ 5 ] , " %d " , & ms ) ! = 1 )
2001-09-28 13:19:43 +00:00
return RESULT_SHOWUSAGE ;
2003-05-01 03:52:45 +00:00
if ( argc > 6 )
silencestr = strchr ( argv [ 6 ] , ' s ' ) ;
if ( ( argc > 7 ) & & ( ! silencestr ) )
silencestr = strchr ( argv [ 7 ] , ' s ' ) ;
if ( ( argc > 8 ) & & ( ! silencestr ) )
silencestr = strchr ( argv [ 8 ] , ' s ' ) ;
if ( silencestr ) {
if ( strlen ( silencestr ) > 2 ) {
if ( ( silencestr [ 0 ] = = ' s ' ) & & ( silencestr [ 1 ] = = ' = ' ) ) {
silencestr + + ;
silencestr + + ;
if ( silencestr )
2007-07-23 22:02:05 +00:00
silence = atoi ( silencestr ) ;
if ( silence > 0 )
silence * = 1000 ;
}
2003-05-01 03:52:45 +00:00
}
}
2007-07-23 22:02:05 +00:00
if ( silence > 0 ) {
rfmt = chan - > readformat ;
res = ast_set_read_format ( chan , AST_FORMAT_SLINEAR ) ;
if ( res < 0 ) {
ast_log ( LOG_WARNING , " Unable to set to linear mode, giving up \n " ) ;
return - 1 ;
}
sildet = ast_dsp_new ( ) ;
if ( ! sildet ) {
ast_log ( LOG_WARNING , " Unable to create silence detector :( \n " ) ;
return - 1 ;
}
2008-03-05 16:23:44 +00:00
ast_dsp_set_threshold ( sildet , ast_dsp_get_threshold_from_settings ( THRESHOLD_SILENCE ) ) ;
2007-07-23 22:02:05 +00:00
}
2008-06-18 00:33:31 +00:00
2003-02-06 22:11:43 +00:00
/* backward compatibility, if no offset given, arg[6] would have been
* caught below and taken to be a beep , else if it is a digit then it is a
* offset */
2003-05-01 03:52:45 +00:00
if ( ( argc > 6 ) & & ( sscanf ( argv [ 6 ] , " %ld " , & sample_offset ) ! = 1 ) & & ( ! strchr ( argv [ 6 ] , ' = ' ) ) )
2003-02-06 22:11:43 +00:00
res = ast_streamfile ( chan , " beep " , chan - > language ) ;
2001-09-28 13:19:43 +00:00
2003-05-01 03:52:45 +00:00
if ( ( argc > 7 ) & & ( ! strchr ( argv [ 7 ] , ' = ' ) ) )
2002-09-04 16:53:39 +00:00
res = ast_streamfile ( chan , " beep " , chan - > language ) ;
2003-05-01 03:52:45 +00:00
2002-09-04 16:53:39 +00:00
if ( ! res )
res = ast_waitstream ( chan , argv [ 4 ] ) ;
2005-07-15 23:00:47 +00:00
if ( res ) {
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=%d (randomerror) endpos=%ld \n " , res , sample_offset ) ;
2005-07-15 23:00:47 +00:00
} else {
2006-12-21 19:44:20 +00:00
fs = ast_writefile ( argv [ 2 ] , argv [ 3 ] , NULL , O_CREAT | O_WRONLY | ( sample_offset ? O_APPEND : 0 ) , 0 , AST_FILE_MODE ) ;
2002-09-04 16:53:39 +00:00
if ( ! fs ) {
res = - 1 ;
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=%d (writefile) \n " , res ) ;
2004-03-10 21:17:13 +00:00
if ( sildet )
ast_dsp_free ( sildet ) ;
2002-09-04 16:53:39 +00:00
return RESULT_FAILURE ;
}
2008-06-18 00:33:31 +00:00
2006-05-24 17:02:54 +00:00
/* Request a video update */
ast_indicate ( chan , AST_CONTROL_VIDUPDATE ) ;
2008-06-18 00:33:31 +00:00
2003-02-06 22:11:43 +00:00
chan - > stream = fs ;
ast_applystream ( chan , fs ) ;
/* really should have checks */
ast_seekstream ( fs , sample_offset , SEEK_SET ) ;
ast_truncstream ( fs ) ;
2008-06-18 00:33:31 +00:00
2005-07-15 23:00:47 +00:00
start = ast_tvnow ( ) ;
while ( ( ms < 0 ) | | ast_tvdiff_ms ( ast_tvnow ( ) , start ) < ms ) {
2008-12-11 16:52:24 +00:00
res = ast_waitfor ( chan , ms - ast_tvdiff_ms ( ast_tvnow ( ) , start ) ) ;
2002-09-04 16:53:39 +00:00
if ( res < 0 ) {
ast_closestream ( fs ) ;
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=%d (waitfor) endpos=%ld \n " , res , sample_offset ) ;
2004-03-10 21:17:13 +00:00
if ( sildet )
ast_dsp_free ( sildet ) ;
2001-09-28 13:19:43 +00:00
return RESULT_FAILURE ;
}
f = ast_read ( chan ) ;
if ( ! f ) {
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=%d (hangup) endpos=%ld \n " , - 1 , sample_offset ) ;
2002-09-04 16:53:39 +00:00
ast_closestream ( fs ) ;
2004-03-10 21:17:13 +00:00
if ( sildet )
ast_dsp_free ( sildet ) ;
2001-09-28 13:19:43 +00:00
return RESULT_FAILURE ;
}
switch ( f - > frametype ) {
case AST_FRAME_DTMF :
if ( strchr ( argv [ 4 ] , f - > subclass ) ) {
2005-10-04 22:35:43 +00:00
/* This is an interrupting chracter, so rewind to chop off any small
amount of DTMF that may have been recorded
*/
ast_stream_rewind ( fs , 200 ) ;
ast_truncstream ( fs ) ;
2003-02-06 22:11:43 +00:00
sample_offset = ast_tellstream ( fs ) ;
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=%d (dtmf) endpos=%ld \n " , f - > subclass , sample_offset ) ;
2002-09-04 16:53:39 +00:00
ast_closestream ( fs ) ;
2001-09-28 13:19:43 +00:00
ast_frfree ( f ) ;
2004-03-10 21:17:13 +00:00
if ( sildet )
ast_dsp_free ( sildet ) ;
2001-09-28 13:19:43 +00:00
return RESULT_SUCCESS ;
}
break ;
case AST_FRAME_VOICE :
2002-09-04 16:53:39 +00:00
ast_writestream ( fs , f ) ;
2003-02-06 22:11:43 +00:00
/* this is a safe place to check progress since we know that fs
* is valid after a write , and it will then have our current
* location */
sample_offset = ast_tellstream ( fs ) ;
2007-07-23 22:02:05 +00:00
if ( silence > 0 ) {
dspsilence = 0 ;
ast_dsp_silence ( sildet , f , & dspsilence ) ;
if ( dspsilence ) {
totalsilence = dspsilence ;
} else {
totalsilence = 0 ;
}
if ( totalsilence > silence ) {
/* Ended happily with silence */
gotsilence = 1 ;
break ;
}
}
2001-09-28 13:19:43 +00:00
break ;
2006-05-24 17:02:54 +00:00
case AST_FRAME_VIDEO :
ast_writestream ( fs , f ) ;
2006-08-31 01:59:02 +00:00
default :
/* Ignore all other frames */
2006-05-24 17:02:54 +00:00
break ;
2001-09-28 13:19:43 +00:00
}
ast_frfree ( f ) ;
2003-05-01 03:52:45 +00:00
if ( gotsilence )
break ;
2007-07-23 22:02:05 +00:00
}
2003-05-01 03:52:45 +00:00
2008-06-18 00:33:31 +00:00
if ( gotsilence ) {
ast_stream_rewind ( fs , silence - 1000 ) ;
ast_truncstream ( fs ) ;
sample_offset = ast_tellstream ( fs ) ;
}
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=%d (timeout) endpos=%ld \n " , res , sample_offset ) ;
2002-09-04 16:53:39 +00:00
ast_closestream ( fs ) ;
2005-07-15 23:00:47 +00:00
}
2003-05-01 03:52:45 +00:00
2007-07-23 22:02:05 +00:00
if ( silence > 0 ) {
res = ast_set_read_format ( chan , rfmt ) ;
if ( res )
ast_log ( LOG_WARNING , " Unable to restore read format on '%s' \n " , chan - > name ) ;
2008-06-18 00:33:31 +00:00
ast_dsp_free ( sildet ) ;
2007-07-23 22:02:05 +00:00
}
2008-06-18 00:33:31 +00:00
2002-09-04 16:53:39 +00:00
return RESULT_SUCCESS ;
}
2001-09-28 13:19:43 +00:00
2003-02-23 06:00:11 +00:00
static int handle_autohangup ( struct ast_channel * chan , AGI * agi , int argc , char * argv [ ] )
2002-09-04 16:53:39 +00:00
{
2008-05-01 23:06:23 +00:00
double timeout ;
struct timeval whentohangup = { 0 , 0 } ;
2001-09-28 13:19:43 +00:00
2002-09-04 16:53:39 +00:00
if ( argc ! = 3 )
return RESULT_SHOWUSAGE ;
2008-05-01 23:06:23 +00:00
if ( sscanf ( argv [ 2 ] , " %lf " , & timeout ) ! = 1 )
2002-09-04 16:53:39 +00:00
return RESULT_SHOWUSAGE ;
if ( timeout < 0 )
timeout = 0 ;
2008-05-01 23:06:23 +00:00
if ( timeout ) {
whentohangup . tv_sec = timeout ;
whentohangup . tv_usec = ( timeout - whentohangup . tv_sec ) * 1000000.0 ;
}
ast_channel_setwhentohangup_tv ( chan , whentohangup ) ;
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=0 \n " ) ;
2002-09-04 16:53:39 +00:00
return RESULT_SUCCESS ;
}
2003-02-23 06:00:11 +00:00
static int handle_hangup ( struct ast_channel * chan , AGI * agi , int argc , char * * argv )
2002-09-04 16:53:39 +00:00
{
2005-01-08 19:35:20 +00:00
struct ast_channel * c ;
2007-07-11 21:09:42 +00:00
2005-01-08 19:35:20 +00:00
if ( argc = = 1 ) {
/* no argument: hangup the current channel */
ast_softhangup ( chan , AST_SOFTHANGUP_EXPLICIT ) ;
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=1 \n " ) ;
2005-01-08 19:35:20 +00:00
return RESULT_SUCCESS ;
2005-06-06 02:29:18 +00:00
} else if ( argc = = 2 ) {
2005-01-08 19:35:20 +00:00
/* one argument: look for info on the specified channel */
2005-06-06 02:29:18 +00:00
c = ast_get_channel_by_name_locked ( argv [ 1 ] ) ;
if ( c ) {
/* we have a matching channel */
ast_softhangup ( c , AST_SOFTHANGUP_EXPLICIT ) ;
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=1 \n " ) ;
2006-04-29 14:50:18 +00:00
ast_channel_unlock ( c ) ;
2005-06-06 02:29:18 +00:00
return RESULT_SUCCESS ;
2005-01-08 19:35:20 +00:00
}
/* if we get this far no channel name matched the argument given */
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=-1 \n " ) ;
2005-01-08 19:35:20 +00:00
return RESULT_SUCCESS ;
} else {
return RESULT_SHOWUSAGE ;
}
2002-09-04 16:53:39 +00:00
}
2003-02-23 06:00:11 +00:00
static int handle_exec ( struct ast_channel * chan , AGI * agi , int argc , char * * argv )
2002-09-04 16:53:39 +00:00
{
int res ;
2008-08-10 00:47:56 +00:00
struct ast_app * app_to_exec ;
2002-09-04 16:53:39 +00:00
if ( argc < 2 )
return RESULT_SHOWUSAGE ;
2007-07-26 15:49:18 +00:00
ast_verb ( 3 , " AGI Script Executing Application: (%s) Options: (%s) \n " , argv [ 1 ] , argv [ 2 ] ) ;
2002-09-04 16:53:39 +00:00
2008-08-10 00:47:56 +00:00
if ( ( app_to_exec = pbx_findapp ( argv [ 1 ] ) ) ) {
2008-10-06 23:08:21 +00:00
if ( ! strcasecmp ( argv [ 1 ] , PARK_APP_NAME ) ) {
ast_masq_park_call ( chan , NULL , 0 , NULL ) ;
}
2008-06-03 22:05:16 +00:00
if ( ast_compat_res_agi & & ! ast_strlen_zero ( argv [ 2 ] ) ) {
char * compat = alloca ( strlen ( argv [ 2 ] ) * 2 + 1 ) , * cptr , * vptr ;
for ( cptr = compat , vptr = argv [ 2 ] ; * vptr ; vptr + + ) {
if ( * vptr = = ' , ' ) {
* cptr + + = ' \\ ' ;
* cptr + + = ' , ' ;
} else if ( * vptr = = ' | ' ) {
* cptr + + = ' , ' ;
} else {
* cptr + + = * vptr ;
}
}
* cptr = ' \0 ' ;
2008-08-10 00:47:56 +00:00
res = pbx_exec ( chan , app_to_exec , compat ) ;
2008-06-03 22:05:16 +00:00
} else {
2008-08-10 00:47:56 +00:00
res = pbx_exec ( chan , app_to_exec , argv [ 2 ] ) ;
2008-06-03 22:05:16 +00:00
}
2002-09-04 16:53:39 +00:00
} else {
ast_log ( LOG_WARNING , " Could not find application (%s) \n " , argv [ 1 ] ) ;
res = - 2 ;
2001-09-28 13:19:43 +00:00
}
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=%d \n " , res ) ;
2002-09-04 16:53:39 +00:00
2007-07-30 18:45:09 +00:00
/* Even though this is wrong, users are depending upon this result. */
return res ;
2002-09-04 16:53:39 +00:00
}
2003-02-23 06:00:11 +00:00
static int handle_setcallerid ( struct ast_channel * chan , AGI * agi , int argc , char * * argv )
2002-09-04 16:53:39 +00:00
{
2004-10-02 00:58:31 +00:00
char tmp [ 256 ] = " " ;
char * l = NULL , * n = NULL ;
2005-01-08 19:35:20 +00:00
2004-10-02 00:58:31 +00:00
if ( argv [ 2 ] ) {
2005-07-10 23:21:39 +00:00
ast_copy_string ( tmp , argv [ 2 ] , sizeof ( tmp ) ) ;
2004-10-02 00:58:31 +00:00
ast_callerid_parse ( tmp , & n , & l ) ;
if ( l )
ast_shrink_phone_number ( l ) ;
else
l = " " ;
if ( ! n )
n = " " ;
ast_set_callerid ( chan , l , n , NULL ) ;
}
2002-09-04 16:53:39 +00:00
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=1 \n " ) ;
2001-09-28 13:19:43 +00:00
return RESULT_SUCCESS ;
}
2003-02-23 06:00:11 +00:00
static int handle_channelstatus ( struct ast_channel * chan , AGI * agi , int argc , char * * argv )
2002-09-04 16:53:39 +00:00
{
2004-06-04 19:01:07 +00:00
struct ast_channel * c ;
2005-01-08 19:35:20 +00:00
if ( argc = = 2 ) {
2004-06-04 19:01:07 +00:00
/* no argument: supply info on the current channel */
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=%d \n " , chan - > _state ) ;
2004-06-04 19:01:07 +00:00
return RESULT_SUCCESS ;
2005-01-08 19:35:20 +00:00
} else if ( argc = = 3 ) {
2004-06-04 19:01:07 +00:00
/* one argument: look for info on the specified channel */
2005-06-06 02:29:18 +00:00
c = ast_get_channel_by_name_locked ( argv [ 2 ] ) ;
if ( c ) {
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=%d \n " , c - > _state ) ;
2006-04-29 14:50:18 +00:00
ast_channel_unlock ( c ) ;
2005-06-06 02:29:18 +00:00
return RESULT_SUCCESS ;
2004-06-04 19:01:07 +00:00
}
/* if we get this far no channel name matched the argument given */
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=-1 \n " ) ;
2004-06-04 19:01:07 +00:00
return RESULT_SUCCESS ;
} else {
return RESULT_SHOWUSAGE ;
}
2003-02-06 22:11:43 +00:00
}
2003-02-23 06:00:11 +00:00
static int handle_setvariable ( struct ast_channel * chan , AGI * agi , int argc , char * * argv )
2003-02-06 22:11:43 +00:00
{
if ( argv [ 3 ] )
pbx_builtin_setvar_helper ( chan , argv [ 2 ] , argv [ 3 ] ) ;
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=1 \n " ) ;
2003-02-06 22:11:43 +00:00
return RESULT_SUCCESS ;
}
2003-02-23 06:00:11 +00:00
static int handle_getvariable ( struct ast_channel * chan , AGI * agi , int argc , char * * argv )
2003-02-06 22:11:43 +00:00
{
2006-08-23 16:14:18 +00:00
char * ret ;
2004-12-23 02:47:01 +00:00
char tempstr [ 1024 ] ;
2005-01-08 19:35:20 +00:00
2004-11-17 04:02:56 +00:00
if ( argc ! = 3 )
return RESULT_SHOWUSAGE ;
2005-08-22 23:00:23 +00:00
/* check if we want to execute an ast_custom_function */
2006-08-23 16:14:18 +00:00
if ( ! ast_strlen_zero ( argv [ 2 ] ) & & ( argv [ 2 ] [ strlen ( argv [ 2 ] ) - 1 ] = = ' ) ' ) ) {
2006-02-12 04:28:58 +00:00
ret = ast_func_read ( chan , argv [ 2 ] , tempstr , sizeof ( tempstr ) ) ? NULL : tempstr ;
2006-08-23 16:14:18 +00:00
} else {
pbx_retrieve_variable ( chan , argv [ 2 ] , & ret , tempstr , sizeof ( tempstr ) , NULL ) ;
}
2005-08-22 23:00:23 +00:00
2004-12-23 02:47:01 +00:00
if ( ret )
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=1 (%s) \n " , ret ) ;
2003-02-06 22:11:43 +00:00
else
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=0 \n " ) ;
2003-02-06 22:11:43 +00:00
return RESULT_SUCCESS ;
}
2004-11-17 04:02:56 +00:00
static int handle_getvariablefull ( struct ast_channel * chan , AGI * agi , int argc , char * * argv )
{
This commits the performance mods that give the priority processing engine in the pbx, a 25-30% speed boost. The two updates used, are, first, to merge the ast_exists_extension() and the ast_spawn_extension() where they are called sequentially in a loop in the code, into a slightly upgraded version of ast_spawn_extension(), with a few extra args; and, second, I modified the substitute_variables_helper_full, so it zeroes out the byte after the evaluated string instead of demanding you pre-zero the buffer; I also went thru the code and removed the code that zeroed this buffer before every call to the substitute_variables_helper_full. The first fix provides about a 9% speedup, and the second the rest. These figures come from the 'PIPS' benchmark I describe in blogs, conf. reports, etc.
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@88166 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2007-11-01 22:26:51 +00:00
char tmp [ 4096 ] ;
2004-11-17 04:02:56 +00:00
struct ast_channel * chan2 = NULL ;
2005-01-08 19:35:20 +00:00
2004-11-17 04:02:56 +00:00
if ( ( argc ! = 4 ) & & ( argc ! = 5 ) )
return RESULT_SHOWUSAGE ;
if ( argc = = 5 ) {
2005-06-06 02:29:18 +00:00
chan2 = ast_get_channel_by_name_locked ( argv [ 4 ] ) ;
2004-11-17 04:02:56 +00:00
} else {
chan2 = chan ;
}
2007-09-12 21:57:30 +00:00
if ( chan2 ) {
2004-11-17 04:02:56 +00:00
pbx_substitute_variables_helper ( chan2 , argv [ 3 ] , tmp , sizeof ( tmp ) - 1 ) ;
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=1 (%s) \n " , tmp ) ;
2004-11-17 04:02:56 +00:00
} else {
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=0 \n " ) ;
2004-11-17 04:02:56 +00:00
}
if ( chan2 & & ( chan2 ! = chan ) )
2006-04-29 14:50:18 +00:00
ast_channel_unlock ( chan2 ) ;
2004-11-17 04:02:56 +00:00
return RESULT_SUCCESS ;
}
2003-02-23 06:00:11 +00:00
static int handle_verbose ( struct ast_channel * chan , AGI * agi , int argc , char * * argv )
2003-02-06 22:11:43 +00:00
{
int level = 0 ;
if ( argc < 2 )
return RESULT_SHOWUSAGE ;
if ( argv [ 2 ] )
sscanf ( argv [ 2 ] , " %d " , & level ) ;
2008-02-05 23:00:15 +00:00
ast_verb ( level , " %s: %s \n " , chan - > data , argv [ 1 ] ) ;
2008-06-18 00:33:31 +00:00
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=1 \n " ) ;
2008-06-18 00:33:31 +00:00
2002-09-04 16:53:39 +00:00
return RESULT_SUCCESS ;
}
2003-02-23 06:00:11 +00:00
static int handle_dbget ( struct ast_channel * chan , AGI * agi , int argc , char * * argv )
2003-02-06 22:11:43 +00:00
{
int res ;
char tmp [ 256 ] ;
2005-01-08 19:35:20 +00:00
2003-02-06 22:11:43 +00:00
if ( argc ! = 4 )
return RESULT_SHOWUSAGE ;
res = ast_db_get ( argv [ 2 ] , argv [ 3 ] , tmp , sizeof ( tmp ) ) ;
2008-06-18 00:33:31 +00:00
if ( res )
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=0 \n " ) ;
2003-02-06 22:11:43 +00:00
else
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=1 (%s) \n " , tmp ) ;
2003-02-06 22:11:43 +00:00
return RESULT_SUCCESS ;
}
2003-02-23 06:00:11 +00:00
static int handle_dbput ( struct ast_channel * chan , AGI * agi , int argc , char * * argv )
2003-02-06 22:11:43 +00:00
{
int res ;
2005-01-08 19:35:20 +00:00
2003-02-06 22:11:43 +00:00
if ( argc ! = 5 )
return RESULT_SHOWUSAGE ;
res = ast_db_put ( argv [ 2 ] , argv [ 3 ] , argv [ 4 ] ) ;
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=%c \n " , res ? ' 0 ' : ' 1 ' ) ;
2003-02-06 22:11:43 +00:00
return RESULT_SUCCESS ;
}
2003-02-23 06:00:11 +00:00
static int handle_dbdel ( struct ast_channel * chan , AGI * agi , int argc , char * * argv )
2003-02-06 22:11:43 +00:00
{
int res ;
2005-01-08 19:35:20 +00:00
2003-02-06 22:11:43 +00:00
if ( argc ! = 4 )
return RESULT_SHOWUSAGE ;
res = ast_db_del ( argv [ 2 ] , argv [ 3 ] ) ;
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=%c \n " , res ? ' 0 ' : ' 1 ' ) ;
2003-02-06 22:11:43 +00:00
return RESULT_SUCCESS ;
}
2003-02-23 06:00:11 +00:00
static int handle_dbdeltree ( struct ast_channel * chan , AGI * agi , int argc , char * * argv )
2003-02-06 22:11:43 +00:00
{
int res ;
2007-07-11 21:09:42 +00:00
2003-02-06 22:11:43 +00:00
if ( ( argc < 3 ) | | ( argc > 4 ) )
return RESULT_SHOWUSAGE ;
if ( argc = = 4 )
res = ast_db_deltree ( argv [ 2 ] , argv [ 3 ] ) ;
else
res = ast_db_deltree ( argv [ 2 ] , NULL ) ;
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=%c \n " , res ? ' 0 ' : ' 1 ' ) ;
2003-02-23 06:00:11 +00:00
return RESULT_SUCCESS ;
}
2007-09-18 22:43:45 +00:00
static char * handle_cli_agi_debug ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a )
2004-09-25 14:22:27 +00:00
{
2007-09-18 22:43:45 +00:00
switch ( cmd ) {
case CLI_INIT :
2008-02-08 21:26:32 +00:00
e - > command = " agi set debug [on|off] " ;
2007-09-18 22:43:45 +00:00
e - > usage =
2008-02-08 21:26:32 +00:00
" Usage: agi set debug [on|off] \n "
2007-09-18 22:43:45 +00:00
" Enables/disables dumping of AGI transactions for \n "
" debugging purposes. \n " ;
return NULL ;
case CLI_GENERATE :
return NULL ;
}
2008-02-08 21:26:32 +00:00
if ( a - > argc ! = e - > args )
2007-09-18 22:43:45 +00:00
return CLI_SHOWUSAGE ;
2008-02-08 21:26:32 +00:00
if ( strncasecmp ( a - > argv [ 3 ] , " off " , 3 ) = = 0 ) {
agidebug = 0 ;
} else if ( strncasecmp ( a - > argv [ 3 ] , " on " , 2 ) = = 0 ) {
2007-09-18 22:43:45 +00:00
agidebug = 1 ;
} else {
2008-02-08 21:26:32 +00:00
return CLI_SHOWUSAGE ;
2007-09-18 22:43:45 +00:00
}
ast_cli ( a - > fd , " AGI Debugging %sabled \n " , agidebug ? " En " : " Dis " ) ;
return CLI_SUCCESS ;
2006-09-18 19:54:18 +00:00
}
2004-09-25 14:22:27 +00:00
2003-02-23 06:00:11 +00:00
static int handle_noop ( struct ast_channel * chan , AGI * agi , int arg , char * argv [ ] )
{
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=0 \n " ) ;
2003-02-06 22:11:43 +00:00
return RESULT_SUCCESS ;
}
2003-06-11 12:17:43 +00:00
static int handle_setmusic ( struct ast_channel * chan , AGI * agi , int argc , char * argv [ ] )
{
2006-04-06 16:17:04 +00:00
if ( ! strncasecmp ( argv [ 2 ] , " on " , 2 ) )
2006-07-19 20:44:39 +00:00
ast_moh_start ( chan , argc > 3 ? argv [ 3 ] : NULL , NULL ) ;
2006-04-06 16:17:04 +00:00
else if ( ! strncasecmp ( argv [ 2 ] , " off " , 3 ) )
2003-06-11 12:17:43 +00:00
ast_moh_stop ( chan ) ;
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=0 \n " ) ;
2003-06-11 12:17:43 +00:00
return RESULT_SUCCESS ;
}
2007-12-03 21:03:05 +00:00
static int handle_speechcreate ( struct ast_channel * chan , AGI * agi , int argc , char * * argv )
{
/* If a structure already exists, return an error */
if ( agi - > speech ) {
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=0 \n " ) ;
2007-12-03 21:03:05 +00:00
return RESULT_SUCCESS ;
}
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
if ( ( agi - > speech = ast_speech_new ( argv [ 2 ] , AST_FORMAT_SLINEAR ) ) )
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=1 \n " ) ;
2007-12-03 21:03:05 +00:00
else
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=0 \n " ) ;
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
return RESULT_SUCCESS ;
}
static int handle_speechset ( struct ast_channel * chan , AGI * agi , int argc , char * * argv )
{
/* Check for minimum arguments */
if ( argc ! = 3 )
return RESULT_SHOWUSAGE ;
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
/* Check to make sure speech structure exists */
if ( ! agi - > speech ) {
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=0 \n " ) ;
2007-12-03 21:03:05 +00:00
return RESULT_SUCCESS ;
}
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
ast_speech_change ( agi - > speech , argv [ 2 ] , argv [ 3 ] ) ;
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=1 \n " ) ;
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
return RESULT_SUCCESS ;
}
static int handle_speechdestroy ( struct ast_channel * chan , AGI * agi , int argc , char * * argv )
{
if ( agi - > speech ) {
ast_speech_destroy ( agi - > speech ) ;
agi - > speech = NULL ;
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=1 \n " ) ;
2007-12-03 21:03:05 +00:00
} else {
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=0 \n " ) ;
2007-12-03 21:03:05 +00:00
}
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
return RESULT_SUCCESS ;
}
static int handle_speechloadgrammar ( struct ast_channel * chan , AGI * agi , int argc , char * * argv )
{
if ( argc ! = 5 )
return RESULT_SHOWUSAGE ;
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
if ( ! agi - > speech ) {
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=0 \n " ) ;
2007-12-03 21:03:05 +00:00
return RESULT_SUCCESS ;
}
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
if ( ast_speech_grammar_load ( agi - > speech , argv [ 3 ] , argv [ 4 ] ) )
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=0 \n " ) ;
2007-12-03 21:03:05 +00:00
else
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=1 \n " ) ;
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
return RESULT_SUCCESS ;
}
static int handle_speechunloadgrammar ( struct ast_channel * chan , AGI * agi , int argc , char * * argv )
{
if ( argc ! = 4 )
return RESULT_SHOWUSAGE ;
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
if ( ! agi - > speech ) {
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=0 \n " ) ;
2007-12-03 21:03:05 +00:00
return RESULT_SUCCESS ;
}
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
if ( ast_speech_grammar_unload ( agi - > speech , argv [ 3 ] ) )
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=0 \n " ) ;
2007-12-03 21:03:05 +00:00
else
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=1 \n " ) ;
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
return RESULT_SUCCESS ;
}
static int handle_speechactivategrammar ( struct ast_channel * chan , AGI * agi , int argc , char * * argv )
{
if ( argc ! = 4 )
return RESULT_SHOWUSAGE ;
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
if ( ! agi - > speech ) {
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=0 \n " ) ;
2007-12-03 21:03:05 +00:00
return RESULT_SUCCESS ;
}
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
if ( ast_speech_grammar_activate ( agi - > speech , argv [ 3 ] ) )
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=0 \n " ) ;
2007-12-03 21:03:05 +00:00
else
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=1 \n " ) ;
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
return RESULT_SUCCESS ;
}
static int handle_speechdeactivategrammar ( struct ast_channel * chan , AGI * agi , int argc , char * * argv )
{
if ( argc ! = 4 )
return RESULT_SHOWUSAGE ;
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
if ( ! agi - > speech ) {
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=0 \n " ) ;
2007-12-03 21:03:05 +00:00
return RESULT_SUCCESS ;
}
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
if ( ast_speech_grammar_deactivate ( agi - > speech , argv [ 3 ] ) )
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=0 \n " ) ;
2007-12-03 21:03:05 +00:00
else
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=1 \n " ) ;
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
return RESULT_SUCCESS ;
}
static int speech_streamfile ( struct ast_channel * chan , const char * filename , const char * preflang , int offset )
{
struct ast_filestream * fs = NULL ;
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
if ( ! ( fs = ast_openstream ( chan , filename , preflang ) ) )
return - 1 ;
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
if ( offset )
ast_seekstream ( fs , offset , SEEK_SET ) ;
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
if ( ast_applystream ( chan , fs ) )
return - 1 ;
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
if ( ast_playstream ( fs ) )
return - 1 ;
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
return 0 ;
}
static int handle_speechrecognize ( struct ast_channel * chan , AGI * agi , int argc , char * * argv )
{
struct ast_speech * speech = agi - > speech ;
char * prompt , dtmf = 0 , tmp [ 4096 ] = " " , * buf = tmp ;
int timeout = 0 , offset = 0 , old_read_format = 0 , res = 0 , i = 0 ;
long current_offset = 0 ;
const char * reason = NULL ;
struct ast_frame * fr = NULL ;
struct ast_speech_result * result = NULL ;
size_t left = sizeof ( tmp ) ;
time_t start = 0 , current ;
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
if ( argc < 4 )
return RESULT_SHOWUSAGE ;
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
if ( ! speech ) {
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=0 \n " ) ;
2007-12-03 21:03:05 +00:00
return RESULT_SUCCESS ;
}
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
prompt = argv [ 2 ] ;
timeout = atoi ( argv [ 3 ] ) ;
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
/* If offset is specified then convert from text to integer */
if ( argc = = 5 )
offset = atoi ( argv [ 4 ] ) ;
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
/* We want frames coming in signed linear */
old_read_format = chan - > readformat ;
if ( ast_set_read_format ( chan , AST_FORMAT_SLINEAR ) ) {
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=0 \n " ) ;
2007-12-03 21:03:05 +00:00
return RESULT_SUCCESS ;
}
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
/* Setup speech structure */
if ( speech - > state = = AST_SPEECH_STATE_NOT_READY | | speech - > state = = AST_SPEECH_STATE_DONE ) {
ast_speech_change_state ( speech , AST_SPEECH_STATE_NOT_READY ) ;
ast_speech_start ( speech ) ;
}
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
/* Start playing prompt */
speech_streamfile ( chan , prompt , chan - > language , offset ) ;
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
/* Go into loop reading in frames, passing to speech thingy, checking for hangup, all that jazz */
while ( ast_strlen_zero ( reason ) ) {
/* Run scheduled items */
ast_sched_runq ( chan - > sched ) ;
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
/* See maximum time of waiting */
if ( ( res = ast_sched_wait ( chan - > sched ) ) < 0 )
res = 1000 ;
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
/* Wait for frame */
if ( ast_waitfor ( chan , res ) > 0 ) {
if ( ! ( fr = ast_read ( chan ) ) ) {
reason = " hangup " ;
break ;
}
}
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
/* Perform timeout check */
if ( ( timeout > 0 ) & & ( start > 0 ) ) {
time ( & current ) ;
if ( ( current - start ) > = timeout ) {
reason = " timeout " ;
if ( fr )
ast_frfree ( fr ) ;
break ;
}
}
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
/* Check the speech structure for any changes */
ast_mutex_lock ( & speech - > lock ) ;
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
/* See if we need to quiet the audio stream playback */
if ( ast_test_flag ( speech , AST_SPEECH_QUIET ) & & chan - > stream ) {
current_offset = ast_tellstream ( chan - > stream ) ;
ast_stopstream ( chan ) ;
ast_clear_flag ( speech , AST_SPEECH_QUIET ) ;
}
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
/* Check each state */
switch ( speech - > state ) {
case AST_SPEECH_STATE_READY :
/* If the stream is done, start timeout calculation */
if ( ( timeout > 0 ) & & ( ( ! chan - > stream ) | | ( chan - > streamid = = - 1 & & chan - > timingfunc = = NULL ) ) ) {
ast_stopstream ( chan ) ;
time ( & start ) ;
}
/* Write audio frame data into speech engine if possible */
if ( fr & & fr - > frametype = = AST_FRAME_VOICE )
2008-05-22 16:29:54 +00:00
ast_speech_write ( speech , fr - > data . ptr , fr - > datalen ) ;
2007-12-03 21:03:05 +00:00
break ;
case AST_SPEECH_STATE_WAIT :
/* Cue waiting sound if not already playing */
if ( ( ! chan - > stream ) | | ( chan - > streamid = = - 1 & & chan - > timingfunc = = NULL ) ) {
ast_stopstream ( chan ) ;
/* If a processing sound exists, or is not none - play it */
if ( ! ast_strlen_zero ( speech - > processing_sound ) & & strcasecmp ( speech - > processing_sound , " none " ) )
speech_streamfile ( chan , speech - > processing_sound , chan - > language , 0 ) ;
}
break ;
case AST_SPEECH_STATE_DONE :
/* Get the results */
speech - > results = ast_speech_results_get ( speech ) ;
/* Change state to not ready */
ast_speech_change_state ( speech , AST_SPEECH_STATE_NOT_READY ) ;
reason = " speech " ;
break ;
default :
break ;
}
ast_mutex_unlock ( & speech - > lock ) ;
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
/* Check frame for DTMF or hangup */
if ( fr ) {
if ( fr - > frametype = = AST_FRAME_DTMF ) {
reason = " dtmf " ;
dtmf = fr - > subclass ;
} else if ( fr - > frametype = = AST_FRAME_CONTROL & & fr - > subclass = = AST_CONTROL_HANGUP ) {
reason = " hangup " ;
}
ast_frfree ( fr ) ;
}
}
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
if ( ! strcasecmp ( reason , " speech " ) ) {
/* Build string containing speech results */
for ( result = speech - > results ; result ; result = AST_LIST_NEXT ( result , list ) ) {
/* Build result string */
ast_build_string ( & buf , & left , " %sscore%d=%d text%d= \" %s \" grammar%d=%s " , ( i > 0 ? " " : " " ) , i , result - > score , i , result - > text , i , result - > grammar ) ;
/* Increment result count */
i + + ;
}
/* Print out */
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=1 (speech) endpos=%ld results=%d %s \n " , current_offset , i , tmp ) ;
2007-12-03 21:03:05 +00:00
} else if ( ! strcasecmp ( reason , " dtmf " ) ) {
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=1 (digit) digit=%c endpos=%ld \n " , dtmf , current_offset ) ;
2007-12-03 21:03:05 +00:00
} else if ( ! strcasecmp ( reason , " hangup " ) | | ! strcasecmp ( reason , " timeout " ) ) {
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=1 (%s) endpos=%ld \n " , reason , current_offset ) ;
2007-12-03 21:03:05 +00:00
} else {
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 200 result=0 endpos=%ld \n " , current_offset ) ;
2007-12-03 21:03:05 +00:00
}
2008-06-18 00:33:31 +00:00
2007-12-03 21:03:05 +00:00
return RESULT_SUCCESS ;
}
2003-02-06 22:11:43 +00:00
static char usage_verbose [ ] =
" Usage: VERBOSE <message> <level> \n "
" Sends <message> to the console via verbose message system. \n "
2004-12-28 23:01:22 +00:00
" <level> is the the verbose level (1-4) \n "
" Always returns 1. \n " ;
2003-02-06 22:11:43 +00:00
static char usage_setvariable [ ] =
" Usage: SET VARIABLE <variablename> <value> \n " ;
2002-09-04 16:53:39 +00:00
static char usage_setcallerid [ ] =
" Usage: SET CALLERID <number> \n "
" Changes the callerid of the current channel. \n " ;
2008-06-18 00:33:31 +00:00
static char usage_waitfordigit [ ] =
2001-09-28 13:19:43 +00:00
" Usage: WAIT FOR DIGIT <timeout> \n "
2004-12-28 23:01:22 +00:00
" Waits up to 'timeout' milliseconds for channel to receive a DTMF digit. \n "
2001-09-28 13:19:43 +00:00
" Returns -1 on channel failure, 0 if no digit is received in the timeout, or \n "
" the numerical value of the ascii of the digit if one is received. Use -1 \n "
" for the timeout value if you desire the call to block indefinitely. \n " ;
static char usage_sendtext [ ] =
" Usage: SEND TEXT \" <text to send> \" \n "
2005-01-08 19:35:20 +00:00
" Sends the given text on a channel. Most channels do not support the \n "
2001-09-28 13:19:43 +00:00
" transmission of text. Returns 0 if text is sent, or if the channel does not \n "
" support text transmission. Returns -1 only on error/hangup. Text \n "
" consisting of greater than one word should be placed in quotes since the \n "
" command only accepts a single argument. \n " ;
2001-12-27 11:07:33 +00:00
static char usage_recvchar [ ] =
" Usage: RECEIVE CHAR <timeout> \n "
2005-01-08 19:35:20 +00:00
" Receives a character of text on a channel. Specify timeout to be the \n "
2001-12-27 11:07:33 +00:00
" maximum time to wait for input in milliseconds, or 0 for infinite. Most channels \n "
2005-01-08 19:35:20 +00:00
" do not support the reception of text. Returns the decimal value of the character \n "
2001-12-27 11:07:33 +00:00
" if one is received, or 0 if the channel does not support text reception. Returns \n "
" -1 only on error/hangup. \n " ;
2005-06-21 01:16:18 +00:00
static char usage_recvtext [ ] =
2005-08-15 02:45:34 +00:00
" Usage: RECEIVE TEXT <timeout> \n "
2005-06-21 01:16:18 +00:00
" Receives a string of text on a channel. Specify timeout to be the \n "
" maximum time to wait for input in milliseconds, or 0 for infinite. Most channels \n "
" do not support the reception of text. Returns -1 for failure or 1 for success, and the string in parentheses. \n " ;
2001-12-27 11:07:33 +00:00
static char usage_tddmode [ ] =
" Usage: TDD MODE <on|off> \n "
2004-12-28 23:01:22 +00:00
" Enable/Disable TDD transmission/reception on a channel. Returns 1 if \n "
2001-12-27 11:07:33 +00:00
" successful, or 0 if channel is not TDD-capable. \n " ;
2001-09-28 13:19:43 +00:00
static char usage_sendimage [ ] =
" Usage: SEND IMAGE <image> \n "
2005-01-08 19:35:20 +00:00
" Sends the given image on a channel. Most channels do not support the \n "
" transmission of images. Returns 0 if image is sent, or if the channel does not \n "
" support image transmission. Returns -1 only on error/hangup. Image names \n "
2001-09-28 13:19:43 +00:00
" should not include extensions. \n " ;
static char usage_streamfile [ ] =
2003-02-06 22:11:43 +00:00
" Usage: STREAM FILE <filename> <escape digits> [sample offset] \n "
2004-12-28 23:01:22 +00:00
" Send the given file, allowing playback to be interrupted by the given \n "
2005-01-08 19:35:20 +00:00
" digits, if any. Use double quotes for the digits if you wish none to be \n "
" permitted. If sample offset is provided then the audio will seek to sample \n "
2003-02-06 22:11:43 +00:00
" offset before play starts. Returns 0 if playback completes without a digit \n "
" being pressed, or the ASCII numerical value of the digit if one was pressed, \n "
2005-01-08 19:35:20 +00:00
" or -1 on error or if the channel was disconnected. Remember, the file \n "
2003-02-06 22:11:43 +00:00
" extension must not be included in the filename. \n " ;
2001-09-28 13:19:43 +00:00
2005-08-22 21:59:24 +00:00
static char usage_controlstreamfile [ ] =
" Usage: CONTROL STREAM FILE <filename> <escape digits> [skipms] [ffchar] [rewchr] [pausechr] \n "
" Send the given file, allowing playback to be controled by the given \n "
" digits, if any. Use double quotes for the digits if you wish none to be \n "
" permitted. Returns 0 if playback completes without a digit \n "
" being pressed, or the ASCII numerical value of the digit if one was pressed, \n "
" or -1 on error or if the channel was disconnected. Remember, the file \n "
" extension must not be included in the filename. \n \n "
" Note: ffchar and rewchar default to * and # respectively. \n " ;
2001-09-28 13:19:43 +00:00
static char usage_saynumber [ ] =
2007-05-11 16:16:50 +00:00
" Usage: SAY NUMBER <number> <escape digits> [gender] \n "
2004-12-28 23:01:22 +00:00
" Say a given number, returning early if any of the given DTMF digits \n "
2001-09-28 13:19:43 +00:00
" are received on the channel. Returns 0 if playback completes without a digit \n "
" being pressed, or the ASCII numerical value of the digit if one was pressed or \n "
" -1 on error/hangup. \n " ;
2002-06-14 15:50:42 +00:00
static char usage_saydigits [ ] =
" Usage: SAY DIGITS <number> <escape digits> \n "
2004-12-28 23:01:22 +00:00
" Say a given digit string, returning early if any of the given DTMF digits \n "
2005-01-08 19:35:20 +00:00
" are received on the channel. Returns 0 if playback completes without a digit \n "
2002-06-14 15:50:42 +00:00
" being pressed, or the ASCII numerical value of the digit if one was pressed or \n "
" -1 on error/hangup. \n " ;
2004-12-14 18:39:25 +00:00
static char usage_sayalpha [ ] =
" Usage: SAY ALPHA <number> <escape digits> \n "
2004-12-28 23:01:22 +00:00
" Say a given character string, returning early if any of the given DTMF digits \n "
2005-01-08 19:35:20 +00:00
" are received on the channel. Returns 0 if playback completes without a digit \n "
2004-12-14 18:39:25 +00:00
" being pressed, or the ASCII numerical value of the digit if one was pressed or \n "
" -1 on error/hangup. \n " ;
2005-03-30 07:00:49 +00:00
static char usage_saydate [ ] =
" Usage: SAY DATE <date> <escape digits> \n "
" Say a given date, returning early if any of the given DTMF digits are \n "
" received on the channel. <date> is number of seconds elapsed since 00:00:00 \n "
" on January 1, 1970, Coordinated Universal Time (UTC). Returns 0 if playback \n "
" completes without a digit being pressed, or the ASCII numerical value of the \n "
" digit if one was pressed or -1 on error/hangup. \n " ;
2004-02-27 08:30:30 +00:00
static char usage_saytime [ ] =
" Usage: SAY TIME <time> <escape digits> \n "
2004-12-28 23:01:22 +00:00
" Say a given time, returning early if any of the given DTMF digits are \n "
2004-02-27 08:30:30 +00:00
" received on the channel. <time> is number of seconds elapsed since 00:00:00 \n "
2005-01-08 19:35:20 +00:00
" on January 1, 1970, Coordinated Universal Time (UTC). Returns 0 if playback \n "
2004-02-27 08:30:30 +00:00
" completes without a digit being pressed, or the ASCII numerical value of the \n "
" digit if one was pressed or -1 on error/hangup. \n " ;
2005-04-11 03:23:54 +00:00
static char usage_saydatetime [ ] =
" Usage: SAY DATETIME <time> <escape digits> [format] [timezone] \n "
" Say a given time, returning early if any of the given DTMF digits are \n "
" received on the channel. <time> is number of seconds elapsed since 00:00:00 \n "
" on January 1, 1970, Coordinated Universal Time (UTC). [format] is the format \n "
" the time should be said in. See voicemail.conf (defaults to \" ABdY \n "
" 'digits/at' IMp \" ). Acceptable values for [timezone] can be found in \n "
" /usr/share/zoneinfo. Defaults to machine default. Returns 0 if playback \n "
" completes without a digit being pressed, or the ASCII numerical value of the \n "
" digit if one was pressed or -1 on error/hangup. \n " ;
2004-05-03 00:54:16 +00:00
static char usage_sayphonetic [ ] =
" Usage: SAY PHONETIC <string> <escape digits> \n "
2004-12-28 23:01:22 +00:00
" Say a given character string with phonetics, returning early if any of the \n "
2005-01-08 19:35:20 +00:00
" given DTMF digits are received on the channel. Returns 0 if playback \n "
2004-12-28 23:01:22 +00:00
" completes without a digit pressed, the ASCII numerical value of the digit \n "
" if one was pressed, or -1 on error/hangup. \n " ;
2004-05-03 00:54:16 +00:00
2001-09-28 13:19:43 +00:00
static char usage_setcontext [ ] =
" Usage: SET CONTEXT <desired context> \n "
2004-12-28 23:01:22 +00:00
" Sets the context for continuation upon exiting the application. \n " ;
2001-09-28 13:19:43 +00:00
static char usage_setextension [ ] =
" Usage: SET EXTENSION <new extension> \n "
2004-12-28 23:01:22 +00:00
" Changes the extension for continuation upon exiting the application. \n " ;
2001-09-28 13:19:43 +00:00
static char usage_setpriority [ ] =
2005-05-19 02:57:45 +00:00
" Usage: SET PRIORITY <priority> \n "
" Changes the priority for continuation upon exiting the application. \n "
" The priority must be a valid priority or label. \n " ;
2001-09-28 13:19:43 +00:00
static char usage_recordfile [ ] =
2004-12-28 23:01:22 +00:00
" Usage: RECORD FILE <filename> <format> <escape digits> <timeout> \\ \n "
" [offset samples] [BEEP] [s=silence] \n "
" Record to a file until a given dtmf digit in the sequence is received \n "
2001-09-28 13:19:43 +00:00
" Returns -1 on hangup or error. The format will specify what kind of file \n "
" will be recorded. The timeout is the maximum record time in milliseconds, or \n "
2004-12-28 23:01:22 +00:00
" -1 for no timeout. \" Offset samples \" is optional, and, if provided, will seek \n "
" to the offset without exceeding the end of the file. \" silence \" is the number \n "
2003-05-01 03:52:45 +00:00
" of seconds of silence allowed before the function returns despite the \n "
" lack of dtmf digits or reaching timeout. Silence value must be \n "
2004-12-28 23:01:22 +00:00
" preceeded by \" s= \" and is also optional. \n " ;
2001-09-28 13:19:43 +00:00
2002-09-04 16:53:39 +00:00
static char usage_autohangup [ ] =
" Usage: SET AUTOHANGUP <time> \n "
2004-12-28 23:01:22 +00:00
" Cause the channel to automatically hangup at <time> seconds in the \n "
2005-01-08 19:35:20 +00:00
" future. Of course it can be hungup before then as well. Setting to 0 will \n "
2004-12-28 23:01:22 +00:00
" cause the autohangup feature to be disabled on this channel. \n " ;
2002-09-04 16:53:39 +00:00
2007-12-03 21:03:05 +00:00
static char usage_speechcreate [ ] =
" Usage: SPEECH CREATE <engine> \n "
" Create a speech object to be used by the other Speech AGI commands. \n " ;
static char usage_speechset [ ] =
" Usage: SPEECH SET <name> <value> \n "
" Set an engine-specific setting. \n " ;
static char usage_speechdestroy [ ] =
" Usage: SPEECH DESTROY \n "
" Destroy the speech object created by SPEECH CREATE. \n " ;
static char usage_speechloadgrammar [ ] =
" Usage: SPEECH LOAD GRAMMAR <grammar name> <path to grammar> \n "
" Loads the specified grammar as the specified name. \n " ;
static char usage_speechunloadgrammar [ ] =
" Usage: SPEECH UNLOAD GRAMMAR <grammar name> \n "
" Unloads the specified grammar. \n " ;
static char usage_speechactivategrammar [ ] =
" Usage: SPEECH ACTIVATE GRAMMAR <grammar name> \n "
" Activates the specified grammar on the speech object. \n " ;
static char usage_speechdeactivategrammar [ ] =
" Usage: SPEECH DEACTIVATE GRAMMAR <grammar name> \n "
" Deactivates the specified grammar on the speech object. \n " ;
static char usage_speechrecognize [ ] =
" Usage: SPEECH RECOGNIZE <prompt> <timeout> [<offset>] \n "
" Plays back given prompt while listening for speech and dtmf. \n " ;
2007-07-02 19:01:16 +00:00
/*!
2007-07-23 22:02:05 +00:00
* \ brief AGI commands list
2007-07-02 19:01:16 +00:00
*/
2007-07-30 19:11:28 +00:00
static struct agi_command commands [ ] = {
2008-11-12 00:17:43 +00:00
{ { " answer " , NULL } , handle_answer , NULL , NULL , 0 } ,
{ { " channel " , " status " , NULL } , handle_channelstatus , NULL , NULL , 0 } ,
2008-11-12 02:20:05 +00:00
{ { " database " , " del " , NULL } , handle_dbdel , NULL , NULL , 1 } ,
2008-11-12 13:43:46 +00:00
{ { " database " , " deltree " , NULL } , handle_dbdeltree , NULL , NULL , 1 } ,
{ { " database " , " get " , NULL } , handle_dbget , NULL , NULL , 1 } ,
2008-11-12 02:20:05 +00:00
{ { " database " , " put " , NULL } , handle_dbput , NULL , NULL , 1 } ,
2008-11-12 13:43:46 +00:00
{ { " exec " , NULL } , handle_exec , NULL , NULL , 1 } ,
{ { " get " , " data " , NULL } , handle_getdata , NULL , NULL , 0 } ,
{ { " get " , " full " , " variable " , NULL } , handle_getvariablefull , NULL , NULL , 1 } ,
2008-12-04 13:45:32 +00:00
{ { " get " , " option " , NULL } , handle_getoption , NULL , NULL , 0 } ,
{ { " get " , " variable " , NULL } , handle_getvariable , NULL , NULL , 1 } ,
{ { " hangup " , NULL } , handle_hangup , NULL , NULL , 0 } ,
{ { " noop " , NULL } , handle_noop , NULL , NULL , 1 } ,
2007-07-23 22:02:05 +00:00
{ { " receive " , " char " , NULL } , handle_recvchar , " Receives one character from channels supporting it " , usage_recvchar , 0 } ,
{ { " receive " , " text " , NULL } , handle_recvtext , " Receives text from channels supporting it " , usage_recvtext , 0 } ,
{ { " record " , " file " , NULL } , handle_recordfile , " Records to a given file " , usage_recordfile , 0 } ,
{ { " say " , " alpha " , NULL } , handle_sayalpha , " Says a given character string " , usage_sayalpha , 0 } ,
{ { " say " , " digits " , NULL } , handle_saydigits , " Says a given digit string " , usage_saydigits , 0 } ,
{ { " say " , " number " , NULL } , handle_saynumber , " Says a given number " , usage_saynumber , 0 } ,
{ { " say " , " phonetic " , NULL } , handle_sayphonetic , " Says a given character string with phonetics " , usage_sayphonetic , 0 } ,
{ { " say " , " date " , NULL } , handle_saydate , " Says a given date " , usage_saydate , 0 } ,
{ { " say " , " time " , NULL } , handle_saytime , " Says a given time " , usage_saytime , 0 } ,
{ { " say " , " datetime " , NULL } , handle_saydatetime , " Says a given time as specfied by the format given " , usage_saydatetime , 0 } ,
{ { " send " , " image " , NULL } , handle_sendimage , " Sends images to channels supporting it " , usage_sendimage , 0 } ,
{ { " send " , " text " , NULL } , handle_sendtext , " Sends text to channels supporting it " , usage_sendtext , 0 } ,
{ { " set " , " autohangup " , NULL } , handle_autohangup , " Autohangup channel in some time " , usage_autohangup , 0 } ,
{ { " set " , " callerid " , NULL } , handle_setcallerid , " Sets callerid for the current channel " , usage_setcallerid , 0 } ,
{ { " set " , " context " , NULL } , handle_setcontext , " Sets channel context " , usage_setcontext , 0 } ,
{ { " set " , " extension " , NULL } , handle_setextension , " Changes channel extension " , usage_setextension , 0 } ,
2008-11-12 02:20:05 +00:00
{ { " set " , " music " , NULL } , handle_setmusic , NULL , NULL , 0 } ,
2007-07-23 22:02:05 +00:00
{ { " set " , " priority " , NULL } , handle_setpriority , " Set channel dialplan priority " , usage_setpriority , 0 } ,
{ { " set " , " variable " , NULL } , handle_setvariable , " Sets a channel variable " , usage_setvariable , 1 } ,
{ { " stream " , " file " , NULL } , handle_streamfile , " Sends audio file on channel " , usage_streamfile , 0 } ,
{ { " control " , " stream " , " file " , NULL } , handle_controlstreamfile , " Sends audio file on channel and allows the listner to control the stream " , usage_controlstreamfile , 0 } ,
{ { " tdd " , " mode " , NULL } , handle_tddmode , " Toggles TDD mode (for the deaf) " , usage_tddmode , 0 } ,
{ { " verbose " , NULL } , handle_verbose , " Logs a message to the asterisk verbose log " , usage_verbose , 1 } ,
{ { " wait " , " for " , " digit " , NULL } , handle_waitfordigit , " Waits for a digit to be pressed " , usage_waitfordigit , 0 } ,
2007-12-03 21:03:05 +00:00
{ { " speech " , " create " , NULL } , handle_speechcreate , " Creates a speech object " , usage_speechcreate , 0 } ,
{ { " speech " , " set " , NULL } , handle_speechset , " Sets a speech engine setting " , usage_speechset , 0 } ,
{ { " speech " , " destroy " , NULL } , handle_speechdestroy , " Destroys a speech object " , usage_speechdestroy , 1 } ,
{ { " speech " , " load " , " grammar " , NULL } , handle_speechloadgrammar , " Loads a grammar " , usage_speechloadgrammar , 0 } ,
{ { " speech " , " unload " , " grammar " , NULL } , handle_speechunloadgrammar , " Unloads a grammar " , usage_speechunloadgrammar , 1 } ,
{ { " speech " , " activate " , " grammar " , NULL } , handle_speechactivategrammar , " Activates a grammar " , usage_speechactivategrammar , 0 } ,
{ { " speech " , " deactivate " , " grammar " , NULL } , handle_speechdeactivategrammar , " Deactivates a grammar " , usage_speechdeactivategrammar , 0 } ,
{ { " speech " , " recognize " , NULL } , handle_speechrecognize , " Recognizes speech " , usage_speechrecognize , 0 } ,
2001-09-28 13:19:43 +00:00
} ;
2007-07-23 23:14:20 +00:00
static AST_RWLIST_HEAD_STATIC ( agi_commands , agi_command ) ;
2007-07-23 22:02:05 +00:00
2007-09-18 22:43:45 +00:00
static char * help_workhorse ( int fd , char * match [ ] )
2003-02-06 22:11:43 +00:00
{
2008-11-12 00:17:43 +00:00
char fullcmd [ MAX_CMD_LEN ] , matchstr [ MAX_CMD_LEN ] ;
2003-02-06 22:11:43 +00:00
struct agi_command * e ;
2007-07-11 21:09:42 +00:00
2003-02-06 22:11:43 +00:00
if ( match )
2005-12-20 20:20:04 +00:00
ast_join ( matchstr , sizeof ( matchstr ) , match ) ;
2007-07-11 21:09:42 +00:00
2007-12-03 21:03:05 +00:00
ast_cli ( fd , " %5.5s %30.30s %s \n " , " Dead " , " Command " , " Description " ) ;
2007-07-23 22:02:05 +00:00
AST_RWLIST_RDLOCK ( & agi_commands ) ;
AST_RWLIST_TRAVERSE ( & agi_commands , e , list ) {
2005-12-20 20:20:04 +00:00
if ( ! e - > cmda [ 0 ] )
break ;
2003-02-06 22:11:43 +00:00
/* Hide commands that start with '_' */
2005-12-20 20:20:04 +00:00
if ( ( e - > cmda [ 0 ] ) [ 0 ] = = ' _ ' )
continue ;
ast_join ( fullcmd , sizeof ( fullcmd ) , e - > cmda ) ;
if ( match & & strncasecmp ( matchstr , fullcmd , strlen ( matchstr ) ) )
2003-02-06 22:11:43 +00:00
continue ;
2007-12-03 21:03:05 +00:00
ast_cli ( fd , " %5.5s %30.30s %s \n " , e - > dead ? " Yes " : " No " , fullcmd , e - > summary ) ;
2003-02-06 22:11:43 +00:00
}
2007-07-23 22:02:05 +00:00
AST_RWLIST_UNLOCK ( & agi_commands ) ;
2007-09-18 22:43:45 +00:00
return CLI_SUCCESS ;
2003-02-06 22:11:43 +00:00
}
2007-07-30 19:11:28 +00:00
int ast_agi_register ( struct ast_module * mod , agi_command * cmd )
2004-07-17 20:25:39 +00:00
{
2008-11-12 00:17:43 +00:00
char fullcmd [ MAX_CMD_LEN ] ;
2007-07-30 19:11:28 +00:00
ast_join ( fullcmd , sizeof ( fullcmd ) , cmd - > cmda ) ;
2008-11-12 00:17:43 +00:00
if ( ! find_command ( cmd - > cmda , 1 ) ) {
cmd - > docsrc = AST_STATIC_DOC ;
# ifdef AST_XML_DOCS
if ( ast_strlen_zero ( cmd - > summary ) & & ast_strlen_zero ( cmd - > usage ) ) {
cmd - > summary = ast_xmldoc_build_synopsis ( " agi " , fullcmd ) ;
cmd - > usage = ast_xmldoc_build_description ( " agi " , fullcmd ) ;
cmd - > syntax = ast_xmldoc_build_syntax ( " agi " , fullcmd ) ;
cmd - > seealso = ast_xmldoc_build_seealso ( " agi " , fullcmd ) ;
cmd - > docsrc = AST_XML_DOC ;
}
# endif
2007-08-01 04:36:15 +00:00
cmd - > mod = mod ;
2007-07-23 22:02:05 +00:00
AST_RWLIST_WRLOCK ( & agi_commands ) ;
2007-07-30 19:11:28 +00:00
AST_LIST_INSERT_TAIL ( & agi_commands , cmd , list ) ;
2007-07-23 22:02:05 +00:00
AST_RWLIST_UNLOCK ( & agi_commands ) ;
2007-07-30 19:11:28 +00:00
if ( mod ! = ast_module_info - > self )
ast_module_ref ( ast_module_info - > self ) ;
ast_verb ( 2 , " AGI Command '%s' registered \n " , fullcmd ) ;
2007-07-23 22:02:05 +00:00
return 1 ;
} else {
ast_log ( LOG_WARNING , " Command already registered! \n " ) ;
return 0 ;
2004-07-17 20:25:39 +00:00
}
}
2007-07-30 19:11:28 +00:00
int ast_agi_unregister ( struct ast_module * mod , agi_command * cmd )
2004-07-17 20:25:39 +00:00
{
2007-07-23 22:02:05 +00:00
struct agi_command * e ;
int unregistered = 0 ;
2008-11-12 00:17:43 +00:00
char fullcmd [ MAX_CMD_LEN ] ;
2008-06-18 00:33:31 +00:00
2007-07-30 19:11:28 +00:00
ast_join ( fullcmd , sizeof ( fullcmd ) , cmd - > cmda ) ;
2007-07-23 22:02:05 +00:00
AST_RWLIST_WRLOCK ( & agi_commands ) ;
AST_RWLIST_TRAVERSE_SAFE_BEGIN ( & agi_commands , e , list ) {
if ( cmd = = e ) {
2007-11-08 05:28:47 +00:00
AST_RWLIST_REMOVE_CURRENT ( list ) ;
2007-07-30 19:11:28 +00:00
if ( mod ! = ast_module_info - > self )
ast_module_unref ( ast_module_info - > self ) ;
2008-11-12 00:17:43 +00:00
# ifdef AST_XML_DOCS
if ( e - > docsrc = = AST_XML_DOC ) {
ast_free ( e - > summary ) ;
ast_free ( e - > usage ) ;
ast_free ( e - > syntax ) ;
ast_free ( e - > seealso ) ;
e - > summary = NULL , e - > usage = NULL ;
e - > syntax = NULL , e - > seealso = NULL ;
}
# endif
2007-07-23 22:02:05 +00:00
unregistered = 1 ;
break ;
2004-07-17 20:25:39 +00:00
}
}
2007-11-08 05:28:47 +00:00
AST_RWLIST_TRAVERSE_SAFE_END ;
2007-07-23 22:02:05 +00:00
AST_RWLIST_UNLOCK ( & agi_commands ) ;
2007-07-30 19:11:28 +00:00
if ( unregistered )
ast_verb ( 2 , " AGI Command '%s' unregistered \n " , fullcmd ) ;
else
ast_log ( LOG_WARNING , " Unable to unregister command: '%s'! \n " , fullcmd ) ;
2007-07-23 22:02:05 +00:00
return unregistered ;
2004-07-17 20:25:39 +00:00
}
2008-11-19 12:42:19 +00:00
int ast_agi_register_multiple ( struct ast_module * mod , struct agi_command * cmd , unsigned int len )
2007-07-30 19:11:28 +00:00
{
2008-11-19 12:42:19 +00:00
unsigned int i , x = 0 ;
2007-07-30 19:11:28 +00:00
2008-11-19 12:42:19 +00:00
for ( i = 0 ; i < len ; i + + ) {
if ( ast_agi_register ( mod , cmd + i ) = = 1 ) {
x + + ;
continue ;
}
/* registration failed, unregister everything
that had been registered up to that point
*/
for ( ; x > 0 ; x - - ) {
/* we are intentionally ignoring the
result of ast_agi_unregister ( ) here ,
but it should be safe to do so since
we just registered these commands and
the only possible way for unregistration
to fail is if the command is not
registered
*/
( void ) ast_agi_unregister ( mod , cmd + x - 1 ) ;
}
return - 1 ;
}
2007-07-30 19:11:28 +00:00
2008-11-19 12:42:19 +00:00
return 0 ;
2007-07-30 19:11:28 +00:00
}
2008-11-19 12:42:19 +00:00
int ast_agi_unregister_multiple ( struct ast_module * mod , struct agi_command * cmd , unsigned int len )
2007-07-30 19:11:28 +00:00
{
2008-11-19 12:42:19 +00:00
unsigned int i ;
int res = 0 ;
2007-07-30 19:11:28 +00:00
2008-11-19 12:42:19 +00:00
for ( i = 0 ; i < len ; i + + ) {
/* remember whether any of the unregistration
attempts failed . . . there is no recourse if
any of them do
*/
res | = ast_agi_unregister ( mod , cmd + i ) ;
}
return res ;
2007-07-30 19:11:28 +00:00
}
2003-02-06 22:11:43 +00:00
static agi_command * find_command ( char * cmds [ ] , int exact )
2001-09-28 13:19:43 +00:00
{
2007-07-23 22:02:05 +00:00
int y , match ;
struct agi_command * e ;
2005-01-08 19:35:20 +00:00
2007-07-23 22:02:05 +00:00
AST_RWLIST_RDLOCK ( & agi_commands ) ;
AST_RWLIST_TRAVERSE ( & agi_commands , e , list ) {
if ( ! e - > cmda [ 0 ] )
2005-01-08 19:35:20 +00:00
break ;
2007-08-01 04:36:15 +00:00
/* start optimistic */
match = 1 ;
for ( y = 0 ; match & & cmds [ y ] ; y + + ) {
/* If there are no more words in the command (and we're looking for
an exact match ) or there is a difference between the two words ,
then this is not a match */
if ( ! e - > cmda [ y ] & & ! exact )
break ;
/* don't segfault if the next part of a command doesn't exist */
2008-07-15 18:46:40 +00:00
if ( ! e - > cmda [ y ] ) {
AST_RWLIST_UNLOCK ( & agi_commands ) ;
2007-08-01 04:36:15 +00:00
return NULL ;
2008-07-15 18:46:40 +00:00
}
2007-08-01 04:36:15 +00:00
if ( strcasecmp ( e - > cmda [ y ] , cmds [ y ] ) )
2001-09-28 13:19:43 +00:00
match = 0 ;
2007-08-01 04:36:15 +00:00
}
/* If more words are needed to complete the command then this is not
a candidate ( unless we ' re looking for a really inexact answer */
if ( ( exact > - 1 ) & & e - > cmda [ y ] )
match = 0 ;
2008-07-15 18:46:40 +00:00
if ( match ) {
AST_RWLIST_UNLOCK ( & agi_commands ) ;
2007-08-01 04:36:15 +00:00
return e ;
2008-07-15 18:46:40 +00:00
}
2001-09-28 13:19:43 +00:00
}
2007-07-23 22:02:05 +00:00
AST_RWLIST_UNLOCK ( & agi_commands ) ;
2001-09-28 13:19:43 +00:00
return NULL ;
}
static int parse_args ( char * s , int * max , char * argv [ ] )
{
2007-07-11 21:09:42 +00:00
int x = 0 , quoted = 0 , escaped = 0 , whitespace = 1 ;
2001-09-28 13:19:43 +00:00
char * cur ;
cur = s ;
while ( * s ) {
switch ( * s ) {
case ' " ' :
/* If it's escaped, put a literal quote */
2008-06-18 00:33:31 +00:00
if ( escaped )
2001-09-28 13:19:43 +00:00
goto normal ;
2008-06-18 00:33:31 +00:00
else
2001-09-28 13:19:43 +00:00
quoted = ! quoted ;
if ( quoted & & whitespace ) {
/* If we're starting a quote, coming off white space start a new word, too */
argv [ x + + ] = cur ;
whitespace = 0 ;
}
escaped = 0 ;
break ;
case ' ' :
case ' \t ' :
if ( ! quoted & & ! escaped ) {
/* If we're not quoted, mark this as whitespace, and
end the previous argument */
whitespace = 1 ;
* ( cur + + ) = ' \0 ' ;
} else
2008-06-18 00:33:31 +00:00
/* Otherwise, just treat it as anything else */
2001-09-28 13:19:43 +00:00
goto normal ;
break ;
case ' \\ ' :
/* If we're escaped, print a literal, otherwise enable escaping */
if ( escaped ) {
goto normal ;
} else {
escaped = 1 ;
}
break ;
default :
normal :
if ( whitespace ) {
if ( x > = MAX_ARGS - 1 ) {
ast_log ( LOG_WARNING , " Too many arguments, truncating \n " ) ;
break ;
}
/* Coming off of whitespace, start the next argument */
argv [ x + + ] = cur ;
whitespace = 0 ;
}
* ( cur + + ) = * s ;
escaped = 0 ;
}
s + + ;
}
/* Null terminate */
* ( cur + + ) = ' \0 ' ;
argv [ x ] = NULL ;
* max = x ;
return 0 ;
}
2007-07-23 22:02:05 +00:00
static int agi_handle_command ( struct ast_channel * chan , AGI * agi , char * buf , int dead )
2001-09-28 13:19:43 +00:00
{
char * argv [ MAX_ARGS ] ;
2007-07-11 21:09:42 +00:00
int argc = MAX_ARGS , res ;
2001-09-28 13:19:43 +00:00
agi_command * c ;
2007-12-14 17:29:27 +00:00
const char * ami_res = " Unknown Result " ;
char * ami_cmd = ast_strdupa ( buf ) ;
int command_id = ast_random ( ) , resultcode = 200 ;
2008-11-24 21:56:22 +00:00
manager_event ( EVENT_FLAG_AGI , " AGIExec " ,
2007-12-14 17:29:27 +00:00
" SubEvent: Start \r \n "
" Channel: %s \r \n "
" CommandId: %d \r \n "
" Command: %s \r \n " , chan - > name , command_id , ami_cmd ) ;
2001-09-28 13:19:43 +00:00
parse_args ( buf , & argc , argv ) ;
2007-07-23 22:02:05 +00:00
if ( ( c = find_command ( argv , 0 ) ) & & ( ! dead | | ( dead & & c - > dead ) ) ) {
2007-07-30 19:11:28 +00:00
/* if this command wasnt registered by res_agi, be sure to usecount
the module we are using */
if ( c - > mod ! = ast_module_info - > self )
ast_module_ref ( c - > mod ) ;
2003-02-23 06:00:11 +00:00
res = c - > handler ( chan , agi , argc , argv ) ;
2007-07-30 19:11:28 +00:00
if ( c - > mod ! = ast_module_info - > self )
ast_module_unref ( c - > mod ) ;
2007-12-14 17:29:27 +00:00
switch ( res ) {
case RESULT_SHOWUSAGE : ami_res = " Usage " ; resultcode = 520 ; break ;
case RESULT_FAILURE : ami_res = " Failure " ; resultcode = - 1 ; break ;
case RESULT_SUCCESS : ami_res = " Success " ; resultcode = 200 ; break ;
}
2008-11-24 21:56:22 +00:00
manager_event ( EVENT_FLAG_AGI , " AGIExec " ,
2007-12-14 17:29:27 +00:00
" SubEvent: End \r \n "
" Channel: %s \r \n "
" CommandId: %d \r \n "
" Command: %s \r \n "
" ResultCode: %d \r \n "
" Result: %s \r \n " , chan - > name , command_id , ami_cmd , resultcode , ami_res ) ;
2001-09-28 13:19:43 +00:00
switch ( res ) {
case RESULT_SHOWUSAGE :
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 520-Invalid command syntax. Proper usage follows: \n " ) ;
2008-11-19 13:45:48 +00:00
ast_agi_send ( agi - > fd , chan , " %s " , c - > usage ) ;
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 520 End of proper usage. \n " ) ;
2001-09-28 13:19:43 +00:00
break ;
case RESULT_FAILURE :
/* They've already given the failure. We've been hung up on so handle this
appropriately */
return - 1 ;
}
2007-07-23 22:02:05 +00:00
} else if ( ( c = find_command ( argv , 0 ) ) ) {
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 511 Command Not Permitted on a dead channel \n " ) ;
2008-11-24 21:56:22 +00:00
manager_event ( EVENT_FLAG_AGI , " AGIExec " ,
2007-12-14 17:29:27 +00:00
" SubEvent: End \r \n "
" Channel: %s \r \n "
" CommandId: %d \r \n "
" Command: %s \r \n "
" ResultCode: 511 \r \n "
" Result: Command not permitted on a dead channel \r \n " , chan - > name , command_id , ami_cmd ) ;
2001-09-28 13:19:43 +00:00
} else {
2008-11-19 12:42:19 +00:00
ast_agi_send ( agi - > fd , chan , " 510 Invalid or unknown command \n " ) ;
2008-11-24 21:56:22 +00:00
manager_event ( EVENT_FLAG_AGI , " AGIExec " ,
2007-12-14 17:29:27 +00:00
" SubEvent: End \r \n "
" Channel: %s \r \n "
" CommandId: %d \r \n "
" Command: %s \r \n "
" ResultCode: 510 \r \n "
" Result: Invalid or unknown command \r \n " , chan - > name , command_id , ami_cmd ) ;
2001-09-28 13:19:43 +00:00
}
return 0 ;
}
2006-10-17 23:06:13 +00:00
static enum agi_result run_agi ( struct ast_channel * chan , char * request , AGI * agi , int pid , int * status , int dead , int argc , char * argv [ ] )
2001-09-28 13:19:43 +00:00
{
struct ast_channel * c ;
2007-07-23 22:02:05 +00:00
int outfd , ms , needhup = 0 ;
2006-05-25 16:44:22 +00:00
enum agi_result returnstatus = AGI_RESULT_SUCCESS ;
2001-09-28 13:19:43 +00:00
struct ast_frame * f ;
2007-09-11 15:28:46 +00:00
char buf [ AGI_BUF_LEN ] ;
2007-09-18 22:46:05 +00:00
char * res = NULL ;
2001-09-28 13:19:43 +00:00
FILE * readf ;
2008-06-18 00:33:31 +00:00
/* how many times we'll retry if ast_waitfor_nandfs will return without either
2004-03-03 18:58:57 +00:00
channel or file descriptor in case select is interrupted by a system call ( EINTR ) */
2007-09-11 15:28:46 +00:00
int retry = AGI_NANDFS_RETRY ;
2008-08-09 01:15:38 +00:00
const char * sighup ;
2003-11-05 23:43:31 +00:00
2003-02-23 06:00:11 +00:00
if ( ! ( readf = fdopen ( agi - > ctrl , " r " ) ) ) {
2001-09-28 13:19:43 +00:00
ast_log ( LOG_WARNING , " Unable to fdopen file descriptor \n " ) ;
2004-09-22 15:21:36 +00:00
if ( pid > - 1 )
kill ( pid , SIGHUP ) ;
2004-04-23 04:38:11 +00:00
close ( agi - > ctrl ) ;
2006-05-25 16:44:22 +00:00
return AGI_RESULT_FAILURE ;
2001-09-28 13:19:43 +00:00
}
setlinebuf ( readf ) ;
2006-10-17 23:06:13 +00:00
setup_env ( chan , request , agi - > fd , ( agi - > audio > - 1 ) , argc , argv ) ;
2001-09-28 13:19:43 +00:00
for ( ; ; ) {
2007-07-23 22:02:05 +00:00
if ( needhup ) {
needhup = 0 ;
dead = 1 ;
2008-08-09 01:15:38 +00:00
if ( pid > - 1 ) {
2007-12-10 16:07:33 +00:00
kill ( pid , SIGHUP ) ;
2008-08-09 01:15:38 +00:00
} else if ( agi - > fast ) {
send ( agi - > ctrl , " HANGUP \n " , 7 , MSG_OOB ) ;
}
2007-07-23 22:02:05 +00:00
}
2001-09-28 13:19:43 +00:00
ms = - 1 ;
2004-03-03 18:58:57 +00:00
c = ast_waitfor_nandfds ( & chan , dead ? 0 : 1 , & agi - > ctrl , 1 , NULL , & outfd , & ms ) ;
2001-09-28 13:19:43 +00:00
if ( c ) {
2007-09-11 15:28:46 +00:00
retry = AGI_NANDFS_RETRY ;
2001-09-28 13:19:43 +00:00
/* Idle the channel until we get a command */
f = ast_read ( c ) ;
if ( ! f ) {
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , " %s hungup \n " , chan - > name ) ;
2006-05-25 16:44:22 +00:00
returnstatus = AGI_RESULT_HANGUP ;
2007-07-23 22:02:05 +00:00
needhup = 1 ;
continue ;
2001-09-28 13:19:43 +00:00
} else {
2003-02-23 06:00:11 +00:00
/* If it's voice, write it to the audio pipe */
if ( ( agi - > audio > - 1 ) & & ( f - > frametype = = AST_FRAME_VOICE ) ) {
/* Write, ignoring errors */
2008-11-02 18:52:13 +00:00
if ( write ( agi - > audio , f - > data . ptr , f - > datalen ) < 0 ) {
}
2003-02-23 06:00:11 +00:00
}
2001-09-28 13:19:43 +00:00
ast_frfree ( f ) ;
}
} else if ( outfd > - 1 ) {
2007-09-18 22:46:05 +00:00
size_t len = sizeof ( buf ) ;
size_t buflen = 0 ;
2007-09-11 15:28:46 +00:00
retry = AGI_NANDFS_RETRY ;
2007-08-22 19:54:52 +00:00
buf [ 0 ] = ' \0 ' ;
2007-09-11 15:28:46 +00:00
2007-09-18 22:46:05 +00:00
while ( buflen < ( len - 1 ) ) {
res = fgets ( buf + buflen , len , readf ) ;
2008-06-18 00:33:31 +00:00
if ( feof ( readf ) )
2007-09-18 22:46:05 +00:00
break ;
2008-06-18 00:33:31 +00:00
if ( ferror ( readf ) & & ( ( errno ! = EINTR ) & & ( errno ! = EAGAIN ) ) )
2007-09-18 22:46:05 +00:00
break ;
if ( res ! = NULL & & ! agi - > fast )
break ;
buflen = strlen ( buf ) ;
2007-10-01 19:58:29 +00:00
if ( buflen & & buf [ buflen - 1 ] = = ' \n ' )
break ;
2007-09-18 22:46:05 +00:00
len - = buflen ;
if ( agidebug )
ast_verbose ( " AGI Rx << temp buffer %s - errno %s \n " , buf , strerror ( errno ) ) ;
}
if ( ! buf [ 0 ] ) {
2001-09-28 13:19:43 +00:00
/* Program terminated */
2008-12-22 14:16:54 +00:00
if ( returnstatus ) {
2001-09-28 13:19:43 +00:00
returnstatus = - 1 ;
2008-12-22 14:16:54 +00:00
}
2007-11-07 00:00:38 +00:00
ast_verb ( 3 , " <%s>AGI Script %s completed, returning %d \n " , chan - > name , request , returnstatus ) ;
2006-10-24 20:22:29 +00:00
if ( pid > 0 )
waitpid ( pid , status , 0 ) ;
2001-09-28 13:19:43 +00:00
/* No need to kill the pid anymore, since they closed us */
pid = - 1 ;
break ;
}
2007-09-11 15:28:46 +00:00
2008-04-17 12:59:04 +00:00
/* Special case for inability to execute child process */
if ( * buf & & strncasecmp ( buf , " failure " , 7 ) = = 0 ) {
returnstatus = AGI_RESULT_FAILURE ;
break ;
}
2005-01-08 19:35:20 +00:00
/* get rid of trailing newline, if any */
2001-12-27 11:07:33 +00:00
if ( * buf & & buf [ strlen ( buf ) - 1 ] = = ' \n ' )
buf [ strlen ( buf ) - 1 ] = 0 ;
2004-09-25 14:22:27 +00:00
if ( agidebug )
2007-11-07 00:00:38 +00:00
ast_verbose ( " <%s>AGI Rx << %s \n " , chan - > name , buf ) ;
2007-07-23 22:02:05 +00:00
returnstatus | = agi_handle_command ( chan , agi , buf , dead ) ;
2001-09-28 13:19:43 +00:00
/* If the handle_command returns -1, we need to stop */
2008-12-22 14:16:54 +00:00
if ( returnstatus < 0 ) {
2007-07-23 22:02:05 +00:00
needhup = 1 ;
continue ;
2001-09-28 13:19:43 +00:00
}
} else {
2003-11-05 23:43:31 +00:00
if ( - - retry < = 0 ) {
ast_log ( LOG_WARNING , " No channel, no fd? \n " ) ;
2006-05-25 16:44:22 +00:00
returnstatus = AGI_RESULT_FAILURE ;
2003-11-05 23:43:31 +00:00
break ;
}
2001-09-28 13:19:43 +00:00
}
}
/* Notify process */
2008-08-09 01:15:38 +00:00
sighup = pbx_builtin_getvar_helper ( chan , " AGISIGHUP " ) ;
if ( ast_strlen_zero ( sighup ) | | ! ast_false ( sighup ) ) {
if ( pid > - 1 ) {
2008-05-14 21:39:06 +00:00
if ( kill ( pid , SIGHUP ) ) {
2006-05-25 19:01:26 +00:00
ast_log ( LOG_WARNING , " unable to send SIGHUP to AGI process %d: %s \n " , pid , strerror ( errno ) ) ;
2008-05-14 21:39:06 +00:00
} else { /* Give the process a chance to die */
usleep ( 1 ) ;
}
2008-08-09 01:15:38 +00:00
waitpid ( pid , status , WNOHANG ) ;
} else if ( agi - > fast ) {
send ( agi - > ctrl , " HANGUP \n " , 7 , MSG_OOB ) ;
2006-05-25 19:01:26 +00:00
}
2005-08-03 04:17:12 +00:00
}
2001-09-28 13:19:43 +00:00
fclose ( readf ) ;
return returnstatus ;
}
2007-09-18 22:43:45 +00:00
static char * handle_cli_agi_show ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a )
2006-04-06 16:17:04 +00:00
{
2007-09-18 22:43:45 +00:00
struct agi_command * command ;
2008-11-12 00:17:43 +00:00
char fullcmd [ MAX_CMD_LEN ] ;
int error = 0 ;
2007-07-11 21:09:42 +00:00
2007-09-18 22:43:45 +00:00
switch ( cmd ) {
case CLI_INIT :
2008-09-28 23:32:14 +00:00
e - > command = " agi show commands [topic] " ;
2007-09-18 22:43:45 +00:00
e - > usage =
2008-09-28 23:32:14 +00:00
" Usage: agi show commands [topic] \n "
2007-09-18 22:43:45 +00:00
" When called with a topic as an argument, displays usage \n "
" information on the given command. If called without a \n "
" topic, it provides a list of AGI commands. \n " ;
case CLI_GENERATE :
return NULL ;
}
2008-10-12 09:19:23 +00:00
if ( a - > argc < e - > args - 1 | | ( a - > argc > = e - > args & & strcasecmp ( a - > argv [ e - > args - 1 ] , " topic " ) ) )
2007-09-18 22:43:45 +00:00
return CLI_SHOWUSAGE ;
2008-10-12 09:19:23 +00:00
if ( a - > argc > e - > args - 1 ) {
2007-09-18 22:43:45 +00:00
command = find_command ( a - > argv + e - > args , 1 ) ;
if ( command ) {
2008-11-12 00:17:43 +00:00
char * synopsis = NULL , * description = NULL , * syntax = NULL , * seealso = NULL ;
char info [ 30 + MAX_CMD_LEN ] ; /* '-= Info about...' */
char infotitle [ 30 + MAX_CMD_LEN + AST_TERM_MAX_ESCAPE_CHARS ] ; /* '-= Info about...' with colors */
char syntitle [ 11 + AST_TERM_MAX_ESCAPE_CHARS ] ; /* [Syntax]\n with colors */
char desctitle [ 15 + AST_TERM_MAX_ESCAPE_CHARS ] ; /* [Description]\n with colors */
char deadtitle [ 13 + AST_TERM_MAX_ESCAPE_CHARS ] ; /* [Runs Dead]\n with colors */
char deadcontent [ 3 + AST_TERM_MAX_ESCAPE_CHARS ] ; /* 'Yes' or 'No' with colors */
char seealsotitle [ 12 + AST_TERM_MAX_ESCAPE_CHARS ] ; /* [See Also]\n with colors */
char stxtitle [ 10 + AST_TERM_MAX_ESCAPE_CHARS ] ; /* [Syntax]\n with colors */
size_t synlen , desclen , seealsolen , stxlen ;
term_color ( syntitle , " [Synopsis] \n " , COLOR_MAGENTA , 0 , sizeof ( syntitle ) ) ;
term_color ( desctitle , " [Description] \n " , COLOR_MAGENTA , 0 , sizeof ( desctitle ) ) ;
term_color ( deadtitle , " [Runs Dead] \n " , COLOR_MAGENTA , 0 , sizeof ( deadtitle ) ) ;
term_color ( seealsotitle , " [See Also] \n " , COLOR_MAGENTA , 0 , sizeof ( seealsotitle ) ) ;
term_color ( stxtitle , " [Syntax] \n " , COLOR_MAGENTA , 0 , sizeof ( stxtitle ) ) ;
term_color ( deadcontent , command - > dead ? " Yes " : " No " , COLOR_CYAN , 0 , sizeof ( deadcontent ) ) ;
ast_join ( fullcmd , sizeof ( fullcmd ) , a - > argv + e - > args ) ;
snprintf ( info , sizeof ( info ) , " \n -= Info about agi '%s' =- " , fullcmd ) ;
term_color ( infotitle , info , COLOR_CYAN , 0 , sizeof ( infotitle ) ) ;
# ifdef AST_XML_DOCS
if ( command - > docsrc = = AST_XML_DOC ) {
synopsis = ast_xmldoc_printable ( S_OR ( command - > summary , " Not available " ) , 1 ) ;
description = ast_xmldoc_printable ( S_OR ( command - > usage , " Not available " ) , 1 ) ;
seealso = ast_xmldoc_printable ( S_OR ( command - > seealso , " Not available " ) , 1 ) ;
if ( ! seealso | | ! description | | ! synopsis ) {
error = 1 ;
goto return_cleanup ;
}
} else
# endif
{
synlen = strlen ( S_OR ( command - > summary , " Not available " ) ) + AST_TERM_MAX_ESCAPE_CHARS ;
synopsis = ast_malloc ( synlen ) ;
desclen = strlen ( S_OR ( command - > usage , " Not available " ) ) + AST_TERM_MAX_ESCAPE_CHARS ;
description = ast_malloc ( desclen ) ;
seealsolen = strlen ( S_OR ( command - > seealso , " Not available " ) ) + AST_TERM_MAX_ESCAPE_CHARS ;
seealso = ast_malloc ( seealsolen ) ;
if ( ! synopsis | | ! description | | ! seealso ) {
error = 1 ;
goto return_cleanup ;
}
term_color ( synopsis , S_OR ( command - > summary , " Not available " ) , COLOR_CYAN , 0 , synlen ) ;
term_color ( description , S_OR ( command - > usage , " Not available " ) , COLOR_CYAN , 0 , desclen ) ;
term_color ( seealso , S_OR ( command - > seealso , " Not available " ) , COLOR_CYAN , 0 , seealsolen ) ;
}
stxlen = strlen ( S_OR ( command - > syntax , " Not available " ) ) + AST_TERM_MAX_ESCAPE_CHARS ;
syntax = ast_malloc ( stxlen ) ;
if ( ! syntax ) {
error = 1 ;
goto return_cleanup ;
}
term_color ( syntax , S_OR ( command - > syntax , " Not available " ) , COLOR_CYAN , 0 , stxlen ) ;
ast_cli ( a - > fd , " %s \n \n %s%s \n \n %s%s \n \n %s%s \n \n %s%s \n \n %s%s \n \n " , infotitle , stxtitle , syntax ,
desctitle , description , syntitle , synopsis , deadtitle , deadcontent ,
seealsotitle , seealso ) ;
return_cleanup :
ast_free ( synopsis ) ;
ast_free ( description ) ;
ast_free ( syntax ) ;
ast_free ( seealso ) ;
2007-07-23 22:02:05 +00:00
} else {
2007-09-18 22:43:45 +00:00
if ( find_command ( a - > argv + e - > args , - 1 ) ) {
return help_workhorse ( a - > fd , a - > argv + e - > args ) ;
2003-02-06 22:11:43 +00:00
} else {
2007-09-18 22:43:45 +00:00
ast_join ( fullcmd , sizeof ( fullcmd ) , a - > argv + e - > args ) ;
ast_cli ( a - > fd , " No such command '%s'. \n " , fullcmd ) ;
2003-02-06 22:11:43 +00:00
}
}
} else {
2007-09-18 22:43:45 +00:00
return help_workhorse ( a - > fd , NULL ) ;
2003-02-06 22:11:43 +00:00
}
2008-11-12 00:17:43 +00:00
return ( error ? CLI_FAILURE : CLI_SUCCESS ) ;
2003-02-06 22:11:43 +00:00
}
2007-02-16 11:47:48 +00:00
/*! \brief Convert string to use HTML escaped characters
\ note Maybe this should be a generic function ?
*/
static void write_html_escaped ( FILE * htmlfile , char * str )
{
char * cur = str ;
while ( * cur ) {
switch ( * cur ) {
case ' < ' :
fprintf ( htmlfile , " %s " , " < " ) ;
break ;
case ' > ' :
fprintf ( htmlfile , " %s " , " > " ) ;
break ;
case ' & ' :
fprintf ( htmlfile , " %s " , " & " ) ;
break ;
case ' " ' :
fprintf ( htmlfile , " %s " , " " " ) ;
break ;
default :
fprintf ( htmlfile , " %c " , * cur ) ;
break ;
}
cur + + ;
}
return ;
}
2008-02-08 21:26:32 +00:00
static int write_htmldump ( char * filename )
2006-04-06 16:17:04 +00:00
{
2007-09-18 22:43:45 +00:00
struct agi_command * command ;
2008-11-12 00:17:43 +00:00
char fullcmd [ MAX_CMD_LEN ] ;
2003-02-06 22:11:43 +00:00
FILE * htmlfile ;
2008-02-08 21:26:32 +00:00
if ( ! ( htmlfile = fopen ( filename , " wt " ) ) )
return - 1 ;
2003-02-06 22:11:43 +00:00
fprintf ( htmlfile , " <HTML> \n <HEAD> \n <TITLE>AGI Commands</TITLE> \n </HEAD> \n " ) ;
fprintf ( htmlfile , " <BODY> \n <CENTER><B><H1>AGI Commands</H1></B></CENTER> \n \n " ) ;
fprintf ( htmlfile , " <TABLE BORDER= \" 0 \" CELLSPACING= \" 10 \" > \n " ) ;
2007-07-23 22:02:05 +00:00
AST_RWLIST_RDLOCK ( & agi_commands ) ;
2007-09-18 22:43:45 +00:00
AST_RWLIST_TRAVERSE ( & agi_commands , command , list ) {
2008-11-12 00:17:43 +00:00
# ifdef AST_XML_DOCS
char * stringptmp ;
# endif
char * tempstr , * stringp ;
2008-06-18 00:33:31 +00:00
2007-09-18 22:43:45 +00:00
if ( ! command - > cmda [ 0 ] ) /* end ? */
2005-12-20 20:20:04 +00:00
break ;
2003-02-06 22:11:43 +00:00
/* Hide commands that start with '_' */
2007-09-18 22:43:45 +00:00
if ( ( command - > cmda [ 0 ] ) [ 0 ] = = ' _ ' )
2003-02-06 22:11:43 +00:00
continue ;
2007-09-18 22:43:45 +00:00
ast_join ( fullcmd , sizeof ( fullcmd ) , command - > cmda ) ;
2003-02-06 22:11:43 +00:00
fprintf ( htmlfile , " <TR><TD><TABLE BORDER= \" 1 \" CELLPADDING= \" 5 \" WIDTH= \" 100%% \" > \n " ) ;
2007-09-18 22:43:45 +00:00
fprintf ( htmlfile , " <TR><TH ALIGN= \" CENTER \" ><B>%s - %s</B></TH></TR> \n " , fullcmd , command - > summary ) ;
2008-11-12 00:17:43 +00:00
# ifdef AST_XML_DOCS
stringptmp = ast_xmldoc_printable ( command - > usage , 0 ) ;
stringp = stringptmp ;
# else
2007-09-18 22:43:45 +00:00
stringp = command - > usage ;
2008-11-12 00:17:43 +00:00
# endif
2003-02-06 22:11:43 +00:00
tempstr = strsep ( & stringp , " \n " ) ;
2007-02-16 11:47:48 +00:00
fprintf ( htmlfile , " <TR><TD ALIGN= \" CENTER \" > " ) ;
write_html_escaped ( htmlfile , tempstr ) ;
fprintf ( htmlfile , " </TD></TR> \n " ) ;
2003-02-06 22:11:43 +00:00
fprintf ( htmlfile , " <TR><TD ALIGN= \" CENTER \" > \n " ) ;
2007-09-18 22:43:45 +00:00
2007-02-16 11:47:48 +00:00
while ( ( tempstr = strsep ( & stringp , " \n " ) ) ! = NULL ) {
write_html_escaped ( htmlfile , tempstr ) ;
fprintf ( htmlfile , " <BR> \n " ) ;
}
2003-02-06 22:11:43 +00:00
fprintf ( htmlfile , " </TD></TR> \n " ) ;
fprintf ( htmlfile , " </TABLE></TD></TR> \n \n " ) ;
2008-11-12 00:17:43 +00:00
# ifdef AST_XML_DOCS
ast_free ( stringptmp ) ;
# endif
2003-02-06 22:11:43 +00:00
}
2007-07-23 22:02:05 +00:00
AST_RWLIST_UNLOCK ( & agi_commands ) ;
2003-02-06 22:11:43 +00:00
fprintf ( htmlfile , " </TABLE> \n </BODY> \n </HTML> \n " ) ;
fclose ( htmlfile ) ;
2008-02-08 21:26:32 +00:00
return 0 ;
}
static char * handle_cli_agi_dump_html ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a )
{
switch ( cmd ) {
case CLI_INIT :
e - > command = " agi dump html " ;
e - > usage =
" Usage: agi dump html <filename> \n "
" Dumps the AGI command list in HTML format to the given \n "
" file. \n " ;
return NULL ;
case CLI_GENERATE :
return NULL ;
}
if ( a - > argc ! = e - > args + 1 )
return CLI_SHOWUSAGE ;
if ( write_htmldump ( a - > argv [ e - > args ] ) < 0 ) {
ast_cli ( a - > fd , " Could not create file '%s' \n " , a - > argv [ e - > args ] ) ;
return CLI_SHOWUSAGE ;
}
ast_cli ( a - > fd , " AGI HTML commands dumped to: %s \n " , a - > argv [ e - > args ] ) ;
return CLI_SUCCESS ;
}
2004-03-03 18:58:57 +00:00
static int agi_exec_full ( struct ast_channel * chan , void * data , int enhanced , int dead )
2001-09-28 13:19:43 +00:00
{
2006-05-25 16:44:22 +00:00
enum agi_result res ;
2007-09-11 15:28:46 +00:00
char buf [ AGI_BUF_LEN ] = " " , * tmp = buf ;
2007-07-23 20:27:26 +00:00
int fds [ 2 ] , efd = - 1 , pid ;
AST_DECLARE_APP_ARGS ( args ,
AST_APP_ARG ( arg ) [ MAX_ARGS ] ;
) ;
2003-02-23 06:00:11 +00:00
AGI agi ;
2005-01-08 19:35:20 +00:00
2005-11-08 01:55:31 +00:00
if ( ast_strlen_zero ( data ) ) {
2001-09-28 13:19:43 +00:00
ast_log ( LOG_WARNING , " AGI requires an argument (script) \n " ) ;
return - 1 ;
}
2008-02-21 17:44:34 +00:00
if ( dead )
ast_debug ( 3 , " Hungup channel detected, running agi in dead mode. \n " ) ;
2005-07-10 23:21:39 +00:00
ast_copy_string ( buf , data , sizeof ( buf ) ) ;
2003-02-23 06:00:11 +00:00
memset ( & agi , 0 , sizeof ( agi ) ) ;
2007-07-23 20:27:26 +00:00
AST_STANDARD_APP_ARGS ( args , tmp ) ;
args . argv [ args . argc ] = NULL ;
2002-06-14 15:50:42 +00:00
#if 0
2001-09-28 13:19:43 +00:00
/* Answer if need be */
2007-07-23 22:02:05 +00:00
if ( chan - > _state ! = AST_STATE_UP ) {
2008-02-11 18:29:18 +00:00
if ( ast_answer ( chan ) )
2001-09-28 13:19:43 +00:00
return - 1 ;
}
2002-06-14 15:50:42 +00:00
# endif
2008-01-03 06:16:48 +00:00
res = launch_script ( chan , args . argv [ 0 ] , args . argv , fds , enhanced ? & efd : NULL , & pid ) ;
/* Async AGI do not require run_agi(), so just proceed if normal AGI
or Fast AGI are setup with success . */
2007-09-18 22:46:05 +00:00
if ( res = = AGI_RESULT_SUCCESS | | res = = AGI_RESULT_SUCCESS_FAST ) {
2006-05-25 18:31:19 +00:00
int status = 0 ;
2003-02-23 06:00:11 +00:00
agi . fd = fds [ 1 ] ;
agi . ctrl = fds [ 0 ] ;
agi . audio = efd ;
2007-09-18 22:46:05 +00:00
agi . fast = ( res = = AGI_RESULT_SUCCESS_FAST ) ? 1 : 0 ;
2007-07-23 20:27:26 +00:00
res = run_agi ( chan , args . argv [ 0 ] , & agi , pid , & status , dead , args . argc , args . argv ) ;
2006-05-25 18:31:19 +00:00
/* If the fork'd process returns non-zero, set AGISTATUS to FAILURE */
2007-09-18 22:46:05 +00:00
if ( ( res = = AGI_RESULT_SUCCESS | | res = = AGI_RESULT_SUCCESS_FAST ) & & status )
2006-05-25 18:31:19 +00:00
res = AGI_RESULT_FAILURE ;
2006-09-06 20:04:17 +00:00
if ( fds [ 1 ] ! = fds [ 0 ] )
close ( fds [ 1 ] ) ;
2003-02-23 06:00:11 +00:00
if ( efd > - 1 )
close ( efd ) ;
2008-06-18 00:33:31 +00:00
}
2008-04-16 22:57:54 +00:00
ast_safe_fork_cleanup ( ) ;
2006-05-25 16:44:22 +00:00
switch ( res ) {
case AGI_RESULT_SUCCESS :
2007-09-18 22:46:05 +00:00
case AGI_RESULT_SUCCESS_FAST :
2008-01-03 06:16:48 +00:00
case AGI_RESULT_SUCCESS_ASYNC :
2006-05-25 16:44:22 +00:00
pbx_builtin_setvar_helper ( chan , " AGISTATUS " , " SUCCESS " ) ;
break ;
case AGI_RESULT_FAILURE :
pbx_builtin_setvar_helper ( chan , " AGISTATUS " , " FAILURE " ) ;
break ;
2007-06-21 15:58:05 +00:00
case AGI_RESULT_NOTFOUND :
pbx_builtin_setvar_helper ( chan , " AGISTATUS " , " NOTFOUND " ) ;
break ;
2006-05-25 16:44:22 +00:00
case AGI_RESULT_HANGUP :
pbx_builtin_setvar_helper ( chan , " AGISTATUS " , " HANGUP " ) ;
return - 1 ;
}
2008-09-06 15:26:45 +00:00
return 0 ;
2001-09-28 13:19:43 +00:00
}
2003-02-23 06:00:11 +00:00
static int agi_exec ( struct ast_channel * chan , void * data )
{
2007-07-23 22:02:05 +00:00
if ( ! ast_check_hangup ( chan ) )
return agi_exec_full ( chan , data , 0 , 0 ) ;
else
return agi_exec_full ( chan , data , 0 , 1 ) ;
2003-02-23 06:00:11 +00:00
}
static int eagi_exec ( struct ast_channel * chan , void * data )
{
2007-07-11 21:09:42 +00:00
int readformat , res ;
2005-01-08 19:35:20 +00:00
2007-07-19 15:59:19 +00:00
if ( ast_check_hangup ( chan ) ) {
2008-03-06 19:31:50 +00:00
ast_log ( LOG_ERROR , " EAGI cannot be run on a dead/hungup channel, please use AGI. \n " ) ;
2007-07-19 15:59:19 +00:00
return 0 ;
}
2003-02-23 06:00:11 +00:00
readformat = chan - > readformat ;
2004-04-06 22:17:32 +00:00
if ( ast_set_read_format ( chan , AST_FORMAT_SLINEAR ) ) {
2003-02-23 06:00:11 +00:00
ast_log ( LOG_WARNING , " Unable to set channel '%s' to linear mode \n " , chan - > name ) ;
return - 1 ;
}
2004-03-03 18:58:57 +00:00
res = agi_exec_full ( chan , data , 1 , 0 ) ;
2003-02-23 06:00:11 +00:00
if ( ! res ) {
2004-04-06 22:17:32 +00:00
if ( ast_set_read_format ( chan , readformat ) ) {
2003-08-16 05:10:35 +00:00
ast_log ( LOG_WARNING , " Unable to restore channel '%s' to format %s \n " , chan - > name , ast_getformatname ( readformat ) ) ;
2003-02-23 06:00:11 +00:00
}
}
return res ;
}
2004-03-03 18:58:57 +00:00
static int deadagi_exec ( struct ast_channel * chan , void * data )
{
2007-11-28 15:38:03 +00:00
ast_log ( LOG_WARNING , " DeadAGI has been deprecated, please use AGI in all cases! \n " ) ;
2007-07-23 22:02:05 +00:00
return agi_exec ( chan , data ) ;
2004-03-03 18:58:57 +00:00
}
2006-09-18 19:54:18 +00:00
static struct ast_cli_entry cli_agi [ ] = {
2008-01-03 06:16:48 +00:00
AST_CLI_DEFINE ( handle_cli_agi_add_cmd , " Add AGI command to a channel in Async AGI " ) ,
2008-02-08 21:26:32 +00:00
AST_CLI_DEFINE ( handle_cli_agi_debug , " Enable/Disable AGI debugging " ) ,
AST_CLI_DEFINE ( handle_cli_agi_show , " List AGI commands or specific help " ) ,
2008-11-12 06:46:04 +00:00
AST_CLI_DEFINE ( handle_cli_agi_dump_html , " Dumps a list of AGI commands in HTML format " )
2006-09-18 19:54:18 +00:00
} ;
2003-02-06 22:11:43 +00:00
2006-08-21 02:11:39 +00:00
static int unload_module ( void )
2001-09-28 13:19:43 +00:00
{
2008-12-05 10:31:25 +00:00
ast_cli_unregister_multiple ( cli_agi , ARRAY_LEN ( cli_agi ) ) ;
2008-11-19 12:42:19 +00:00
/* we can safely ignore the result of ast_agi_unregister_multiple() here, since it cannot fail, as
we know that these commands were registered by this module and are still registered
*/
( void ) ast_agi_unregister_multiple ( ast_module_info - > self , commands , ARRAY_LEN ( commands ) ) ;
2003-02-23 06:00:11 +00:00
ast_unregister_application ( eapp ) ;
2004-03-03 18:58:57 +00:00
ast_unregister_application ( deadapp ) ;
2008-01-03 06:16:48 +00:00
ast_manager_unregister ( " AGI " ) ;
2001-09-28 13:19:43 +00:00
return ast_unregister_application ( app ) ;
}
2006-08-21 02:11:39 +00:00
static int load_module ( void )
2001-09-28 13:19:43 +00:00
{
2008-12-05 10:31:25 +00:00
ast_cli_register_multiple ( cli_agi , ARRAY_LEN ( cli_agi ) ) ;
2008-11-19 12:42:19 +00:00
/* we can safely ignore the result of ast_agi_register_multiple() here, since it cannot fail, as
no other commands have been registered yet
*/
( void ) ast_agi_register_multiple ( ast_module_info - > self , commands , ARRAY_LEN ( commands ) ) ;
2004-03-03 18:58:57 +00:00
ast_register_application ( deadapp , deadagi_exec , deadsynopsis , descrip ) ;
2004-01-30 05:37:39 +00:00
ast_register_application ( eapp , eagi_exec , esynopsis , descrip ) ;
2008-11-24 21:56:22 +00:00
ast_manager_register2 ( " AGI " , EVENT_FLAG_AGI , action_add_agi_cmd , " Add an AGI command to execute by Async AGI " , mandescr_asyncagi ) ;
2001-09-28 13:19:43 +00:00
return ast_register_application ( app , agi_exec , synopsis , descrip ) ;
}
2007-05-20 18:01:05 +00:00
AST_MODULE_INFO ( ASTERISK_GPL_KEY , AST_MODFLAG_GLOBAL_SYMBOLS , " Asterisk Gateway Interface (AGI) " ,
2007-07-23 22:02:05 +00:00
. load = load_module ,
. unload = unload_module ,
2007-05-20 18:01:05 +00:00
) ;