2003-02-12 13:59:15 +00:00
/*
2005-09-14 20:46:50 +00:00
* Asterisk - - An open source telephony toolkit .
2003-02-12 13:59:15 +00:00
*
2005-09-14 20:46:50 +00:00
* Copyright ( C ) 1999 - 2005 , Digium , Inc .
2003-02-12 13:59:15 +00:00
*
* Mark Spencer < markster @ digium . com >
*
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 .
*
2003-02-12 13:59:15 +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 Dial plan macro Implementation
2005-12-30 21:18:06 +00:00
*
* \ author Mark Spencer < markster @ digium . com >
2005-09-14 20:46:50 +00:00
*
2005-11-06 15:09:47 +00:00
* \ ingroup applications
2003-02-12 13:59:15 +00:00
*/
2011-07-14 20:28:54 +00:00
/*** MODULEINFO
2011-09-27 20:15:30 +00:00
< support_level > core < / support_level >
2011-07-14 20:28:54 +00:00
< replacement > app_stack ( GoSub ) < / replacement >
2011-11-10 23:21:30 +00:00
* * */
2011-07-14 20:28:54 +00:00
2006-06-07 18:54:56 +00:00
# include "asterisk.h"
ASTERISK_FILE_VERSION ( __FILE__ , " $Revision$ " )
2005-04-21 06:02:45 +00:00
# include "asterisk/file.h"
# include "asterisk/channel.h"
# include "asterisk/pbx.h"
# include "asterisk/module.h"
# include "asterisk/config.h"
# include "asterisk/utils.h"
# include "asterisk/lock.h"
2009-06-01 20:57:31 +00:00
# include "asterisk/app.h"
2003-02-12 13:59:15 +00:00
2008-11-05 18:46:29 +00:00
/*** DOCUMENTATION
< application name = " Macro " language = " en_US " >
< synopsis >
Macro Implementation .
< / synopsis >
< syntax >
< parameter name = " name " required = " true " >
< para > The name of the macro < / para >
< / parameter >
< parameter name = " args " >
< argument name = " arg1 " required = " true " / >
< argument name = " arg2 " multiple = " true " / >
< / parameter >
< / syntax >
< description >
< para > Executes a macro using the context macro - < replaceable > name < / replaceable > ,
jumping to the < literal > s < / literal > extension of that context and executing each step ,
then returning when the steps end . < / para >
< para > The calling extension , context , and priority are stored in < variable > MACRO_EXTEN < / variable > ,
< variable > MACRO_CONTEXT < / variable > and < variable > MACRO_PRIORITY < / variable > respectively . Arguments
become < variable > ARG1 < / variable > , < variable > ARG2 < / variable > , etc in the macro context . < / para >
< para > If you Goto out of the Macro context , the Macro will terminate and control will be returned
at the location of the Goto . < / para >
< para > If < variable > MACRO_OFFSET < / variable > is set at termination , Macro will attempt to continue
at priority MACRO_OFFSET + N + 1 if such a step exists , and N + 1 otherwise . < / para >
< warning > < para > Because of the way Macro is implemented ( it executes the priorities contained within
it via sub - engine ) , and a fixed per - thread memory stack allowance , macros are limited to 7 levels
of nesting ( macro calling macro calling macro , etc . ) ; It may be possible that stack - intensive
applications in deeply nested macros could cause asterisk to crash earlier than this limit .
It is advised that if you need to deeply nest macro calls , that you use the Gosub application
( now allows arguments like a Macro ) with explict Return ( ) calls instead . < / para > < / warning >
2009-05-18 14:45:23 +00:00
< warning > < para > Use of the application < literal > WaitExten < / literal > within a macro will not function
as expected . Please use the < literal > Read < / literal > application in order to read DTMF from a channel
currently executing a macro . < / para > < / warning >
2008-11-05 18:46:29 +00:00
< / description >
< see - also >
< ref type = " application " > MacroExit < / ref >
< ref type = " application " > Goto < / ref >
< ref type = " application " > Gosub < / ref >
< / see - also >
< / application >
< application name = " MacroIf " language = " en_US " >
< synopsis >
Conditional Macro implementation .
< / synopsis >
< syntax argsep = " ? " >
< parameter name = " expr " required = " true " / >
< parameter name = " destination " required = " true " argsep = " : " >
< argument name = " macroiftrue " required = " true " >
< argument name = " macroiftrue " required = " true " / >
< argument name = " arg1 " multiple = " true " / >
< / argument >
< argument name = " macroiffalse " >
< argument name = " macroiffalse " required = " true " / >
< argument name = " arg1 " multiple = " true " / >
< / argument >
< / parameter >
< / syntax >
< description >
< para > Executes macro defined in < replaceable > macroiftrue < / replaceable > if
< replaceable > expr < / replaceable > is true ( otherwise < replaceable > macroiffalse < / replaceable >
if provided ) < / para >
< para > Arguments and return values as in application Macro ( ) < / para >
2009-05-18 14:45:23 +00:00
< xi : include xpointer = " xpointer(/docs/application[@name='Macro']/description/warning[2]) " / >
2008-11-05 18:46:29 +00:00
< / description >
< see - also >
< ref type = " application " > GotoIf < / ref >
< ref type = " application " > GosubIf < / ref >
< ref type = " function " > IF < / ref >
< / see - also >
< / application >
< application name = " MacroExclusive " language = " en_US " >
< synopsis >
Exclusive Macro Implementation .
< / synopsis >
< syntax >
< parameter name = " name " required = " true " >
< para > The name of the macro < / para >
< / parameter >
< parameter name = " arg1 " / >
< parameter name = " arg2 " multiple = " true " / >
< / syntax >
< description >
< para > Executes macro defined in the context macro - < replaceable > name < / replaceable > .
Only one call at a time may run the macro . ( we ' ll wait if another call is busy
executing in the Macro ) < / para >
< para > Arguments and return values as in application Macro ( ) < / para >
2009-05-18 14:45:23 +00:00
< xi : include xpointer = " xpointer(/docs/application[@name='Macro']/description/warning[2]) " / >
2008-11-05 18:46:29 +00:00
< / description >
< see - also >
< ref type = " application " > Macro < / ref >
< / see - also >
< / application >
< application name = " MacroExit " language = " en_US " >
< synopsis >
Exit from Macro .
< / synopsis >
< syntax / >
< description >
< para > Causes the currently running macro to exit as if it had
ended normally by running out of priorities to execute .
If used outside a macro , will likely cause unexpected behavior . < / para >
< / description >
< see - also >
< ref type = " application " > Macro < / ref >
< / see - also >
< / application >
* * */
2003-02-12 13:59:15 +00:00
# define MAX_ARGS 80
2005-01-07 05:42:31 +00:00
/* special result value used to force macro exit */
# define MACRO_EXIT_RESULT 1024
2003-02-12 13:59:15 +00:00
static char * app = " Macro " ;
2004-11-22 23:41:34 +00:00
static char * if_app = " MacroIf " ;
2006-08-14 03:24:06 +00:00
static char * exclusive_app = " MacroExclusive " ;
2005-01-07 05:42:31 +00:00
static char * exit_app = " MacroExit " ;
2003-02-12 13:59:15 +00:00
2008-12-17 21:18:57 +00:00
static void macro_fixup ( void * data , struct ast_channel * old_chan , struct ast_channel * new_chan ) ;
2012-07-18 17:18:20 +00:00
static const struct ast_datastore_info macro_ds_info = {
2008-12-17 21:18:57 +00:00
. type = " MACRO " ,
. chan_fixup = macro_fixup ,
} ;
static void macro_fixup ( void * data , struct ast_channel * old_chan , struct ast_channel * new_chan )
{
int i ;
char varname [ 10 ] ;
pbx_builtin_setvar_helper ( new_chan , " MACRO_DEPTH " , " 0 " ) ;
pbx_builtin_setvar_helper ( new_chan , " MACRO_CONTEXT " , NULL ) ;
pbx_builtin_setvar_helper ( new_chan , " MACRO_EXTEN " , NULL ) ;
pbx_builtin_setvar_helper ( new_chan , " MACRO_PRIORITY " , NULL ) ;
pbx_builtin_setvar_helper ( new_chan , " MACRO_OFFSET " , NULL ) ;
for ( i = 1 ; i < 100 ; i + + ) {
snprintf ( varname , sizeof ( varname ) , " ARG%d " , i ) ;
while ( pbx_builtin_getvar_helper ( new_chan , varname ) ) {
/* Kill all levels of arguments */
pbx_builtin_setvar_helper ( new_chan , varname , NULL ) ;
}
}
}
2007-04-08 14:23:16 +00:00
static struct ast_exten * find_matching_priority ( struct ast_context * c , const char * exten , int priority , const char * callerid )
{
struct ast_exten * e ;
struct ast_include * i ;
struct ast_context * c2 ;
for ( e = ast_walk_context_extensions ( c , NULL ) ; e ; e = ast_walk_context_extensions ( c , e ) ) {
if ( ast_extension_match ( ast_get_extension_name ( e ) , exten ) ) {
int needmatch = ast_get_extension_matchcid ( e ) ;
if ( ( needmatch & & ast_extension_match ( ast_get_extension_cidmatch ( e ) , callerid ) ) | |
( ! needmatch ) ) {
/* This is the matching extension we want */
struct ast_exten * p ;
for ( p = ast_walk_extension_priorities ( e , NULL ) ; p ; p = ast_walk_extension_priorities ( e , p ) ) {
if ( priority ! = ast_get_extension_priority ( p ) )
continue ;
return p ;
}
}
}
}
/* No match; run through includes */
for ( i = ast_walk_context_includes ( c , NULL ) ; i ; i = ast_walk_context_includes ( c , i ) ) {
for ( c2 = ast_walk_contexts ( NULL ) ; c2 ; c2 = ast_walk_contexts ( c2 ) ) {
if ( ! strcmp ( ast_get_context_name ( c2 ) , ast_get_include_name ( i ) ) ) {
e = find_matching_priority ( c2 , exten , priority , callerid ) ;
if ( e )
return e ;
}
}
}
return NULL ;
}
2009-05-21 21:13:09 +00:00
static int _macro_exec ( struct ast_channel * chan , const char * data , int exclusive )
2003-02-12 13:59:15 +00:00
{
2005-12-03 19:25:33 +00:00
const char * s ;
2005-01-08 18:45:13 +00:00
char * tmp ;
char * cur , * rest ;
char * macro ;
char fullmacro [ 80 ] ;
char varname [ 80 ] ;
2007-05-08 22:40:21 +00:00
char runningapp [ 80 ] , runningdata [ 1024 ] ;
2005-01-08 18:45:13 +00:00
char * oldargs [ MAX_ARGS + 1 ] = { NULL , } ;
int argc , x ;
int res = 0 ;
char oldexten [ 256 ] = " " ;
2007-04-08 14:23:16 +00:00
int oldpriority , gosub_level = 0 ;
2005-09-07 20:39:20 +00:00
char pc [ 80 ] , depthc [ 12 ] ;
2005-07-10 23:49:57 +00:00
char oldcontext [ AST_MAX_CONTEXT ] = " " ;
2007-02-07 15:35:44 +00:00
const char * inhangupc ;
2006-08-17 15:48:49 +00:00
int offset , depth = 0 , maxdepth = 7 ;
2005-01-08 18:45:13 +00:00
int setmacrocontext = 0 ;
2008-12-17 21:18:57 +00:00
int autoloopflag , inhangup = 0 ;
2009-04-29 18:53:01 +00:00
struct ast_str * tmp_subst = NULL ;
2003-02-12 13:59:15 +00:00
2005-01-08 18:45:13 +00:00
char * save_macro_exten ;
char * save_macro_context ;
char * save_macro_priority ;
char * save_macro_offset ;
2008-12-17 21:18:57 +00:00
struct ast_datastore * macro_store = ast_channel_datastore_find ( chan , & macro_ds_info , NULL ) ;
2009-04-29 18:53:01 +00:00
2005-10-26 19:48:14 +00:00
if ( ast_strlen_zero ( data ) ) {
2007-07-31 01:10:47 +00:00
ast_log ( LOG_WARNING , " Macro() requires arguments. See \" core show application macro \" for help. \n " ) ;
2005-10-19 18:19:02 +00:00
return - 1 ;
2005-01-08 18:45:13 +00:00
}
2004-11-22 23:41:34 +00:00
2008-12-17 21:18:57 +00:00
do {
if ( macro_store ) {
break ;
}
2008-12-17 21:28:51 +00:00
if ( ! ( macro_store = ast_datastore_alloc ( & macro_ds_info , NULL ) ) ) {
2008-12-17 21:18:57 +00:00
ast_log ( LOG_WARNING , " Unable to allocate new datastore. \n " ) ;
break ;
}
/* Just the existence of this datastore is enough. */
macro_store - > inheritance = DATASTORE_INHERIT_FOREVER ;
ast_channel_datastore_add ( chan , macro_store ) ;
} while ( 0 ) ;
2006-08-17 15:48:49 +00:00
/* does the user want a deeper rabbit hole? */
2008-04-30 19:21:04 +00:00
ast_channel_lock ( chan ) ;
if ( ( s = pbx_builtin_getvar_helper ( chan , " MACRO_RECURSION " ) ) ) {
2009-08-10 19:20:57 +00:00
sscanf ( s , " %30d " , & maxdepth ) ;
2008-04-30 19:21:04 +00:00
}
2005-09-07 20:39:20 +00:00
/* Count how many levels deep the rabbit hole goes */
2008-04-30 19:21:04 +00:00
if ( ( s = pbx_builtin_getvar_helper ( chan , " MACRO_DEPTH " ) ) ) {
2009-08-10 19:20:57 +00:00
sscanf ( s , " %30d " , & depth ) ;
2008-04-30 19:21:04 +00:00
}
2007-02-07 15:35:44 +00:00
/* Used for detecting whether to return when a Macro is called from another Macro after hangup */
2012-02-13 17:27:06 +00:00
if ( strcmp ( ast_channel_exten ( chan ) , " h " ) = = 0 )
2007-02-07 15:35:44 +00:00
pbx_builtin_setvar_helper ( chan , " MACRO_IN_HANGUP " , " 1 " ) ;
2008-04-30 19:21:04 +00:00
if ( ( inhangupc = pbx_builtin_getvar_helper ( chan , " MACRO_IN_HANGUP " ) ) ) {
2009-08-10 19:20:57 +00:00
sscanf ( inhangupc , " %30d " , & inhangup ) ;
2008-04-30 19:21:04 +00:00
}
ast_channel_unlock ( chan ) ;
2007-02-07 15:35:44 +00:00
2006-08-17 15:48:49 +00:00
if ( depth > = maxdepth ) {
2005-09-07 20:39:20 +00:00
ast_log ( LOG_ERROR , " Macro(): possible infinite loop detected. Returning early. \n " ) ;
return 0 ;
}
snprintf ( depthc , sizeof ( depthc ) , " %d " , depth + 1 ) ;
2005-10-19 18:19:02 +00:00
tmp = ast_strdupa ( data ) ;
2005-01-08 18:45:13 +00:00
rest = tmp ;
2007-07-23 19:51:41 +00:00
macro = strsep ( & rest , " , " ) ;
2005-10-26 19:48:14 +00:00
if ( ast_strlen_zero ( macro ) ) {
2005-01-08 18:45:13 +00:00
ast_log ( LOG_WARNING , " Invalid macro name specified \n " ) ;
return 0 ;
}
2006-08-14 03:24:06 +00:00
2005-01-08 18:45:13 +00:00
snprintf ( fullmacro , sizeof ( fullmacro ) , " macro-%s " , macro ) ;
2010-07-14 15:48:36 +00:00
if ( ! ast_exists_extension ( chan , fullmacro , " s " , 1 ,
2012-02-29 16:52:47 +00:00
S_COR ( ast_channel_caller ( chan ) - > id . number . valid , ast_channel_caller ( chan ) - > id . number . str , NULL ) ) ) {
2006-08-14 03:24:06 +00:00
if ( ! ast_context_find ( fullmacro ) )
2012-02-13 17:27:06 +00:00
ast_log ( LOG_WARNING , " No such context '%s' for macro '%s'. Was called by %s@%s \n " , fullmacro , macro , ast_channel_exten ( chan ) , ast_channel_context ( chan ) ) ;
2005-01-08 18:45:13 +00:00
else
2006-08-14 03:24:06 +00:00
ast_log ( LOG_WARNING , " Context '%s' for macro '%s' lacks 's' extension, priority 1 \n " , fullmacro , macro ) ;
2005-01-08 18:45:13 +00:00
return 0 ;
}
2006-08-14 03:24:06 +00:00
/* If we are to run the macro exclusively, take the mutex */
if ( exclusive ) {
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , " Locking macrolock for '%s' \n " , fullmacro ) ;
2006-08-14 03:24:06 +00:00
ast_autoservice_start ( chan ) ;
if ( ast_context_lockmacro ( fullmacro ) ) {
ast_log ( LOG_WARNING , " Failed to lock macro '%s' as in-use \n " , fullmacro ) ;
ast_autoservice_stop ( chan ) ;
return 0 ;
}
ast_autoservice_stop ( chan ) ;
}
2009-04-29 18:53:01 +00:00
if ( ! ( tmp_subst = ast_str_create ( 16 ) ) ) {
return - 1 ;
}
2005-01-08 18:45:13 +00:00
/* Save old info */
2012-02-20 23:43:27 +00:00
oldpriority = ast_channel_priority ( chan ) ;
2012-02-13 17:27:06 +00:00
ast_copy_string ( oldexten , ast_channel_exten ( chan ) , sizeof ( oldexten ) ) ;
ast_copy_string ( oldcontext , ast_channel_context ( chan ) , sizeof ( oldcontext ) ) ;
if ( ast_strlen_zero ( ast_channel_macrocontext ( chan ) ) ) {
ast_channel_macrocontext_set ( chan , ast_channel_context ( chan ) ) ;
ast_channel_macroexten_set ( chan , ast_channel_exten ( chan ) ) ;
2012-02-20 23:43:27 +00:00
ast_channel_macropriority_set ( chan , ast_channel_priority ( chan ) ) ;
2005-01-08 18:45:13 +00:00
setmacrocontext = 1 ;
}
argc = 1 ;
/* Save old macro variables */
2006-01-13 18:38:55 +00:00
save_macro_exten = ast_strdup ( pbx_builtin_getvar_helper ( chan , " MACRO_EXTEN " ) ) ;
2005-01-08 18:45:13 +00:00
pbx_builtin_setvar_helper ( chan , " MACRO_EXTEN " , oldexten ) ;
2003-02-12 13:59:15 +00:00
2006-01-13 18:38:55 +00:00
save_macro_context = ast_strdup ( pbx_builtin_getvar_helper ( chan , " MACRO_CONTEXT " ) ) ;
2005-01-08 18:45:13 +00:00
pbx_builtin_setvar_helper ( chan , " MACRO_CONTEXT " , oldcontext ) ;
2003-02-12 13:59:15 +00:00
2006-01-13 18:38:55 +00:00
save_macro_priority = ast_strdup ( pbx_builtin_getvar_helper ( chan , " MACRO_PRIORITY " ) ) ;
2005-01-08 18:45:13 +00:00
snprintf ( pc , sizeof ( pc ) , " %d " , oldpriority ) ;
pbx_builtin_setvar_helper ( chan , " MACRO_PRIORITY " , pc ) ;
2003-02-12 13:59:15 +00:00
2006-01-13 18:38:55 +00:00
save_macro_offset = ast_strdup ( pbx_builtin_getvar_helper ( chan , " MACRO_OFFSET " ) ) ;
2005-01-08 18:45:13 +00:00
pbx_builtin_setvar_helper ( chan , " MACRO_OFFSET " , NULL ) ;
2003-02-12 13:59:15 +00:00
2009-04-29 18:53:01 +00:00
pbx_builtin_setvar_helper ( chan , " MACRO_DEPTH " , depthc ) ;
2005-01-08 18:45:13 +00:00
/* Setup environment for new run */
2012-02-13 17:27:06 +00:00
ast_channel_exten_set ( chan , " s " ) ;
ast_channel_context_set ( chan , fullmacro ) ;
2012-02-20 23:43:27 +00:00
ast_channel_priority_set ( chan , 1 ) ;
2003-02-12 13:59:15 +00:00
2008-04-30 19:21:04 +00:00
ast_channel_lock ( chan ) ;
2007-07-23 19:51:41 +00:00
while ( ( cur = strsep ( & rest , " , " ) ) & & ( argc < MAX_ARGS ) ) {
2008-08-10 14:45:25 +00:00
const char * argp ;
2005-01-08 18:45:13 +00:00
/* Save copy of old arguments if we're overwriting some, otherwise
let them pass through to the other macro */
snprintf ( varname , sizeof ( varname ) , " ARG%d " , argc ) ;
2008-08-10 14:45:25 +00:00
if ( ( argp = pbx_builtin_getvar_helper ( chan , varname ) ) ) {
oldargs [ argc ] = ast_strdup ( argp ) ;
2008-04-30 19:21:04 +00:00
}
2005-01-08 18:45:13 +00:00
pbx_builtin_setvar_helper ( chan , varname , cur ) ;
argc + + ;
}
2008-04-30 19:21:04 +00:00
ast_channel_unlock ( chan ) ;
2012-03-13 18:20:34 +00:00
autoloopflag = ast_test_flag ( ast_channel_flags ( chan ) , AST_FLAG_IN_AUTOLOOP ) ;
ast_set_flag ( ast_channel_flags ( chan ) , AST_FLAG_IN_AUTOLOOP ) ;
2012-02-20 23:43:27 +00:00
while ( ast_exists_extension ( chan , ast_channel_context ( chan ) , ast_channel_exten ( chan ) , ast_channel_priority ( chan ) ,
2012-02-29 16:52:47 +00:00
S_COR ( ast_channel_caller ( chan ) - > id . number . valid , ast_channel_caller ( chan ) - > id . number . str , NULL ) ) ) {
2007-05-08 22:40:21 +00:00
struct ast_context * c ;
struct ast_exten * e ;
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
int foundx ;
2007-05-31 17:16:38 +00:00
runningapp [ 0 ] = ' \0 ' ;
runningdata [ 0 ] = ' \0 ' ;
2007-05-08 22:40:21 +00:00
2007-04-08 14:23:16 +00:00
/* What application will execute? */
2007-05-08 22:40:21 +00:00
if ( ast_rdlock_contexts ( ) ) {
ast_log ( LOG_WARNING , " Failed to lock contexts list \n " ) ;
} else {
for ( c = ast_walk_contexts ( NULL ) , e = NULL ; c ; c = ast_walk_contexts ( c ) ) {
2012-02-13 17:27:06 +00:00
if ( ! strcmp ( ast_get_context_name ( c ) , ast_channel_context ( chan ) ) ) {
2007-05-08 22:40:21 +00:00
if ( ast_rdlock_context ( c ) ) {
ast_log ( LOG_WARNING , " Unable to lock context? \n " ) ;
} else {
2012-02-20 23:43:27 +00:00
e = find_matching_priority ( c , ast_channel_exten ( chan ) , ast_channel_priority ( chan ) ,
2012-02-29 16:52:47 +00:00
S_COR ( ast_channel_caller ( chan ) - > id . number . valid , ast_channel_caller ( chan ) - > id . number . str , NULL ) ) ;
2007-05-31 17:16:38 +00:00
if ( e ) { /* This will only be undefined for pbx_realtime, which is majorly broken. */
ast_copy_string ( runningapp , ast_get_extension_app ( e ) , sizeof ( runningapp ) ) ;
ast_copy_string ( runningdata , ast_get_extension_app_data ( e ) , sizeof ( runningdata ) ) ;
}
2007-05-08 22:40:21 +00:00
ast_unlock_context ( c ) ;
}
break ;
}
2007-04-08 14:23:16 +00:00
}
}
2007-05-08 22:40:21 +00:00
ast_unlock_contexts ( ) ;
2007-04-08 14:23:16 +00:00
2005-09-07 20:39:20 +00:00
/* Reset the macro depth, if it was changed in the last iteration */
pbx_builtin_setvar_helper ( chan , " MACRO_DEPTH " , depthc ) ;
2007-04-08 14:23:16 +00:00
2012-02-20 23:43:27 +00:00
res = ast_spawn_extension ( chan , ast_channel_context ( chan ) , ast_channel_exten ( chan ) , ast_channel_priority ( chan ) ,
2012-02-29 16:52:47 +00:00
S_COR ( ast_channel_caller ( chan ) - > id . number . valid , ast_channel_caller ( chan ) - > id . number . str , NULL ) ,
2010-07-14 15:48:36 +00:00
& foundx , 1 ) ;
if ( res ) {
2005-01-08 18:45:13 +00:00
/* Something bad happened, or a hangup has been requested. */
if ( ( ( res > = ' 0 ' ) & & ( res < = ' 9 ' ) ) | | ( ( res > = ' A ' ) & & ( res < = ' F ' ) ) | |
( res = = ' * ' ) | | ( res = = ' # ' ) ) {
/* Just return result as to the previous application as if it had been dialed */
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , " Oooh, got something to jump out with ('%c')! \n " , res ) ;
2005-01-08 18:45:13 +00:00
break ;
}
switch ( res ) {
2006-02-05 17:20:29 +00:00
case MACRO_EXIT_RESULT :
res = 0 ;
2005-01-08 18:45:13 +00:00
goto out ;
default :
2012-02-20 23:43:27 +00:00
ast_debug ( 2 , " Spawn extension (%s,%s,%d) exited non-zero on '%s' in macro '%s' \n " , ast_channel_context ( chan ) , ast_channel_exten ( chan ) , ast_channel_priority ( chan ) , ast_channel_name ( chan ) , macro ) ;
ast_verb ( 2 , " Spawn extension (%s, %s, %d) exited non-zero on '%s' in macro '%s' \n " , ast_channel_context ( chan ) , ast_channel_exten ( chan ) , ast_channel_priority ( chan ) , ast_channel_name ( chan ) , macro ) ;
2005-01-08 18:45:13 +00:00
goto out ;
}
2003-02-12 13:59:15 +00:00
}
2007-04-08 14:23:16 +00:00
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , " Executed application: %s \n " , runningapp ) ;
2007-04-08 14:23:16 +00:00
2007-05-08 22:40:21 +00:00
if ( ! strcasecmp ( runningapp , " GOSUB " ) ) {
2007-04-08 14:23:16 +00:00
gosub_level + + ;
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , " Incrementing gosub_level \n " ) ;
2007-05-08 22:40:21 +00:00
} else if ( ! strcasecmp ( runningapp , " GOSUBIF " ) ) {
2009-05-21 21:13:09 +00:00
char * cond , * app_arg ;
char * app2 ;
2009-04-29 18:53:01 +00:00
ast_str_substitute_variables ( & tmp_subst , 0 , chan , runningdata ) ;
app2 = ast_str_buffer ( tmp_subst ) ;
2007-04-08 14:23:16 +00:00
cond = strsep ( & app2 , " ? " ) ;
2008-08-10 14:45:25 +00:00
app_arg = strsep ( & app2 , " : " ) ;
2007-04-08 14:23:16 +00:00
if ( pbx_checkcondition ( cond ) ) {
2008-08-10 14:45:25 +00:00
if ( ! ast_strlen_zero ( app_arg ) ) {
2007-04-08 14:23:16 +00:00
gosub_level + + ;
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , " Incrementing gosub_level \n " ) ;
2007-04-08 14:23:16 +00:00
}
} else {
if ( ! ast_strlen_zero ( app2 ) ) {
gosub_level + + ;
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , " Incrementing gosub_level \n " ) ;
2007-04-08 14:23:16 +00:00
}
}
2007-05-08 22:40:21 +00:00
} else if ( ! strcasecmp ( runningapp , " RETURN " ) ) {
2007-04-08 14:23:16 +00:00
gosub_level - - ;
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , " Decrementing gosub_level \n " ) ;
2007-05-08 22:40:21 +00:00
} else if ( ! strcasecmp ( runningapp , " STACKPOP " ) ) {
2007-04-08 14:23:16 +00:00
gosub_level - - ;
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , " Decrementing gosub_level \n " ) ;
2007-05-08 22:40:21 +00:00
} else if ( ! strncasecmp ( runningapp , " EXEC " , 4 ) ) {
2007-04-08 14:23:16 +00:00
/* Must evaluate args to find actual app */
2009-04-29 18:53:01 +00:00
char * tmp2 , * tmp3 = NULL ;
ast_str_substitute_variables ( & tmp_subst , 0 , chan , runningdata ) ;
tmp2 = ast_str_buffer ( tmp_subst ) ;
2007-05-08 22:40:21 +00:00
if ( ! strcasecmp ( runningapp , " EXECIF " ) ) {
2009-05-21 21:13:09 +00:00
if ( ( tmp3 = strchr ( tmp2 , ' | ' ) ) ) {
2007-04-08 14:23:16 +00:00
* tmp3 + + = ' \0 ' ;
2009-04-29 18:53:01 +00:00
}
if ( ! pbx_checkcondition ( tmp2 ) ) {
2007-04-08 14:23:16 +00:00
tmp3 = NULL ;
2009-04-29 18:53:01 +00:00
}
} else {
2007-04-08 14:23:16 +00:00
tmp3 = tmp2 ;
2009-04-29 18:53:01 +00:00
}
2007-04-08 14:23:16 +00:00
2009-04-29 18:53:01 +00:00
if ( tmp3 ) {
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , " Last app: %s \n " , tmp3 ) ;
2009-04-29 18:53:01 +00:00
}
2007-04-08 14:23:16 +00:00
if ( tmp3 & & ! strncasecmp ( tmp3 , " GOSUB " , 5 ) ) {
gosub_level + + ;
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , " Incrementing gosub_level \n " ) ;
2007-04-08 14:23:16 +00:00
} else if ( tmp3 & & ! strncasecmp ( tmp3 , " RETURN " , 6 ) ) {
gosub_level - - ;
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , " Decrementing gosub_level \n " ) ;
2007-04-08 14:23:16 +00:00
} else if ( tmp3 & & ! strncasecmp ( tmp3 , " STACKPOP " , 8 ) ) {
gosub_level - - ;
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , " Decrementing gosub_level \n " ) ;
2007-04-08 14:23:16 +00:00
}
}
2012-02-13 17:27:06 +00:00
if ( gosub_level = = 0 & & strcasecmp ( ast_channel_context ( chan ) , fullmacro ) ) {
2012-01-09 22:15:50 +00:00
ast_verb ( 2 , " Channel '%s' jumping out of macro '%s' \n " , ast_channel_name ( chan ) , macro ) ;
2003-02-12 13:59:15 +00:00
break ;
2005-01-08 18:45:13 +00:00
}
2007-04-08 14:23:16 +00:00
2005-01-08 18:45:13 +00:00
/* don't stop executing extensions when we're in "h" */
2007-08-01 15:39:54 +00:00
if ( ast_check_hangup ( chan ) & & ! inhangup ) {
2012-02-20 23:43:27 +00:00
ast_debug ( 1 , " Extension %s, macroexten %s, priority %d returned normally even though call was hung up \n " , ast_channel_exten ( chan ) , ast_channel_macroexten ( chan ) , ast_channel_priority ( chan ) ) ;
2003-02-12 13:59:15 +00:00
goto out ;
}
2012-02-20 23:43:27 +00:00
ast_channel_priority_set ( chan , ast_channel_priority ( chan ) + 1 ) ;
2005-01-08 18:45:13 +00:00
}
out :
2008-12-17 21:18:57 +00:00
/* Don't let the channel change now. */
ast_channel_lock ( chan ) ;
2005-09-07 20:39:20 +00:00
/* Reset the depth back to what it was when the routine was entered (like if we called Macro recursively) */
snprintf ( depthc , sizeof ( depthc ) , " %d " , depth ) ;
2008-12-17 21:18:57 +00:00
pbx_builtin_setvar_helper ( chan , " MACRO_DEPTH " , depthc ) ;
2012-03-13 18:20:34 +00:00
ast_set2_flag ( ast_channel_flags ( chan ) , autoloopflag , AST_FLAG_IN_AUTOLOOP ) ;
2005-09-07 20:39:20 +00:00
2006-02-05 17:20:29 +00:00
for ( x = 1 ; x < argc ; x + + ) {
2005-01-08 18:45:13 +00:00
/* Restore old arguments and delete ours */
snprintf ( varname , sizeof ( varname ) , " ARG%d " , x ) ;
if ( oldargs [ x ] ) {
2008-12-17 21:18:57 +00:00
pbx_builtin_setvar_helper ( chan , varname , oldargs [ x ] ) ;
2007-06-06 21:20:11 +00:00
ast_free ( oldargs [ x ] ) ;
2008-12-17 21:18:57 +00:00
} else {
2005-01-08 18:45:13 +00:00
pbx_builtin_setvar_helper ( chan , varname , NULL ) ;
}
}
2003-02-12 13:59:15 +00:00
2005-01-08 18:45:13 +00:00
/* Restore macro variables */
2008-12-17 21:18:57 +00:00
pbx_builtin_setvar_helper ( chan , " MACRO_EXTEN " , save_macro_exten ) ;
pbx_builtin_setvar_helper ( chan , " MACRO_CONTEXT " , save_macro_context ) ;
pbx_builtin_setvar_helper ( chan , " MACRO_PRIORITY " , save_macro_priority ) ;
2005-01-08 18:45:13 +00:00
if ( save_macro_exten )
2007-06-06 21:20:11 +00:00
ast_free ( save_macro_exten ) ;
2005-01-08 18:45:13 +00:00
if ( save_macro_context )
2007-06-06 21:20:11 +00:00
ast_free ( save_macro_context ) ;
2005-01-08 18:45:13 +00:00
if ( save_macro_priority )
2007-06-06 21:20:11 +00:00
ast_free ( save_macro_priority ) ;
2006-02-05 17:20:29 +00:00
2008-12-17 21:18:57 +00:00
if ( setmacrocontext ) {
2012-02-13 17:27:06 +00:00
ast_channel_macrocontext_set ( chan , " " ) ;
ast_channel_macroexten_set ( chan , " " ) ;
2012-02-20 23:43:27 +00:00
ast_channel_macropriority_set ( chan , 0 ) ;
2005-01-08 18:45:13 +00:00
}
2003-02-12 13:59:15 +00:00
2012-02-13 17:27:06 +00:00
if ( ! strcasecmp ( ast_channel_context ( chan ) , fullmacro ) ) {
2010-11-22 19:42:02 +00:00
const char * offsets ;
2005-01-08 18:45:13 +00:00
/* If we're leaving the macro normally, restore original information */
2012-02-20 23:43:27 +00:00
ast_channel_priority_set ( chan , oldpriority ) ;
2012-02-13 17:27:06 +00:00
ast_channel_context_set ( chan , oldcontext ) ;
ast_channel_exten_set ( chan , oldexten ) ;
2010-11-22 19:42:02 +00:00
if ( ( offsets = pbx_builtin_getvar_helper ( chan , " MACRO_OFFSET " ) ) ) {
/* Handle macro offset if it's set by checking the availability of step n + offset + 1, otherwise continue
normally if there is any problem */
if ( sscanf ( offsets , " %30d " , & offset ) = = 1 ) {
2012-02-13 17:27:06 +00:00
if ( ast_exists_extension ( chan , ast_channel_context ( chan ) , ast_channel_exten ( chan ) ,
2012-02-20 23:43:27 +00:00
ast_channel_priority ( chan ) + offset + 1 ,
2012-02-29 16:52:47 +00:00
S_COR ( ast_channel_caller ( chan ) - > id . number . valid , ast_channel_caller ( chan ) - > id . number . str , NULL ) ) ) {
2012-02-20 23:43:27 +00:00
ast_channel_priority_set ( chan , ast_channel_priority ( chan ) + offset ) ;
2003-03-12 06:00:18 +00:00
}
2003-02-12 13:59:15 +00:00
}
}
}
2008-12-17 21:18:57 +00:00
pbx_builtin_setvar_helper ( chan , " MACRO_OFFSET " , save_macro_offset ) ;
2005-01-08 18:45:13 +00:00
if ( save_macro_offset )
2007-06-06 21:20:11 +00:00
ast_free ( save_macro_offset ) ;
2006-08-14 03:24:06 +00:00
/* Unlock the macro */
if ( exclusive ) {
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , " Unlocking macrolock for '%s' \n " , fullmacro ) ;
2006-08-14 03:24:06 +00:00
if ( ast_context_unlockmacro ( fullmacro ) ) {
ast_log ( LOG_ERROR , " Failed to unlock macro '%s' - that isn't good \n " , fullmacro ) ;
res = 0 ;
}
}
2008-12-17 21:18:57 +00:00
ast_channel_unlock ( chan ) ;
2009-04-29 18:53:01 +00:00
ast_free ( tmp_subst ) ;
2006-08-21 02:11:39 +00:00
2005-01-08 18:45:13 +00:00
return res ;
2003-02-12 13:59:15 +00:00
}
2009-05-21 21:13:09 +00:00
static int macro_exec ( struct ast_channel * chan , const char * data )
2006-08-14 03:24:06 +00:00
{
2006-08-14 03:34:16 +00:00
return _macro_exec ( chan , data , 0 ) ;
2006-08-14 03:24:06 +00:00
}
2009-05-21 21:13:09 +00:00
static int macroexclusive_exec ( struct ast_channel * chan , const char * data )
2006-08-14 03:24:06 +00:00
{
2006-08-14 03:34:16 +00:00
return _macro_exec ( chan , data , 1 ) ;
2006-08-14 03:24:06 +00:00
}
2009-05-21 21:13:09 +00:00
static int macroif_exec ( struct ast_channel * chan , const char * data )
2004-11-22 23:41:34 +00:00
{
char * expr = NULL , * label_a = NULL , * label_b = NULL ;
int res = 0 ;
2012-07-31 20:21:43 +00:00
expr = ast_strdupa ( data ) ;
2005-10-19 18:19:02 +00:00
if ( ( label_a = strchr ( expr , ' ? ' ) ) ) {
* label_a = ' \0 ' ;
label_a + + ;
if ( ( label_b = strchr ( label_a , ' : ' ) ) ) {
* label_b = ' \0 ' ;
label_b + + ;
}
2006-05-05 14:47:22 +00:00
if ( pbx_checkcondition ( expr ) )
2007-07-30 14:53:14 +00:00
res = macro_exec ( chan , label_a ) ;
2005-10-19 18:19:02 +00:00
else if ( label_b )
2007-07-30 14:53:14 +00:00
res = macro_exec ( chan , label_b ) ;
2005-10-19 18:19:02 +00:00
} else
ast_log ( LOG_WARNING , " Invalid Syntax. \n " ) ;
2004-11-22 23:41:34 +00:00
return res ;
}
2009-05-21 21:13:09 +00:00
static int macro_exit_exec ( struct ast_channel * chan , const char * data )
2005-01-07 05:42:31 +00:00
{
return MACRO_EXIT_RESULT ;
}
2006-08-21 02:11:39 +00:00
static int unload_module ( void )
2003-02-12 13:59:15 +00:00
{
2005-10-18 22:52:21 +00:00
int res ;
res = ast_unregister_application ( if_app ) ;
res | = ast_unregister_application ( exit_app ) ;
res | = ast_unregister_application ( app ) ;
2006-08-14 03:24:06 +00:00
res | = ast_unregister_application ( exclusive_app ) ;
2005-10-18 22:52:21 +00:00
return res ;
2003-02-12 13:59:15 +00:00
}
2006-08-21 02:11:39 +00:00
static int load_module ( void )
2003-02-12 13:59:15 +00:00
{
2005-10-18 22:52:21 +00:00
int res ;
2008-11-05 18:46:29 +00:00
res = ast_register_application_xml ( exit_app , macro_exit_exec ) ;
res | = ast_register_application_xml ( if_app , macroif_exec ) ;
res | = ast_register_application_xml ( exclusive_app , macroexclusive_exec ) ;
res | = ast_register_application_xml ( app , macro_exec ) ;
2005-10-18 22:52:21 +00:00
return res ;
2003-02-12 13:59:15 +00:00
}
2006-08-21 02:11:39 +00:00
AST_MODULE_INFO_STANDARD ( ASTERISK_GPL_KEY , " Extension Macros " ) ;