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 */
2023-12-23 16:26:13 +00:00
2021-06-28 16:56:18 +00:00
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 ) ) ) {
2023-12-23 16:26:13 +00:00
2021-06-28 16:56:18 +00:00
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 ) ;
2023-12-23 16:26:13 +00:00
/* For EndIf, simply go to the next priority.
2024-01-28 13:57:47 +00:00
* We do not add 1 to ast_channel_priority because the dialplan will
* auto - increment the priority when we return , so just keep the priority as is .
2023-12-23 16:26:13 +00:00
* For ExitIf or false If ( ) condition , we need to find the end of the current
* If branch ( at same indentation ) and branch there . */
2024-01-28 13:57:47 +00:00
endifpri = end = = 2 ? ast_channel_priority ( chan ) : find_matching_endif ( chan , NULL ) ;
2021-06-28 16:56:18 +00:00
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 " ) ;