2021-06-28 16:56:18 +00:00
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright 2022, Naveen Albert <asterisk@phreaknet.org>
*
* Naveen Albert <asterisk@phreaknet.org>
*
* 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.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*! \file
*
* \brief If Branch Implementation
*
* \author Naveen Albert <asterisk@phreaknet.org>
*
* \ingroup applications
*/
/*** MODULEINFO
<support_level>extended</support_level>
***/
#include "asterisk.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/channel.h"
/*** DOCUMENTATION
<application name="If" language="en_US">
<synopsis>
Start an if branch.
</synopsis>
<syntax>
<parameter name="expr" required="true" />
</syntax>
<description>
<para>Start an If branch. Execution will continue inside the branch
if expr is true.</para>
<note><para>This application (and related applications) set variables
internally during execution.</para></note>
</description>
<see-also>
<ref type="application">ElseIf</ref>
<ref type="application">Else</ref>
<ref type="application">EndIf</ref>
<ref type="application">ExitIf</ref>
</see-also>
</application>
<application name="ElseIf" language="en_US">
<synopsis>
Start an else if branch.
</synopsis>
<syntax>
<parameter name="expr" required="true" />
</syntax>
<description>
<para>Start an optional ElseIf branch. Execution will continue inside the branch
if expr is true and if previous If and ElseIf branches evaluated to false.</para>
<para>Please note that execution inside a true If branch will fallthrough into
ElseIf unless the If segment is terminated with an ExitIf call. This is only
necessary with ElseIf but not with Else.</para>
</description>
<see-also>
<ref type="application">If</ref>
<ref type="application">Else</ref>
<ref type="application">EndIf</ref>
<ref type="application">ExitIf</ref>
</see-also>
</application>
<application name="Else" language="en_US">
<synopsis>
Define an optional else branch.
</synopsis>
<syntax>
<parameter name="expr" required="true" />
</syntax>
<description>
<para>Start an Else branch. Execution will jump here if all previous
If and ElseIf branches evaluated to false.</para>
</description>
<see-also>
<ref type="application">If</ref>
<ref type="application">ElseIf</ref>
<ref type="application">EndIf</ref>
<ref type="application">ExitIf</ref>
</see-also>
</application>
<application name="EndIf" language="en_US">
<synopsis>
End an if branch.
</synopsis>
<syntax />
<description>
<para>Ends the branch begun by the preceding <literal>If()</literal> application.</para>
</description>
<see-also>
<ref type="application">If</ref>
<ref type="application">ElseIf</ref>
<ref type="application">Else</ref>
<ref type="application">ExitIf</ref>
</see-also>
</application>
<application name="ExitIf" language="en_US">
<synopsis>
End an If branch.
</synopsis>
<syntax />
<description>
<para>Exits an <literal>If()</literal> branch, whether or not it has completed.</para>
</description>
<see-also>
<ref type="application">If</ref>
<ref type="application">ElseIf</ref>
<ref type="application">Else</ref>
<ref type="application">EndIf</ref>
</see-also>
</application>
***/
static char * if_app = "If" ;
static char * elseif_app = "ElseIf" ;
static char * else_app = "Else" ;
static char * stop_app = "EndIf" ;
static char * exit_app = "ExitIf" ;
#define VAR_SIZE 64
static const char * get_index ( struct ast_channel * chan , const char * prefix , int idx )
{
char varname [ VAR_SIZE ];
snprintf ( varname , VAR_SIZE , "%s_%d" , prefix , idx );
return pbx_builtin_getvar_helper ( chan , varname );
}
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_context * c2 ;
int idx ;
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 ( idx = 0 ; idx < ast_context_includes_count ( c ); idx ++ ) {
const struct ast_include * i = ast_context_includes_get ( c , idx );
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 ;
}
static int find_matching_endif ( struct ast_channel * chan , const char * otherapp )
{
struct ast_context * c ;
int res = - 1 ;
if ( ast_rdlock_contexts ()) {
ast_log ( LOG_ERROR , "Failed to lock contexts list \n " );
return - 1 ;
}
for ( c = ast_walk_contexts ( NULL ); c ; c = ast_walk_contexts ( c )) {
struct ast_exten * e ;
if ( ! ast_rdlock_context ( c )) {
if ( ! strcmp ( ast_get_context_name ( c ), ast_channel_context ( chan ))) {
/* This is the matching context we want */
int cur_priority = ast_channel_priority ( chan ) + 1 , level = 1 ;
for ( e = find_matching_priority ( c , ast_channel_exten ( chan ), cur_priority ,
S_COR ( ast_channel_caller ( chan ) -> id . number . valid , ast_channel_caller ( chan ) -> id . number . str , NULL ));
e ;
e = find_matching_priority ( c , ast_channel_exten ( chan ), ++ cur_priority ,
S_COR ( ast_channel_caller ( chan ) -> id . number . valid , ast_channel_caller ( chan ) -> id . number . str , NULL ))) {
if ( ! strcasecmp ( ast_get_extension_app ( e ), "IF" )) {
level ++ ;
} else if ( ! strcasecmp ( ast_get_extension_app ( e ), "ENDIF" )) {
level -- ;
}
if ( ! otherapp && level == 0 ) {
res = cur_priority ;
break ;
} else if ( otherapp && level == 1 && ! strcasecmp ( ast_get_extension_app ( e ), otherapp )) {
res = cur_priority ;
break ;
}
}
}
ast_unlock_context ( c );
if ( res > 0 ) {
break ;
}
}
}
ast_unlock_contexts ();
return res ;
}
static int if_helper ( struct ast_channel * chan , const char * data , int end )
{
int res = 0 ;
const char * if_pri = NULL ;
char * my_name = NULL ;
const char * label = NULL ;
2022-12-12 15:16:17 +00:00
char varname [ VAR_SIZE + 3 ]; /* + IF_ */
char end_varname [ sizeof ( varname ) + 4 ]; /* + END_ + sizeof(varname) */
2021-06-28 16:56:18 +00:00
const char * prefix = "IF" ;
size_t size = 0 ;
int used_index_i = - 1 , x = 0 ;
char used_index [ VAR_SIZE ] = "0" , new_index [ VAR_SIZE ] = "0" ;
if ( ! chan ) {
return - 1 ;
}
for ( x = 0 ;; x ++ ) {
if ( get_index ( chan , prefix , x )) {
used_index_i = x ;
} else {
break ;
}
}
2022-12-12 15:16:17 +00:00
snprintf ( used_index , sizeof ( used_index ), "%d" , used_index_i );
snprintf ( new_index , sizeof ( new_index ), "%d" , used_index_i + 1 );
2021-06-28 16:56:18 +00:00
size = strlen ( ast_channel_context ( chan )) + strlen ( ast_channel_exten ( chan )) + 32 ;
my_name = ast_alloca ( size );
memset ( my_name , 0 , size );
snprintf ( my_name , size , "%s_%s_%d" , ast_channel_context ( chan ), ast_channel_exten ( chan ), ast_channel_priority ( chan ));
ast_channel_lock ( chan );
if ( end > 1 ) {
label = used_index ;
} else if ( ! ( label = pbx_builtin_getvar_helper ( chan , my_name ))) {
label = new_index ;
pbx_builtin_setvar_helper ( chan , my_name , label );
}
snprintf ( varname , sizeof ( varname ), "%s_%s" , prefix , label );
if (( if_pri = pbx_builtin_getvar_helper ( chan , varname )) && ! end ) {
if_pri = ast_strdupa ( if_pri );
snprintf ( end_varname , sizeof ( end_varname ), "END_%s" , varname );
}
ast_channel_unlock ( chan );
if (( end <= 1 && ! pbx_checkcondition ( ast_strdupa ( data ))) || ( end > 1 )) {
/* Condition Met (clean up helper vars) */
const char * goto_str ;
int pri , endifpri ;
pbx_builtin_setvar_helper ( chan , varname , NULL );
pbx_builtin_setvar_helper ( chan , my_name , NULL );
snprintf ( end_varname , sizeof ( end_varname ), "END_%s" , varname );
ast_channel_lock ( chan );
endifpri = find_matching_endif ( chan , NULL );
if (( goto_str = pbx_builtin_getvar_helper ( chan , end_varname ))) {
ast_parseable_goto ( chan , goto_str );
pbx_builtin_setvar_helper ( chan , end_varname , NULL );
} else if ( end <= 1 && ( pri = find_matching_endif ( chan , "ElseIf" )) > 0 && pri < endifpri ) {
pri -- ; /* back up a priority, since it returned the priority after the ElseIf */
/* If is false, and ElseIf exists, so jump to ElseIf */
ast_verb ( 3 , "Taking conditional false branch, jumping to priority %d \n " , pri );
ast_channel_priority_set ( chan , pri );
} else if ( end <= 1 && ( pri = find_matching_endif ( chan , "Else" )) > 0 && pri < endifpri ) {
/* don't need to back up a priority, because we don't actually need to execute Else, just jump to the priority after. Directly executing Else will exit the conditional. */
/* If is false, and Else exists, so jump to Else */
ast_verb ( 3 , "Taking absolute false branch, jumping to priority %d \n " , pri );
ast_channel_priority_set ( chan , pri );
} else {
pri = endifpri ;
if ( pri > 0 ) {
ast_verb ( 3 , "Exiting conditional, jumping to priority %d \n " , pri );
ast_channel_priority_set ( chan , pri );
} else if ( end == 4 ) { /* Condition added because of end > 0 instead of end == 4 */
ast_log ( LOG_WARNING , "Couldn't find matching EndIf? (If at %s@%s priority %d) \n " , ast_channel_context ( chan ), ast_channel_exten ( chan ), ast_channel_priority ( chan ));
}
}
ast_channel_unlock ( chan );
return res ;
}
if ( end <= 1 && ! if_pri ) {
char * goto_str ;
size = strlen ( ast_channel_context ( chan )) + strlen ( ast_channel_exten ( chan )) + 32 ;
goto_str = ast_alloca ( size );
memset ( goto_str , 0 , size );
snprintf ( goto_str , size , "%s,%s,%d" , ast_channel_context ( chan ), ast_channel_exten ( chan ), ast_channel_priority ( chan ));
pbx_builtin_setvar_helper ( chan , varname , goto_str );
} else if ( end > 1 && if_pri ) {
/* END of branch */
snprintf ( end_varname , sizeof ( end_varname ), "END_%s" , varname );
if ( ! pbx_builtin_getvar_helper ( chan , end_varname )) {
char * goto_str ;
size = strlen ( ast_channel_context ( chan )) + strlen ( ast_channel_exten ( chan )) + 32 ;
goto_str = ast_alloca ( size );
memset ( goto_str , 0 , size );
snprintf ( goto_str , size , "%s,%s,%d" , ast_channel_context ( chan ), ast_channel_exten ( chan ), ast_channel_priority ( chan ) + 1 );
pbx_builtin_setvar_helper ( chan , end_varname , goto_str );
}
ast_parseable_goto ( chan , if_pri );
}
return res ;
}
static int if_exec ( struct ast_channel * chan , const char * data ) {
return if_helper ( chan , data , 0 );
}
static int elseif_exec ( struct ast_channel * chan , const char * data ) {
return if_helper ( chan , data , 1 );
}
static int end_exec ( struct ast_channel * chan , const char * data ) {
return if_helper ( chan , data , 2 );
}
static int else_exec ( struct ast_channel * chan , const char * data ) {
return if_helper ( chan , data , 3 );
}
static int exit_exec ( struct ast_channel * chan , const char * data ) {
return if_helper ( chan , data , 4 );
}
static int unload_module ( void )
{
int res ;
res = ast_unregister_application ( if_app );
res |= ast_unregister_application ( elseif_app );
res |= ast_unregister_application ( stop_app );
res |= ast_unregister_application ( else_app );
res |= ast_unregister_application ( exit_app );
return res ;
}
static int load_module ( void )
{
int res ;
res = ast_register_application_xml ( if_app , if_exec );
res |= ast_register_application_xml ( elseif_app , elseif_exec );
res |= ast_register_application_xml ( stop_app , end_exec );
res |= ast_register_application_xml ( else_app , else_exec );
res |= ast_register_application_xml ( exit_app , exit_exec );
return res ;
}
AST_MODULE_INFO_STANDARD_EXTENDED ( ASTERISK_GPL_KEY , "If Branch and Conditional Execution" );