378 lines
10 KiB
C
378 lines
10 KiB
C
/*
|
|
* Asterisk -- An open source telephony toolkit.
|
|
*
|
|
* Copyright (C) 2006, Digium, Inc.
|
|
*
|
|
* Steve Murphy <murf@parsetree.com>
|
|
*
|
|
* 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 Compile symbolic Asterisk Extension Logic into Asterisk extensions, version 2.
|
|
*
|
|
*/
|
|
|
|
/*** MODULEINFO
|
|
<depend>res_ael_share</depend>
|
|
<support_level>extended</support_level>
|
|
***/
|
|
|
|
#include "asterisk.h"
|
|
|
|
#include <ctype.h>
|
|
#include <regex.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include "asterisk/pbx.h"
|
|
#include "asterisk/config.h"
|
|
#include "asterisk/module.h"
|
|
#include "asterisk/logger.h"
|
|
#include "asterisk/cli.h"
|
|
#include "asterisk/app.h"
|
|
#include "asterisk/callerid.h"
|
|
#include "asterisk/hashtab.h"
|
|
#include "asterisk/ael_structs.h"
|
|
#include "asterisk/pval.h"
|
|
#ifdef AAL_ARGCHECK
|
|
#include "asterisk/argdesc.h"
|
|
#endif
|
|
|
|
/*** DOCUMENTATION
|
|
<application name="AELSub" language="en_US">
|
|
<synopsis>
|
|
Launch subroutine built with AEL
|
|
</synopsis>
|
|
<syntax>
|
|
<parameter name="routine" required="true">
|
|
<para>Named subroutine to execute.</para>
|
|
</parameter>
|
|
<parameter name="args" required="false" />
|
|
</syntax>
|
|
<description>
|
|
<para>Execute the named subroutine, defined in AEL, from another dialplan
|
|
language, such as extensions.conf, Realtime extensions, or Lua.</para>
|
|
<para>The purpose of this application is to provide a sane entry point into
|
|
AEL subroutines, the implementation of which may change from time to time.</para>
|
|
</description>
|
|
</application>
|
|
***/
|
|
|
|
/* these functions are in ../ast_expr2.fl */
|
|
|
|
#define DEBUG_READ (1 << 0)
|
|
#define DEBUG_TOKENS (1 << 1)
|
|
#define DEBUG_MACROS (1 << 2)
|
|
#define DEBUG_CONTEXTS (1 << 3)
|
|
|
|
static char *config = "extensions.ael";
|
|
static char *registrar = "pbx_ael";
|
|
static int pbx_load_module(void);
|
|
|
|
#ifndef AAL_ARGCHECK
|
|
/* for the time being, short circuit all the AAL related structures
|
|
without permanently removing the code; after/during the AAL
|
|
development, this code can be properly re-instated
|
|
*/
|
|
|
|
#endif
|
|
|
|
#ifdef AAL_ARGCHECK
|
|
int option_matches_j( struct argdesc *should, pval *is, struct argapp *app);
|
|
int option_matches( struct argdesc *should, pval *is, struct argapp *app);
|
|
int ael_is_funcname(char *name);
|
|
#endif
|
|
|
|
int check_app_args(pval *appcall, pval *arglist, struct argapp *app);
|
|
void check_pval(pval *item, struct argapp *apps, int in_globals);
|
|
void check_pval_item(pval *item, struct argapp *apps, int in_globals);
|
|
void check_switch_expr(pval *item, struct argapp *apps);
|
|
void ast_expr_register_extra_error_info(char *errmsg);
|
|
void ast_expr_clear_extra_error_info(void);
|
|
struct pval *find_macro(char *name);
|
|
struct pval *find_context(char *name);
|
|
struct pval *find_context(char *name);
|
|
struct pval *find_macro(char *name);
|
|
struct ael_priority *new_prio(void);
|
|
struct ael_extension *new_exten(void);
|
|
void destroy_extensions(struct ael_extension *exten);
|
|
void set_priorities(struct ael_extension *exten);
|
|
void add_extensions(struct ael_extension *exten);
|
|
int ast_compile_ael2(struct ast_context **local_contexts, struct ast_hashtab *local_table, struct pval *root);
|
|
void destroy_pval(pval *item);
|
|
void destroy_pval_item(pval *item);
|
|
int is_float(char *arg );
|
|
int is_int(char *arg );
|
|
int is_empty(char *arg);
|
|
|
|
/* static void substitute_commas(char *str); */
|
|
|
|
static int aeldebug = 0;
|
|
|
|
/* interface stuff */
|
|
|
|
#ifndef STANDALONE
|
|
static char *aelsub = "AELSub";
|
|
|
|
static int aelsub_exec(struct ast_channel *chan, const char *vdata)
|
|
{
|
|
char buf[256], *data = ast_strdupa(vdata);
|
|
struct ast_app *gosub = pbx_findapp("Gosub");
|
|
AST_DECLARE_APP_ARGS(args,
|
|
AST_APP_ARG(name);
|
|
AST_APP_ARG(args);
|
|
);
|
|
|
|
if (gosub) {
|
|
AST_STANDARD_RAW_ARGS(args, data);
|
|
snprintf(buf, sizeof(buf), "%s,~~s~~,1(%s)", args.name, args.args);
|
|
return pbx_exec(chan, gosub, buf);
|
|
}
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
/* if all the below are static, who cares if they are present? */
|
|
|
|
static int pbx_load_module(void)
|
|
{
|
|
int errs=0, sem_err=0, sem_warn=0, sem_note=0;
|
|
char *rfilename;
|
|
struct ast_context *local_contexts=NULL, *con;
|
|
struct ast_hashtab *local_table=NULL;
|
|
|
|
struct pval *parse_tree;
|
|
|
|
ast_debug(1, "Starting AEL load process.\n");
|
|
if (config[0] == '/')
|
|
rfilename = (char *)config;
|
|
else {
|
|
rfilename = ast_alloca(strlen(config) + strlen(ast_config_AST_CONFIG_DIR) + 2);
|
|
sprintf(rfilename, "%s/%s", ast_config_AST_CONFIG_DIR, config);
|
|
}
|
|
if (access(rfilename,R_OK) != 0) {
|
|
ast_log(LOG_NOTICE, "File %s not found; AEL declining load\n", rfilename);
|
|
return AST_MODULE_LOAD_DECLINE;
|
|
}
|
|
|
|
parse_tree = ael2_parse(rfilename, &errs);
|
|
ast_debug(1, "AEL load process: parsed config file name '%s'.\n", rfilename);
|
|
ael2_semantic_check(parse_tree, &sem_err, &sem_warn, &sem_note);
|
|
if (errs == 0 && sem_err == 0) {
|
|
ast_debug(1, "AEL load process: checked config file name '%s'.\n", rfilename);
|
|
local_table = ast_hashtab_create(11, ast_hashtab_compare_contexts, ast_hashtab_resize_java, ast_hashtab_newsize_java, ast_hashtab_hash_contexts, 0);
|
|
if (ast_compile_ael2(&local_contexts, local_table, parse_tree)) {
|
|
ast_log(LOG_ERROR, "AEL compile failed! Aborting.\n");
|
|
destroy_pval(parse_tree); /* free up the memory */
|
|
return AST_MODULE_LOAD_DECLINE;
|
|
}
|
|
ast_debug(1, "AEL load process: compiled config file name '%s'.\n", rfilename);
|
|
|
|
ast_merge_contexts_and_delete(&local_contexts, local_table, registrar);
|
|
local_table = NULL; /* it's the dialplan global now */
|
|
local_contexts = NULL;
|
|
ast_debug(1, "AEL load process: merged config file name '%s'.\n", rfilename);
|
|
for (con = ast_walk_contexts(NULL); con; con = ast_walk_contexts(con))
|
|
ast_context_verify_includes(con);
|
|
ast_debug(1, "AEL load process: verified config file name '%s'.\n", rfilename);
|
|
} else {
|
|
ast_log(LOG_ERROR, "Sorry, but %d syntax errors and %d semantic errors were detected. It doesn't make sense to compile.\n", errs, sem_err);
|
|
destroy_pval(parse_tree); /* free up the memory */
|
|
return AST_MODULE_LOAD_DECLINE;
|
|
}
|
|
destroy_pval(parse_tree); /* free up the memory */
|
|
|
|
return AST_MODULE_LOAD_SUCCESS;
|
|
}
|
|
|
|
/* CLI interface */
|
|
static char *handle_cli_ael_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
|
{
|
|
switch (cmd) {
|
|
case CLI_INIT:
|
|
e->command = "ael set debug {read|tokens|macros|contexts|off}";
|
|
e->usage =
|
|
"Usage: ael set debug {read|tokens|macros|contexts|off}\n"
|
|
" Enable AEL read, token, macro, or context debugging,\n"
|
|
" or disable all AEL debugging messages. Note: this\n"
|
|
" currently does nothing.\n";
|
|
return NULL;
|
|
case CLI_GENERATE:
|
|
return NULL;
|
|
}
|
|
|
|
if (a->argc != e->args)
|
|
return CLI_SHOWUSAGE;
|
|
|
|
if (!strcasecmp(a->argv[3], "read"))
|
|
aeldebug |= DEBUG_READ;
|
|
else if (!strcasecmp(a->argv[3], "tokens"))
|
|
aeldebug |= DEBUG_TOKENS;
|
|
else if (!strcasecmp(a->argv[3], "macros"))
|
|
aeldebug |= DEBUG_MACROS;
|
|
else if (!strcasecmp(a->argv[3], "contexts"))
|
|
aeldebug |= DEBUG_CONTEXTS;
|
|
else if (!strcasecmp(a->argv[3], "off"))
|
|
aeldebug = 0;
|
|
else
|
|
return CLI_SHOWUSAGE;
|
|
|
|
return CLI_SUCCESS;
|
|
}
|
|
|
|
static char *handle_cli_ael_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
|
{
|
|
switch (cmd) {
|
|
case CLI_INIT:
|
|
e->command = "ael reload";
|
|
e->usage =
|
|
"Usage: ael reload\n"
|
|
" Reloads AEL configuration.\n";
|
|
return NULL;
|
|
case CLI_GENERATE:
|
|
return NULL;
|
|
}
|
|
|
|
if (a->argc != 2)
|
|
return CLI_SHOWUSAGE;
|
|
|
|
#ifndef STANDALONE
|
|
/* Lock-Protected reload. It is VERY BAD to have simultaneous ael load_module() executing at the same time */
|
|
return ast_module_reload("pbx_ael") == AST_MODULE_RELOAD_SUCCESS ? CLI_SUCCESS : CLI_FAILURE;
|
|
#else
|
|
/* Lock-Protected reload not needed (and not available) when running standalone (Example: via aelparse cli tool). No reload contention is possible */
|
|
return (pbx_load_module() ? CLI_FAILURE : CLI_SUCCESS);
|
|
#endif
|
|
}
|
|
|
|
static struct ast_cli_entry cli_ael[] = {
|
|
AST_CLI_DEFINE(handle_cli_ael_reload, "Reload AEL configuration"),
|
|
AST_CLI_DEFINE(handle_cli_ael_set_debug, "Enable AEL debugging flags")
|
|
};
|
|
|
|
static int unload_module(void)
|
|
{
|
|
ast_context_destroy(NULL, registrar);
|
|
ast_cli_unregister_multiple(cli_ael, ARRAY_LEN(cli_ael));
|
|
#ifndef STANDALONE
|
|
ast_unregister_application(aelsub);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int load_module(void)
|
|
{
|
|
ast_cli_register_multiple(cli_ael, ARRAY_LEN(cli_ael));
|
|
#ifndef STANDALONE
|
|
ast_register_application_xml(aelsub, aelsub_exec);
|
|
#endif
|
|
return (pbx_load_module());
|
|
}
|
|
|
|
static int reload(void)
|
|
{
|
|
/* Lock-Protected reload not needed because we're already being called from ast_module_reload() */
|
|
return pbx_load_module();
|
|
}
|
|
|
|
#ifdef STANDALONE
|
|
#define AST_MODULE "ael"
|
|
int ael_external_load_module(void);
|
|
int ael_external_load_module(void)
|
|
{
|
|
pbx_load_module();
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Asterisk Extension Language Compiler",
|
|
.support_level = AST_MODULE_SUPPORT_EXTENDED,
|
|
.load = load_module,
|
|
.unload = unload_module,
|
|
.reload = reload,
|
|
.requires = "res_ael_share",
|
|
);
|
|
|
|
#ifdef AAL_ARGCHECK
|
|
static const char * const ael_funclist[] =
|
|
{
|
|
"AGENT",
|
|
"ARRAY",
|
|
"BASE64_DECODE",
|
|
"BASE64_ENCODE",
|
|
"CALLERID",
|
|
"CDR",
|
|
"CHANNEL",
|
|
"CHECKSIPDOMAIN",
|
|
"CHECK_MD5",
|
|
"CURL",
|
|
"CUT",
|
|
"DB",
|
|
"DB_EXISTS",
|
|
"DUNDILOOKUP",
|
|
"ENUMLOOKUP",
|
|
"ENV",
|
|
"EVAL",
|
|
"EXISTS",
|
|
"FIELDQTY",
|
|
"FILTER",
|
|
"GROUP",
|
|
"GROUP_COUNT",
|
|
"GROUP_LIST",
|
|
"GROUP_MATCH_COUNT",
|
|
"IAXPEER",
|
|
"IF",
|
|
"IFTIME",
|
|
"ISNULL",
|
|
"KEYPADHASH",
|
|
"LANGUAGE",
|
|
"LEN",
|
|
"MATH",
|
|
"MD5",
|
|
"MUSICCLASS",
|
|
"QUEUEAGENTCOUNT",
|
|
"QUEUE_MEMBER_COUNT",
|
|
"QUEUE_MEMBER_LIST",
|
|
"QUOTE",
|
|
"RAND",
|
|
"REGEX",
|
|
"SET",
|
|
"SHA1",
|
|
"SIPCHANINFO",
|
|
"SIPPEER",
|
|
"SIP_HEADER",
|
|
"SORT",
|
|
"STAT",
|
|
"STRFTIME",
|
|
"STRPTIME",
|
|
"TIMEOUT",
|
|
"TXTCIDNAME",
|
|
"URIDECODE",
|
|
"URIENCODE",
|
|
"VMCOUNT"
|
|
};
|
|
|
|
|
|
int ael_is_funcname(char *name)
|
|
{
|
|
int s,t;
|
|
t = sizeof(ael_funclist)/sizeof(char*);
|
|
s = 0;
|
|
while ((s < t) && strcasecmp(name, ael_funclist[s]))
|
|
s++;
|
|
if ( s < t )
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
#endif
|